Java适配Groovy Parrot解析器时,如何在启用类型检查的同时允许指定未定义变量?
Java适配Groovy Parrot解析器时,如何在启用类型检查的同时允许指定未定义变量?
我正在升级一个Java代码库,它需要解析用户提供的Groovy脚本,目标是兼容Groovy的Parrot解析器,但目前遇到了不小的麻烦。
旧版解析器的语法错误捕获逻辑
在使用Antlr2解析器(Parrot之前的版本)时,我们可以像这样轻松捕获语法错误:
@Test(expected = MultipleCompilationErrorsException.class) public void testFailingOldWay() { CompilerConfiguration config = new CompilerConfiguration(); config.setPluginFactory(ParserPluginFactory.antlr2()); new GroovyShell(config).parse("this is not valid"); }
这种方式会正常抛出MultipleCompilationErrorsException,完全符合预期。
切换到Parrot解析器后的问题
但换成Parrot解析器(基于Antlr4实现)后,原本明显无效的代码居然不会触发任何解析错误。我一开始以为是Groovy的bug,后来才搞明白:Groovy的动态特性允许传入动态方法和变量,所以这类代码在理论上存在合法的可能性,因此解析器不会直接报错:
@Test public void testFailingNewWayDoesNotWork() { CompilerConfiguration config = new CompilerConfiguration(); // 原本期望下面这行抛出编译错误,但实际并没有 new GroovyShell(config).parse("this is not valid"); }
尝试用类型检查解决问题
因为我明确知道脚本可以使用的变量列表,所以想到启用静态类型检查来捕获非法代码,于是写了这样的测试:
@Test(expected = MultipleCompilationErrorsException.class) public void testNewIdea() { CompilerConfiguration config = new CompilerConfiguration(); config.addCompilationCustomizers(new ASTTransformationCustomizer(TypeChecked.class)); new GroovyShell(config).parse("this is not valid"); }
这个方法确实能抛出预期的编译错误,但新问题来了:我找不到办法告诉类型检查器,允许某些特定的变量处于未定义状态。
我查了Groovy的官方文档,还看了一篇Stack Overflow的相关帖子,但文档只覆盖了Java实现的部分内容,而帖子全是基于Groovy代码的实现方案,和我的Java场景完全不匹配。
自定义扩展的思路(但遇到空指针)
我自己琢磨出一个方案:写一个自定义注解和类型检查扩展类,来控制哪些未定义变量可以被允许。
首先是自定义注解类:
import org.codehaus.groovy.transform.GroovyASTTransformationClass; @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @GroovyASTTransformationClass("flexagon.fd.model.script.AllowUnresolvedVariables") public @interface NoUnresolvedVariablesAnnotation { }
然后是继承自AbstractTypeCheckingExtension的扩展类:
public class AllowUnresolvedVariables extends AbstractTypeCheckingExtension { public AllowUnresolvedVariables(final StaticTypeCheckingVisitor typeCheckingVisitor) { super(typeCheckingVisitor); } @Override public boolean handleUnresolvedVariableExpression(final VariableExpression vexp) { return true; } }
但在配置中引用这些类时,出现了空指针异常。我还专门做了一个简单的项目来复现这个问题。
真心希望能得到大家的帮助!
备注:内容来源于stack exchange,提问作者Karl Henselin
相关产品推荐
相关产品推荐

