我当前的工作项目允许在特定的上下文中评估用户提供的表达式,作为他们扩展和影响工作流的一种方式。这些表达式通常是逻辑上的。为了使它对非程序员来说有点可口,我想给他们选择使用文字运算符(例如和,或者,而不是代替&,|,!)。
简单搜索&替换是不够的,因为数据可能包含引号内的那些单词并构建解析器,虽然可行,但可能不是最优雅和有效的解决方案。
要明确问题:Groovy中是否有一种方法允许用户编写
x > 10 and y = 20 or not z
但是让Groovy对它进行评估,就像它一样:
x > 10 && y == 20 || !z
谢谢。
答案 0 :(得分:4)
最新版本的Groovy支持Command chains,因此确实可以写这个:
compute x > 10 and y == 20 or not(z)
单词" compute"这是任意的,但不能省略,因为它是第一个"动词"在命令链中。随后的所有内容在动词和名词之间交替出现:
compute x > 10 and y == 20 or not(z)
───┬─── ──┬─── ─┬─ ───┬─── ─┬─ ──┬───
verb noun verb noun verb noun
命令链的编译如下:
verb(noun).verb(noun).verb(noun)...
所以上面的例子编译为:
compute(x > 10).and(y == 20).or(not(z))
有很多方法可以实现这一点。这只是一个快速的&脏概念,不会实现运算符优先级,其中包括:
class Compute {
private value
Compute(boolean v) { value = v }
def or (boolean w) { value = value || w; this }
def and(boolean w) { value = value && w; this }
String toString() { value }
}
def compute(v) { new Compute(v) }
def not(boolean v) { !v }
您可以单独使用命令链(作为顶级语句)或使用赋值运算符的右侧(局部变量或属性赋值),但不能在其他表达式中使用。
答案 1 :(得分:1)
如果您可以分别将>
和=
之类的运算符换成类似小小的gt
和eq
,我认为您的情况可能是可行的,尽管它会需要付出很多努力:
x gt 10 and y eq 20 or not z
解析为:
x(gt).10(and).y(eq).20(or).not(z)
这将是解析的地狱。
@Brian Henry提出的方式是最简单的方法,虽然不是用户友好的,因为它需要parens和dot。
好吧,考虑到我们可以交换运算符,你可以尝试拦截Integer.call
来启动表达式。将脚本中缺少的属性解析为操作可以解决新的关键字问题。然后,您可以构建表达式并将它们保存到列表中,并在脚本的末尾执行它们。它还没有完成,但我带来了这个:
// the operators that can be used in the script
enum Operation { eq, and, gt, not }
// every unresolved variable here will try to be resolved as an Operation
def propertyMissing(String property) { Operation.find { it.name() == property} }
// a class to contain what should be executed in the end of the script
@groovy.transform.ToString
class Instruction { def left; Operation operation; def right }
// a class to handle the next allowed tokens
class Expression {
Closure handler; Instruction instruction
def methodMissing(String method, args) {
println "method=$method, args=$args"
handler method, args
}
}
// a list to contain the instructions that will need to be parsed
def instructions = []
// the start of the whole mess: an integer will get this called
Integer.metaClass {
call = { Operation op ->
instruction = new Instruction(operation: op, left: delegate)
instructions << instruction
new Expression(
instruction: instruction,
handler:{ String method, args ->
instruction.right = method.toInteger()
println instructions
this
})
}
}
x = 12
y = 19
z = false
x gt 10 and y eq 20 or not z
由于not()
部分没有实现,会产生异常,但它可以在失败之前构建两个Instruction对象:
[Instruction(12, gt, 10), Instruction(19, eq, 20)]
不确定是否值得。
答案 2 :(得分:0)
and()
将or()
和Boolean not(Boolean b) {return !b}
方法解决为布尔值。如果您提供了类似
(x > 10).and(y == 20).or(not(4 == 1))
你可以写点像
{{1}}
但我不确定这写起来特别容易。