square / kotlinpoet

A Kotlin API for generating .kt source files.
https://square.github.io/kotlinpoet/
Apache License 2.0
3.89k stars 287 forks source link

DSL for Kotlin generation #62

Open Egorand opened 7 years ago

Egorand commented 7 years ago

In addition to the APIs, it would be great to have a fluent DSL for defining Kotlin files.

burntcookie90 commented 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.

JakeWharton commented 7 years ago

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.

EddieRingle commented 7 years ago

a fluent DSL for defining Kotlin files

I chuckled a little at this since I imagine most would just end up re-inventing Kotlin.

max4t commented 7 years ago

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())
tieskedh commented 7 years ago

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::class){ //code }

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 }

wakingrufus commented 6 years ago

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.

jonnyzzz commented 6 years ago

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/

friedrich-goetze commented 5 years ago

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.

tieskedh commented 5 years ago

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...

tieskedh commented 5 years ago

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:

Omico commented 1 year ago

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

DRSchlaubi commented 9 months ago

We've implemented a generator for a Kotlinpoet DSL here and would love to see your feedback on it, it is currently published here