我想定义一个语法,该语法应该解析与度量单位有关的单词,例如千克:'kg', 'kg', 'kg', 'kg', 'kg', 'l', '公升','升'等。
我已经使用Java enum
类做了类似的事情来验证应该代表度量单位的输入字符串。
我想知道是否有可能在ANTLR语法文件内的枚举类中重用已经定义的度量单位。基本上,我想在中设置词法分析器。g4语法文件如:
UNITS: UnitMeasures.values()
其中.values()
方法返回UnitMeasures
枚举Java类中的枚举值,这"应该等同于"ANTLR语法词法器:
UNITS: ('kg' | 'KG' | 'kilograms' | 'l' | 'litres' | 'liters' );
原因为什么我试图这样做:
- 我想避免枚举Java类和ANTLR语法文件之间的代码重复;
- 我不能只使用ANTLR和删除枚举Java类,因为它已经在许多不同的地方使用;
- 现在我正试图在一个更复杂的场景中使用度量单位,我需要解析数量,度量单位和其他相关的东西,所以我决定使用ANTLR。
是否有可能以某种方式避免这种代码重复?
如果枚举还没有出现在您的程序中,我建议基于语法本身生成运行时工件。
既然已经定义了枚举,那么让我们在使用AbstractParseTreeVisitor完成解析后实现单元识别。
1) 添加units
解析器规则并泛化UNITS
词法分析器规则:
...
unit : ID
;
...
ID: [a-zA-Z_0-9]+ ; // whatever you want/need
...
现在你的语法没有复制任何代码,但是你的单位规则太笼统了。我们将在java方面解决这个问题。
2) 生成一个访问者并覆盖visitUnit(UnitContext)
。
@Override
public Object visitUnit(UnitContext ctx) {
String unitId = ctx.ID();
try{
// Next line will throw exception if unitId is not
// the name of one of your enums.
UnitMeasures unit = UnitMeasures.valueOf(unitId);
// do something maybe?
} catch (IllegalArgumentException(e) {
throw new RuntimeException("Invalid unit: " + unitId);
}
return super.visitUnit(ctx);
}
这将消除任何代码重复。现在,每次向UnitMeasures
添加新枚举时,都不必更改语法。您甚至不需要重新生成解析器。
另一种选择:这将在你的语法中添加一个java依赖,但是你可以在你的unit
规则之后添加一点动作,如果ID
不是基于你的enum
的有效单元,它可以适当地响应。
unit : ID
{
try {
UnitMeasures.valueOf($unit.text);
}
catch(IllegalArgumentException e) {
//report invalid unit
}
}
;