5

我当前的工作项目允许在特定上下文中评估用户提供的表达式,作为他们扩展和影响工作流程的一种方式。这些表达式是通常的逻辑表达式 f。为了让非程序员更容易接受,我想让他们选择使用文字运算符(例如,and、or,而不是 &、|、!)。

简单的搜索和替换是不够的,因为数据可能包含引号中的这些单词,并且构建解析器虽然可行,但可能不是最优雅和最有效的解决方案。

把问题说清楚:Groovy 中有没有一种方法可以让用户编写

x > 10 and y = 20 or not z 

但是让 Groovy 像这样评估它:

x > 10 && y == 20 || !z

谢谢你。

4

3 回答 3

4

最近版本的 Groovy 支持Commandchains,所以确实可以这样写:

compute x > 10 and y == 20 or not(z)

这里的“计算”一词是任意的,但不能省略,因为它是命令链中的第一个“动词”。接下来的所有内容都在动词和名词之间交替:

 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 }

您可以单独使用命令链(作为顶级语句)或赋值运算符的右侧(局部变量或属性赋值),但不能在其他表达式中使用。

于 2016-05-04T23:35:22.087 回答
1

如果您可以分别将操作符和操作符换成facelets -like>和,我认为您的情况可能是可行的,尽管这需要付出很多努力:=gteq

x gt 10 and y eq 20 or not z 

决议:

x(gt).10(and).y(eq).20(or).not(z)

这将是地狱解析。

@Brian Henry 建议的方式是最简单的方式,虽然不是用户友好的,因为它需要括号和圆点。

好吧,考虑到我们可以交换运算符,您可以尝试拦截Integer.callto start 表达式。将脚本中缺少的属性解析为操作可以解决您的新关键字问题。然后您可以构建表达式并将它们保存到列表中,在脚本末尾执行它们。它还没有完成,但我伴随着这个:

// 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(12, gt, 10), Instruction(19, eq, 20)]

不确定是否值得。

于 2012-12-09T01:34:38.883 回答
0

GDK对布尔值进行附加and()or()方法。如果您提供了类似的方法

Boolean not(Boolean b) {return !b}

你可以写一些像

(x > 10).and(y == 20).or(not(4 == 1)) 

不过,我不确定这是否特别容易编写。

于 2012-12-08T19:13:57.017 回答