Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/identity origin #392

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f1a38cc
Correct how we generate dummy notes in lists
ftomassetti Jan 31, 2025
2c74800
Correct how we generate dummy nodes in translateCasted, translateOpti…
ftomassetti Jan 31, 2025
6b39b2a
Introduce defaultTransformation and IDENTTITY_TRANSFORMATION
ftomassetti Feb 4, 2025
f25dc3f
Merge pull request #388 from Strumenta/bugfix/transformation
ftomassetti Feb 4, 2025
4a42954
Merge pull request #389 from Strumenta/feature/defaultTransformation
ftomassetti Feb 4, 2025
6c16be7
Revert LionWebVersion used in Kolasu to 2023.1
ftomassetti Feb 4, 2025
972b738
Merge remote-tracking branch 'origin/maintenance/kolasu15' into featu…
ftomassetti Feb 4, 2025
1166911
Revert LionWebVersion used in functional tests to 2023.1
ftomassetti Feb 4, 2025
8c605d2
Improving error message on failed instantiation in LionWebModelConverter
ftomassetti Feb 4, 2025
64a0a0e
Make IssueNode and ParsingResultNode use LW2023.1
ftomassetti Feb 4, 2025
7e73582
[Gradle Release Plugin] - pre tag commit: '1.5.75'.
ftomassetti Feb 5, 2025
82a06fa
[Gradle Release Plugin] - new version commit: '1.5.76-SNAPSHOT'.
ftomassetti Feb 5, 2025
2dcb10a
Merge branch 'maintenance/kolasu15' into feature/configureLWVersion
ftomassetti Feb 5, 2025
7d8de52
Correct version of LionWeb Java used in LionWebGradlePlugin
ftomassetti Feb 5, 2025
b20cdf6
Support usage of IdentityTransformation in the middle of an AST
ftomassetti Feb 6, 2025
3981421
Formatting
ftomassetti Feb 6, 2025
41b6e41
Make translateOptional allow a transformation producing null for a no…
ftomassetti Feb 6, 2025
707d469
Formatting
ftomassetti Feb 6, 2025
b1b9012
Merge pull request #391 from Strumenta/bugfix/identity
ftomassetti Feb 6, 2025
398a707
Merge remote-tracking branch 'origin/maintenance/kolasu15' into featu…
ftomassetti Feb 6, 2025
c1a7386
Update dependency on LW Java and LW Kotlin
ftomassetti Feb 6, 2025
eb4921b
Merge pull request #390 from Strumenta/feature/configureLWVersion
ftomassetti Feb 6, 2025
a1927e9
[Gradle Release Plugin] - pre tag commit: '1.5.76'.
ftomassetti Feb 6, 2025
a20fa9a
[Gradle Release Plugin] - new version commit: '1.5.77-SNAPSHOT'.
ftomassetti Feb 6, 2025
4bed72b
fixing identity origin + adding test to check it
tiagobstr Feb 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions core/src/main/kotlin/com/strumenta/kolasu/mapping/Support.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.strumenta.kolasu.mapping

