从输入流中的值设置 Lexer 规则

Set Lexer rule from value in input stream

我有这个简单的语法文件:

expr         : ID Divider ID;
divider_stat : 'Divider' Divider;

Divider : '#';

ID      : ALPHA ('_' | ALPHA | DIGIT)*;

fragment ALPHA  : [a-zA-Z];
fragment DIGIT  : [0-9];

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

在这种情况下,Divider 是固定的 (#)。但在实际场景中,Divider 将被定义为 'Divider' 关键字后的字符。

有没有根据divider_stat中的值设置Divider

输入:

Divider -
id1 - id2

代币将是:

<ID>,'id1'
<Divider>,'-'
<ID>,'id2'

输入:

Divider $
id1$id2

代币将是:

<ID>,'id1'
<Divider>,'$'
<ID>,'id2'

分隔符始终为 1 个字符

您可以使用 lexical mode for this, a bit of target specific code and a predicate。每当词法分析器“看到”关键字 "Divider" 时,它就会在 DividerMode 中移动,其中只能匹配(并跳过)space 或匹配非 space ,这将成为新的分隔符。在 Divider 词法分析器规则中,您首先检查(使用谓词)流中的下一个字符是否为当前分隔字符。

这是一个小 Java 演示:

DemoLexer.g4

lexer grammar DemoLexer;

@members {
  private char divider = '#';
}

K_Divider : 'Divider' -> skip, pushMode(DividerMode);
Divider   : {_input.LA(1) == divider}? . ;
ID        : ALPHA ('_' | ALPHA | DIGIT)*;

fragment ALPHA  : [a-zA-Z];
fragment DIGIT  : [0-9];

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

mode DividerMode;
 Spaces     : [ \t\r\n]+ -> skip;
 NewDivider : ~[ \t\r\n] {this.divider = getText().charAt(0);} -> skip, popMode;

DemoParser.g4

parser grammar DemoParser;

options {
  tokenVocab=DemoLexer;
}

parse : expr+ EOF;
expr  : ID Divider ID;

还有一个小 Java class 来测试它:

String source =
    "id1 # id2\n" +
    "Divider -\n" +
    "id3 - id4";

DemoLexer lexer = new DemoLexer(CharStreams.fromString(source));
DemoParser parser = new DemoParser(new CommonTokenStream(lexer));
ParseTree root = parser.parse();

System.out.println(root.toStringTree(parser));

将打印:

(parse (expr id1 # id2) (expr id3 - id4) <EOF>)

使用词法模式时,需要将词法分析器和解析器语法文件分开。您也可以使用组合语法,但是您需要一次性匹配 Divider ?

Demo.g4

grammar Demo;

@lexer::members {
  private char divider = '#';
}

parse : expr+ EOF;
expr  : ID Divider ID;

K_Divider : 'Divider' [ \t\r\n]+ ~[ \t\r\n] {this.divider = getText().charAt(getText().length() - 1);} -> skip;
Divider   : {_input.LA(1) == divider}? . ;
ID        : ALPHA ('_' | ALPHA | DIGIT)*;

fragment ALPHA  : [a-zA-Z];
fragment DIGIT  : [0-9];

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