在运行时确定字符串中 Java 表达式的 return 类型
Determine return type of a Java expression in a String at runtime
在运行时,在我的 Java 程序中,给定一个字符串,我想知道 return 类型。例如:
1 + 1
returns int
1L + 1L
returns long
1L + 1
returns long
1 + 1.5
returns double
1 + 2 - 3 * 4 / 5
returns int
1 / 0
returns int
1 + Math.nextInt()
returns int
1.5 + Math.nextInt()
returns double
Color.RED
returns java.awt.Color
- 假设
a
是一个整数:a + 1
returns int
- 假设
a
是一个整数:a + 1.5
returns double
不需要实际评估代码:我只需要 return 类型。 如何使用 JDK 运行时编译器、ECJ JDT 或任何其他纯 Java 依赖项来执行此操作?
详细代码:这是此代码的简化伪代码单元测试:
public static void ExpressionTyper {
public String determineType(String expression, Map<String, String> variableTypes) {
... // How do I implement this?
}
}
public void ExpressionTyperTest {
@Test public void determineType() {
assertEquals("int", ExpressionTyper.determineType("1 + 1", emptyMap());
assertEquals("long", ExpressionTyper.determineType("1 + 1L", emptyMap());
assertEquals("double", ExpressionTyper.determineType("1 + 1.5", emptyMap());
assertEquals("int", ExpressionTyper.determineType("a + 1", mapOf({"a", "int"}));
assertEquals("int", ExpressionTyper.determineType("a + b", mapOf({"a", "int"}, {"b", "int"}));
assertEquals("double", ExpressionTyper.determineType("a + b", mapOf({"a", "double"}, {"b", "int"}));
}
}
我认为这取决于您希望能够处理的输入范围。
你看,最后你问的是:我如何在运行时评估字符串表达式。
所以,简短的回答是:您需要某种解释器/REPL 实现;或至少 "parts" 个。
另一种方法可能是使用 javax 编译器简单地编译东西,然后推导类型,如 here。
其他选项与某些 "compiler construction" 主题类似,例如 constant folding。
我没试过这个...
用这样的代码包装你的表达式:
public class Test {
private Test xxx = <<insert expression>>;
}
编译代码。
- 解析编译错误消息以提取编译器对 RHS 类型的想法。
问题是像 Math.nextInt()
这样的表达式可能需要导入才能编译,而且我怀疑是否有一种可靠的方法来推断导入应该是什么。不过,这应该适用于有用的表达式子集。
这种方法也很脆弱且不可移植,因为它取决于编译错误消息的精确形式,而编译错误消息可能与编译器/版本相关。
更好的解决方案(但需要更多工作)是为您的 Java 子集表达式语言实现解析器和类型检查器。
要为 任意 表达式执行此操作,您需要一个完整的 Java 前端来解析字符串并确定其类型。本质上,您需要编译器的功能。
Eclipse JDT 可能会提供一个解决方案;我不是很熟悉
我们带有 DMS 完整 Java 前端的 DMS 软件再造工具包可以做到这一点。
DMS根据它使用的前端解析源代码,然后可以调用前端服务来分析该代码。
Java前端提供对Java各种版本和各种解析方式(文件、流、字符串)的解析,然后会为解析后的内容构建一棵树文本。可以要求前端内置的 Java 名称解析器根据在代码中特定点生效的范围规则计算任意表达式的类型。
对于 OP,这可能不是他想要的,因为他坚持基于 Java 的答案而 DMS 不是基于 Java 的(他确实要求“......任何其他依赖”)。最接近的可能是将 DMS 作为子流程调用,并要求 DMS 打印表达式的类型。如果他的表达式的类型数量极少(如他的示例所示),这可能会奏效。如果没有,他将需要一些复杂的机制来读取表达式类型,这些表达式类型(例如,包解析、模板化形式)可能非常复杂。
如果 OP 愿意将他需要处理的 class 表达式严格限制为简单的算术运算,他也许能够构建自己的表达式解析器并自己计算出类型。请参阅我关于如何手动构建递归下降解析器的 SO 回答: 这个相同的答案线程也显示了如何构建树和 'evaluate' 它;他可以根据类型而不是计算结果对其进行评估。
不然他就需要大铁锤了
在运行时,在我的 Java 程序中,给定一个字符串,我想知道 return 类型。例如:
1 + 1
returnsint
1L + 1L
returnslong
1L + 1
returnslong
1 + 1.5
returnsdouble
1 + 2 - 3 * 4 / 5
returnsint
1 / 0
returnsint
1 + Math.nextInt()
returnsint
1.5 + Math.nextInt()
returnsdouble
Color.RED
returnsjava.awt.Color
- 假设
a
是一个整数:a + 1
returnsint
- 假设
a
是一个整数:a + 1.5
returnsdouble
不需要实际评估代码:我只需要 return 类型。 如何使用 JDK 运行时编译器、ECJ JDT 或任何其他纯 Java 依赖项来执行此操作?
详细代码:这是此代码的简化伪代码单元测试:
public static void ExpressionTyper {
public String determineType(String expression, Map<String, String> variableTypes) {
... // How do I implement this?
}
}
public void ExpressionTyperTest {
@Test public void determineType() {
assertEquals("int", ExpressionTyper.determineType("1 + 1", emptyMap());
assertEquals("long", ExpressionTyper.determineType("1 + 1L", emptyMap());
assertEquals("double", ExpressionTyper.determineType("1 + 1.5", emptyMap());
assertEquals("int", ExpressionTyper.determineType("a + 1", mapOf({"a", "int"}));
assertEquals("int", ExpressionTyper.determineType("a + b", mapOf({"a", "int"}, {"b", "int"}));
assertEquals("double", ExpressionTyper.determineType("a + b", mapOf({"a", "double"}, {"b", "int"}));
}
}
我认为这取决于您希望能够处理的输入范围。
你看,最后你问的是:我如何在运行时评估字符串表达式。
所以,简短的回答是:您需要某种解释器/REPL 实现;或至少 "parts" 个。
另一种方法可能是使用 javax 编译器简单地编译东西,然后推导类型,如 here。
其他选项与某些 "compiler construction" 主题类似,例如 constant folding。
我没试过这个...
用这样的代码包装你的表达式:
public class Test { private Test xxx = <<insert expression>>; }
编译代码。
- 解析编译错误消息以提取编译器对 RHS 类型的想法。
问题是像 Math.nextInt()
这样的表达式可能需要导入才能编译,而且我怀疑是否有一种可靠的方法来推断导入应该是什么。不过,这应该适用于有用的表达式子集。
这种方法也很脆弱且不可移植,因为它取决于编译错误消息的精确形式,而编译错误消息可能与编译器/版本相关。
更好的解决方案(但需要更多工作)是为您的 Java 子集表达式语言实现解析器和类型检查器。
要为 任意 表达式执行此操作,您需要一个完整的 Java 前端来解析字符串并确定其类型。本质上,您需要编译器的功能。
Eclipse JDT 可能会提供一个解决方案;我不是很熟悉
我们带有 DMS 完整 Java 前端的 DMS 软件再造工具包可以做到这一点。 DMS根据它使用的前端解析源代码,然后可以调用前端服务来分析该代码。
Java前端提供对Java各种版本和各种解析方式(文件、流、字符串)的解析,然后会为解析后的内容构建一棵树文本。可以要求前端内置的 Java 名称解析器根据在代码中特定点生效的范围规则计算任意表达式的类型。
对于 OP,这可能不是他想要的,因为他坚持基于 Java 的答案而 DMS 不是基于 Java 的(他确实要求“......任何其他依赖”)。最接近的可能是将 DMS 作为子流程调用,并要求 DMS 打印表达式的类型。如果他的表达式的类型数量极少(如他的示例所示),这可能会奏效。如果没有,他将需要一些复杂的机制来读取表达式类型,这些表达式类型(例如,包解析、模板化形式)可能非常复杂。
如果 OP 愿意将他需要处理的 class 表达式严格限制为简单的算术运算,他也许能够构建自己的表达式解析器并自己计算出类型。请参阅我关于如何手动构建递归下降解析器的 SO 回答: 这个相同的答案线程也显示了如何构建树和 'evaluate' 它;他可以根据类型而不是计算结果对其进行评估。
不然他就需要大铁锤了