消除歧义

时间:2011-10-22 19:54:34

标签: parsing compiler-construction bison yacc

我有以下野牛语法(作为更复杂的语法的一部分):

classDeclaration : CLASS ID EXTENDS ID LBRACE variableDeclarationList methodDeclarationList RBRACE
                 ;

variableDeclarationList : variableDeclarationList variableDeclaration 
                        | /* empty */
                        ;

variableDeclaration : type ID SEMICOLON
                    ;

type : NATTYPE | ID
     ;

methodDeclarationList : methodDeclarationList methodDeclaration
                      | /* empty */
                      ;

methodDeclaration : type ID LPAREN parameterDeclarationList RPAREN variableExpressionBlock
          ;

应该描述类似的声明:

class foo extends object
{
    nat number;

    nat divide(nat aNumber)
    {
        0;
    }
}

或者这个:

class foo extends object
{
    nat divide(nat aNumber)
    {
        0;
    }
}

或者这个:

class foo extends object
{
}

问题在于变量声明结束和方法声明开始存在歧义(2个移位/减少冲突)。例如,方法声明看起来像变量声明,直到它看到括号。

如何重写此语法以消除这种歧义?

澄清:类主体可以为空,唯一的约束是变量声明在方法声明之前出现(如果有的话)。

4 个答案:

答案 0 :(得分:1)

这不是一个含糊不清的问题,它是一个先行问题。问题是你需要3个前瞻标记(直到下一个声明的SEMICOLONLPAREN),解析器才能找出variableDeclarationList结束的位置,因为它需要减少一个empty methodDeclarationList在开始解析更多methodDeclarations之前。

解决此问题的方法是在方法声明列表的开头删除空减少的需要:

methodDeclarationList : nonEmptyMethodDeclarationList | /*empty */ ;

nonEmptyMethodDeclarationList : nonEmptyMethodDeclarationList methodDeclaration
                              | methodDeclaration
                              ;

有了这个,解析器不需要减少一个空方法DeclarationList,除非根本没有方法 - 在这种情况下,只需要一个前瞻标记来查看RBRACE

答案 1 :(得分:1)

另一种方法是甚至没有空规则,而是使用多个选项,一个使用非终结,一个不使用。

classDeclaration : CLASS ID EXTENDS ID LBRACE RBRACE
                 | CLASS ID EXTENDS ID LBRACE methodDeclarationList RBRACE
                 | CLASS ID EXTENDS ID LBRACE variableDeclarationList RBRACE
                 | CLASS ID EXTENDS ID LBRACE variableDeclarationList methodDeclarationList RBRACE           
                 ;

variableDeclarationList : variableDeclaration 
                        | variableDeclarationList variableDeclaration
                        ;

variableDeclaration : type ID SEMICOLON
                    ;

type : NATTYPE 
     | ID
     ;

methodDeclarationList : methodDeclaration 
                      | methodDeclarationList methodDeclaration
                      ;

methodDeclaration :  type ID LPAREN RPAREN variableExpressionBlock
                  |  type ID LPAREN parameterList RPAREN variableExpressionBlock
                  ;

答案 2 :(得分:0)

答案 3 :(得分:0)

这个稍微修改过的语法有效:

%token CLASS EXTENDS ID LBRACE RBRACE SEMICOLON NATTYPE LPAREN RPAREN DIGIT COMMA 
%%
classDeclaration : CLASS ID EXTENDS ID LBRACE declarationList RBRACE
        ;

declarationList : /* Empty */
        | declarationList declaration
        ;

declaration :   variableDeclaration
        |       methodDeclaration
        ;

variableDeclaration : parameterDeclaration SEMICOLON
        ;

type : NATTYPE | ID
        ;

methodDeclaration : parameterDeclaration LPAREN parameterDeclarationList RPAREN
                    variableExpressionBlock
        ;

variableExpressionBlock : LBRACE DIGIT RBRACE
        ;

parameterDeclarationList : /* empty */
        |   parameterDeclarationList COMMA parameterDeclaration
        ;

parameterDeclaration : type ID
        ;

您可能希望将非终端'parameterDeclaration'重命名为'singleVariableDeclaration',但是通过避免连续两个可能为空的规则(原始的'variableDeclarationList'和'methodDeclarationList',您可以避免歧义。< / p>

这在语法上允许与类的declarationList中的变量交织的方法。如果由于某种原因这是不可接受的,请考虑使其成为语义错误而不是语法错误。如果它必须是语法错误,那么有人将不得不做一些思考;我投票让你做这个想法。


如果你坚持至少一个方法声明,那么语法是明确的:

methodDeclarationList : methodDeclarationList methodDeclaration
        | methodDeclaration /* empty */
        ;

如果对变量声明列表进行相同的尝试,语法仍会有两个S / R冲突。

一种不可完全忽略的可能性是使用Bison功能%expect 2来表示预期会发生2次转换/减少冲突。

%expect 2
%token CLASS EXTENDS ID LBRACE RBRACE SEMICOLON NATTYPE LPAREN RPAREN DIGIT COMMA
%%
classDeclaration : CLASS ID EXTENDS ID LBRACE variableDeclarationList methodDeclarationList RBRACE
        ;

variableDeclarationList : variableDeclarationList variableDeclaration 
        | /* empty */
        ;

variableDeclaration : singleVariableDeclaration SEMICOLON
        ;

type : NATTYPE | ID
        ;

methodDeclarationList : methodDeclarationList methodDeclaration
        | /* empty */
        ;

methodDeclaration : singleVariableDeclaration LPAREN parameterDeclarationList RPAREN variableExpressionBlock
        ;

variableExpressionBlock : LBRACE DIGIT RBRACE
        ;

parameterDeclarationList : /* empty */
        |   parameterDeclarationList COMMA parameterDeclaration
        ;

parameterDeclaration : singleVariableDeclaration
        ;

singleVariableDeclaration : type ID
        ;