yacc移位/减少冲突:函数调用与函数定义

yacc移位/减少冲突:函数调用与函数定义,yacc,shift-reduce-conflict,Yacc,Shift Reduce Conflict,我有以下法律定义: [a-zA-Z][a-zA-Z0-9_]* return NAME; \, return COMMA; \: return COLON; \; return SEMICOLON; \( return OPAREN; \) return CPAREN; \+ return PLUS; 以及以下yacc生成规则

我有以下法律定义:

[a-zA-Z][a-zA-Z0-9_]*       return NAME;

\,              return COMMA;
\:              return COLON;
\;              return SEMICOLON;
\(              return OPAREN;
\)              return CPAREN;
\+              return PLUS;
以及以下yacc生成规则:

program: 
    | program statement;

arglist:
    OPAREN CPAREN
    | OPAREN expressionlist CPAREN;

trailed:
    NAME
    | trailed arglist;

expression:
    trailed
    | expression PLUS trailed;

expressionlist:
    expression
    | expressionlist COMMA expression;

statement:
    expression SEMICOLON
    |NAME arglist COLON expression SEMICOLON;
如果我把最后一条规则注释掉,一切都会顺利进行。与上一条规则发生冲突:

yacc: 1 shift/reduce conflict.
所以我猜,yacc无法决定是将下一个符号移到堆栈上,还是使用给定的规则减少堆栈

  • 我的语法含糊不清吗

  • 规则“trailed:trailed arglist”和“statement:NAME arglist冒号表达式分号”之间的决定是否应该没有冲突,因为前者从来没有冒号,而后者总是有

  • 这与前瞻缓冲区的大小有关吗

  • 我如何修正这个语法,将“a(b)();”和“a(b,c):b+c;”都解析为有效语句

  • 如何以更详细的方式回溯冲突

  • ----编辑

    关于MichaelMoser的回答:

    改变

    arglist:
        OPAREN CPAREN
        | OPAREN expressionlist CPAREN;
    
    expressionlist:
        expression
        | expressionlist COMMA expression;
    


    正如我所建议的那样没有帮助。当
    语句的第二条规则处于活动状态时,仍然会出现冲突,一旦对该规则进行注释,就不会出现冲突。

    如果要调试shift/reduce错误,请将--report=all--report file=pars.report添加到bison命令行,生成的pars.report文件会让您明白

    简化语法文件

    %token OPAREN CPAREN NAME PLUS COMMA SEMICOLON COLON %% program: | program statement; arglist: OPAREN CPAREN | OPAREN expressionlist CPAREN; trailed: NAME | trailed arglist; expression: trailed | expression PLUS trailed; expressionlist: expression | expressionlist COMMA expression; statement: expression SEMICOLON |NAME arglist COLON expression SEMICOLON; %标记OPAREN CPAREN名称加逗号分号冒号 %% 节目: |程序语句; arglist: OPAREN CPAREN |OPAREN表达列表; 跟踪: 名称 |跟踪arglist; 表达方式: 跟踪 |表情加拖尾; 表达列表: 表情 |表达式列表逗号表达式; 声明: 表达式分号 |名称arglist冒号表达式分号; 提供以下报告:

    State 3 conflicts: 1 shift/reduce .... state 3 3 arglist: . OPAREN CPAREN 4 | . OPAREN expressionlist CPAREN 5 trailed: NAME . [OPAREN, PLUS, SEMICOLON] 12 statement: NAME . arglist COLON expression SEMICOLON OPAREN shift, and go to state 7 OPAREN [reduce using rule 5 (trailed)] $default reduce using rule 5 (trailed) arglist go to state 8 状态3冲突:1转移/减少 .... 国家3 3 arglist:。OPAREN CPAREN 4 | . OPAREN表达式列表CPAREN 5:姓名。[蛋白石,加号,分号] 12声明:姓名。arglist冒号表达式分号 OPAREN移位,进入状态7 OPAREN[使用规则5(trailed)减少] $default使用规则5减少(拖尾) arglist转到状态8 在报告的开头,有导致错误的解析器状态,随后您可以看到解析状态的详细信息。这个文件也很有趣,因为它实际上解释了解析器在每个步骤中所做的事情

    冲突在于

    statement : NAME arglist COLON expression SEMICOLON; 语句:名称arglist冒号表达式分号; 及

    陈述:表达; 表达方式:拖尾; trailed:NAME | tailed arglist;
    给定源语法:

    %token COLON COMMA CPAREN NAME OPAREN PLUS SEMICOLON
    
    %%
    
    program: 
        | program statement
        ;
    
    arglist:
          OPAREN CPAREN
        | OPAREN expressionlist CPAREN
        ;
    
    trailed:
          NAME
        | trailed arglist
        ;
    
    expression:
          trailed
        | expression PLUS trailed
        ;
    
    expressionlist:
          expression
        | expressionlist COMMA expression
        ;
    
    statement:
          expression SEMICOLON
        | NAME arglist COLON expression SEMICOLON
        ;
    
    我从
    bison-v xyz.y
    中得到的报告给出了
    xyz.y:conflicts:1 shift/reduce
    ,但是
    xyz.output
    中的描述与his中的报告有些不同

    这意味着,当语法读取了一个名称并获取了一个OPAREN时,它无法推断该名称是在
    后面紧跟着
    arglist
    语句中,还是在
    语句中紧跟着
    arglist
    语句中。在到达冒号之前,它无法确定差异,冒号太远了,一个前瞻标记无法确定


    这使得普通Yacc无法解析语法,因为Yacc只能向前看一个标记。(我不确定这是否会使它变得模棱两可或其他什么——“在Yacc中不起作用”涵盖了这种情况。Bison提供了GLR语法,这可能会有所帮助。)

    正如其他人所指出的,问题是,要区分函数定义和函数调用,需要不止一个前瞻标记。所编写的语法的问题在于,当向前看是
    OPAREN
    时,它需要在看到
    NAME
    后决定是减少规则
    trailed:NAME
    还是移位以匹配规则
    语句:NAME arglist冒号表达式分号
    。但是,在看到arglist之后,它才能决定它后面是否有
    冒号(这是区分这两种情况的原因)

    要解决这个问题,您需要重构语法,这样在到达
    冒号之前,就不需要减少只出现在一个选项上的任何内容。使用此语法,您可以通过重构
    trailed
    规则,使其始终至少需要一个arglist,并将不包含
    arglist
    NAME
    作为一个单独的
    表达式
    规则:

    trailed:
        NAME arglist
        | trailed arglist;
    
    expression:
        NAME
        | trailed
        | expression PLUS NAME
        | expression PLUS trailed;
    

    现在,当您获得一个输入时,
    NAME OPAREN…
    还没有必要减少任何内容——您只需切换到匹配
    arglist
    的规则,在匹配
    arglist
    之后,您就可以看到下一个标记,并决定这是函数调用还是函数定义。

    谢谢。那么,当我删除规则
    语句:NAME arglist冒号表达式分号时,为什么arglist不产生冲突呢。如果我在第二条语句规则前面引入另一个标记(例如
    DEF
    ),为什么冲突会被删除。我刚刚尝试了您的尝试。如果我答对了,请查看我问题的编辑。冲突仍然取决于
    语句的定义。冲突发生在
    trailed:NAME
    的减少和移位之间。所以这个问题与两个arglist规则无关,根本不是一个模糊问题——它需要一个以上的前瞻标记。不模糊——它只需要一个以上的前瞻标记。谢谢,我担心这是由于前瞻。我会重构我的语法,看看会发生什么。太好了。我确信这与未来的展望有关。我一到电脑前就要试试。是的,这就成功了。我必须重写很多,但我可以从这里开始工作。再次感谢。
    %token COLON COMMA CPAREN NAME OPAREN PLUS SEMICOLON
    
    %%
    
    program: 
        | program statement
        ;
    
    arglist:
          OPAREN CPAREN
        | OPAREN expressionlist CPAREN
        ;
    
    trailed:
          NAME
        | trailed arglist
        ;
    
    expression:
          trailed
        | expression PLUS trailed
        ;
    
    expressionlist:
          expression
        | expressionlist COMMA expression
        ;
    
    statement:
          expression SEMICOLON
        | NAME arglist COLON expression SEMICOLON
        ;
    
    State 3 conflicts: 1 shift/reduce
    
    …
    
    state 3
    
        5 trailed: NAME .
       12 statement: NAME . arglist COLON expression SEMICOLON
    
        OPAREN  shift, and go to state 7
    
        OPAREN    [reduce using rule 5 (trailed)]
        $default  reduce using rule 5 (trailed)
    
        arglist  go to state 8
    
    trailed:
        NAME arglist
        | trailed arglist;
    
    expression:
        NAME
        | trailed
        | expression PLUS NAME
        | expression PLUS trailed;