Question

Is it possible to create a dsl with infix operators in Groovy ?

I've researched the builder pattern, invokeMethod, propertyInvoke and the MetaClass and I can only see how to use it to create tree structures using prefix operators (polish notation). What I want to do is build tree structures with infix operators that have binding precedence rules.

eg: if this pseudo code evaluates to 8:

add(2,multiply(3, 2))

I want to express it as:

2 add 3 multiply 2

where multiply binds more tightly than add. I'm using arithmetic just as an example - my application is something quite different.

I don't want to write and support my own parser so I'm hoping groovy has a mechanism for this ???

EDIT: While searching for solutions I've found this is possible and well documented in Scala (see chapter 33: Combinator Parsers in Programming in Scala by Martin Odersky).

Was it helpful?

Solution

I see you found an answer in Scala, but anyway, in Groovy, although it needs the dot (or an AST to insert it automatically), it's not hard to do the command tree:

UPDATE: added a floatNode method, whose precedence is based in the precedence list. The nodes with higher precedence "float" upward:

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)" }
}

Tests:

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

The println will output:

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)))))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top