我正试图为Answer Set Programming(ASP)的方言编写一个语法分析器,它在语法方面看起来像带有一些扩展的Prolog。例如,一个扩展是扩展,这意味着例如fact(1..3).
在fact(1). fact(2). fact(3).
中扩展。请注意,该语言理解INT
和FLOAT
数字,并使用.
作为终止符。
在某些情况下,解析器无法区分整数、浮点、扩展和分隔符,因为我认为这种语言显然是模糊的。在这种情况下,我必须用空格显式地分隔标记。然而,任何Prolog或ASP解析器都能正确地处理此类生成。我读到ANTLR4可以自动消除有问题的制作的歧义,但可能需要一些帮助,但我不知道该怎么做!;-)我在这里和这里读到了一些类似的东西,但显然他们对我没有帮助
有人能告诉我该怎么克服这种歧义吗?请注意,我不能更改语言,因为它很标准。为了简化专家的工作,我创建了一个最小的工作示例。
grammar Test;
program:
statement* ;
statement: // DOT is the statement terminator
range DOT |
intNum DOT |
floatNum DOT ;
intNum: // not needed, but helps in TestRig
INT;
floatNum: // not needed, but helps in TestRig
FLOAT;
range: // defines an expansion
INT DOTS INT ;
DOTS: '..';
DOT: '.';
FLOAT: DIGIT+ '.' DIGIT* | '.' DIGIT+ ;
INT: DIGIT+ ;
WS: [ trn]+ -> skip ;
fragment NONZERO : [1-9] ;
fragment DIGIT : [0] | NONZERO ;
我使用以下输入:
1 .
1. .
1.5 .
.5 .
1 .. 5 .
1.
1..
1.5.
.5.
1..5.
我得到了以下错误,这些错误被其他工具解析并更正:
line 8:0 extraneous input '1.' expecting '.'
line 11:2 extraneous input '.5' expecting '.'
非常感谢!
在您的DOTS规则之前,为语句终端点添加一个唯一规则,并消除DOTS规则的歧义(并更改其他规则以使用terminal):
TERMINAL: DOT { isTerminal(1) }? ;
DOTS: DOT DOT { !isTerminal(2) }? ;
DOT: '.';
其中谓词方法只是向前看_input字符流,看看在当前令牌索引处,下一个字符是否为空白。在语法中的@member块中放入这样的内容:
public boolean isTerminal(int la) {
int offset = _tokenStartCharIndex + 1 + la;
String s = _input.getText(Interval.of(offset, offset));
if (Character.isWhitespace(s.charAt(0))) {
return true;
}
return false;
}
如果空白在DOTS和后面的INT之间有效,可能需要做更多的工作。
我建议将工作转移到解析器。
如果lexer不能决定1..2
是1.
、.2
还是1 .. 2
,则由解析器决定。
也许有一种语境可以将其解释为第一备选方案,而另一种语境则可以将其理解为第二备选方案。
Btw:1..2.
可被解释为1 .. 2 .
(range
)或1. . 2 .
(floatNum
、intNum
)。你想怎么处理这个问题?
下面的语法应该解析所有内容。但请注意,. .
被视为dots
,而1 . 23
是floatNum
!您可以在解析时或解析后检查这些困难(取决于它是否会影响解析)。
grammar Test;
program:
statement* ;
statement: // DOT is the statement terminator
range DOT |
intNum DOT |
floatNum DOT ;
intNum: // not needed, but helps in TestRig
INT;
floatNum:
INT DOT INT? | DOT INT ;
range: // defines an expansion
INT dots INT ;
dots : DOT DOT;
DOT: '.';
INT: DIGIT+ ;
WS: [ trn]+ -> skip ;
fragment NONZERO : [1-9] ;
fragment DIGIT : [0] | NONZERO ;
Prolog不接受1.
作为浮点值。这个功能会让你的语法变得更加模糊,所以也许可以尝试删除这个功能。