我如何在 Antlr4 的解析树中检测空格?
How can I detect whitespace in my parse tree in Antlr4?
当一条规则在 antlr4 中匹配时,并且您获得了该规则的文本,词法分析器通常会使用
去除空格
WS: [ \n\t\r]+ -> skip;
是否可以在解析树访问者中询问"Did this rule skip over any whitespace?"
例如
WS: [ \n\t\r]+ -> skip;
ALPHA: [a-z];
NUMERIC: [0-9];
myrule: (ALPHA | NUMERIC)+;
然后在访问者中(我用的是C++):
antlrcpp::Any MyVisitor::visitMyrule(dlParser::MyruleContext *ctx) {
if (ctx->didSkipSomeWhitespace()) {
/* There was whitespace */
} else {
/* There was no whitespace */
}
return false;
}
所以:
f56fhj => no whitespace
o9f g66ff o => whitespace
我已经尝试获取令牌的 start/stop 索引,以便我可以将文本长度与进入其中的字符数进行比较,但停止令牌并不总是可用,如果它然后这些值与我期望的索引不一致,并且访问构成令牌的原始输入字符似乎并不简单。
在这种情况下,您不应该 skip
这些 space 标记。这样解析器就不知道它们。相反,您应该将这些 space 令牌放在不同的通道上(例如 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)
;
运行 class:
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;
}
}
将向您的控制台打印以下内容:
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
当一条规则在 antlr4 中匹配时,并且您获得了该规则的文本,词法分析器通常会使用
去除空格WS: [ \n\t\r]+ -> skip;
是否可以在解析树访问者中询问"Did this rule skip over any whitespace?"
例如
WS: [ \n\t\r]+ -> skip;
ALPHA: [a-z];
NUMERIC: [0-9];
myrule: (ALPHA | NUMERIC)+;
然后在访问者中(我用的是C++):
antlrcpp::Any MyVisitor::visitMyrule(dlParser::MyruleContext *ctx) {
if (ctx->didSkipSomeWhitespace()) {
/* There was whitespace */
} else {
/* There was no whitespace */
}
return false;
}
所以:
f56fhj => no whitespace
o9f g66ff o => whitespace
我已经尝试获取令牌的 start/stop 索引,以便我可以将文本长度与进入其中的字符数进行比较,但停止令牌并不总是可用,如果它然后这些值与我期望的索引不一致,并且访问构成令牌的原始输入字符似乎并不简单。
在这种情况下,您不应该 skip
这些 space 标记。这样解析器就不知道它们。相反,您应该将这些 space 令牌放在不同的通道上(例如 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)
;
运行 class:
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;
}
}
将向您的控制台打印以下内容:
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