ANTLR4 - 使 space 在标记之间可选

ANTLR4 - Make space optional between tokens

我有以下语法:

grammar Hello;

prog:   stat+ EOF;

stat:   DELIMITER_OPEN expr DELIMITER_CLOSE;
expr:   NOTES COMMA value=VAR_VALUE #delim_body;

VAR_VALUE:  ANBang*;
NOTES:  WS* 'notes' WS*;
COMMA:  ',';
DELIMITER_OPEN: '<<!';
DELIMITER_CLOSE:    '!>>';

fragment ANBang:    AlphaNum | Bang;
fragment AlphaNum:  [a-zA-Z0-9];
fragment Bang:  '!';

WS    : [ \t\r\n]+ -> skip ;

正在解析以下作品:

<<! notes, Test !>>

并且变量值为 "Test",但是,当我删除 DELIMITER_OPEN 和注释之间的 space 时,解析器失败:

<<!notes, Test !>>

line 1:3 mismatched input 'notes' expecting NOTES

这是 又一个 词法分析器规则排序不当的案例。

当词法分析器扫描下一个标记时,它首先尝试找到匹配最长标记的规则。如果多个规则匹配,它将通过选择定义顺序中的 第一个 来消除歧义。

<<! notes, Test !>> 将被标记为:
DELIMITER_OPEN NOTES COMMA VAR_VALUE WS DELIMITER_CLOSE

这是因为 NOTES 规则可以匹配以下内容:

<<! notes, Test !>>
   \____/

其中 包括 空格。如果删除它:

<<!notes, Test !>>

那么NOTESVAR_VALUE规则都可以匹配文本notes,并且,VAR_VALUE定义在在语法中,所以它获得优先权。标记化是:
DELIMITER_OPEN VAR_VALUE COMMA VAR_VALUE WS DELIMITER_CLOSE
它不符合您的 expr 规则。

像这样更改您的规则以解决问题:

NOTES:  'notes';
VAR_VALUE:  ANBang+;

WS* 添加到其他规则没有多大意义,因为 WS 被跳过了。并且将标记声明为具有可能的零宽度 * 也是没有意义的,因此请改用 +。最后,重新排序 规则,以便最具体的匹配拳头。

这样,notes 就变成了语法中的 关键字 。如果您不希望它成为关键字,请完全删除 NOTES 规则,并使用带有谓词的 VAR_VALUE 规则。或者,您可以使用词法分析器模式。