ANTLR (Lexer):将任意标识符与关键字分开

ANTLR (Lexer): separate arbitrary identifiers from keywords

我正在尝试为 bat/cmd 文件(用于语法着色)创建一个(简单的)词法分析器。作为此任务的一部分,我需要将关键字与(任意)标识符分开。但是根据 ANTLR 试图让最长的比赛胜过较短的比赛。到目前为止我的语法看起来像这样

lexer grammar CmdLexer;

Identifier
    : IdentifierNonDigit
      (  IdentifierNonDigit
      |  Digit
      )+
    ;

Number
    : Digit+
    ;

fragment IdentifierNonDigit
    : [a-zA-Z_\u0080-\uffff]
    ;

fragment Digit
    : [0-9]
    ;

Punctuation
    : [\u0021-\u002f\u003a-\u0040\u005b-\u0060\u007b-\u007f]+
    ;

Keyword
    : A P P E N D
    | A T
    | A T T R I B
    | B R E A K
    | C A L L
    | C D
    | C H C P
    | C H D I R
    | C L S
    | C O L O R
    | C O P Y
    | D A T E
    | D E L
    | D I R
    | D O
    | E C H O
    | E D I T
    | E N D L O C A L
    | E Q U
    | E X I S T
    | E X I T
    | F C
    | F O R
    | F T Y P E
    | G O T O
    | G E Q
    | G T R
    | I F
    | I N
    | L E Q
    | L S S
    | M D
    | M K D I R
    | M K L I N K
    | M O R E
    | M O V E
    | N E Q
    | N O T
    | N U L
    | P A T H
    | P A U S E
    | P O P D
    | P U S H D
    | R D
    | R E N
    | R E N A M E
    | S E T
    | S E T L O C A L
    | S H I F T
    | S T A R T
    | T I T L E
    | T R E E
    | T Y P E
    | W H E R E
    | W H O A M I
    | X C O P Y
    ;

fragment A:('a'|'A');
fragment B:('b'|'B');
fragment C:('c'|'C');
fragment D:('d'|'D');
fragment E:('e'|'E');
fragment F:('f'|'F');
fragment G:('g'|'G');
fragment H:('h'|'H');
fragment I:('i'|'I');
fragment J:('j'|'J');
fragment K:('k'|'K');
fragment L:('l'|'L');
fragment M:('m'|'M');
fragment N:('n'|'N');
fragment O:('o'|'O');
fragment P:('p'|'P');
fragment Q:('q'|'Q');
fragment R:('r'|'R');
fragment S:('s'|'S');
fragment T:('t'|'T');
fragment U:('u'|'U');
fragment V:('v'|'V');
fragment W:('w'|'W');
fragment X:('x'|'X');
fragment Y:('y'|'Y');
fragment Z:('z'|'Z');

Whitespace
    : [ \t]+
      -> skip
    ;

Newline
    : ( '\r' '\n'?
      | '\n'
      )
      -> skip
    ;

LineComment
    : ( '@'? R E M ~[\r\n]*
      | '@'? '::' ~[\r\n]*
      )
      -> skip
    ;

但它始终匹配 Identifier 中的所有内容,甚至像 appendCALL 这样的词。我在这里看不到模式如何解决这个问题,但是如何给某个规则更高的优先级(这里 Keyword 高于另一个(这里 Identifier)?

But according to this answer ANTLR tries to let the longest match win over shorter ones.

确实如此,这应该是您想要的。请注意,此规则(所谓的最大咀嚼规则)与 append 是否作为关键字或标识符匹配无关。与appendix是否作为关键字append匹配,后跟标识符ix有关;或作为单个标识符 appendix。由于后者在大多数情况下显然是人们想要的,因此最大咀嚼规则很有用。

但在这种情况下重要的是如果多个规则产生相同长度的匹配会发生什么。在那种情况下,ANTLR 应用首先在语法中定义的规则。因此,如果您更改定义的顺序,使 Keyword 出现在 Identifier 之前,则 Keyword 规则将在两个规则产生相同长度的匹配(并且在不是这种情况的情况下,最长的比赛仍然会获胜)。因此,像 append appendix 这样的输入将被标记为关键字 append,后跟标识符 appendix,这应该是您想要的。

PS:我不确定 where/how 你的词法分析器是否会被使用,但通常你会想要区分不同的关键字,而不是拥有一个匹配所有关键字的规则。如果标记将用作解析器的输入,那么在不知道它是哪个关键字的情况下,关于某物是关键字的信息不是很有用。