问题描述
我正在和Antlr玩耍,设计一种玩具语言,我认为这是大多数人开始的地方! -我有一个问题,关于如何最好地考虑开启令牌类型。
考虑一种语言中的“函数调用”,在该语言中函数可以使用字符串,数字或变量-例如下面的示例(project()
是函数调用)
project("ABC")
vs project(123)
vs project($SOME_VARIABLE)
我的语法中有一个运算符,所以语法可以解析正确的内容,但是在访问者代码中,最好分辨出上述三个版本之间的区别。
@Override
public ASTRoot visitCreateproj(projectmgmtParser.CreateprojContext ctx) {
try {
s1 = ctx.STRING_LIteraL().getText();
}catch(Exception e){}
try{
s2 = ctx.NUM().getText();
}catch(Exception e){}
System.out.println("Created Project via => " + ctx.getChild(1).toString());
}
上面的代码有效,具体取决于s1
或s2
是否为空,我可以推断出我如何被调用(使用文字或数字,我没有在上面显示变量的大小写) ,但我对是否有更好或更优雅的方式感兴趣-例如,在访问者代码中启用令牌类型以实际处理该语言。
我上面的语法是
createproj: 'project('WS?(STRING_LIteraL|NUM)')';
当我使用intellij antlr插件时,似乎知道project()
函数的参数的令牌类型-但我似乎无法从我的代码中获取它。
解决方法
您可以执行以下操作:
createproj
: 'project' '(' WS? param ')'
;
param
: STRING_LITERAL
| NUM
;
以及您的访客代码:
@Override
public ASTRoot visitCreateproj(projectmgmtParser.CreateprojContext ctx) {
switch(ctx.param().start.getType()) {
case YourLexerName.STRING_LITERAL:
...
case YourLexerName.NUM:
...
...
}
}
因此,通过在我原来的语法中插入标记,我失去了在访问者代码中检查标记的机会了?
不是,您也可以这样:
createproj
: 'project' '(' WS? param_token=(STRING_LITERAL | NUM) ')'
;
然后可以执行以下操作:
@Override
public ASTRoot visitCreateproj(projectmgmtParser.CreateprojContext ctx) {
switch(ctx.param_token.getType()) {
case YourLexerName.STRING_LITERAL:
...
case YourLexerName.NUM:
...
...
}
}
只需确保在集合param_token=( ... )
中不要混合使用词法分析器规则(令牌)和解析器规则。当它是解析器规则时,ctx.param_token.getType()
将失败(然后必须为ctx.param_token.start.getType()
)。这就是为什么我建议添加一个额外的解析器规则的原因,因为这样仍然可以:
param
: STRING_LITERAL
| NUM
| some_parser_rule
;