来自ANTLR4中列表的令牌的非贪婪匹配

时间:2016-08-04 14:17:27

标签: antlr4

a previous question with a simple grammar中,我学会了处理可以包含关键字列表中的关键字的ID。我的实际语法稍微复杂一些:在不同类型的句子中有几个关键词列表。这是我尝试用简单的语法讲述故事:

grammar Hello;

file     : ( fixedSentence )* EOF ;
sentence : KEYWORD1 ID+ KEYWORD2 ID+ PERIOD
         | KEYWORD3 ID+ KEYWORD4 ID+ PERIOD;

KEYWORD1 : 'hello' | 'howdy' | 'hi' ;
KEYWORD2 : 'bye' | 'goodbye' | 'adios' ;
KEYWORD3 : 'dear' | 'dearest' ;
KEYWORD4 : 'love' | 'yours' ;
PERIOD  : '.' ;
ID      : [a-z]+ ;
WS      : [ \t\r\n]+ -> skip ;

所以我想要匹配的句子是,例如:

hello snickers bar goodbye mars bar.
dear peter this is fun yours james.

这很有效。但我也希望匹配包含不希望终止ID +块的关键字的句子。例如

hello hello kitty goodbye my dearest raggedy ann and andy.

hello拳头显示为KEYWORD1,然后仅作为第一个ID +的一部分。按照上述链接问题的示例,我可以这样解决:

// ugly solution:
fixedSentence : KEYWORD1 a=(ID|KEYWORD1|KEYWORD3|KEYWORD4)+ KEYWORD2 b=(ID|KEYWORD1|KEYWORD2|KEYWORD3|KEYWORD4)+ PERIOD
              | KEYWORD3 a=(ID|KEYWORD1|KEYWORD2|KEYWORD3)+ KEYWORD4 b=(ID|KEYWORD1|KEYWORD2|KEYWORD3|KEYWORD4)+ PERIOD;

有效,完全符合我的要求。用我的真实语言,我有数百个关键字列表,用于不同类型的句子,所以如果我尝试这种方法,我肯定会犯这样的错误,当我创建新的用我的语言结构,我必须回去编辑所有其他的。

根据ANTLR4书中的评论示例,从列表中进行非贪婪匹配会更好。所以我尝试了这个

// non-greedy matching concept:
KEYWORD : KEYWORD1 | KEYWORD2 | KEYWORD3 | KEYWORD4 ;
niceID : ( ID | KEYWORD ) ;
niceSentence : KEYWORD1 niceID+? KEYWORD2 niceID+? PERIOD
             | KEYWORD2 niceID+? KEYWORD3 niceID+? PERIOD;

我认为遵循评论模型(例如,在本书的第81页给出):

COMMENT : '/*' .*? '*/' -> skip ;

使用?建议非贪婪。 (虽然示例是词法规则,这会改变其含义吗?)fixedSentence有效,但niceSentence失败。我从哪里开始?

具体而言,解析上面的hello kitty测试句中报告的错误是

测试规则sentence

line 1:6 extraneous input 'hello' expecting ID
line 1:29 extraneous input 'dearest' expecting {'.', ID}

测试规则fixedSentence:没有错误。

测试规则niceSentence

line 1:6 extraneous input 'hello' expecting {ID, KEYWORD}
line 1:29 extraneous input 'dearest' expecting {KEYWORD2, ID, KEYWORD}
line 1:57 extraneous input '.' expecting {KEYWORD2, ID, KEYWORD}

如果查看解析树有帮助,here they are

1 个答案:

答案 0 :(得分:1)

认识到解析器非常适合处理语法,,结构,而不是语义区别。关键字在一个上下文中是否为ID终止符而在另一个上下文中是否在语法上是等价的,本质上是语义的。

处理语义歧义的典型ANTLR方法是创建一个尽可能多地识别结构差异的解析树,然后走树分析每个节点相对于周围节点(在本例中)以解决歧义。 / p>

如果这解析为您的解析器

sentences : ( ID+ PERIOD )* EOF ;
那么你的句子基本上是免费的。更合适的工具可能是NLP库 - 斯坦福有一个很好的工具。

其他

如果您将词法规则定义为

KEYWORD1 : 'hello' | 'howdy' | 'hi' ;
KEYWORD2 : 'bye' | 'goodbye' | 'adios' ;
KEYWORD3 : 'dear' | 'dearest' ;
KEYWORD4 : 'love' | 'yours' ;
. . . .
KEYWORD : KEYWORD1 | KEYWORD2 | KEYWORD3 | KEYWORD4 ;
词法分析器永远不会发出KEYWORD令牌 - '你好'作为KEYWORD1使用和发出,并且永远不会评估KEYWORD规则。由于解析树无法识别令牌的类型(显然),因此它不是很有启发性。转储令牌流以查看词法分析器实际执行的操作

hello    hello    kitty goodbye  my dearest  ...
KEYWORD1 KEYWORD1 ID    KEYWORD2 ID KEYWORD3 ...

如果将KEYWORD规则放在其他规则之前,则词法分析器将仅发出KEYWORD个令牌。

更改为解析器规则

niceID : ( ID | keyword ) ;
keyword : KEYWORD1 | KEYWORD2 | KEYWORD3 | KEYWORD4 ;

将允许这个非常有限的示例工作。