yacc/bison的最大咀嚼规则——似乎是最小咀嚼

yacc/bison的最大咀嚼规则——似乎是最小咀嚼,bison,yacc,Bison,Yacc,我试图在大型/复杂语法中引入一种新的语言结构。我知道这会使语法变得模棱两可,但我希望用“最大咀嚼”规则来解决这个问题。也就是说,把我的构造放在第一位,所以它是按优先顺序取的。我取得了一些成功,但树上的其他部分没有那么多。考虑: ( 1, 2, 3 ) // triple ( 4, 5 ) // twople ( 6 ) // *not* a oneple, see below () //

我试图在大型/复杂语法中引入一种新的语言结构。我知道这会使语法变得模棱两可,但我希望用“最大咀嚼”规则来解决这个问题。也就是说,把我的构造放在第一位,所以它是按优先顺序取的。我取得了一些成功,但树上的其他部分没有那么多。考虑:

( 1, 2, 3 )          // triple
( 4, 5 )             // twople
( 6 )                // *not* a oneple, see below
()                   // zerople

(7 + 8) * 9          // single parens used to override usual precedence
( (( 7 + 8 )) * 9 )  // currently this means the same, but nobody would write that!

(( 6 ))              // I want to be a oneple
( ( 6 ) )            // this also a oneple
( ( ( 6 ) ) )        // this also a oneple with a superfluous pair of parens, ambiguous
((  ( 6 )  ))        // superflous parens are innermost
(  (( 6 ))  )        // superfluous parens are outermost

((7 + 8) * (9 + 10)) // not a oneple, despite the (( ... ))
这三个例子在三对括号中有6个是模棱两可的,我不在乎语法取哪一个,它们在语义上是相同的。根据“最大咀嚼”规则,应该取三者的中间部分,即将最里面的部分视为多余

lexer将每个标记作为单独的标记。目前,解析器将6视为等效于6,其中6被解析为expr/int,也就是说,在我尝试更改它之前,语法做了什么

语法有许多由其他标记定义的标记级别。某些上下文中的某些表达式识别双参数OK。其他人则没有这么多,很难将其归结为一个合理规模的例子。有没有一般的方法可以说服野牛吃最多的东西

补充:这种歧义与第一种使用BNF:ALGOL 60的语言中著名的歧义相似

if cond1    then 
  if cond2  then blah2
 else            blah3;      // is this on cond2 False or cond1 False?
这是通过说else连接到最里面的if/then而解决的,if/then还没有得到else,也就是说,在本例中,cond2为False;离开cond1时没有其他分支。谢天谢地,阿尔戈尔没有越位规则

Addit2:好的,根据大众的要求,在我开始修改之前的yacc代码现在你会希望你从来没有问过。例如,这是在规则aexp中工作,中间行是我的mod

  | '(' exp ')'         {$$ = gc3($2);}
  | '(' '(' exp ')' ')'     {$$ = gc5(buildTuple(cons($3,NIL)));}   /* ADC Apr-2020  */
  | '(' exps2 ')'       {$$ = gc3(buildTuple($2));}
这一行不起作用-在规则a中,pat一直被解析为多括号的普通模式

apat      : NUMLIT          {$$ = $1;}
  | var             {$$ = $1;}
  | '(' '(' pat ')' ')'     {$$ = gc5(buildTuple(singleton($3)));}  /* ADC Apr-2020 */
  | apat_vI         {$$ = $1;}
  ;
我试着从帕特那里把这条线插到树上下的各种地方;我甚至试着在同一时间把它放在多部作品中,这似乎很不可靠。解析器似乎总是忽略它


谢谢@Chris Dodd的回答。我认为,尽管出现了这种移位/减少错误,但我已经阅读了其他SO答案,认为bison“做了正确的事情”——也就是说,如果你把一个产品放在另一个产品之上,它会优先考虑这个问题。

所以你没有显示你的代码,但我猜你在尝试类似的事情:

%token NUM NAME
%left '+' '-'
%left '*' '/'
%nonassoc PREFIX
%%

