Lexer用于处理带行号前缀的行

时间:2014-04-01 14:35:49

标签: antlr antlr4

我正在编写一个解析器,其语言如下所示:

L00<<identifier>>
L10<<keyword>>
L250<<identifier>>
<<identifier>>

也就是说,每行可能会也可能不会以Lxxx..形式的行号开头(&#39; L&#39;后跟一个或多个数字),后跟一个标识符或关键字。标识符为标准[a-zA-Z_][a-zA-Z0-9_]*L后面的位数不固定。行号和后面的identifer / keyword之间的空格是可选的(在大多数情况下不存在)。

我目前的词法分析器如下:

// Parser rules
commands      : command*;
command       : LINE_NUM? keyword NEWLINE
              | LINE_NUM? IDENTIFIER NEWLINE;
keyword       : KEYWORD_A | KEYWORD_B | ... ;

// Lexer rules
fragment INT  : [0-9]+;
LINE_NUM      : 'L' INT;
KEYWORD_A     : 'someKeyword';
KEYWORD_B     : 'reservedWord';
...
IDENTIFIER    : [a-zA-Z_][a-zA-Z0-9_]*

然而,这导致所有以LINE_NUM标记开头的行被标记为IDENTIFIER s。

有没有办法使用ANTLR语法正确地标记这个输入?

1 个答案:

答案 0 :(得分:1)

您需要向IDENTIFIER添加语义谓词:

IDENTIFIER
  : {_input.getCharPositionInLine() != 0
      || _input.LA(1) != 'L'
      || !Character.isDigit(_input.LA(2))}?
    [a-zA-Z_] [a-zA-Z0-9_]*
  ;

您还可以使用词法模式来避免语义谓词。

//
// Default mode is active at the beginning of a line
//

LINE_NUM
  : 'L' [0-9]+ -> pushMode(NotBeginningOfLine)
  ;

KEYWORD_A : 'someKeyword' -> pushMode(NotBeginningOfLine);
KEYWORD_B : 'reservedWord' -> pushMode(NotBeginningOfLine);
IDENTIFIER
  : ( 'L'
    | 'L' [a-zA-Z_] [a-zA-Z0-9_]*
    | [a-zA-KM-Z_] [a-zA-Z0-9_]*
    )
    -> pushMode(NotBeginningOfLine)
  ;
NL : ('\r' '\n'? | '\n');

mode NotBeginningOfLine;

  NotBeginningOfLine_NL : ('\r' '\n'? | '\n') -> type(NL), popMode;
  NotBeginningOfLine_KEYWORD_A : KEYWORD_A -> type(KEYWORD_A);
  NotBeginningOfLine_KEYWORD_B : KEYWORD_B -> type(KEYWORD_B);
  NotBeginningOfLine_IDENTIFIER
    : [a-zA-Z_] [a-zA-Z0-9_]* -> type(IDENTIFIER)
    ;