Groovy DSL - 中缀操作符是否可行?

3

在Groovy中创建具有中缀运算符的DSL是否可能?

我已经研究了构建器模式、invokeMethod、propertyInvoke和MetaClass,但是我只能看到如何使用它来使用前缀运算符(波兰表达式)创建树结构。我想要做的是使用具有绑定优先级规则的中缀运算符构建树结构。

例如:如果这个伪代码计算结果为8:

add(2,multiply(3, 2))

I want to express it as:

2 add 3 multiply 2

在这里,乘法比加法的优先级更高,我只是举算术作为例子 - 我的应用程序是完全不同的。

我不想编写和支持自己的解析器,所以我希望Groovy有一个机制来实现这一点???

编辑:在寻找解决方案时,我发现在Scala中可以实现这一点,并且由Martin Odersky编写的《Programming in Scala》第33章对此进行了详细说明。


如果是“2加3乘以2减去4”,它应该被理解为“add(2,subtract(multiply(3,2),4)”? - Will
是的,完全正确 - 这只是算术运算的正常顺序。 - user1373164
为了提供更多的上下文,运算符返回要评估的表达式,这样我们就可以构建一个可以递归地评估的语法树。在这个例子中,表达式评估为数字:2 + 3 * 2 - 4 = 2 + 6 - 4 = 8 - 4 = 4。 - user1373164
我明白了。可以加上那个点吗? - Will
如果有办法让它工作,我可以接受这个点。 - user1373164
显示剩余2条评论
1个回答

2

我看到你在Scala找到了答案,但在Groovy中,虽然需要点号(或AST自动插入点号),但做命令树并不难:

更新:添加了一个floatNode方法,其优先级基于precedence列表。具有较高优先级的节点“浮动”向上:

class Operation {

  static final precedence = ['minus', 'add', 'multiply', 'divide']

  def left, right, method
  Operation parent

  def invokeMethod(String method, args) {
    def o = new Operation(
      parent: this, left: right, method: method, right: args[0])

    this.floatNode(o)
  }

  def floatNode(Operation op) {
    if (op.hasHigherPrecedenceThan(this)) {
      op.parent = this.parent
      this.parent = op
      if (op.parent) { this.parent = op.parent.floatNode(op) }
      return this
    }
    else {
      return op
    }
  }

  def hasHigherPrecedenceThan(Operation o) {
    return precedence.indexOf(this.method) > precedence.indexOf(o.method)
  }

  String toString() { "Operation($left $method $right, parent=$parent)" }
}

测试:

Integer.metaClass.invokeMethod = { String method, args ->
  new Operation(parent: null, method: method, left: delegate, right: args.head())
}


a = 2.add 3 multiply 4 minus 5 divide 6 add 7 

println a
println 会输出:
Operation(3 minus 5, 
    parent=Operation(5 add 7, 
        parent=Operation(2 add 3, 
            parent=Operation(3 multiply 4, 
                parent=Operation(5 divide 6, parent=null)))))

我还没有尝试过这个,但它看起来很有前途并且回答了我的原始问题。不过我不确定优先级解析会如何处理(左边:这将按遇到的顺序嵌套吗?),在例子中3 * 4应该是要首先计算的。但这是一个很好的起点 - 感谢您的帮助。 - user1373164
是的,left:this 将按照遇到的顺序嵌套。我认为可以遍历树来处理运算符优先级。会尽快写上相关内容。 - Will
1
我认为点号可以被一个函数调用所替代,类似于 let 2 add 3 multiply 4 minus 5... - Will
是的,非常好 - 它更像是一个链表而不是一棵树,但它似乎能够正确解决问题,而且这是一个很好的简单方法。再次感谢您的建议。 - user1373164
你是正确的。此外,“left”必须根据操作进行更改,而不是值。 - Will

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接