首页 > 解决方案 > 根据输入流中的值设置 Lexer 规则

问题描述

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

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会被定义为 char after'Divider'关键字。

无论如何要Divider根据中的值进行设置divider_stat吗?

对于输入:

Divider -
id1 - id2

代币将是:

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

对于输入:

Divider $
id1$id2

代币将是:

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

分隔符始终为 1 个字符

标签: antlr4

解决方案


您可以为此使用词法模式、一些特定于目标的代码和一个predicate。每当词法分析器“看到”关键字"Divider"时,它就会移动到DividerMode只能匹配(并跳过)空格或匹配非空格的位置,这将成为新的分隔符字符。在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 类来测试它:

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;

推荐阅读