我有以下野牛语法(作为更复杂的语法的一部分):
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个移位/减少冲突)。例如,方法声明看起来像变量声明,直到它看到括号。
如何重写此语法以消除这种歧义?
澄清:类主体可以为空,唯一的约束是变量声明在方法声明之前出现(如果有的话)。
答案 0 :(得分:1)
这不是一个含糊不清的问题,它是一个先行问题。问题是你需要3个前瞻标记(直到下一个声明的SEMICOLON
或LPAREN
),解析器才能找出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
;