我想将Nashorn引擎用作通用计算引擎。它功能强大,速度快,并具有大量内置函数,并且使用@FunctionalInterface或static methods非常容易添加新功能。更好的是,它还提供了诸如循环依赖项检查,语法检查等增值功能。
但是,当依赖项更改时,我需要自动更新“输出”变量。
一般的想法是,在Java中,我会遇到类似的东西:
class CalculationEngine {
Data addData(String name, Number value){
...
}
Data addData(String name, String formula){
...
}
String getScript(){
...
}
}
CalculationEngine engine = new CalculationEngine();
Data datum1 = engine.addData("datum1", 1); // Constant integer 1
Data datum2 = engine.addData("datum2", 2); // Constant integer 2
Data datum3 = engine.addData("datum3", "datum1*10");
Data datum4 = engine.addData("datum4", "datum3+datum2");
CalculationEngine服务类知道如何使用Nashorn从Data
对象中创建脚本字符串,如下所示:
final String script = engine.getScript(); // "var datum1=1; var datum2=2; var datum3=datum1*10; var datum4=datum3+datum2;"
我知道我可以使用Nashorn解析器来解析脚本:
final CompilationUnitTree tree = parser.parse("test", script, null);
但是我如何提取依赖项:
List<Data> whatDependsOn(Data input){
// Process the parsed tree
return list;
}
使得whatDependsOn(datum2)
返回[datum4],而whatDependsOn(datum1)
返回[datum3,datum4]吗?
或反函数getReferencedVariables
使得getReferencedVariables(datum3)
返回[datum1],而getReferencedVariables(datum4)
返回[datum2,datum3](我可以递归查询getReferencedVariables
直到所有引用的变量已找到)。
基本上,当我的Data
对象之一的“值”发生变化(由于外部事件)时,我如何确定哪些脚本公式受到影响并需要重新计算?
我知道Nashorn script can be parsed,但是我不知道如何使用SimpleTreeVisitorES6
建立变量依赖图:
final CompilationUnitTree tree = parser.parse("test", script, null);
if (tree != null) {
tree.accept(new SimpleTreeVisitorES6<Void, Void>() {
@Override
public Void visitVariable(VariableTree tree, Void v) {
final Kind kind = tree.getKind();
System.out.println("Found a variable: " + kind);
System.out.println(" name: " + kind.toString());
IdentifierTree binding = (IdentifierTree) tree.getBinding();
System.out.println(" kind: " + binding.getKind().name());
System.out.println(" name: " + binding.getName());
System.out.println(" val: " + kind.name());
return null;
}
}, null);
}
答案 0 :(得分:0)
Nashorn开发人员之一。您想要做的是在源代码上计算所谓的def-use关系(很可能是它们的传递闭包,但是我离题了)。这是一个很好理解的编译器理论概念。好消息是CompilationUnitTree
和朋友应该给您足够的信息,以实现用于计算该信息的算法。坏消息是,恐怕您将不得不袖手旁观并自行实施。您基本上必须收集这些信息,在控制流连接点(循环的后边缘和出口,if语句的末端)产生合并,但是还必须处理更多奇特的东西,例如switch / case及其后缀语义和还尝试/捕获/最终,这是最不有趣的,因为基本上控制权可以从try块中的任何地方转移到catch块。)您的算法还必须反复评估循环主体,直到收集到的静态信息达到定点。
FWIW,在编写Nashorn时,我不得不使用Nashorn的内部解析器API来实现这些事情几次(这是不同的,但与公共的相似)。如果您需要一些启发,可以在我几年前写的JavaScript函数中研究source code for Nashorn static type analyzer for inferring types of local variables。如果没有其他问题,它将为您提供一个思路,如何走一棵AST树并跟踪控制流边缘以及边缘处的部分计算的静态分析数据。
我希望可以有一种更简单的方法来完成…FWIW,这是一种广义的静态分析器,可以帮助您保持流控制的布尔值。祝你好运。