Strumenta / antlr-kotlin

Support for Kotlin as a target for ANTLR 4
Apache License 2.0
228 stars 47 forks source link

sealed classes instead of multiple nullable values?! #178

Open andretietz opened 7 months ago

andretietz commented 7 months ago

Hey during using this awesome library, I noticed a thing that bugs me a bit.

a parser rule that has multiple options

document: rule EOF;
rule
  : OPTIONA
  | OPTIONB;

generates into:

val rule = document.rule()
rule.getOptiona() // nullable
rule.getOptionb() // nullable

I would very much prefer if that generates into:

sealed class Rule {
   data class Optiona(...) : Rule
   data class Optionb(...) : Rule
}

Would that be possible?

Can you hint me to the generation part where this part happening, may be I take a look myself?

ftomassetti commented 7 months ago

Hey during using this awesome library, I noticed a thing that bugs me a bit.

We just wanted to be sure it was not too awesome, so introduced a few problems on purpose :D

a parser rule that has multiple options

document: rule EOF;
rule
  : OPTIONA
  | OPTIONB;

generates into:

val rule = document.rule()
rule.getOptiona() // nullable
rule.getOptionb() // nullable

I would very much prefer if that generates into:

sealed class Rule {
   data class Optiona(...) : Rule
   data class Optionb(...) : Rule
}

Would that be possible?

I think this has been implemented in this way to reflect what was present in the Java runtime. Now, your approach is exactly what we do when mapping an ANTLR parse-tree to a Kolasu AST. We have an internal tool where we add annotations (in comments) in ANTLR grammars and when we use the annotation @oneOf we generate sealed classes and subclasses.

Personally I think that generating a more "kotlin-esque" AST would be nice, and perhaps we could control the generation through a flag: by default we could generate an AST in a more "standard" or "like-the-java-runtime" way (so that migration to the Kotlin target is very easy) and optionally, when a tag is specified, produce a different AST.

Can you hint me to the generation part where this part happening, may be I take a look myself?

Generation of code for all ANTLR targets is based on templates. Here there is the one for ANTLR Kotlin: https://github.com/Strumenta/antlr-kotlin/blob/master/antlr-kotlin-target/src/main/resources/org/antlr/v4/tool/templates/codegen/Kotlin/Kotlin.stg

andretietz commented 2 months ago

Hey @ftomassetti!

For reasons I still didn't figure out how to use kolasu in my project. Is there a public project you know off, where I can see this in action? Are there some links you can hint me to, to help me understand how to use it? I really need this!

ATM, I am taking the current option and call methods that does the "translation" for me, but it seems so pointless and is definitely an overhead.

Thank you anyways!

ftomassetti commented 2 months ago

@andretietz we should write better tutorials at some point, but perhaps these could help:

https://tomassetti.me/build-an-editor-with-kolasu-and-language-servers/ https://tomassetti.me/building-advanced-parsers-using-kolasu/

andretietz commented 2 months ago

Hey @ftomassetti thank you!

Now I at least understood what kolasu is supposed to do. It still doesn't do what I actually want to. As you mentioned in your first response, this is what I do atm already.

So you're telling me you're using an internal tool that can generate an AST directly in kotlin without kolasu or any other mappings? If so, I would be really interested in that :D.

However I wonder what you use antlr-kotlin for, if you're targeting jvm targets anyway. Why not use the java generator of antlr directly? I don't see the need (at least not for jvm targets).

I successfully used it in several KMP projects, where we target Android/iOS/Mac/Windows/Linux, there jvm compatibility is not a good thing. What I want to say is: "Why providing a java friendly API for a kotlin first generator?" If I would like to target jvm only, I don't need antlr-kotlin anyways, right? Do I oversee something?

Is that something you're going to target one day?

ftomassetti commented 2 months ago

So you're telling me you're using an internal tool that can generate an AST directly in kotlin without kolasu or any other mappings? If so, I would be really interested in that :D.

At this time we do not have any plan to release that, but we have a long term plan to replace it with something we could eventually share. It is a few years down the road :D

However I wonder what you use antlr-kotlin for, if you're targeting jvm targets anyway.

Currently, we do not use it in production. However we plan to use it with version 1.6 of Kolasu, to provide parsers that run both on the JVM and in the browser. Possibly, we could also use the native version from Python (generating appropriate wrappers)

Why not use the java generator of antlr directly? I don't see the need (at least not for jvm targets).

Well, we started Kolasu as a small effort built on top of ANTLR, while adding a generator to ANTLR requires quite some work. We now have the skills and the time to do that, but we went another route 7 years ago and built habits, tools, and libraries to support working in this way so changing it is not trivial

What I want to say is: "Why providing a java friendly API for a kotlin first generator?"

Because we may want to generate a parser that we then integrate both in a Typescript application and in a Java application. In this situation we want to build such parser using ANTLR-Kotlin because of its multi-platform nature. In that similar context we could not use ANTLR Java.

In this case by parser I mean the combination of the basic ANTLR parser, generated by ANTLR and some Kotlin multi-platform code built on top of it. For example, I may write a simple grammar for an expression language. Then I may write some code in Kotlin that invoke the ANTLR parser, get the parse tree and validate it programmatically. We may want to use such piece of code from Typescript and Java, so we have to use ANTLR Kotlin + Kotlin multiplatform code. In this scenario we want the code to be convenient to use from Java.

Does this make sense? Sorry if the explanation sounds confused, it has been a long day so far