如何在 Antlr4 的解析树中检测空格?

izb*_*izb 1 c++ grammar parsing antlr antlr4

当一个规则在 antlr4 中匹配,并且您获得该规则的文本时,词法分析器通常会删除空格

WS: [ \n\t\r]+ -> skip;
Run Code Online (Sandbox Code Playgroud)

是否可以在解析树访问者中询问“此规则是否跳过了任何空格?”

例如

WS: [ \n\t\r]+ -> skip;
ALPHA: [a-z];
NUMERIC: [0-9];

myrule: (ALPHA | NUMERIC)+;
Run Code Online (Sandbox Code Playgroud)

然后在访问者中(我使用的是 C++):

antlrcpp::Any MyVisitor::visitMyrule(dlParser::MyruleContext *ctx) {
    if (ctx->didSkipSomeWhitespace()) {
        /* There was whitespace */
    } else {
        /* There was no whitespace */
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

所以:

f56fhj => no whitespace
o9f g66ff o => whitespace
Run Code Online (Sandbox Code Playgroud)

我尝试获取标记的开始/停止索引,以便可以将文本长度与进入其中的字符数进行比较,但停止标记并不总是可用,如果是,则值不可用与我期望的索引对齐,并且访问形成令牌的原始输入字符似乎并不简单。

Bar*_*ers 6

在这种情况下,您不应该使用skip这些空格标记。这样解析器就不知道它们了。相反,您应该将这些空格标记放在不同的通道上(HIDDEN例如)。这样,解析器不使用这些HIDDEN令牌,但令牌存在于令牌流中并且可以在您的代码中访问。

Java 中的快速演示(我没有运行 C++):

grammar IntList;

list
 : '[' ( list_item ( ',' list_item )* )? ']' EOF
 ;

list_item
 : INT
 ;

INT
 : '0'
 | [1-9] [0-9]*
 ;

SPACES
 : [ \t\f\r\n] -> channel(HIDDEN)
 ;
Run Code Online (Sandbox Code Playgroud)

运行类:

import org.antlr.v4.runtime.*;

public class Main {

  public static void main(String[] args) {

    String source = "[1,    2,3,\t4,5]";

    IntListLexer lexer = new IntListLexer(CharStreams.fromString(source));
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    IntListParser parser = new IntListParser(tokens);

    new SpaceInspectionVisitor(tokens).visit(parser.list());
  }
}

class SpaceInspectionVisitor extends IntListBaseVisitor<Object> {

  private final CommonTokenStream tokens;

  SpaceInspectionVisitor(CommonTokenStream tokens) {
    this.tokens = tokens;
  }

  @Override
  public Object visitList_item(IntListParser.List_itemContext ctx) {
    Token previous = tokens.get(ctx.start.getTokenIndex() - 1);
    System.out.printf("token: '%s', previous == SPACES: %s\n", ctx.getText(), previous.getType() == IntListLexer.SPACES);
    return null;
  }
}
Run Code Online (Sandbox Code Playgroud)

将以下内容打印到您的控制台:

token: '1', previous == SPACES: false
token: '2', previous == SPACES: true
token: '3', previous == SPACES: false
token: '4', previous == SPACES: true
token: '5', previous == SPACES: false
Run Code Online (Sandbox Code Playgroud)