expr: expr '+' expr
    | expr '-' expr
    | expr '*' expr
    | expr '/' expr
    | '-' expr %prec PREFIX
    | '(' expr ')'   /* parentheses for grouping */
    | NUM
    | NAME
    | '(' exprlist ',' expr ')' /* tuple */
    | '(' '(' expr ')' ')'      /* one-ple */
    ;

exprlist: expr | exprlist ',' expr ;
这种语法是不明确的,因此有一个移位/减少冲突。但是默认的prefershift-over-reduce解析将导致使用一个ple规则而不是两次圆括号规则对其进行解析


但是,如果您添加%left指令,它将以相反的方式解决此问题,并报告由于一个ple规则冲突而无效的规则

我已修复此问题;这与%left/%right优先级无关

注意,在Addit2:代码中,两个规则中出现了相同的非终端pat,这可能是不明确的;区分它们的终端也表现为单对排列和双对排列。然而,在有问题的示例中,“竞争”非终端的名称不同;没有终端

在apat的那些规则中,NUMLIT不能以开头,var.apat_vI也不能以开头,所以将pat的这个规则移到其中。看看我链接到的回购协议,如果你在家里也跟着

但仅仅将规则移入apat_vI是不够的。以下是关键的几行:

  | '(' pat_npk ')'     {$$ = gc3($2);}   
  | '(' npk ')'         {$$ = gc3($2);}                  
/*    | '(' '(' pat ')' ')'     {$$ = gc5(buildTuple(singleton($3)));}      /*   ignored */
  | '(' '(' pat_npk ')' ')'     {$$ = gc5(buildTuple(singleton($3)));}  /* same non-term */
与pat的注释行是我移动的,但它不能解决歧义。并且不会减少轮班次数/减少冲突

看看pat的作品,他们是npk或pat_npk。在这种情况下,npk不能从开始-所以忽略它;帕特·恩普克可能会。因此,将pat更改为pat_npk,这会将移位/减少冲突减少2

6现在的解析方式与6相同,与裸6不同。以下是使用以下语言的会话:

Main> let foo x = (( x )) in foo 6           -- (( x )) in exp always worked
(( 6 ))
Main> let bar (( x )) = x in bar (( 6 ))     -- (( x )) in pat got treated as bare x
6
Main> let bar (( x )) = x in bar (( (6) ))   -- three pairs of parens treated as a double
6
Main> let bar (( x )) = x in bar (( ((6)) )) -- four pairs of parens treated as two doubles
(( 6 ))
关键特征是:

bison必须能够看到apat_vI的规则中有两个产品包含相同的非终端pat_npk;和 它们的上下文因一对帕伦而不同。 然后,当在传入的令牌字符串中遇到一个标记时,解析器将移动并等待查看是否得到另一个标记。
就像在if/else冲突中一样:鼓励解析器移动第一个else,然后等待它是否得到另一个;这是词汇分析的一个特点。这在CFG中不是一个真正有意义的概念。谢谢。好的,lexer将每个标记作为单独的标记。我想让语法把两个放在一起,前提是它们和一个结束的两个成对。如果这不是“最大咀嚼”,还有其他术语吗?你没有展示你的语法,所以不可能说出你做了什么。但是,如果您希望在不同的情况下被视为一个或两个令牌,这取决于将来的令牌,那么您将需要不止一个令牌前瞻。你可能是一个
我们可以用bison的glr模式做一些事情,但是传统的yacc无法做任何事情。为什么你认为三个括号内有6的三个例子是不明确的?他们不是。这些例子中的所有括号都是多余的,而不仅仅是一对。不清楚你在问什么。展示你的语法。既然没有语法提示,就没有什么“模棱两可”的,我不想像你说的那样用相反的方式来解决。所以剩下的%没有帮助。所以这不是答案。根据这里排名第一的答案,我必须注意在模棱两可的产品中出现的终端本身。事实上,在这些案例中,他们确实做到了。但我已经改变了那些没有出现的规则,这也没什么好的。嗯,如果你不想这样做,就不要使用%left。如果你所拥有的东西不起作用,那可能是因为你拥有它或同等的东西,你只需要移除它。但是当你拒绝透露你语法的秘密时,没有人能帮你更多。根据这里的说法,%左vs%右是指符号在其他非终端之间出现两次或更多中缀。那不是我所拥有的。我现在已经展示了我的整个解析器。