ANTLR 语法规则的正确排序

ANTLR proper ordering of grammar rules

我正在尝试编写一种将 <<word>> 识别为特殊标记但将 <word> 视为常规文字的语法。

这是我的语法:

grammar test;

doc: item+ ;
item: func | atom ;

func: '<<' WORD '>>' ;
atom: PUNCT+            #punctAtom
    | NEWLINE+          #newlineAtom
    | WORD              #wordAtom
    ;

WS : [ \t] -> skip ;
NEWLINE : [\n\r]+ ;
PUNCT : [.,?!]+ ;
WORD : CHAR+ ;

fragment CHAR : (LETTER | DIGIT | SYMB | PUNCT) ;
fragment LETTER : [a-zA-Z] ;
fragment DIGIT : [0-9] ;
fragment SYMB : ~[a-zA-Z0-9.,?! |{}\n\r\t] ;

所以像 <<word>> 这样的东西会被两个规则匹配,funcatom。我想让它被识别为 func,所以我把 func 规则放在第一位。

当我用 <word> 测试我的语法时,它按预期将其视为 atom。但是,当我测试语法并给它 <<word>> 时,它也将其视为 atom

有什么我遗漏的吗?

PS - 我已将 atom 分成 PUNCTNEWLINEWORD,并给它们标签 #punctAtom#newlineAtom, 和 #wordAtom 因为我想在遍历解析树时区别对待它们中的每一个。此外,WORD 可以包含 PUNCT 因为,例如,有人可以写 "Hello," 并且我想将其视为一个单词(稍后为简单起见)。

PPS - 我试过的一件事是我在最后一条规则中包含了 <>,这是我 "disallowing" 存在于 WORD 中。这解决了一个问题,因为 <<word>> 现在被识别为 func,但它产生了一个新问题,因为 <word> 不再被接受为 atom.

ANTLR 的词法分析器尝试匹配尽可能多的字符,因此 <<WORD>><WORD> 都被词法分析器规则 WORD 匹配。因此,在这些情况下,将不会创建令牌 <<>>(或 <>)。

您可以看到 运行 这些代码行正在创建哪些令牌:

Lexer lexer = new testLexer(CharStreams.fromString("<word> <<word>>"));
CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.fill();

for (Token t : tokens.getTokens()) {
  System.out.printf("%-20s %s\n", testLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
}

这将打印:

WORD                 <word>
WORD                 <<word>>
EOF                  <EOF>

你可以做的是这样的:

func
 : '<<' WORD '>>' 
 ;

atom
 : PUNCT+   #punctAtom
 | NEWLINE+ #newlineAtom
 | word     #wordAtom
 ;

word
 : WORD
 | '<' WORD '>'
 ;

...

fragment SYMB : ~[<>a-zA-Z0-9.,?! |{}\n\r\t] ;

当然,像 foo<bar 这样的东西不会像以前那样变成一个 WORD