在antlr语法g4文件中使用Java代码

时间:2014-07-09 16:02:59

标签: java parsing enums antlr4

我想定义一个语法,该语法应解析与度量单位相关的单词,例如公斤:'kg','KG','千克','公斤','l','升','升'等。

我已经在使用Java enum类来做类似的事情,以验证应该代表度量单位的输入字符串。

我想知道是否可以在ANTLR语法文件中的enum类中重用已定义的度量单位。基本上我想在 .g4 语法文件中设置 lexer ,如:

UNITS: UnitMeasures.values()

.values()方法返回UnitMeasures枚举Java类中的枚举值,这与“ANTLR语法词法分析器”相同“

UNITS: ('kg' | 'KG' | 'kilograms' | 'l' | 'litres' | 'liters' );

原因我尝试这样做的原因是:

  • 我想在enum Java类和ANTLR语法文件之间避免代码重复;
  • 我不能仅使用ANTLR并删除枚举Java类,因为它已在许多不同的地方使用过;
  • 现在我试图在一个更复杂的场景中使用度量单位,我需要解析数量,度量单位和其他相关内容,所以我决定使用ANTLR。

是否有可能以某种方式避免此代码重复?

1 个答案:

答案 0 :(得分:2)

如果您的程序中尚未存在枚举,我建议根据语法本身生成运行时工件。

由于您已经定义了枚举,因此在使用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
         }
     }
     ;