Skip to content

Commit

Permalink
Support usage of IdentityTransformation in the middle of an AST
Browse files Browse the repository at this point in the history
  • Loading branch information
ftomassetti committed Feb 6, 2025
1 parent 82a06fa commit b20cdf6
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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
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 @@ -238,22 +238,6 @@ data class ChildNodeFactory<Source, Target, Child : Any>(
*/
private val NO_CHILD_NODE = ChildNodeFactory<Any, Any, Any>("", { x -> x }, { _, _ -> }, Node::class)

val IDENTTITY_TRANSFORMATION: (source: Any?, parent: Node?, expectedType: KClass<out Node>) -> List<Node> = {
source: Any?, parent: Node?, expectedType: KClass<out Node> ->
when (source) {
null -> {
emptyList()
}
is Node -> {
source.parent = parent
listOf(source)
}
else -> {
throw IllegalArgumentException("An Identity Transformation expect to receive a Node")
}
}
}

/**
* Implementation of a tree-to-tree transformation. For each source node type, we can register a factory that knows how
* to create a transformed node. Then, this transformer can read metadata in the transformed node to recursively
Expand All @@ -275,7 +259,7 @@ open class ASTTransformer(
* fail.
*/
val faultTollerant: Boolean = !throwOnUnmappedNode,
val defaultTransformation: ((source: Any?, parent: Node?, expectedType: KClass<out Node>) -> List<Node>)? = null
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 Down Expand Up @@ -332,7 +316,7 @@ open class ASTTransformer(
}
} else {
if (defaultTransformation != null) {
nodes = defaultTransformation.invoke(source, parent, expectedType)
nodes = defaultTransformation.invoke(source, parent, expectedType, this)
} else if (allowGenericNode) {
val origin = asOrigin(source)
nodes = listOf(GenericNode(parent).withOrigin(origin))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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
Expand Down Expand Up @@ -396,6 +397,112 @@ class ASTTransformerTest {
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
)
}
}

data class BazRoot(var stmts: MutableList<BazStmt> = mutableListOf()) : Node()
Expand All @@ -406,3 +513,12 @@ 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)

0 comments on commit b20cdf6

Please sign in to comment.