【问题标题】:How to annotate builder-like DSL structure?如何注释类似构建器的 DSL 结构?
【发布时间】:2018-02-12 12:05:07
【问题描述】:

我在我的应用程序中使用 groovy 构建器实现了多个 DSL。我想在某些节点中支持注释,例如:

builder.define {

  @Secure
  checkService {
    ... 
  }

}

但是,此脚本在语法上不正确,并且无法编译(我无法对方法的调用进行注释)。

是否可以在 Groovy 中使用 AST 转换或任何其他方法来实现这种 DSL?如果是的话,有人有例子吗?

我使用临时属性,例如checkService(secure:true),但它不是很漂亮...!

【问题讨论】:

  • secured { checkService {}}

标签: groovy


【解决方案1】:

注释不能放在MethodCallExpression。即使new Groovy 3 Syntax 也不允许这样做。

正如 cfrick 所说,您必须寻找其他选择。

带有闭包和省略括号的普通 groovy

您可以通过将secure 定义为方法来创建类似的语法,从每个方法返回Closure 并使用groovys 可选大括号:

class Builder {
    def define(@DelegatesTo(InnerBuilder) Closure defineClosure) {
        println("start define")
        def innerBuilder = new InnerBuilder()
        defineClosure.delegate = innerBuilder
        Closure returnedClosure = defineClosure()

        // also call the returned Closure
        returnedClosure.delegate = innerBuilder
        returnedClosure()
        println("end define")
    }

    class InnerBuilder {
        def secure(Closure c) {
            return { ->
                println("start being secure")
                c()
                println("stop being secure")
            }
        }

        def elegant(Closure c) {
            return { ->
                println("start being elegant")
                c()
                println("stop being elegant")
            }
        }

        def checkService(Closure c) {
            return { ->
                println("start checkService")
                c()
                println("stop checkService")
            }
        }
    }

}

def builder = new Builder()
builder.define {
    checkService {
        println "running a service check"
    }
}

println()
println("with secure")
println()

builder.define {
    secure checkService {
        println "running a service check"
    }
}

//    NOT WORKING
//    println()
//    println("with new-line but missing the backslash")
//    println()
//
//    builder.define {
//        secure // this will NOT be secure!
//        checkService {
//            println "running a service check"
//        }
//    }

println()
println("with new-line and backslash")
println()

builder.define {
    // this WILL be secure!
    secure \
    checkService {
        println "running a service check"
    }
}

println()
println("two")
println()

builder.define {
    // needs braces
    elegant secure(checkService {
        println "running a service check"
    })
}

println()
println("Both orders work")
println()

builder.define {
    secure elegant(checkService {
        println "running a service check"
    })
}

println()
println("Multiple with line-break")
println()

builder.define {
    secure \
    elegant(
    checkService {
        println "running a service check"
    })
}

将打印

start define
start checkService
running a service check
stop checkService
end define

with secure

start define
start being secure
start checkService
running a service check
stop checkService
stop being secure
end define

with new-line and backslash

start define
start being secure
start checkService
running a service check
stop checkService
stop being secure
end define

two

start define
start being elegant
start being secure
start checkService
running a service check
stop checkService
stop being secure
stop being elegant
end define

Both orders work

start define
start being secure
start being elegant
start checkService
running a service check
stop checkService
stop being elegant
stop being secure
end define

Multiple with line-break

start define
start being secure
start being elegant
start checkService
running a service check
stop checkService
stop being elegant
stop being secure
end define

AST 变换

这有点复杂,我认为它没有提供值得付出努力的价值,但您可以使用 AST 转换来缓解先前版本的语法问题。

class Builder {
    def define(@DelegatesTo(InnerBuilder) Closure defineClosure) {
        println("start define")
        def innerBuilder = new InnerBuilder()
        defineClosure.delegate = innerBuilder
        defineClosure()
        println("end define")
    }

    class InnerBuilder {
        def secure = "just a string"

        def elegant = "just a string"

        def checkService(Closure c) {
            println("start checkService")
            c()
            println("stop checkService")
        }
    }

}

def builder = new Builder()
builder.define {
    secure
    elegant
    checkService {
        println "running a service check"
    }
}

到目前为止secure 没有任何意义(它是NOP)。你需要编写一个 AST Transform 来改变它。这里secureelegant 应该(即未验证)是VariableExpression,它们位于ExpressionStatement 内,作为BlockStatement 的一部分,即周围@ 的code 属性987654336@。实际的实现留给读者练习;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多