import com.strumenta.kolasu.model.Node
import com.strumenta.kolasu.parsing.getOriginalText
import com.strumenta.kolasu.transformation.ASTTransformer
import org.antlr.v4.runtime.ParserRuleContext
Expand All @@ -12,8 +13,8 @@ import org.antlr.v4.runtime.ParserRuleContext
* JPostIncrementExpr(translateCasted<JExpression>(expression().first()))
* ```
*/
fun <T> ASTTransformer.translateCasted(original: Any): T {
val result = transform(original)
inline fun <reified T : Node> ASTTransformer.translateCasted(original: Any): T {
val result = transform(original, expectedType = T::class)
if (result is Nothing) {
throw IllegalStateException("Transformation produced Nothing")
}
Expand All @@ -29,8 +30,9 @@ fun <T> ASTTransformer.translateCasted(original: Any): T {
* JExtendsType(translateCasted(pt.typeType()), translateList(pt.annotation()))
* ```
*/
fun <T> ASTTransformer.translateList(original: Collection<out Any>?): MutableList<T> {
return original?.map { transformIntoNodes(it) as List<T> }?.flatten()?.toMutableList() ?: mutableListOf()
inline fun <reified T : Node> ASTTransformer.translateList(original: Collection<out Any>?): MutableList<T> {
return original?.map { transformIntoNodes(it, expectedType = T::class) as List<T> }?.flatten()?.toMutableList()
?: mutableListOf()
}

/**
Expand All @@ -46,8 +48,15 @@ fun <T> ASTTransformer.translateList(original: Collection<out Any>?): MutableLis
* )
* ```
*/
fun <T> ASTTransformer.translateOptional(original: Any?): T? {
return original?.let { transform(it) as T }
inline fun <reified T : Node> ASTTransformer.translateOptional(original: Any?): T? {
return original?.let {
val transformed = transform(it, expectedType = T::class)
if (transformed == null) {
return null
} else {
transformed as T
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.strumenta.kolasu.transformation

import com.strumenta.kolasu.mapping.translateList
import com.strumenta.kolasu.model.Node
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.javaType

val IDENTTITY_TRANSFORMATION: (
source: Any?,
parent: Node?,
expectedType: KClass<out Node>,
astTransformer: ASTTransformer
) -> List<Node> = {
source: Any?, parent: Node?, expectedType: KClass<out Node>, astTransformer: ASTTransformer ->
when (source) {
null -> {
emptyList()
}
is Node -> {
val kClass = source.javaClass.kotlin
val primaryConstructor = kClass.primaryConstructor
?: throw IllegalStateException(
"No primary constructor found for $kClass: cannot apply " +
"identity transformation"
)
val params = mutableMapOf<KParameter, Any?>()
primaryConstructor.parameters.forEach { parameter ->
val mt = parameter.type.javaType
val correspondingProperty = source.javaClass.kotlin.memberProperties.find {
it.name == parameter.name
} ?: throw IllegalStateException(
"Cannot find property named as parameter $parameter"
)
val originalValue = correspondingProperty.get(source)
// mt is ParameterizedType && mt.rawType == List::class.java -> mutableListOf<Any>()
when {
(parameter.type.classifier as KClass<*>).isSubclassOf(Node::class) -> {
params[parameter] = astTransformer.transform(originalValue)
}
mt is ParameterizedType && mt.rawType == List::class.java &&
(mt.actualTypeArguments.first() as? Class<*>)?.kotlin?.isSubclassOf(Node::class) == true -> {
params[parameter] = astTransformer.translateList<Node>(originalValue as List<Node>)
}
else -> params[parameter] = originalValue
}
}

val newInstance = primaryConstructor.callBy(params) as Node
newInstance.parent = parent
newInstance.origin = source
listOf(newInstance)
}
else -> {
throw IllegalArgumentException("An Identity Transformation expect to receive a Node")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,13 @@ open class ASTTransformer(
* with the origin FailingASTTransformation. If the flag is not set, then the transformation will just
* fail.
*/
val faultTollerant: Boolean = !throwOnUnmappedNode
val faultTollerant: Boolean = !throwOnUnmappedNode,
val defaultTransformation: (
(
source: Any?, parent: Node?, expectedType: KClass<out Node>,
astTransformer: ASTTransformer
) -> List<Node>
)? = null
) {
/**
* Factories that map from source tree node to target tree node.
Expand All @@ -272,8 +278,8 @@ open class ASTTransformer(
* This ensures that the generated value is a single Node or null.
*/
@JvmOverloads
fun transform(source: Any?, parent: Node? = null): Node? {
val result = transformIntoNodes(source, parent)
fun transform(source: Any?, parent: Node? = null, expectedType: KClass<out Node> = Node::class): Node? {
val result = transformIntoNodes(source, parent, expectedType)
return when (result.size) {
0 -> null
1 -> {
Expand Down Expand Up @@ -314,7 +320,9 @@ open class ASTTransformer(
node.parent = parent
}
} else {
if (allowGenericNode) {
if (defaultTransformation != null) {
nodes = defaultTransformation.invoke(source, parent, expectedType, this)
} else if (allowGenericNode) {
val origin = asOrigin(source)
nodes = listOf(GenericNode(parent).withOrigin(origin))
issues.add(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.strumenta.kolasu.transformation

import com.strumenta.kolasu.mapping.translateCasted
import com.strumenta.kolasu.mapping.translateList
import com.strumenta.kolasu.model.Node
import com.strumenta.kolasu.model.children
import com.strumenta.kolasu.model.hasValidParents
import com.strumenta.kolasu.model.withOrigin
import com.strumenta.kolasu.testing.assertASTsAreEqual
Expand Down Expand Up @@ -349,11 +352,195 @@ class ASTTransformerTest {
)
assertIs<MissingASTTransformation>(bazRoot1.stmts[0].origin)
}
}

@Test
fun testIdentityTransformation() {
val transformer = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION)

val original = BarRoot(
stmts = mutableListOf(
BarStmt("a"),
BarStmt("b")
)
)
val transformed = transformer.transform(original) as Node
assertASTsAreEqual(
original,
transformed
)
}

@Test
fun testPartialIdentityTransformation() {
val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION)
transformer1.registerNodeFactory(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ ->
FooRoot(
desc = "#children = ${original.children.size}",
stmts = astTransformer.translateList(original.stmts)
)
}
val original = BarRoot(
stmts = mutableListOf(
BarStmt("a"),
BarStmt("b")
)
)
val transformed = transformer1.transform(original) as FooRoot
assertASTsAreEqual(
FooRoot(
"#children = 2",
mutableListOf(
BarStmt("a"),
BarStmt("b")
)
),
transformed
)
}

@Test
fun testIdentityTransformationOfIntermediateNodes() {
val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION)
transformer1.registerNodeFactory(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ ->
FooRoot(
desc = "#children = ${original.children.size}",
stmts = astTransformer.translateList(original.stmts)
)
}
val original = AA(
a = "my_a",
child = AB(
b = "my_b",
child = AC(
c = "my_c",
children = mutableListOf(
AD("my_d1"),
AD("my_d2"),
AD("my_d3")
)
)
)
)
// All identity
assertASTsAreEqual(
AA(
a = "my_a",
child = AB(
b = "my_b",
child = AC(
c = "my_c",
children = mutableListOf(
AD("my_d1"),
AD("my_d2"),
AD("my_d3")
)
)
)
),
transformer1.transform(original) as AA
)
// All identity besides AA
transformer1.registerNodeFactory(AA::class) { original, t, _ ->
BA("your_" + original.a.removePrefix("my_"), t.translateCasted(original.child))
}
assertASTsAreEqual(
BA(
a = "your_a",
child = AB(
b = "my_b",
child = AC(
c = "my_c",
children = mutableListOf(
AD("my_d1"),
AD("my_d2"),
AD("my_d3")
)
)
)
),
transformer1.transform(original) as AA
)
// All identity besides AA and AB
transformer1.registerNodeFactory(AB::class) { original, t, _ ->
BB("your_" + original.b.removePrefix("my_"), t.translateCasted(original.child))
}
assertASTsAreEqual(
BA(
a = "your_a",
child = BB(
b = "your_b",
child = AC(
c = "my_c",
children = mutableListOf(
AD("my_d1"),
AD("my_d2"),
AD("my_d3")
)
)
)
),
transformer1.transform(original) as AA
)
// All identity besides AA and AB and AD
transformer1.registerNodeFactory(AD::class) { original, t, _ ->
BD("your_" + original.d.removePrefix("my_"))
}
assertASTsAreEqual(
BA(
a = "your_a",
child = BB(
b = "your_b",
child = AC(
c = "my_c",
children = mutableListOf(
BD("your_d1"),
BD("your_d2"),
BD("your_d3")
)
)
)
),
transformer1.transform(original) as AA
)
}

@Test
fun testIdentityTransformationOfIntermediateNodesWithOrigin() {
val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION)
transformer1.registerNodeFactory(AA::class) { original, t, _ ->
BA("your_" + original.a.removePrefix("my_"), t.translateCasted(original.child))
}
val original = AA(
a = "my_a",
child = AB(
b = "my_b",
child = AC(
c = "my_c",
children = mutableListOf(
AD("my_d1")
)
)
)
)
val transformedAST = transformer1.transform(original) as AA
// verify that the origin is set correctly
assert(transformedAST.origin == original)
}
}
data class BazRoot(var stmts: MutableList<BazStmt> = mutableListOf()) : Node()

data class BazStmt(val desc: String? = null) : Node()

data class BarRoot(var stmts: MutableList<BarStmt> = mutableListOf()) : Node()
data class BarStmt(val desc: String) : Node()

data class FooRoot(var desc: String, var stmts: MutableList<BarStmt> = mutableListOf()) : Node()

open class AA(var a: String, val child: AB) : Node()
open class AB(var b: String, val child: AC) : Node()
open class AC(var c: String, val children: MutableList<AD>) : Node()
open class AD(var d: String) : Node()

class BA(a: String, child: AB) : AA(a, child)
class BB(b: String, child: AC) : AB(b, child)
class BD(d: String) : AD(d)
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=1.5.75-SNAPSHOT
version=1.5.77-SNAPSHOT
# Note that we use the EXACT same version of Kotlin as the one included in Gradle, to avoid
# conflicts
kotlin_version=1.8.20
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ superPublish = { id = "com.vanniktech.maven.publish", version = "0.22.0" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "11.5.1" }

[versions]
lwjava = "0.3.0"
lwkotlin = "0.3.0"
lwjava = "0.3.1"
lwkotlin = "0.3.2"
kotestVersion= "1.3.3"

[libraries]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.strumenta.kolasu.lionwebgen
import com.google.devtools.ksp.gradle.KspExtension
import com.strumenta.kolasu.lionweb.ASTGenerator
import com.strumenta.kolasu.lionweb.KotlinCodeProcessor
import com.strumenta.kolasu.lionweb.LIONWEB_VERSION_USED_BY_KOLASU
import com.strumenta.kolasu.lionweb.StarLasuLWLanguage
import io.lionweb.lioncore.java.language.Language
import io.lionweb.lioncore.java.serialization.JsonSerialization
Expand Down Expand Up @@ -46,7 +47,8 @@ class LionWebGradlePlugin : Plugin<Project> {
println("processing languageFile $languageFile")
when (languageFile.extension) {
"json" -> {
val jsonser = SerializationProvider.getStandardJsonSerialization()
val jsonser = SerializationProvider.getStandardJsonSerialization(
LIONWEB_VERSION_USED_BY_KOLASU)
jsonser.instanceResolver.addTree(StarLasuLWLanguage)
val language = jsonser.deserializeToNodes(FileInputStream(languageFile)).first() as Language
val existingKotlinClasses = KotlinCodeProcessor().classesDeclaredInDir(project.file("src/main/kotlin"))
Expand Down Expand Up @@ -164,7 +166,7 @@ class LionWebGradlePlugin : Plugin<Project> {
addKolasuModule("lionweb-gen")
project.dependencies.add("ksp", "com.strumenta.kolasu:kolasu-lionweb-ksp:${project.kolasuVersion}")
project.dependencies.add("api", "com.github.ajalt.clikt:clikt:3.5.0")
project.dependencies.add("api", "io.lionweb.lionweb-java:lionweb-java-2023.1-core:${project.lionwebJavaVersion}")
project.dependencies.add("api", "io.lionweb.lionweb-java:lionweb-java-2024.1-core:${project.lionwebJavaVersion}")
}

}
Loading