Open Egorand opened 7 years ago
I had written a DSL for javapoet, it was nice until it wasn't. I'm not too much a fan of it.
I think we can do a few niceties like #39 and then maybe .apply
and .let
will get us 90% of what a DSL would without the overhead trying to project the API into it.
a fluent DSL for defining Kotlin files
I chuckled a little at this since I imagine most would just end up re-inventing Kotlin.
90% of what a DSL would
Minus the readability
By overloading the methods in the builder, the example from the Readme could look like this :
val greeterClass = ClassName.get("", "Greeter")
val kotlinFile = KotlinFile.builder("", "HelloWorld") {
addType("Greeter") {
primaryConstructor() {
addParameter("name", String::class)
}
addProperty("name", String::class) {
initializer("name")
}
addFun("greet") {
addStatement("println(%S)", "Hello, \$name")
}
}
addFun("main") {
addParameter("args", TypeName.get(Array<String>::class))
addStatement("%T(args[0]).greet()", greeterClass)
}
}
kotlinFile.writeTo(System.out)
which, i think, is much less "dense"
Of course, it would require to overloading the methods to match the builder of the parameter with something like this (in KotlinFile.kt
):
fun addType(name: String, block: TypeSpec.Builder.() -> Unit) = addType(
TypeSpec.classBuilder(name).apply(block).build())
Maybe you can add the function
infix fun String.of(clazz: KClazz) = pairOf(this, clazz)
and make addFun accept a vararg of these pairs, then you can call the method like:
AddFun("main", "args" of Array
Almost like I did here: https://github.com/tieskedh/KotlinPoetDSL
But I made it more advanced, too also include
AddFun("main", "args" varArg String::class){ //code }
I have started implementing a dsl wrapper for poet in my project. If I get it working, I will open a PR to contribute it upstream here.
Yet another comment from #435, answering also to @breandan request ;) I was doing Kotlin code generation with DSL some time ago. The idea was to make the DSL code look as close as possible to the Kotlin code it generates. That makes some sense and simplifies the way one generates the code. Of course, the syntax of the language does not allow the idea to be implemented fully. The DSL should use callable references to extract types and names from all references one uses in generated code.
I do not have full examples of that, the only thing I blogged was about Gradle. You may see how the idea is implemented there https://jonnyzzz.com/blog/2017/11/02/gradle-dsl/. Few places of an incomplete experiment are also on my GitHub at https://github.com/jonnyzzz/kotlin.generator/
I just added a pull request #496, where extension functions for most builders are provided. It follows @tieskedh 's approach:
fun addType(name: String, block: TypeSpec.Builder.() -> Unit) = addType(
TypeSpec.classBuilder(name).apply(block).build())
I used these functions for my own project and it worked very well. One great advantage is that you'll get less indentation in comparison with a builder chain, which saves you a lot horizontal space. Also, I find the code much more natural.
I think you mean @max4t. My DSL - I need to continue to work on it... - is way more advanced at the moment. Also, it uses custom builders, as my wrapper uses interfaces for adding functions to objects and this repo does not provide these interfaces.
Ps. If anyone can help with the licenses, I would be very happy, as I don't know almost nothing about them...
I added a fourth approach for using DSL-marker. I created a PR to @friedrich-goetze . Having said that, I think annotating the builder-classes is better because:
Hello, a very interesting thing has just been introduced to the DSL support library I wrote for kotlinpoet. Generate DSL source code directly through Kotlin PSI Elements via reading kotlinpoet's source code. Currently, I have only implemented part of it for experimentation. https://github.com/Omico/Elucidator/commit/042ca38c6c7de665cb202f001ac11b21b34de04a
In addition to the APIs, it would be great to have a fluent DSL for defining Kotlin files.