我正在尝试为简单的编程语言创建ANTLR语法。
它有C-like if语句:
def question(letter):
return input(letter + '? ')
a = question('a')
print(a)
似乎工作正常,但当我使用program
: statement* EOF
;
statement
: block # blockStatement
| SEMI # emptyStatement
| assignment # assignmentStatement
| declaration # variableDeclarationStatement
| 'if' parExpression ifBody=statement ('else' elseBody=statement)? # ifStatement
..........
;
block
: '{' statement* '}'
;
expression
: literal # literalExpression
| Identifier # variableReference
..........
;
parExpression : '(' expression ')';
assignment : Identifier assignmentOp expression SEMI;
SEMI : ';';
Identifier : (LETTER | '_') (LETTER | DIGIT | '_')* ;
时,我会收到错误
DiagnosticErrorListener
代码如此
reportAttemptingFullContext d=1 (statement), input='else', Line 3:0
reportContextSensitivity d=1 (statement), input='else', Line 3:0
reportAttemptingFullContext d=1 (statement), input='else', Line 5:0
reportContextSensitivity d=1 (statement), input='else', Line 5:0
我不确定我是否理解这里的错误,但正如我在其他情况下所理解的那样(例如if (flag1)
x = 42;
else if (flag2)
x = 43;
else
x = 44;
),这种语法可能含糊不清。
我该如何解决?
答案 0 :(得分:4)
这称为悬空其他问题。 解析文本:
if (flag1)
if (flag2) x=2;
else x=3;
可以通过两种方式匹配你的语法:
if (flag1)
if (flag2) x=2;
else x=3; // belongs to if (flag2)
和
if (flag1)
if (flag2) x=2;
else x=3; // belongs to if (flag1)
因为你使else子句成为可选匹配。这意味着语法规则提供了一个模糊的匹配,这是你从ANTLR得到的抱怨。
你想要的是强迫else匹配最近的unclosed if语句;这是大多数编程语言中if ... else的解释。
您必须修改语句解析规则:
statement
: non_if_statement
| if_statement
;
if_statement
: 'if' parExpression
ifBody= ( non_if_statement 'else' elseBody=statement
| if_statement )
;
non_if_statement
:block
| SEMI
| assignment
| declaration
..........
;
写这有点尴尬但应该有用。
许多解析器生成器允许您强制转换"遇到一个令牌。如果您强制转换原始语法中的else关键字,您将获得相同的效果。我不知道怎么说对于ANTLR,如果确实可以的话。
[Lischke说如果你忽略这个错误,你可能会用你原来的语法得到正确的结果。我认为他是对的;那是因为解析器生成器被迫选择两种解释中的一种作为它所接受的解释。]
答案 1 :(得分:0)
看起来这个语义谓词可以在ANTLR中用来避免这个警告:
'if' parExpression ifBody=statement ('else' elseBody=statement | {_input.LA(1) != ELSE}?)
(来自这个答案https://stackoverflow.com/a/21416927/964478)
另据我自https://github.com/antlr/antlr4/issues/42了解,自2013年以来存在更好的方法:ifStmt : 'if' ID stmt (options{sll=true;} : 'else' stmt | );
但它对我不起作用。