From 60a5243ab1ea1bcd326fe696681474a0046ff2b4 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Wed, 22 Oct 2025 15:07:03 +0200 Subject: [PATCH 01/11] #417 rename NodeFactory -> Transform and remove deprecated elements from AST Transformers --- .../mapping/ParseTreeToASTTransformer.kt | 14 +- .../starlasu/transformation/GenericNodes.kt | 27 --- .../starlasu/transformation/Transformation.kt | 229 ++++++++---------- ...rivialFactoryOfParseTreeToASTTransform.kt} | 10 +- .../mapping/ParseTreeToASTTransformerTest.kt | 55 ++--- .../transformation/ASTTransformerTest.kt | 47 ++-- .../starlasu/javalib/ASTTransformer.java | 33 ++- .../javalib/ParseTreeToASTTransformer.java | 17 +- 8 files changed, 173 insertions(+), 259 deletions(-) delete mode 100644 core/src/main/kotlin/com/strumenta/starlasu/transformation/GenericNodes.kt rename core/src/main/kotlin/com/strumenta/starlasu/transformation/{TrivialFactoryOfParseTreeToASTNodeFactory.kt => TrivialFactoryOfParseTreeToASTTransform.kt} (95%) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index 47b46762..723c0a16 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -7,7 +7,7 @@ import com.strumenta.starlasu.model.Source import com.strumenta.starlasu.parsing.ParseTreeOrigin import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.transformation.ASTTransformer -import com.strumenta.starlasu.transformation.NodeFactory +import com.strumenta.starlasu.transformation.Transform import com.strumenta.starlasu.validation.Issue import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.tree.ParseTree @@ -21,10 +21,10 @@ open class ParseTreeToASTTransformer @JvmOverloads constructor( issues: MutableList = mutableListOf(), - allowGenericNode: Boolean = true, val source: Source? = null, throwOnUnmappedNode: Boolean = true, - ) : ASTTransformer(issues, allowGenericNode, throwOnUnmappedNode) { + faultTolerant: Boolean = throwOnUnmappedNode, + ) : ASTTransformer(issues, throwOnUnmappedNode, faultTolerant) { /** * Performs the transformation of a node and, recursively, its descendants. In addition to the overridden method, * it also assigns the parseTreeNode to the AST node so that it can keep track of its position. @@ -71,8 +71,8 @@ open class ParseTreeToASTTransformer * wrapper. When there is only a ParserRuleContext child we can transform * that child and return that result. */ - fun

registerNodeFactoryUnwrappingChild(kclass: KClass

): NodeFactory = - registerNodeFactory(kclass) { source, transformer, _ -> + fun

registerTransformUnwrappingChild(kclass: KClass

): Transform = + registerTransform(kclass) { source, transformer, _ -> val nodeChildren = source.children.filterIsInstance() require(nodeChildren.size == 1) { "Node $source (${source.javaClass}) has ${nodeChildren.size} " + @@ -84,6 +84,6 @@ open class ParseTreeToASTTransformer /** * Alternative to registerNodeFactoryUnwrappingChild(KClass) which is slightly more concise. */ - inline fun registerNodeFactoryUnwrappingChild(): NodeFactory = - registerNodeFactoryUnwrappingChild(P::class) + inline fun registerTransformUnwrappingChild(): Transform = + registerTransformUnwrappingChild(P::class) } diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/GenericNodes.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/GenericNodes.kt deleted file mode 100644 index 3569fc83..00000000 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/GenericNodes.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.strumenta.starlasu.transformation - -import com.strumenta.starlasu.model.ASTNode -import com.strumenta.starlasu.model.Node -import com.strumenta.starlasu.traversing.children - -/** - * A generic AST node. We use it to represent parts of a source tree that we don't know how to translate yet. - */ -@Deprecated("To be removed in Kolasu 2.0") -class GenericNode( - parent: ASTNode? = null, -) : Node() { - init { - this.parent = parent - } -} - -@Deprecated("To be removed in Kolasu 2.0") -fun ASTNode.findGenericNode(): GenericNode? = - if (this is GenericNode) { - this - } else { - this.children.firstNotNullOfOrNull { - it.findGenericNode() - } - } diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index d0f6013a..24ad6c49 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -1,7 +1,6 @@ package com.strumenta.starlasu.transformation import com.strumenta.starlasu.model.ASTNode -import com.strumenta.starlasu.model.GenericErrorNode import com.strumenta.starlasu.model.Origin import com.strumenta.starlasu.model.Position import com.strumenta.starlasu.model.PropertyDescription @@ -9,7 +8,6 @@ import com.strumenta.starlasu.model.asContainment import com.strumenta.starlasu.model.children import com.strumenta.starlasu.model.processProperties import com.strumenta.starlasu.model.withOrigin -import com.strumenta.starlasu.transformation.dummyInstance import com.strumenta.starlasu.validation.Issue import com.strumenta.starlasu.validation.IssueSeverity import kotlin.reflect.KClass @@ -26,22 +24,22 @@ import kotlin.reflect.full.superclasses /** * Factory that, given a tree node, will instantiate the corresponding transformed node. */ -class NodeFactory( - val constructor: (Source, ASTTransformer, NodeFactory) -> List, - var children: MutableMap?> = mutableMapOf(), +class Transform( + val constructor: (Source, ASTTransformer, Transform) -> List, + var children: MutableMap?> = mutableMapOf(), var finalizer: (Output) -> Unit = {}, var skipChildren: Boolean = false, var childrenSetAtConstruction: Boolean = false, ) { companion object { fun single( - singleConstructor: (Source, ASTTransformer, NodeFactory) -> Output?, - children: MutableMap?> = mutableMapOf(), + singleConstructor: (Source, ASTTransformer, Transform) -> Output?, + children: MutableMap?> = mutableMapOf(), finalizer: (Output) -> Unit = {}, skipChildren: Boolean = false, childrenSetAtConstruction: Boolean = false, - ): NodeFactory = - NodeFactory({ source, at, nf -> + ): Transform = + Transform({ source, at, nf -> val result = singleConstructor(source, at, nf) if (result == null) emptyList() else listOf(result) }, children, finalizer, skipChildren, childrenSetAtConstruction) @@ -54,7 +52,7 @@ class NodeFactory( * * Example using the scopedToType parameter: * ``` - * on.registerNodeFactory(SASParser.DatasetOptionContext::class) { ctx -> + * on.registerTransform(SASParser.DatasetOptionContext::class) { ctx -> * when { * ... * } @@ -73,7 +71,7 @@ class NodeFactory( targetProperty: KMutableProperty1<*, *>, sourceAccessor: Source.() -> Any?, scopedToType: KClass<*>, - ): NodeFactory = + ): Transform = withChild( get = { source -> source.sourceAccessor() }, set = (targetProperty as KMutableProperty1)::set, @@ -99,7 +97,7 @@ class NodeFactory( fun withChild( targetProperty: KMutableProperty1, sourceAccessor: Source.() -> Any?, - ): NodeFactory = + ): Transform = withChild( get = { source -> source.sourceAccessor() }, set = (targetProperty as KMutableProperty1)::set, @@ -116,7 +114,7 @@ class NodeFactory( fun withChild( targetProperty: KProperty1, sourceAccessor: Source.() -> Any?, - ): NodeFactory = + ): Transform = withChild( get = { source -> source.sourceAccessor() }, null, @@ -137,7 +135,7 @@ class NodeFactory( targetProperty: KProperty1, sourceAccessor: Source.() -> Any?, scopedToType: KClass<*>, - ): NodeFactory = + ): Transform = withChild( get = { source -> source.sourceAccessor() }, null, @@ -158,18 +156,18 @@ class NodeFactory( name: String, scopedToType: KClass<*>? = null, childType: KClass = ASTNode::class, - ): NodeFactory { + ): Transform { val prefix = if (scopedToType != null) scopedToType.qualifiedName + "#" else "" if (set == null) { // given we have no setter we MUST set the children at construction childrenSetAtConstruction = true } - children[prefix + name] = ChildNodeFactory(prefix + name, get, set, childType) + children[prefix + name] = ChildTransform(prefix + name, get, set, childType) return this } - fun withFinalizer(finalizer: (Output) -> Unit): NodeFactory { + fun withFinalizer(finalizer: (Output) -> Unit): Transform { this.finalizer = finalizer return this } @@ -181,7 +179,7 @@ class NodeFactory( * we may configure the transformer as follows: * * ```kotlin - * transformer.registerNodeFactory(XYZContext::class) { ctx -> transformer.transform(ctx.children[0]) } + * transformer.registerTransform(XYZContext::class) { ctx -> transformer.transform(ctx.children[0]) } * ``` * * However, if the result of `transformer.transform(ctx.children[0])` is an instance of a ASTNode with a child @@ -190,7 +188,7 @@ class NodeFactory( * be an instance of `XYZContext` that may not have a child with a corresponding name, and the transformation will * fail – or worse, it will map an unrelated node. */ - fun skipChildren(skip: Boolean = true): NodeFactory { + fun skipChildren(skip: Boolean = true): Transform { this.skipChildren = skip return this } @@ -234,7 +232,7 @@ class NodeFactory( * * @param type the property type if single, the collection's element type if multiple */ -data class ChildNodeFactory( +data class ChildTransform( val name: String, val get: (Source) -> Any?, val setter: ((Target, Child?) -> Unit)?, @@ -258,7 +256,7 @@ data class ChildNodeFactory( /** * Sentinel value used to represent the information that a given property is not a child node. */ -private val NO_CHILD_NODE = ChildNodeFactory("", { x -> x }, { _, _ -> }, ASTNode::class) +private val NO_CHILD_NODE = ChildTransform("", { x -> x }, { _, _ -> }, ASTNode::class) /** * Implementation of a tree-to-tree transformation. For each source node type, we can register a factory that knows how @@ -274,15 +272,13 @@ open class ASTTransformer * Additional issues found during the transformation process. */ val issues: MutableList = mutableListOf(), - @Deprecated("To be removed in Kolasu 1.6") - val allowGenericNode: Boolean = true, val throwOnUnmappedNode: Boolean = false, /** - * When the fault tollerant flag is set, in case a transformation fails we will add a node + * When the fault-tolerant flag is set, in case a transformation fails we will add a node * with the origin FailingASTTransformation. If the flag is not set, then the transformation will just * fail. */ - val faultTollerant: Boolean = !throwOnUnmappedNode, + val faultTolerant: Boolean = !throwOnUnmappedNode, val defaultTransformation: ( ( source: Any?, @@ -295,7 +291,7 @@ open class ASTTransformer /** * Factories that map from source tree node to target tree node. */ - val factories = mutableMapOf, NodeFactory<*, *>>() + val transforms = mutableMapOf, Transform<*, *>>() private val _knownClasses = mutableMapOf>>() val knownClasses: Map>> = _knownClasses @@ -339,35 +335,20 @@ open class ASTTransformer if (source is Collection<*>) { throw Error("Mapping error: received collection when value was expected") } - val factory = getNodeFactory(source::class as KClass) + val transform = getTransform(source::class as KClass) val nodes: List - if (factory != null) { - nodes = makeNodes(factory, source, allowGenericNode = allowGenericNode) - if (!factory.skipChildren && !factory.childrenSetAtConstruction) { - nodes.forEach { node -> setChildren(factory, source, node) } + if (transform != null) { + nodes = makeNodes(transform, source) + if (!transform.skipChildren && !transform.childrenSetAtConstruction) { + nodes.forEach { node -> setChildren(transform, source, node) } } nodes.forEach { node -> - factory.finalizer(node) + transform.finalizer(node) node.parent = parent } } else { 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( - Issue.semantic( - "Source node not mapped: ${source::class.qualifiedName}", - IssueSeverity.WARNING, - origin?.position, - ), - ) } else if (expectedType.isDirectlyOrIndirectlyInstantiable() && !throwOnUnmappedNode) { try { val node = expectedType.dummyInstance() @@ -389,18 +370,18 @@ open class ASTTransformer } protected open fun setChildren( - factory: NodeFactory, + transform: Transform, source: Any, node: ASTNode, ) { node.processProperties { pd -> - val childNodeFactory = factory.getChildNodeFactory(node, pd.name) - if (childNodeFactory != null) { - if (childNodeFactory != NO_CHILD_NODE) { - setChild(childNodeFactory, source, node, pd) + val childTransform = transform.getChildTransform(node, pd.name) + if (childTransform != null) { + if (childTransform != NO_CHILD_NODE) { + setChild(childTransform, source, node, pd) } } else { - factory.children[getChildKey(node.nodeType, pd.name)] = NO_CHILD_NODE + transform.children[getChildKey(node.nodeType, pd.name)] = NO_CHILD_NODE } } } @@ -408,12 +389,12 @@ open class ASTTransformer open fun asOrigin(source: Any): Origin? = if (source is Origin) source else null protected open fun setChild( - childNodeFactory: ChildNodeFactory<*, *, *>, + childTransform: ChildTransform<*, *, *>, source: Any, node: ASTNode, pd: PropertyDescription, ) { - val childFactory = childNodeFactory as ChildNodeFactory + val childFactory = childTransform as ChildTransform val childrenSource = childFactory.get(getSource(node, source)) val child: Any? = if (pd.multiple) { @@ -425,9 +406,9 @@ open class ASTTransformer transform(childrenSource, node) } try { - childNodeFactory.set(node, child) + childTransform.set(node, child) } catch (e: IllegalArgumentException) { - throw Error("Could not set child $childNodeFactory", e) + throw Error("Could not set child $childTransform", e) } } @@ -437,24 +418,10 @@ open class ASTTransformer ): Any = source protected open fun makeNodes( - factory: NodeFactory, + transform: Transform, source: S, - allowGenericNode: Boolean = true, ): List { - val nodes = - try { - factory.constructor(source, this, factory) - } catch (e: Exception) { - if (allowGenericNode) { - listOf( - GenericErrorNode( - e, - ), - ) - } else { - throw e - } - } + val nodes = transform.constructor(source, this, transform) nodes.forEach { node -> if (node.origin == null) { node.withOrigin(asOrigin(source)) @@ -463,63 +430,63 @@ open class ASTTransformer return nodes } - protected open fun getNodeFactory(kClass: KClass): NodeFactory? { - val factory = factories[kClass] - if (factory != null) { - return factory as NodeFactory + protected open fun getTransform(kClass: KClass): Transform? { + val transform = transforms[kClass] + if (transform != null) { + return transform as Transform } else { if (kClass == Any::class) { return null } for (superclass in kClass.superclasses) { - val nodeFactory = getNodeFactory(superclass as KClass) - if (nodeFactory != null) { - return nodeFactory + val transform = getTransform(superclass as KClass) + if (transform != null) { + return transform } } } return null } - fun registerNodeFactory( + fun registerTransform( kclass: KClass, - factory: (S, ASTTransformer, NodeFactory) -> T?, - ): NodeFactory { - val nodeFactory = NodeFactory.single(factory) - factories[kclass] = nodeFactory - return nodeFactory + factory: (S, ASTTransformer, Transform) -> T?, + ): Transform { + val transform = Transform.single(factory) + transforms[kclass] = transform + return transform } - fun registerMultipleNodeFactory( + fun registerMultipleTransform( kclass: KClass, - factory: (S, ASTTransformer, NodeFactory) -> List, - ): NodeFactory { - val nodeFactory = NodeFactory(factory) - factories[kclass] = nodeFactory - return nodeFactory + factory: (S, ASTTransformer, Transform) -> List, + ): Transform { + val transform = Transform(factory) + transforms[kclass] = transform + return transform } - fun registerNodeFactory( + fun registerTransform( kclass: KClass, factory: (S, ASTTransformer) -> T?, - ): NodeFactory = registerNodeFactory(kclass) { source, transformer, _ -> factory(source, transformer) } + ): Transform = registerTransform(kclass) { source, transformer, _ -> factory(source, transformer) } - inline fun registerNodeFactory( + inline fun registerTransform( crossinline factory: S.(ASTTransformer) -> T?, - ): NodeFactory = registerNodeFactory(S::class) { source, transformer, _ -> source.factory(transformer) } + ): Transform = registerTransform(S::class) { source, transformer, _ -> source.factory(transformer) } /** * We need T to be reified because we may need to install dummy classes of T. */ - inline fun registerNodeFactory( + inline fun registerTransform( kclass: KClass, crossinline factory: (S) -> T?, - ): NodeFactory = - registerNodeFactory(kclass) { input, _, _ -> + ): Transform = + registerTransform(kclass) { input, _, _ -> try { factory(input) } catch (t: NotImplementedError) { - if (faultTollerant) { + if (faultTolerant) { val node = T::class.dummyInstance() node.origin = FailingASTTransformation( @@ -532,7 +499,7 @@ open class ASTTransformer throw RuntimeException("Failed to transform $input into $kclass", t) } } catch (e: Exception) { - if (faultTollerant) { + if (faultTolerant) { val node = T::class.dummyInstance() node.origin = FailingASTTransformation( @@ -546,16 +513,16 @@ open class ASTTransformer } } - fun registerMultipleNodeFactory( + fun registerMultipleTransform( kclass: KClass, factory: (S) -> List, - ): NodeFactory = registerMultipleNodeFactory(kclass) { input, _, _ -> factory(input) } + ): Transform = registerMultipleTransform(kclass) { input, _, _ -> factory(input) } - inline fun registerNodeFactory(): NodeFactory = - registerNodeFactory(S::class, T::class) + inline fun registerTransform(): Transform = + registerTransform(S::class, T::class) - inline fun notTranslateDirectly(): NodeFactory = - registerNodeFactory { + inline fun notTranslateDirectly(): Transform = + registerTransform { throw java.lang.IllegalStateException( "A ASTNode of this type (${this.javaClass.canonicalName}) should never be translated directly. " + "It is expected that the container will not delegate the translation of this node but it " + @@ -566,9 +533,9 @@ open class ASTTransformer private fun parameterValue( kParameter: KParameter, source: S, - childNodeFactory: ChildNodeFactory, + childTransform: ChildTransform, ): ParameterValue = - when (val childSource = childNodeFactory.get.invoke(source)) { + when (val childSource = childTransform.get.invoke(source)) { null -> { AbsentParameterValue } @@ -612,30 +579,30 @@ open class ASTTransformer * @param nodeType the [ASTNode.nodeType] of the target node. Normally, the node type is the same as the class name, * however, [ASTNode] subclasses may want to override it, and in that case, the parameter must be provided explicitly. */ - fun registerNodeFactory( + fun registerTransform( source: KClass, target: KClass, nodeType: String = target.qualifiedName!!, - ): NodeFactory { + ): Transform { registerKnownClass(target) // We are looking for any constructor with does not take parameters or have default // values for all its parameters val emptyLikeConstructor = target.constructors.find { it.parameters.all { param -> param.isOptional } } - val nodeFactory = - NodeFactory.single( - { source: S, _, thisFactory -> + val transform = + Transform.single( + { source: S, _, thisTransform -> if (target.isSealed) { throw IllegalStateException("Unable to instantiate sealed class $target") } fun getConstructorParameterValue(kParameter: KParameter): ParameterValue { try { - val childNodeFactory = - thisFactory.getChildNodeFactory( + val childTransform = + thisTransform.getChildTransform( nodeType, kParameter.name!!, ) - if (childNodeFactory == null) { + if (childTransform == null) { if (kParameter.isOptional) { return AbsentParameterValue } @@ -643,7 +610,7 @@ open class ASTTransformer "We do not know how to produce parameter ${kParameter.name!!} for $target", ) } else { - return parameterValue(kParameter, source, childNodeFactory) + return parameterValue(kParameter, source, childTransform) } } catch (t: Throwable) { throw RuntimeException( @@ -658,7 +625,7 @@ open class ASTTransformer // so we should really check the value that `childrenSetAtConstruction` time has when we actually invoke // the factory. val instance = - if (thisFactory.childrenSetAtConstruction) { + if (thisTransform.childrenSetAtConstruction) { val constructor = target.preferredConstructor() val constructorParamValues = constructor.parameters @@ -701,16 +668,16 @@ open class ASTTransformer // Note that we are assuming that either we set no children at construction time or we set all of them childrenSetAtConstruction = emptyLikeConstructor == null, ) - factories[source] = nodeFactory - return nodeFactory + transforms[source] = transform + return transform } /** * Here the method needs to be inlined and the type parameter reified as in the invoked - * registerNodeFactory we need to access the nodeClass + * registerTransform we need to access the nodeClass */ inline fun registerIdentityTransformation(nodeClass: KClass) = - registerNodeFactory(nodeClass) { node -> node }.skipChildren() + registerTransform(nodeClass) { node -> node }.skipChildren() private fun registerKnownClass(target: KClass<*>) { val qualifiedName = target.qualifiedName @@ -740,21 +707,21 @@ open class ASTTransformer } } -private fun NodeFactory<*, *>.getChildNodeFactory( +private fun Transform<*, *>.getChildTransform( node: Target, parameterName: String, -): ChildNodeFactory? = getChildNodeFactory(node.nodeType, parameterName) +): ChildTransform? = getChildTransform(node.nodeType, parameterName) -private fun NodeFactory<*, *>.getChildNodeFactory( +private fun Transform<*, *>.getChildTransform( nodeType: String, parameterName: String, -): ChildNodeFactory? { +): ChildTransform? { val childKey = getChildKey(nodeType, parameterName) - var childNodeFactory = this.children[childKey] - if (childNodeFactory == null) { - childNodeFactory = this.children[parameterName] + var childTransform = this.children[childKey] + if (childTransform == null) { + childTransform = this.children[parameterName] } - return childNodeFactory as ChildNodeFactory? + return childTransform as ChildTransform? } private fun getChildKey( diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTNodeFactory.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt similarity index 95% rename from core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTNodeFactory.kt rename to core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt index bcfa0ebb..1b7bdfa5 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTNodeFactory.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt @@ -18,7 +18,7 @@ import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.memberProperties import kotlin.reflect.full.primaryConstructor -object TrivialFactoryOfParseTreeToASTNodeFactory { +object TrivialFactoryOfParseTreeToASTTransform { fun convertString( text: String, astTransformer: ASTTransformer, @@ -80,7 +80,7 @@ object TrivialFactoryOfParseTreeToASTNodeFactory { } } - inline fun trivialFactory( + inline fun trivialTransform( vararg nameConversions: Pair, ): ( S, @@ -132,9 +132,9 @@ object TrivialFactoryOfParseTreeToASTNodeFactory { inline fun ASTTransformer.registerTrivialPTtoASTConversion( vararg nameConversions: Pair, ) { - this.registerNodeFactory( + this.registerTransform( S::class, - TrivialFactoryOfParseTreeToASTNodeFactory.trivialFactory(*nameConversions), + TrivialFactoryOfParseTreeToASTTransform.trivialTransform(*nameConversions), ) } @@ -147,7 +147,7 @@ inline fun ParseTreeToASTTransformer ) inline fun ParseTreeToASTTransformer.unwrap(wrappingMember: KCallable<*>) { - this.registerNodeFactory(S::class) { parseTreeNode, astTransformer -> + this.registerTransform(S::class) { parseTreeNode, astTransformer -> val wrapped = wrappingMember.call(parseTreeNode) astTransformer.transform(wrapped) as T? } diff --git a/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt index 78709808..1f4baf4a 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt @@ -6,7 +6,6 @@ import com.strumenta.simplelang.AntlrScriptLexer import com.strumenta.simplelang.AntlrScriptParser import com.strumenta.simplelang.SimpleLangLexer import com.strumenta.simplelang.SimpleLangParser -import com.strumenta.starlasu.model.GenericErrorNode import com.strumenta.starlasu.model.Named import com.strumenta.starlasu.model.Node import com.strumenta.starlasu.model.Position @@ -18,8 +17,7 @@ import com.strumenta.starlasu.model.invalidPositions import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.testing.assertASTsAreEqual import com.strumenta.starlasu.transformation.ASTTransformer -import com.strumenta.starlasu.transformation.GenericNode -import com.strumenta.starlasu.transformation.TrivialFactoryOfParseTreeToASTNodeFactory +import com.strumenta.starlasu.transformation.TrivialFactoryOfParseTreeToASTTransform import com.strumenta.starlasu.transformation.registerTrivialPTtoASTConversion import com.strumenta.starlasu.transformation.unwrap import com.strumenta.starlasu.traversing.walk @@ -189,18 +187,8 @@ class ParseTreeToASTTransformerTest { CU( statements = listOf( - GenericErrorNode( - message = - "RuntimeException: Failed to transform [8] into " + - "class com.strumenta.simplelang.SimpleLangParser${'$'}SetStmtContext " + - "-> IllegalStateException: Parse error", - ).withParseTreeNode(pt.statement(0)), - GenericErrorNode( - message = - "RuntimeException: Failed to transform [8] into " + - "class com.strumenta.simplelang.SimpleLangParser${'$'}DisplayStmtContext " + - "-> IllegalStateException: Parse error", - ).withParseTreeNode(pt.statement(1)), + SetStatement(variable = "DUMMY").withParseTreeNode(pt.statement(0)), + DisplayIntStatement(value = 0).withParseTreeNode(pt.statement(1)), ), ).withParseTreeNode(pt) val transformedCU = transformer.transform(pt)!! as CU @@ -209,17 +197,6 @@ class ParseTreeToASTTransformerTest { assertNull(transformedCU.invalidPositions().firstOrNull()) } - @Test - fun testGenericNode() { - val code = "set foo = 123\ndisplay 456" - val lexer = SimpleLangLexer(CharStreams.fromString(code)) - val parser = SimpleLangParser(CommonTokenStream(lexer)) - val pt = parser.compilationUnit() - - val transformer = ParseTreeToASTTransformer() - assertASTsAreEqual(GenericNode(), transformer.transform(pt)!!) - } - @Test fun testGenericASTTransformer() { val code = "set foo = 123\ndisplay 456" @@ -247,9 +224,9 @@ class ParseTreeToASTTransformerTest { private fun configure(transformer: ASTTransformer) { transformer - .registerNodeFactory(SimpleLangParser.CompilationUnitContext::class, CU::class) + .registerTransform(SimpleLangParser.CompilationUnitContext::class, CU::class) .withChild(CU::statements, SimpleLangParser.CompilationUnitContext::statement) - transformer.registerNodeFactory(SimpleLangParser.DisplayStmtContext::class) { ctx -> + transformer.registerTransform(SimpleLangParser.DisplayStmtContext::class) { ctx -> if (ctx.exception != null || ctx.expression().exception != null) { // We throw a custom error so that we can check that it's recorded in the AST throw IllegalStateException("Parse error") @@ -263,7 +240,7 @@ class ParseTreeToASTTransformerTest { .toInt(), ) } - transformer.registerNodeFactory(SimpleLangParser.SetStmtContext::class) { ctx -> + transformer.registerTransform(SimpleLangParser.SetStmtContext::class) { ctx -> if (ctx.exception != null || ctx.expression().exception != null) { // We throw a custom error so that we can check that it's recorded in the AST throw IllegalStateException("Parse error") @@ -440,13 +417,13 @@ class ParseTreeToASTTransformerTest { transformer.registerTrivialPTtoASTConversion( AntlrScriptParser.Int_literal_expressionContext::INT_VALUE to SIntegerLiteral::value, ) - transformer.registerNodeFactory(AntlrScriptParser.String_literal_expressionContext::class) { pt, t -> + transformer.registerTransform(AntlrScriptParser.String_literal_expressionContext::class) { pt, t -> SStringLiteral(pt.text.removePrefix("'").removeSuffix("'")) } - transformer.registerNodeFactory(AntlrScriptParser.Div_mult_expressionContext::class) { pt, t -> + transformer.registerTransform(AntlrScriptParser.Div_mult_expressionContext::class) { pt, t -> when (pt.op.text) { "/" -> { - TrivialFactoryOfParseTreeToASTNodeFactory.trivialFactory< + TrivialFactoryOfParseTreeToASTTransform.trivialTransform< AntlrScriptParser .Div_mult_expressionContext, SDivision, @@ -457,7 +434,7 @@ class ParseTreeToASTTransformerTest { } "*" -> { - TrivialFactoryOfParseTreeToASTNodeFactory.trivialFactory< + TrivialFactoryOfParseTreeToASTTransform.trivialTransform< AntlrScriptParser .Div_mult_expressionContext, SMultiplication, @@ -470,10 +447,10 @@ class ParseTreeToASTTransformerTest { else -> TODO() } } - transformer.registerNodeFactory(AntlrScriptParser.Sum_sub_expressionContext::class) { pt, t -> + transformer.registerTransform(AntlrScriptParser.Sum_sub_expressionContext::class) { pt, t -> when (pt.op.text) { "+" -> { - TrivialFactoryOfParseTreeToASTNodeFactory.trivialFactory< + TrivialFactoryOfParseTreeToASTTransform.trivialTransform< AntlrScriptParser .Sum_sub_expressionContext, SSum, @@ -484,7 +461,7 @@ class ParseTreeToASTTransformerTest { } "-" -> { - TrivialFactoryOfParseTreeToASTNodeFactory.trivialFactory< + TrivialFactoryOfParseTreeToASTTransform.trivialTransform< AntlrScriptParser .Sum_sub_expressionContext, SSubtraction, @@ -581,11 +558,11 @@ class EntTransformer( issues: MutableList = mutableListOf(), ) : ParseTreeToASTTransformer(issues, allowGenericNode = false) { init { - registerNodeFactory(EntCtx::class) { ctx -> Ent(ctx.name) } + registerTransform(EntCtx::class) { ctx -> Ent(ctx.name) } .withChild(Ent::features, EntCtx::features) - registerNodeFactory(EntCtxFeature::class) { ctx -> EntFeature(name = ctx.name) } + registerTransform(EntCtxFeature::class) { ctx -> EntFeature(name = ctx.name) } .withChild(EntFeature::type, EntCtxFeature::type) - this.registerNodeFactory(EntCtxStringType::class, EntStringType::class) + this.registerTransform(EntCtxStringType::class, EntStringType::class) } } diff --git a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt index 6dbf0ecf..48f540dd 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt @@ -2,6 +2,7 @@ package com.strumenta.starlasu.transformation import com.strumenta.starlasu.mapping.translateCasted import com.strumenta.starlasu.mapping.translateList +import com.strumenta.starlasu.model.BaseASTNode import com.strumenta.starlasu.model.Node import com.strumenta.starlasu.model.children import com.strumenta.starlasu.model.hasValidParents @@ -120,7 +121,7 @@ class ASTTransformerTest { fun testIdentitiyTransformer() { val transformer = ASTTransformer() transformer - .registerNodeFactory(CU::class, CU::class) + .registerTransform(CU::class, CU::class) .withChild(CU::statements, CU::statements) transformer.registerIdentityTransformation(DisplayIntStatement::class) transformer.registerIdentityTransformation(SetStatement::class) @@ -145,8 +146,8 @@ class ASTTransformerTest { @Test fun translateBinaryExpression() { val myTransformer = - ASTTransformer(allowGenericNode = false).apply { - registerNodeFactory(GenericBinaryExpression::class) { source: GenericBinaryExpression -> + ASTTransformer().apply { + registerTransform(GenericBinaryExpression::class) { source: GenericBinaryExpression -> when (source.operator) { Operator.MULT -> Mult( @@ -179,15 +180,15 @@ class ASTTransformerTest { @Test fun translateAcrossLanguages() { val myTransformer = - ASTTransformer(allowGenericNode = false).apply { - registerNodeFactory(ALangIntLiteral::class) { source: ALangIntLiteral -> BLangIntLiteral(source.value) } - registerNodeFactory(ALangSum::class) { source: ALangSum -> + ASTTransformer().apply { + registerTransform(ALangIntLiteral::class) { source: ALangIntLiteral -> BLangIntLiteral(source.value) } + registerTransform(ALangSum::class) { source: ALangSum -> BLangSum( transform(source.left) as BLangExpression, transform(source.right) as BLangExpression, ) } - registerNodeFactory(ALangMult::class) { source: ALangMult -> + registerTransform(ALangMult::class) { source: ALangMult -> BLangMult( transform(source.left) as BLangExpression, transform(source.right) as BLangExpression, @@ -220,7 +221,7 @@ class ASTTransformerTest { @Test fun computeTypes() { val myTransformer = - ASTTransformer(allowGenericNode = false).apply { + ASTTransformer().apply { registerIdentityTransformation(TypedSum::class).withFinalizer { if (it.left.type == Type.INT && it.right.type == Type.INT) { it.type = Type.INT @@ -327,9 +328,9 @@ class ASTTransformerTest { fun testDroppingNodes() { val transformer = ASTTransformer() transformer - .registerNodeFactory(CU::class, CU::class) + .registerTransform(CU::class, CU::class) .withChild(CU::statements, CU::statements) - transformer.registerNodeFactory(DisplayIntStatement::class) { _ -> null } + transformer.registerTransform(DisplayIntStatement::class) { _ -> null } transformer.registerIdentityTransformation(SetStatement::class) val cu = @@ -349,11 +350,13 @@ class ASTTransformerTest { @Test fun testNestedOrigin() { + class GenericNode : BaseASTNode() + val transformer = ASTTransformer() transformer - .registerNodeFactory(CU::class, CU::class) + .registerTransform(CU::class, CU::class) .withChild(CU::statements, CU::statements) - transformer.registerNodeFactory(DisplayIntStatement::class) { s -> + transformer.registerTransform(DisplayIntStatement::class) { s -> s.withOrigin(GenericNode()) } @@ -374,9 +377,9 @@ class ASTTransformerTest { fun testTransformingOneNodeToMany() { val transformer = ASTTransformer() transformer - .registerNodeFactory(BarRoot::class, BazRoot::class) + .registerTransform(BarRoot::class, BazRoot::class) .withChild(BazRoot::stmts, BarRoot::stmts) - transformer.registerMultipleNodeFactory(BarStmt::class) { s -> + transformer.registerMultipleTransform(BarStmt::class) { s -> listOf(BazStmt("${s.desc}-1"), BazStmt("${s.desc}-2")) } @@ -406,9 +409,9 @@ class ASTTransformerTest { @Test fun testUnmappedNode() { - val transformer1 = ASTTransformer(allowGenericNode = false) + val transformer1 = ASTTransformer() transformer1 - .registerNodeFactory(BarRoot::class, BazRoot::class) + .registerTransform(BarRoot::class, BazRoot::class) .withChild(BazRoot::stmts) { stmts } val original = BarRoot( @@ -453,7 +456,7 @@ class ASTTransformerTest { @Test fun testPartialIdentityTransformation() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerNodeFactory(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ -> + transformer1.registerTransform(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ -> FooRoot( desc = "#children = ${original.children.size}", stmts = astTransformer.translateList(original.stmts), @@ -483,7 +486,7 @@ class ASTTransformerTest { @Test fun testIdentityTransformationOfIntermediateNodes() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerNodeFactory(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ -> + transformer1.registerTransform(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ -> FooRoot( desc = "#children = ${original.children.size}", stmts = astTransformer.translateList(original.stmts), @@ -529,7 +532,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA - transformer1.registerNodeFactory(AA::class) { original, t, _ -> + transformer1.registerTransform(AA::class) { original, t, _ -> BA("your_" + original.a.removePrefix("my_"), t.translateCasted(original.child)) } assertASTsAreEqual( @@ -553,7 +556,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA and AB - transformer1.registerNodeFactory(AB::class) { original, t, _ -> + transformer1.registerTransform(AB::class) { original, t, _ -> BB("your_" + original.b.removePrefix("my_"), t.translateCasted(original.child)) } assertASTsAreEqual( @@ -577,7 +580,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA and AB and AD - transformer1.registerNodeFactory(AD::class) { original, t, _ -> + transformer1.registerTransform(AD::class) { original, t, _ -> BD("your_" + original.d.removePrefix("my_")) } assertASTsAreEqual( @@ -605,7 +608,7 @@ class ASTTransformerTest { @Test fun testIdentityTransformationOfIntermediateNodesWithOrigin() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerNodeFactory(AA::class) { original, t, _ -> + transformer1.registerTransform(AA::class) { original, t, _ -> BA("your_" + original.a.removePrefix("my_"), t.translateCasted(original.child)) } val original = diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java index 1429b1eb..57521537 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java @@ -1,8 +1,7 @@ package com.strumenta.starlasu.javalib; import com.strumenta.starlasu.model.ASTNode; -import com.strumenta.starlasu.model.BaseASTNode; -import com.strumenta.starlasu.transformation.NodeFactory; +import com.strumenta.starlasu.transformation.Transform; import com.strumenta.starlasu.validation.Issue; import kotlin.Unit; import kotlin.jvm.functions.Function1; @@ -22,38 +21,34 @@ public ASTTransformer(@NotNull List issues) { super(issues); } - public ASTTransformer(@NotNull List issues, boolean allowGenericNode) { - super(issues, allowGenericNode); + public ASTTransformer(@NotNull List issues, boolean throwOnUnmappedNode) { + super(issues, throwOnUnmappedNode); } - public ASTTransformer(@NotNull List issues, boolean allowGenericNode, boolean throwOnUnmappedNode) { - super(issues, allowGenericNode, throwOnUnmappedNode); + public ASTTransformer(@NotNull List issues, boolean throwOnUnmappedNode, boolean faultTolerant) { + super(issues, throwOnUnmappedNode, faultTolerant); } - public ASTTransformer(@NotNull List issues, boolean allowGenericNode, boolean throwOnUnmappedNode, boolean faultTollerant) { - super(issues, allowGenericNode, throwOnUnmappedNode, faultTollerant); + public ASTTransformer(@NotNull List issues, boolean throwOnUnmappedNode, boolean faultTolerant, @Nullable Function4, ? super com.strumenta.starlasu.transformation.ASTTransformer, ? extends List> defaultTransformation) { + super(issues, throwOnUnmappedNode, faultTolerant, defaultTransformation); } - public ASTTransformer(@NotNull List issues, boolean allowGenericNode, boolean throwOnUnmappedNode, boolean faultTollerant, @Nullable Function4, ? super com.strumenta.starlasu.transformation.ASTTransformer, ? extends List> defaultTransformation) { - super(issues, allowGenericNode, throwOnUnmappedNode, faultTollerant, defaultTransformation); - } - - protected @NotNull NodeFactory registerNodeFactory(Class source, Class target) { + protected @NotNull Transform registerNodeFactory(Class source, Class target) { return registerNodeFactory(source, target, target.getName()); } - protected @NotNull NodeFactory registerNodeFactory( + protected @NotNull Transform registerNodeFactory( Class source, Class target, String nodeType ) { - return registerNodeFactory(getKotlinClass(source), getKotlinClass(target), nodeType); + return registerTransform(getKotlinClass(source), getKotlinClass(target), nodeType); } - protected NodeFactory registerNodeFactory(Class source, Function1 function) { - return registerNodeFactory(getKotlinClass(source), (s, t) -> function.invoke(s)); + protected Transform registerNodeFactory(Class source, Function1 function) { + return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); } - protected NodeFactory registerNodeFactory(Class source, Function2 function) { - return registerNodeFactory(getKotlinClass(source), function); + protected Transform registerNodeFactory(Class source, Function2 function) { + return registerTransform(getKotlinClass(source), function); } /** diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index 0e49f02b..d37305fa 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -1,10 +1,9 @@ package com.strumenta.starlasu.javalib; import com.strumenta.starlasu.model.ASTNode; -import com.strumenta.starlasu.model.BaseASTNode; import com.strumenta.starlasu.model.Source; import com.strumenta.starlasu.transformation.ASTTransformer; -import com.strumenta.starlasu.transformation.NodeFactory; +import com.strumenta.starlasu.transformation.Transform; import com.strumenta.starlasu.validation.Issue; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; @@ -32,22 +31,22 @@ public ParseTreeToASTTransformer(@NotNull List issues) { super(issues); } - protected @NotNull NodeFactory registerNodeFactory(Class source, Class target) { + protected @NotNull Transform registerNodeFactory(Class source, Class target) { return registerNodeFactory(source, target, target.getName()); } - protected @NotNull NodeFactory registerNodeFactory( + protected @NotNull Transform registerNodeFactory( Class source, Class target, String nodeType ) { - return registerNodeFactory(getKotlinClass(source), getKotlinClass(target), nodeType); + return registerTransform(getKotlinClass(source), getKotlinClass(target), nodeType); } - protected NodeFactory registerNodeFactory(Class source, Function1 function) { - return registerNodeFactory(getKotlinClass(source), (s, t) -> function.invoke(s)); + protected Transform registerNodeFactory(Class source, Function1 function) { + return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); } - protected NodeFactory registerNodeFactory(Class source, Function2 function) { - return registerNodeFactory(getKotlinClass(source), function); + protected Transform registerNodeFactory(Class source, Function2 function) { + return registerTransform(getKotlinClass(source), function); } } From f0b0ba2a8ddfb713193e856f45de89f5241e6d02 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 23 Oct 2025 10:39:34 +0200 Subject: [PATCH 02/11] #417 make AST transformers stateless and introduce context object #202 --- .../mapping/ParseTreeToASTTransformer.kt | 13 +- .../com/strumenta/starlasu/mapping/Support.kt | 27 +++- .../transformation/IdentityTransformation.kt | 11 +- .../starlasu/transformation/Transformation.kt | 122 +++++++++++------- ...TrivialFactoryOfParseTreeToASTTransform.kt | 21 +-- .../mapping/ParseTreeToASTTransformerTest.kt | 19 +-- .../transformation/ASTTransformerTest.kt | 47 ++++--- .../starlasu/javalib/ASTTransformer.java | 23 ++-- .../javalib/ParseTreeToASTTransformer.java | 25 ++-- .../starlasu/javalib/TransformerTest.java | 5 +- 10 files changed, 183 insertions(+), 130 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index 723c0a16..e9a8e143 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -8,7 +8,7 @@ import com.strumenta.starlasu.parsing.ParseTreeOrigin import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.transformation.ASTTransformer import com.strumenta.starlasu.transformation.Transform -import com.strumenta.starlasu.validation.Issue +import com.strumenta.starlasu.transformation.TransformationContext import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.tree.ParseTree import kotlin.reflect.KClass @@ -20,11 +20,10 @@ import kotlin.reflect.KClass open class ParseTreeToASTTransformer @JvmOverloads constructor( - issues: MutableList = mutableListOf(), val source: Source? = null, throwOnUnmappedNode: Boolean = true, faultTolerant: Boolean = throwOnUnmappedNode, - ) : ASTTransformer(issues, throwOnUnmappedNode, faultTolerant) { + ) : ASTTransformer(throwOnUnmappedNode, faultTolerant) { /** * Performs the transformation of a node and, recursively, its descendants. In addition to the overridden method, * it also assigns the parseTreeNode to the AST node so that it can keep track of its position. @@ -32,10 +31,10 @@ open class ParseTreeToASTTransformer */ override fun transformIntoNodes( source: Any?, - parent: ASTNode?, + context: TransformationContext, expectedType: KClass, ): List { - val transformed = super.transformIntoNodes(source, parent, expectedType) + val transformed = super.transformIntoNodes(source, context, expectedType) return transformed .map { node -> if (source is ParserRuleContext) { @@ -72,13 +71,13 @@ open class ParseTreeToASTTransformer * that child and return that result. */ fun

registerTransformUnwrappingChild(kclass: KClass

): Transform = - registerTransform(kclass) { source, transformer, _ -> + registerTransform(kclass) { source, context, _ -> val nodeChildren = source.children.filterIsInstance() require(nodeChildren.size == 1) { "Node $source (${source.javaClass}) has ${nodeChildren.size} " + "node children: $nodeChildren" } - transformer.transform(nodeChildren[0]) as Node + transform(nodeChildren[0], context) as Node } /** diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/Support.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/Support.kt index 6ef43580..d4916c8d 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/Support.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/Support.kt @@ -3,6 +3,7 @@ package com.strumenta.starlasu.mapping import com.strumenta.starlasu.model.ASTNode import com.strumenta.starlasu.parsing.getOriginalText import com.strumenta.starlasu.transformation.ASTTransformer +import com.strumenta.starlasu.transformation.TransformationContext import org.antlr.v4.runtime.ParserRuleContext /** @@ -13,8 +14,11 @@ import org.antlr.v4.runtime.ParserRuleContext * JPostIncrementExpr(translateCasted(expression().first())) * ``` */ -inline fun ASTTransformer.translateCasted(original: Any): T { - val result = transform(original, expectedType = T::class) +inline fun ASTTransformer.translateCasted( + original: Any, + context: TransformationContext, +): T { + val result = transform(original, context, expectedType = T::class) if (result is Nothing) { throw IllegalStateException("Transformation produced Nothing") } @@ -30,8 +34,11 @@ inline fun ASTTransformer.translateCasted(original: Any): * JExtendsType(translateCasted(pt.typeType()), translateList(pt.annotation())) * ``` */ -inline fun ASTTransformer.translateList(original: Collection?): MutableList = - original?.map { transformIntoNodes(it, expectedType = T::class) as List }?.flatten()?.toMutableList() +inline fun ASTTransformer.translateList( + original: Collection?, + context: TransformationContext, +): MutableList = + original?.map { transformIntoNodes(it, context, expectedType = T::class) as List }?.flatten()?.toMutableList() ?: mutableListOf() /** @@ -47,9 +54,12 @@ inline fun ASTTransformer.translateList(original: Collecti * ) * ``` */ -inline fun ASTTransformer.translateOptional(original: Any?): T? { +inline fun ASTTransformer.translateOptional( + original: Any?, + context: TransformationContext, +): T? { return original?.let { - val transformed = transform(it, expectedType = T::class) + val transformed = transform(it, context, expectedType = T::class) if (transformed == null) { return null } else { @@ -69,7 +79,10 @@ inline fun ASTTransformer.translateOptional(original: Any? * } * ``` */ -fun ParseTreeToASTTransformer.translateOnlyChild(parent: ParserRuleContext): T = translateCasted(parent.onlyChild) +fun ParseTreeToASTTransformer.translateOnlyChild( + parent: ParserRuleContext, + context: TransformationContext, +): T = translateCasted(parent.onlyChild, context) /** * It returns the only child (of type ParseRuleContext). If there is no children or more than diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/IdentityTransformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/IdentityTransformation.kt index 27e1b873..50767af5 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/IdentityTransformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/IdentityTransformation.kt @@ -12,12 +12,12 @@ import kotlin.reflect.jvm.javaType val IDENTTITY_TRANSFORMATION: ( source: Any?, - parent: ASTNode?, + context: TransformationContext, expectedType: KClass, astTransformer: ASTTransformer, ) -> List = { source: Any?, - parent: ASTNode?, + context: TransformationContext, expectedType: KClass, astTransformer: ASTTransformer, -> @@ -47,13 +47,14 @@ val IDENTTITY_TRANSFORMATION: ( // mt is ParameterizedType && mt.rawType == List::class.java -> mutableListOf() when { (parameter.type.classifier as KClass<*>).isSubclassOf(ASTNode::class) -> { - params[parameter] = astTransformer.transform(originalValue) + params[parameter] = astTransformer.transform(originalValue, context) } mt is ParameterizedType && mt.rawType == List::class.java && (mt.actualTypeArguments.first() as? Class<*>)?.kotlin?.isSubclassOf(ASTNode::class) == true -> { - params[parameter] = astTransformer.translateList(originalValue as List) + params[parameter] = + astTransformer.translateList(originalValue as List, context) } else -> params[parameter] = originalValue @@ -61,7 +62,7 @@ val IDENTTITY_TRANSFORMATION: ( } val newInstance = primaryConstructor.callBy(params) as ASTNode - newInstance.parent = parent + newInstance.parent = context.parent newInstance.origin = source listOf(newInstance) } diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index 24ad6c49..934ce863 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -25,22 +25,22 @@ import kotlin.reflect.full.superclasses * Factory that, given a tree node, will instantiate the corresponding transformed node. */ class Transform( - val constructor: (Source, ASTTransformer, Transform) -> List, + val constructor: (Source, TransformationContext, ASTTransformer, Transform) -> List, var children: MutableMap?> = mutableMapOf(), - var finalizer: (Output) -> Unit = {}, + var finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, var skipChildren: Boolean = false, var childrenSetAtConstruction: Boolean = false, ) { companion object { fun single( - singleConstructor: (Source, ASTTransformer, Transform) -> Output?, + singleConstructor: (Source, TransformationContext, ASTTransformer, Transform) -> Output?, children: MutableMap?> = mutableMapOf(), - finalizer: (Output) -> Unit = {}, + finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, skipChildren: Boolean = false, childrenSetAtConstruction: Boolean = false, ): Transform = - Transform({ source, at, nf -> - val result = singleConstructor(source, at, nf) + Transform({ source, ctx, at, nf -> + val result = singleConstructor(source, ctx, at, nf) if (result == null) emptyList() else listOf(result) }, children, finalizer, skipChildren, childrenSetAtConstruction) } @@ -167,11 +167,16 @@ class Transform( return this } - fun withFinalizer(finalizer: (Output) -> Unit): Transform { + fun withFinalizer(finalizer: (Output, TransformationContext) -> Unit): Transform { this.finalizer = finalizer return this } + fun withFinalizer(finalizer: (Output) -> Unit): Transform { + this.finalizer = { n, _ -> finalizer(n) } + return this + } + /** * Tells the transformer whether this factory already takes care of the node's children and no further computation * is desired on that subtree. E.g., when we're mapping an ANTLR parse tree, and we have a context that is only a @@ -258,6 +263,26 @@ data class ChildTransform( */ private val NO_CHILD_NODE = ChildTransform("", { x -> x }, { _, _ -> }, ASTNode::class) +open class TransformationContext + @JvmOverloads + constructor( + /** + * Additional issues found during the transformation process. + */ + val issues: MutableList = mutableListOf(), + var parent: ASTNode? = null, + ) { + fun addIssue( + message: String, + severity: IssueSeverity = IssueSeverity.ERROR, + position: Position? = null, + ): Issue { + val issue = Issue.semantic(message, severity, position) + issues.add(issue) + return issue + } + } + /** * 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 @@ -268,10 +293,6 @@ private val NO_CHILD_NODE = ChildTransform("", { x -> x }, { _, _ open class ASTTransformer @JvmOverloads constructor( - /** - * Additional issues found during the transformation process. - */ - val issues: MutableList = mutableListOf(), val throwOnUnmappedNode: Boolean = false, /** * When the fault-tolerant flag is set, in case a transformation fails we will add a node @@ -282,7 +303,7 @@ open class ASTTransformer val defaultTransformation: ( ( source: Any?, - parent: ASTNode?, + context: TransformationContext, expectedType: KClass, astTransformer: ASTTransformer, ) -> List @@ -302,10 +323,10 @@ open class ASTTransformer @JvmOverloads fun transform( source: Any?, - parent: ASTNode? = null, + context: TransformationContext = TransformationContext(), expectedType: KClass = ASTNode::class, ): ASTNode? { - val result = transformIntoNodes(source, parent, expectedType) + val result = transformIntoNodes(source, context, expectedType) return when (result.size) { 0 -> null 1 -> { @@ -326,7 +347,7 @@ open class ASTTransformer @JvmOverloads open fun transformIntoNodes( source: Any?, - parent: ASTNode? = null, + context: TransformationContext, expectedType: KClass = ASTNode::class, ): List { if (source == null) { @@ -338,17 +359,22 @@ open class ASTTransformer val transform = getTransform(source::class as KClass) val nodes: List if (transform != null) { - nodes = makeNodes(transform, source) + nodes = makeNodes(transform, source, context) + val parent = context.parent if (!transform.skipChildren && !transform.childrenSetAtConstruction) { - nodes.forEach { node -> setChildren(transform, source, node) } + nodes.forEach { node -> + context.parent = node + setChildren(transform, source, context) + } } + context.parent = parent nodes.forEach { node -> - transform.finalizer(node) + transform.finalizer(node, context) node.parent = parent } } else { if (defaultTransformation != null) { - nodes = defaultTransformation.invoke(source, parent, expectedType, this) + nodes = defaultTransformation.invoke(source, context, expectedType, this) } else if (expectedType.isDirectlyOrIndirectlyInstantiable() && !throwOnUnmappedNode) { try { val node = expectedType.dummyInstance() @@ -372,13 +398,14 @@ open class ASTTransformer protected open fun setChildren( transform: Transform, source: Any, - node: ASTNode, + context: TransformationContext, ) { + val node = context.parent!! node.processProperties { pd -> val childTransform = transform.getChildTransform(node, pd.name) if (childTransform != null) { if (childTransform != NO_CHILD_NODE) { - setChild(childTransform, source, node, pd) + setChild(childTransform, source, context, pd) } } else { transform.children[getChildKey(node.nodeType, pd.name)] = NO_CHILD_NODE @@ -391,19 +418,20 @@ open class ASTTransformer protected open fun setChild( childTransform: ChildTransform<*, *, *>, source: Any, - node: ASTNode, + context: TransformationContext, pd: PropertyDescription, ) { + val node = context.parent!! val childFactory = childTransform as ChildTransform val childrenSource = childFactory.get(getSource(node, source)) val child: Any? = if (pd.multiple) { (childrenSource as List<*>?) ?.map { - transformIntoNodes(it, node, childFactory.type) + transformIntoNodes(it, context, childFactory.type) }?.flatten() ?: listOf() } else { - transform(childrenSource, node) + transform(childrenSource, context) } try { childTransform.set(node, child) @@ -420,8 +448,9 @@ open class ASTTransformer protected open fun makeNodes( transform: Transform, source: S, + context: TransformationContext, ): List { - val nodes = transform.constructor(source, this, transform) + val nodes = transform.constructor(source, context, this, transform) nodes.forEach { node -> if (node.origin == null) { node.withOrigin(asOrigin(source)) @@ -450,7 +479,7 @@ open class ASTTransformer fun registerTransform( kclass: KClass, - factory: (S, ASTTransformer, Transform) -> T?, + factory: (S, TransformationContext, ASTTransformer, Transform) -> T?, ): Transform { val transform = Transform.single(factory) transforms[kclass] = transform @@ -459,7 +488,7 @@ open class ASTTransformer fun registerMultipleTransform( kclass: KClass, - factory: (S, ASTTransformer, Transform) -> List, + factory: (S, TransformationContext, ASTTransformer, Transform) -> List, ): Transform { val transform = Transform(factory) transforms[kclass] = transform @@ -468,12 +497,18 @@ open class ASTTransformer fun registerTransform( kclass: KClass, - factory: (S, ASTTransformer) -> T?, - ): Transform = registerTransform(kclass) { source, transformer, _ -> factory(source, transformer) } + factory: (S, TransformationContext, ASTTransformer) -> T?, + ): Transform = + registerTransform(kclass) { source, context, transformer, _ -> factory(source, context, transformer) } + + fun registerTransform( + kclass: KClass, + factory: (S, TransformationContext) -> T?, + ): Transform = registerTransform(kclass) { source, context, _, _ -> factory(source, context) } inline fun registerTransform( - crossinline factory: S.(ASTTransformer) -> T?, - ): Transform = registerTransform(S::class) { source, transformer, _ -> source.factory(transformer) } + crossinline factory: S.(TransformationContext) -> T?, + ): Transform = registerTransform(S::class) { source, context, _, _ -> source.factory(context) } /** * We need T to be reified because we may need to install dummy classes of T. @@ -482,7 +517,7 @@ open class ASTTransformer kclass: KClass, crossinline factory: (S) -> T?, ): Transform = - registerTransform(kclass) { input, _, _ -> + registerTransform(kclass) { input, _, _, _ -> try { factory(input) } catch (t: NotImplementedError) { @@ -516,7 +551,7 @@ open class ASTTransformer fun registerMultipleTransform( kclass: KClass, factory: (S) -> List, - ): Transform = registerMultipleTransform(kclass) { input, _, _ -> factory(input) } + ): Transform = registerMultipleTransform(kclass) { input, _, _, _ -> factory(input) } inline fun registerTransform(): Transform = registerTransform(S::class, T::class) @@ -534,6 +569,7 @@ open class ASTTransformer kParameter: KParameter, source: S, childTransform: ChildTransform, + context: TransformationContext, ): ParameterValue = when (val childSource = childTransform.get.invoke(source)) { null -> { @@ -543,7 +579,7 @@ open class ASTTransformer is List<*> -> { PresentParameterValue( childSource - .map { transformIntoNodes(it) } + .map { transformIntoNodes(it, context) } .flatten() .toMutableList(), ) @@ -562,9 +598,9 @@ open class ASTTransformer AbsentParameterValue } } else if ((kParameter.type.classifier as? KClass<*>)?.isSubclassOf(Collection::class) == true) { - PresentParameterValue(transformIntoNodes(childSource)) + PresentParameterValue(transformIntoNodes(childSource, context)) } else { - PresentParameterValue(transform(childSource)) + PresentParameterValue(transform(childSource, context)) } } } @@ -590,7 +626,7 @@ open class ASTTransformer val emptyLikeConstructor = target.constructors.find { it.parameters.all { param -> param.isOptional } } val transform = Transform.single( - { source: S, _, thisTransform -> + { source: S, context, _, thisTransform -> if (target.isSealed) { throw IllegalStateException("Unable to instantiate sealed class $target") } @@ -610,7 +646,7 @@ open class ASTTransformer "We do not know how to produce parameter ${kParameter.name!!} for $target", ) } else { - return parameterValue(kParameter, source, childTransform) + return parameterValue(kParameter, source, childTransform, context) } } catch (t: Throwable) { throw RuntimeException( @@ -695,16 +731,6 @@ open class ASTTransformer val set = _knownClasses.computeIfAbsent(packageName) { mutableSetOf() } set.add(target) } - - fun addIssue( - message: String, - severity: IssueSeverity = IssueSeverity.ERROR, - position: Position? = null, - ): Issue { - val issue = Issue.semantic(message, severity, position) - issues.add(issue) - return issue - } } private fun Transform<*, *>.getChildTransform( diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt index 1b7bdfa5..65964185 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt @@ -21,7 +21,6 @@ import kotlin.reflect.full.primaryConstructor object TrivialFactoryOfParseTreeToASTTransform { fun convertString( text: String, - astTransformer: ASTTransformer, expectedType: KType, ): Any? = when (expectedType.classifier) { @@ -45,15 +44,16 @@ object TrivialFactoryOfParseTreeToASTTransform { fun convert( value: Any?, astTransformer: ASTTransformer, + context: TransformationContext, expectedType: KType, ): Any? { when (value) { is Token -> { - return convertString(value.text, astTransformer, expectedType) + return convertString(value.text, expectedType) } is List<*> -> { - return value.map { convert(it, astTransformer, expectedType.arguments[0].type!!) } + return value.map { convert(it, astTransformer, context, expectedType.arguments[0].type!!) } } is ParserRuleContext -> { @@ -63,7 +63,7 @@ object TrivialFactoryOfParseTreeToASTTransform { } else -> { - astTransformer.transform(value) + astTransformer.transform(value, context) } } } @@ -73,7 +73,7 @@ object TrivialFactoryOfParseTreeToASTTransform { } is TerminalNode -> { - return convertString(value.text, astTransformer, expectedType) + return convertString(value.text, expectedType) } else -> TODO("value $value (${value.javaClass})") @@ -84,9 +84,10 @@ object TrivialFactoryOfParseTreeToASTTransform { vararg nameConversions: Pair, ): ( S, + TransformationContext, ASTTransformer, ) -> T? = - { parseTreeNode, astTransformer -> + { parseTreeNode, context, astTransformer -> val constructor = T::class.preferredConstructor() val args: Array = constructor.parameters @@ -108,11 +109,11 @@ object TrivialFactoryOfParseTreeToASTTransform { ) } else { val value = method.call(parseTreeNode) - convert(value, astTransformer, it.type) + convert(value, astTransformer, context, it.type) } } else { val value = parseTreeMember.get(parseTreeNode) - convert(value, astTransformer, it.type) + convert(value, astTransformer, context, it.type) } }.toTypedArray() try { @@ -147,9 +148,9 @@ inline fun ParseTreeToASTTransformer ) inline fun ParseTreeToASTTransformer.unwrap(wrappingMember: KCallable<*>) { - this.registerTransform(S::class) { parseTreeNode, astTransformer -> + this.registerTransform(S::class) { parseTreeNode, context -> val wrapped = wrappingMember.call(parseTreeNode) - astTransformer.transform(wrapped) as T? + transform(wrapped, context) as T? } } diff --git a/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt index 1f4baf4a..71ff11da 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt @@ -21,7 +21,6 @@ import com.strumenta.starlasu.transformation.TrivialFactoryOfParseTreeToASTTrans import com.strumenta.starlasu.transformation.registerTrivialPTtoASTConversion import com.strumenta.starlasu.transformation.unwrap import com.strumenta.starlasu.traversing.walk -import com.strumenta.starlasu.validation.Issue import org.antlr.v4.runtime.ANTLRErrorListener import org.antlr.v4.runtime.CharStreams import org.antlr.v4.runtime.CommonTokenStream @@ -328,7 +327,7 @@ class ParseTreeToASTTransformerTest { @Test fun testSimpleEntitiesTransformer() { - val transformer = ParseTreeToASTTransformer(allowGenericNode = false) + val transformer = ParseTreeToASTTransformer() transformer.registerTrivialPTtoASTConversion() transformer.registerTrivialPTtoASTConversion() val expectedAST = @@ -355,7 +354,7 @@ class ParseTreeToASTTransformerTest { @Test fun testEntitiesWithFeaturesTransformer() { - val transformer = ParseTreeToASTTransformer(allowGenericNode = false) + val transformer = ParseTreeToASTTransformer() transformer.registerTrivialPTtoASTConversion() transformer.registerTrivialPTtoASTConversion() transformer.registerTrivialPTtoASTConversion() @@ -405,7 +404,7 @@ class ParseTreeToASTTransformerTest { @Test fun testScriptTransformer() { - val transformer = ParseTreeToASTTransformer(allowGenericNode = false) + val transformer = ParseTreeToASTTransformer() transformer.registerTrivialPTtoASTConversion() transformer.registerTrivialPTtoASTConversion( AntlrScriptParser.Create_statementContext::var_name to SCreateStatement::name, @@ -420,7 +419,7 @@ class ParseTreeToASTTransformerTest { transformer.registerTransform(AntlrScriptParser.String_literal_expressionContext::class) { pt, t -> SStringLiteral(pt.text.removePrefix("'").removeSuffix("'")) } - transformer.registerTransform(AntlrScriptParser.Div_mult_expressionContext::class) { pt, t -> + transformer.registerTransform(AntlrScriptParser.Div_mult_expressionContext::class) { pt, c, t -> when (pt.op.text) { "/" -> { TrivialFactoryOfParseTreeToASTTransform.trivialTransform< @@ -429,6 +428,7 @@ class ParseTreeToASTTransformerTest { SDivision, >()( pt, + c, t, ) } @@ -440,6 +440,7 @@ class ParseTreeToASTTransformerTest { SMultiplication, >()( pt, + c, t, ) } @@ -447,7 +448,7 @@ class ParseTreeToASTTransformerTest { else -> TODO() } } - transformer.registerTransform(AntlrScriptParser.Sum_sub_expressionContext::class) { pt, t -> + transformer.registerTransform(AntlrScriptParser.Sum_sub_expressionContext::class) { pt, c, t -> when (pt.op.text) { "+" -> { TrivialFactoryOfParseTreeToASTTransform.trivialTransform< @@ -456,6 +457,7 @@ class ParseTreeToASTTransformerTest { SSum, >()( pt, + c, t, ) } @@ -467,6 +469,7 @@ class ParseTreeToASTTransformerTest { SSubtraction, >()( pt, + c, t, ) } @@ -554,9 +557,7 @@ class ParseTreeToASTTransformerTest { } } -class EntTransformer( - issues: MutableList = mutableListOf(), -) : ParseTreeToASTTransformer(issues, allowGenericNode = false) { +class EntTransformer : ParseTreeToASTTransformer() { init { registerTransform(EntCtx::class) { ctx -> Ent(ctx.name) } .withChild(Ent::features, EntCtx::features) diff --git a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt index 48f540dd..349cd31a 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt @@ -222,11 +222,11 @@ class ASTTransformerTest { fun computeTypes() { val myTransformer = ASTTransformer().apply { - registerIdentityTransformation(TypedSum::class).withFinalizer { + registerIdentityTransformation(TypedSum::class).withFinalizer { it, c -> if (it.left.type == Type.INT && it.right.type == Type.INT) { it.type = Type.INT } else { - addIssue( + c.addIssue( "Illegal types for sum operation. Only integer values are allowed. " + "Found: (${it.left.type?.name ?: "null"}, ${it.right.type?.name ?: "null"})", IssueSeverity.ERROR, @@ -234,11 +234,11 @@ class ASTTransformerTest { ) } } - registerIdentityTransformation(TypedConcat::class).withFinalizer { + registerIdentityTransformation(TypedConcat::class).withFinalizer { it, c -> if (it.left.type == Type.STR && it.right.type == Type.STR) { it.type = Type.STR } else { - addIssue( + c.addIssue( "Illegal types for concat operation. Only string values are allowed. " + "Found: (${it.left.type?.name ?: "null"}, ${it.right.type?.name ?: "null"})", IssueSeverity.ERROR, @@ -249,6 +249,7 @@ class ASTTransformerTest { registerIdentityTransformation(TypedLiteral::class) } // sum - legal + val context = TransformationContext() assertASTsAreEqual( TypedSum( TypedLiteral("1", Type.INT), @@ -260,9 +261,10 @@ class ASTTransformerTest { TypedLiteral("1", Type.INT), TypedLiteral("1", Type.INT), ), + context, )!!, ) - assertEquals(0, myTransformer.issues.size) + assertEquals(0, context.issues.size) // concat - legal assertASTsAreEqual( TypedConcat( @@ -275,9 +277,10 @@ class ASTTransformerTest { TypedLiteral("test", Type.STR), TypedLiteral("test", Type.STR), ), + context, )!!, ) - assertEquals(0, myTransformer.issues.size) + assertEquals(0, context.issues.size) // sum - error assertASTsAreEqual( TypedSum( @@ -290,15 +293,16 @@ class ASTTransformerTest { TypedLiteral("1", Type.INT), TypedLiteral("test", Type.STR), ), + context, )!!, ) - assertEquals(1, myTransformer.issues.size) + assertEquals(1, context.issues.size) assertEquals( Issue.semantic( "Illegal types for sum operation. Only integer values are allowed. Found: (INT, STR)", IssueSeverity.ERROR, ), - myTransformer.issues[0], + context.issues[0], ) // concat - error assertASTsAreEqual( @@ -312,15 +316,16 @@ class ASTTransformerTest { TypedLiteral("1", Type.INT), TypedLiteral("test", Type.STR), ), + context, )!!, ) - assertEquals(2, myTransformer.issues.size) + assertEquals(2, context.issues.size) assertEquals( Issue.semantic( "Illegal types for concat operation. Only string values are allowed. Found: (INT, STR)", IssueSeverity.ERROR, ), - myTransformer.issues[1], + context.issues[1], ) } @@ -456,10 +461,10 @@ class ASTTransformerTest { @Test fun testPartialIdentityTransformation() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerTransform(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ -> + transformer1.registerTransform(BarRoot::class) { original: BarRoot, c -> FooRoot( desc = "#children = ${original.children.size}", - stmts = astTransformer.translateList(original.stmts), + stmts = transformer1.translateList(original.stmts, c), ) } val original = @@ -486,10 +491,10 @@ class ASTTransformerTest { @Test fun testIdentityTransformationOfIntermediateNodes() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerTransform(BarRoot::class) { original: BarRoot, astTransformer: ASTTransformer, _ -> + transformer1.registerTransform(BarRoot::class) { original: BarRoot, c -> FooRoot( desc = "#children = ${original.children.size}", - stmts = astTransformer.translateList(original.stmts), + stmts = transformer1.translateList(original.stmts, c), ) } val original = @@ -532,8 +537,8 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA - transformer1.registerTransform(AA::class) { original, t, _ -> - BA("your_" + original.a.removePrefix("my_"), t.translateCasted(original.child)) + transformer1.registerTransform(AA::class) { original, c -> + BA("your_" + original.a.removePrefix("my_"), transformer1.translateCasted(original.child, c)) } assertASTsAreEqual( BA( @@ -556,8 +561,8 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA and AB - transformer1.registerTransform(AB::class) { original, t, _ -> - BB("your_" + original.b.removePrefix("my_"), t.translateCasted(original.child)) + transformer1.registerTransform(AB::class) { original, c, _ -> + BB("your_" + original.b.removePrefix("my_"), transformer1.translateCasted(original.child, c)) } assertASTsAreEqual( BA( @@ -580,7 +585,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA and AB and AD - transformer1.registerTransform(AD::class) { original, t, _ -> + transformer1.registerTransform(AD::class) { original -> BD("your_" + original.d.removePrefix("my_")) } assertASTsAreEqual( @@ -608,8 +613,8 @@ class ASTTransformerTest { @Test fun testIdentityTransformationOfIntermediateNodesWithOrigin() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerTransform(AA::class) { original, t, _ -> - BA("your_" + original.a.removePrefix("my_"), t.translateCasted(original.child)) + transformer1.registerTransform(AA::class) { original, c -> + BA("your_" + original.a.removePrefix("my_"), transformer1.translateCasted(original.child, c)) } val original = AA( diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java index 57521537..4712ea86 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java @@ -2,10 +2,12 @@ import com.strumenta.starlasu.model.ASTNode; import com.strumenta.starlasu.transformation.Transform; +import com.strumenta.starlasu.transformation.TransformationContext; import com.strumenta.starlasu.validation.Issue; import kotlin.Unit; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; +import kotlin.jvm.functions.Function3; import kotlin.jvm.functions.Function4; import kotlin.reflect.KClass; import org.jetbrains.annotations.NotNull; @@ -17,20 +19,20 @@ import static kotlin.jvm.JvmClassMappingKt.getKotlinClass; public class ASTTransformer extends com.strumenta.starlasu.transformation.ASTTransformer { - public ASTTransformer(@NotNull List issues) { - super(issues); + public ASTTransformer() { + super(); } - public ASTTransformer(@NotNull List issues, boolean throwOnUnmappedNode) { - super(issues, throwOnUnmappedNode); + public ASTTransformer(boolean throwOnUnmappedNode) { + super(throwOnUnmappedNode); } - public ASTTransformer(@NotNull List issues, boolean throwOnUnmappedNode, boolean faultTolerant) { - super(issues, throwOnUnmappedNode, faultTolerant); + public ASTTransformer(boolean throwOnUnmappedNode, boolean faultTolerant) { + super(throwOnUnmappedNode, faultTolerant); } - public ASTTransformer(@NotNull List issues, boolean throwOnUnmappedNode, boolean faultTolerant, @Nullable Function4, ? super com.strumenta.starlasu.transformation.ASTTransformer, ? extends List> defaultTransformation) { - super(issues, throwOnUnmappedNode, faultTolerant, defaultTransformation); + public ASTTransformer(boolean throwOnUnmappedNode, boolean faultTolerant, @Nullable Function4, ? super com.strumenta.starlasu.transformation.ASTTransformer, ? extends List> defaultTransformation) { + super(throwOnUnmappedNode, faultTolerant, defaultTransformation); } protected @NotNull Transform registerNodeFactory(Class source, Class target) { @@ -47,7 +49,10 @@ protected Transform registerNodeFactory(Class so return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); } - protected Transform registerNodeFactory(Class source, Function2 function) { + protected Transform registerNodeFactory( + Class source, + Function3 function + ) { return registerTransform(getKotlinClass(source), function); } diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index d37305fa..61f76160 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -4,9 +4,11 @@ import com.strumenta.starlasu.model.Source; import com.strumenta.starlasu.transformation.ASTTransformer; import com.strumenta.starlasu.transformation.Transform; +import com.strumenta.starlasu.transformation.TransformationContext; import com.strumenta.starlasu.validation.Issue; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; +import kotlin.jvm.functions.Function3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,22 +17,18 @@ import static kotlin.jvm.JvmClassMappingKt.getKotlinClass; public class ParseTreeToASTTransformer extends com.strumenta.starlasu.mapping.ParseTreeToASTTransformer { - public ParseTreeToASTTransformer(@NotNull List issues, boolean allowGenericNode, @Nullable Source source, boolean throwOnUnmappedNode) { - super(issues, allowGenericNode, source, throwOnUnmappedNode); + public ParseTreeToASTTransformer() { + super(); } - public ParseTreeToASTTransformer(@NotNull List issues, boolean allowGenericNode, @Nullable Source source) { - super(issues, allowGenericNode, source); + public ParseTreeToASTTransformer(@Nullable Source source) { + super(source); } - public ParseTreeToASTTransformer(@NotNull List issues, boolean allowGenericNode) { - super(issues, allowGenericNode); + public ParseTreeToASTTransformer(@Nullable Source source, boolean throwOnUnmappedNode) { + super(source, throwOnUnmappedNode); } - - public ParseTreeToASTTransformer(@NotNull List issues) { - super(issues); - } - + protected @NotNull Transform registerNodeFactory(Class source, Class target) { return registerNodeFactory(source, target, target.getName()); } @@ -45,7 +43,10 @@ protected Transform registerNodeFactory(Class so return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); } - protected Transform registerNodeFactory(Class source, Function2 function) { + protected Transform registerNodeFactory( + Class source, + Function3 function + ) { return registerTransform(getKotlinClass(source), function); } diff --git a/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java b/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java index 79528ce1..146a81ea 100644 --- a/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java +++ b/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java @@ -1,6 +1,7 @@ package com.strumenta.starlasu.javalib; import com.strumenta.starlasu.model.*; +import com.strumenta.starlasu.transformation.TransformationContext; import com.strumenta.starlasu.validation.Issue; import org.junit.Test; @@ -14,13 +15,13 @@ public class TransformerTest { @Test public void testJavaNodes() { ArrayList issues = new ArrayList<>(); - ASTTransformer t = new ASTTransformer(issues, false); + ASTTransformer t = new ASTTransformer( false); t.registerNodeFactory(Node1.class, Node1.class) .withChild(Node1::getNode2, setter(Node1::setNode2), "node2"); t.registerNodeFactory(Node2.class, Node2.class); Node1 node1 = new Node1(); node1.setNode2(new Node2()); - ASTNode transformed = t.transform(node1); + ASTNode transformed = t.transform(node1, new TransformationContext(issues)); assertTrue(issues.toString(), issues.isEmpty()); assertASTsAreEqual(node1, transformed); } From 04c4b06b3e5e008001782cc491c1d664208bc9de Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 23 Oct 2025 11:47:35 +0200 Subject: [PATCH 03/11] AST transformers in case of parse errors #395 --- .../mapping/ParseTreeToASTTransformer.kt | 17 ++++++++++ .../starlasu/transformation/Transformation.kt | 32 ++++++++++++++----- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index e9a8e143..cc21e7e0 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -7,6 +7,7 @@ import com.strumenta.starlasu.model.Source import com.strumenta.starlasu.parsing.ParseTreeOrigin import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.transformation.ASTTransformer +import com.strumenta.starlasu.transformation.FailingASTTransformation import com.strumenta.starlasu.transformation.Transform import com.strumenta.starlasu.transformation.TransformationContext import org.antlr.v4.runtime.ParserRuleContext @@ -34,6 +35,22 @@ open class ParseTreeToASTTransformer context: TransformationContext, expectedType: KClass, ): List { + if (source is ParserRuleContext && source.exception != null) { + val origin = + if (faultTolerant) { + FailingASTTransformation( + asOrigin(source), + "Failed to transform $source into $expectedType because of an error (${source.exception.message})", + ) + } else { + throw RuntimeException("Failed to transform $source into $expectedType", source.exception) + } + val nodes = defaultNodes(source, context, expectedType) + nodes.forEach { node -> + node.origin = origin + } + return nodes + } val transformed = super.transformIntoNodes(source, context, expectedType) return transformed .map { node -> diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index 934ce863..b39afa2a 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -373,13 +373,32 @@ open class ASTTransformer node.parent = parent } } else { - if (defaultTransformation != null) { - nodes = defaultTransformation.invoke(source, context, expectedType, this) - } else if (expectedType.isDirectlyOrIndirectlyInstantiable() && !throwOnUnmappedNode) { + if (defaultTransformation == null && throwOnUnmappedNode) { + throw IllegalStateException( + "Unable to translate node $source (class ${source::class.qualifiedName})", + ) + } + nodes = defaultNodes(source, context, expectedType) + nodes.filter { it.origin == null }.forEach { node -> + node.origin = MissingASTTransformation(asOrigin(source), source, expectedType) + } + } + return nodes + } + + protected fun defaultNodes( + source: Any, + context: TransformationContext, + expectedType: KClass, + nullable: Boolean = false, + ): List = + defaultTransformation?.invoke(source, context, expectedType, this) + ?: if (nullable) { + listOf() + } else if (expectedType.isDirectlyOrIndirectlyInstantiable()) { try { val node = expectedType.dummyInstance() - node.origin = MissingASTTransformation(asOrigin(source), source, expectedType) - nodes = listOf(node) + listOf(node) } catch (e: Exception) { throw IllegalStateException( "Unable to instantiate desired node type ${expectedType.qualifiedName}", @@ -391,9 +410,6 @@ open class ASTTransformer "Unable to translate node $source (class ${source::class.qualifiedName})", ) } - } - return nodes - } protected open fun setChildren( transform: Transform, From 0ffa8deca5f25a3382086c09216305172fe98cc3 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 23 Oct 2025 12:10:18 +0200 Subject: [PATCH 04/11] #291 make KolasuParser aware of AST transformers and skip assignParents for performance --- .../starlasu/parsing/KolasuParser.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/parsing/KolasuParser.kt b/core/src/main/kotlin/com/strumenta/starlasu/parsing/KolasuParser.kt index 638e5375..e1c0bf0b 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/parsing/KolasuParser.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/parsing/KolasuParser.kt @@ -6,6 +6,8 @@ import com.strumenta.starlasu.model.PropertyDescription import com.strumenta.starlasu.model.Source import com.strumenta.starlasu.model.assignParents import com.strumenta.starlasu.model.processProperties +import com.strumenta.starlasu.transformation.ASTTransformer +import com.strumenta.starlasu.transformation.TransformationContext import com.strumenta.starlasu.traversing.walk import com.strumenta.starlasu.validation.Issue import com.strumenta.starlasu.validation.IssueType @@ -164,14 +166,26 @@ abstract class KolasuParser, source: Source? = null, - ): R? + ): R? { + val transformer = setupASTTransformer() + if (transformer == null) { + throw IllegalStateException("No AST transformer available, and parseTreeToAst not overridden.") + } else { + return transformer.transform(parseTreeRoot, TransformationContext(issues = issues)) as R? + } + } + + protected open fun setupASTTransformer(): ASTTransformer? = null protected open fun attachListeners( parser: P, @@ -312,7 +326,6 @@ abstract class KolasuParser Date: Thu, 23 Oct 2025 12:41:13 +0200 Subject: [PATCH 05/11] #417 move the Source to the transformation context (to make the AST transformers really stateless) and --- .../mapping/ParseTreeToASTTransformer.kt | 31 ++++++++------- .../starlasu/transformation/Transformation.kt | 38 +++++++++++++++---- .../javalib/ParseTreeToASTTransformer.java | 8 ++-- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index cc21e7e0..60eefb8f 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -3,7 +3,6 @@ package com.strumenta.starlasu.mapping import com.strumenta.starlasu.model.ASTNode import com.strumenta.starlasu.model.Node import com.strumenta.starlasu.model.Origin -import com.strumenta.starlasu.model.Source import com.strumenta.starlasu.parsing.ParseTreeOrigin import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.transformation.ASTTransformer @@ -21,7 +20,6 @@ import kotlin.reflect.KClass open class ParseTreeToASTTransformer @JvmOverloads constructor( - val source: Source? = null, throwOnUnmappedNode: Boolean = true, faultTolerant: Boolean = throwOnUnmappedNode, ) : ASTTransformer(throwOnUnmappedNode, faultTolerant) { @@ -36,15 +34,14 @@ open class ParseTreeToASTTransformer expectedType: KClass, ): List { if (source is ParserRuleContext && source.exception != null) { + if (!faultTolerant) { + throw RuntimeException("Failed to transform $source into $expectedType", source.exception) + } val origin = - if (faultTolerant) { - FailingASTTransformation( - asOrigin(source), - "Failed to transform $source into $expectedType because of an error (${source.exception.message})", - ) - } else { - throw RuntimeException("Failed to transform $source into $expectedType", source.exception) - } + FailingASTTransformation( + asOrigin(source, context), + "Failed to transform $source into $expectedType because of an error (${source.exception.message})", + ) val nodes = defaultNodes(source, context, expectedType) nodes.forEach { node -> node.origin = origin @@ -56,9 +53,9 @@ open class ParseTreeToASTTransformer .map { node -> if (source is ParserRuleContext) { if (node.origin == null) { - node.withParseTreeNode(source, this.source) + node.withParseTreeNode(source, context.source) } else if (node.position != null && node.source == null) { - node.position!!.source = this.source + node.position!!.source = context.source } } return listOf(node) @@ -73,7 +70,15 @@ open class ParseTreeToASTTransformer return if (origin is ParseTreeOrigin) origin.parseTree else source } - override fun asOrigin(source: Any): Origin? = if (source is ParseTree) ParseTreeOrigin(source) else null + override fun asOrigin( + source: Any, + context: TransformationContext, + ): Origin? = + if (source is ParseTree) { + ParseTreeOrigin(source, context.source) + } else { + null + } override fun asString(source: Any): String? = if (source is ParseTree) { diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index b39afa2a..4dc7681b 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -4,6 +4,7 @@ import com.strumenta.starlasu.model.ASTNode import com.strumenta.starlasu.model.Origin import com.strumenta.starlasu.model.Position import com.strumenta.starlasu.model.PropertyDescription +import com.strumenta.starlasu.model.Source import com.strumenta.starlasu.model.asContainment import com.strumenta.starlasu.model.children import com.strumenta.starlasu.model.processProperties @@ -263,6 +264,18 @@ data class ChildTransform( */ private val NO_CHILD_NODE = ChildTransform("", { x -> x }, { _, _ -> }, ASTNode::class) +/** + * A context holding metadata relevant to the transformation process, such as parent node reference, associated source, + * and issues discovered during transformation. + * + * This is an open class so that specialized AST transformers can extend it to track additional information. + * + * @constructor Creates an instance of the transformation context. + * @param issues A mutable list of issues encountered during the transformation. + * Defaults to an empty list if not provided. + * @param parent The parent [ASTNode] in the hierarchy, if available. Defaults to null. + * @param source The [Source] object associated with this context, if any. Defaults to null. + */ open class TransformationContext @JvmOverloads constructor( @@ -271,13 +284,19 @@ open class TransformationContext */ val issues: MutableList = mutableListOf(), var parent: ASTNode? = null, + var source: Source? = null, ) { fun addIssue( message: String, severity: IssueSeverity = IssueSeverity.ERROR, position: Position? = null, ): Issue { - val issue = Issue.semantic(message, severity, position) + val issue = + Issue.semantic( + message, + severity, + position?.apply { source = this@TransformationContext.source }, + ) issues.add(issue) return issue } @@ -295,7 +314,7 @@ open class ASTTransformer constructor( val throwOnUnmappedNode: Boolean = false, /** - * When the fault-tolerant flag is set, in case a transformation fails we will add a node + * When the fault-tolerant flag is set, in case a transformation fails, we will add a node * with the origin FailingASTTransformation. If the flag is not set, then the transformation will just * fail. */ @@ -380,7 +399,7 @@ open class ASTTransformer } nodes = defaultNodes(source, context, expectedType) nodes.filter { it.origin == null }.forEach { node -> - node.origin = MissingASTTransformation(asOrigin(source), source, expectedType) + node.origin = MissingASTTransformation(asOrigin(source, context), source, expectedType) } } return nodes @@ -429,7 +448,10 @@ open class ASTTransformer } } - open fun asOrigin(source: Any): Origin? = if (source is Origin) source else null + open fun asOrigin( + source: Any, + context: TransformationContext, + ): Origin? = source as? Origin protected open fun setChild( childTransform: ChildTransform<*, *, *>, @@ -469,7 +491,7 @@ open class ASTTransformer val nodes = transform.constructor(source, context, this, transform) nodes.forEach { node -> if (node.origin == null) { - node.withOrigin(asOrigin(source)) + node.withOrigin(asOrigin(source, context)) } } return nodes @@ -533,7 +555,7 @@ open class ASTTransformer kclass: KClass, crossinline factory: (S) -> T?, ): Transform = - registerTransform(kclass) { input, _, _, _ -> + registerTransform(kclass) { input, context -> try { factory(input) } catch (t: NotImplementedError) { @@ -541,7 +563,7 @@ open class ASTTransformer val node = T::class.dummyInstance() node.origin = FailingASTTransformation( - asOrigin(input), + asOrigin(input, context), "Failed to transform $input into $kclass because the implementation is not complete " + "(${t.message}", ) @@ -554,7 +576,7 @@ open class ASTTransformer val node = T::class.dummyInstance() node.origin = FailingASTTransformation( - asOrigin(input), + asOrigin(input, context), "Failed to transform $input into $kclass because of an error (${e.message})", ) node diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index 61f76160..87f0356d 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -21,12 +21,12 @@ public ParseTreeToASTTransformer() { super(); } - public ParseTreeToASTTransformer(@Nullable Source source) { - super(source); + public ParseTreeToASTTransformer(boolean throwOnUnmappedNode) { + super(throwOnUnmappedNode); } - public ParseTreeToASTTransformer(@Nullable Source source, boolean throwOnUnmappedNode) { - super(source, throwOnUnmappedNode); + public ParseTreeToASTTransformer(boolean throwOnUnmappedNode, boolean faultTolerant) { + super(throwOnUnmappedNode, faultTolerant); } protected @NotNull Transform registerNodeFactory(Class source, Class target) { From 6be07ec61c127077c160bfd52f0fa9c44d8378cd Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Fri, 24 Oct 2025 11:10:18 +0200 Subject: [PATCH 06/11] #417 extra checks for exception handling in AST transformers --- .../starlasu/transformation/DummyNodes.kt | 15 +++++-- .../transformation/ASTTransformerTest.kt | 45 ++++++++++++++++++- .../starlasu/javalib/ASTTransformer.java | 1 - .../javalib/ParseTreeToASTTransformer.java | 6 --- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/DummyNodes.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/DummyNodes.kt index d5a00cb0..0eb7b820 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/DummyNodes.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/DummyNodes.kt @@ -1,6 +1,7 @@ package com.strumenta.starlasu.transformation import com.strumenta.starlasu.model.ASTNode +import com.strumenta.starlasu.model.GenericErrorNode import com.strumenta.starlasu.model.Node import com.strumenta.starlasu.model.PossiblyNamed import com.strumenta.starlasu.model.ReferenceByName @@ -15,7 +16,7 @@ import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.javaType /** - * This logic instantiate a node of the given class with dummy values. + * This logic instantiates a node of the given class with dummy values. * This is useful because it permits to add an element that "fit" and make the typesystem happy. * Typically, the only goal of the element would be to hold some annotation that indicates that the element * is representing an error or a missing transformation or something of that sort. @@ -87,12 +88,20 @@ private fun KClass.toInstantiableType(levelOfDummyTree: Int = 0): K } } + this == ASTNode::class -> { + // This happens when the target type is not known, e.g. + // registerTransform(X::class) { TODO("X not yet mapped") } + GenericErrorNode::class as KClass + } + this.isAbstract -> { - throw IllegalStateException("We cannot instantiate an abstract class (but we can handle sealed classes)") + throw IllegalStateException( + "We cannot instantiate the abstract class ${this.qualifiedName} (but we can handle sealed classes)", + ) } this.java.isInterface -> { - throw IllegalStateException("We cannot instantiate an interface") + throw IllegalStateException("We cannot instantiate the interface ${this.qualifiedName}") } else -> { diff --git a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt index 349cd31a..a92fe916 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt @@ -188,7 +188,7 @@ class ASTTransformerTest { transform(source.right) as BLangExpression, ) } - registerTransform(ALangMult::class) { source: ALangMult -> + registerTransform(ALangMult::class) { source -> BLangMult( transform(source.left) as BLangExpression, transform(source.right) as BLangExpression, @@ -646,6 +646,49 @@ class ASTTransformerTest { original.walkDescendants(AC::class).first(), ) } + + @Test + fun `exception handling, root`() { + val transformer = ASTTransformer() + transformer.registerTransform(AA::class) { aa -> + // if (false) needed to detect the target type as AA + if (false) aa else throw Exception("Something went wrong") + } + val original = AA(a = "my_a", child = AB(b = "my_b", child = AC(c = "my_c", children = mutableListOf()))) + val transformedAST = transformer.transform(original)!! + assertIs(transformedAST) + // verify that the origin is set correctly + assertIs(transformedAST.origin) + assertEquals(original, (transformedAST.origin as FailingASTTransformation).origin) + assertEquals( + "Failed to transform com.strumenta.starlasu.transformation.AA(a=my_a, " + + "child=com.strumenta.starlasu.transformation.AB(...)) into class " + + "com.strumenta.starlasu.transformation.AA because of an error (Something went wrong)", + (transformedAST.origin as FailingASTTransformation).message, + ) + } + + @Test + fun `exception handling, internal node`() { + val transformer = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) + transformer.registerTransform(AB::class) { ab -> + // if (false) needed to detect the target type as AA + if (false) ab else throw Exception("Something went wrong") + } + val original = AA(a = "my_a", child = AB(b = "my_b", child = AC(c = "my_c", children = mutableListOf()))) + val transformedAST = transformer.transform(original)!! + assertIs(transformedAST) + // verify that the origin is set correctly + assertIs(transformedAST.origin) + assertIs(transformedAST.child.origin) + assertEquals(original.child, (transformedAST.child.origin as FailingASTTransformation).origin) + assertEquals( + "Failed to transform com.strumenta.starlasu.transformation.AB(b=my_b, " + + "child=com.strumenta.starlasu.transformation.AC(...)) into class " + + "com.strumenta.starlasu.transformation.AB because of an error (Something went wrong)", + (transformedAST.child.origin as FailingASTTransformation).message, + ) + } } data class BazRoot( diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java index 4712ea86..bd1facd3 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java @@ -3,7 +3,6 @@ import com.strumenta.starlasu.model.ASTNode; import com.strumenta.starlasu.transformation.Transform; import com.strumenta.starlasu.transformation.TransformationContext; -import com.strumenta.starlasu.validation.Issue; import kotlin.Unit; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index 87f0356d..e07d56cb 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -1,18 +1,12 @@ package com.strumenta.starlasu.javalib; import com.strumenta.starlasu.model.ASTNode; -import com.strumenta.starlasu.model.Source; import com.strumenta.starlasu.transformation.ASTTransformer; import com.strumenta.starlasu.transformation.Transform; import com.strumenta.starlasu.transformation.TransformationContext; -import com.strumenta.starlasu.validation.Issue; import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; import kotlin.jvm.functions.Function3; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; import static kotlin.jvm.JvmClassMappingKt.getKotlinClass; From 52949e2b7d49af281d7cf2121aebb0a4036446fc Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Tue, 28 Oct 2025 16:54:27 +0100 Subject: [PATCH 07/11] #417 #374 revisit childrenSetAtConstruction --- .../starlasu/transformation/Transformation.kt | 45 ++++++++++++++----- .../transformation/ASTTransformerTest.kt | 16 ++++++- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index 4dc7681b..08c4802a 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -22,6 +22,15 @@ import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.memberProperties import kotlin.reflect.full.superclasses +class ConfigurationException(message: String, cause: Throwable? = null) : Exception(message, cause) + +enum class ChildrenPolicy { + SET_AT_CONSTRUCTION, + SET_AFTER_STANDARD_CONSTRUCTION, + SET_AFTER_CUSTOM_CONSTRUCTION, + SKIP, +} + /** * Factory that, given a tree node, will instantiate the corresponding transformed node. */ @@ -29,21 +38,19 @@ class Transform( val constructor: (Source, TransformationContext, ASTTransformer, Transform) -> List, var children: MutableMap?> = mutableMapOf(), var finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, - var skipChildren: Boolean = false, - var childrenSetAtConstruction: Boolean = false, + var childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION, ) { companion object { fun single( singleConstructor: (Source, TransformationContext, ASTTransformer, Transform) -> Output?, children: MutableMap?> = mutableMapOf(), finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, - skipChildren: Boolean = false, - childrenSetAtConstruction: Boolean = false, + childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION, ): Transform = Transform({ source, ctx, at, nf -> val result = singleConstructor(source, ctx, at, nf) if (result == null) emptyList() else listOf(result) - }, children, finalizer, skipChildren, childrenSetAtConstruction) + }, children, finalizer, childrenPolicy) } /** @@ -158,10 +165,17 @@ class Transform( scopedToType: KClass<*>? = null, childType: KClass = ASTNode::class, ): Transform { + if (childrenPolicy == ChildrenPolicy.SKIP) { + throw ConfigurationException("Children are configured to be skipped, calling withChild is illegal") + } val prefix = if (scopedToType != null) scopedToType.qualifiedName + "#" else "" if (set == null) { - // given we have no setter we MUST set the children at construction - childrenSetAtConstruction = true + if (childrenPolicy == ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION) { + // given we have no setter we MUST set the children at construction + childrenPolicy = ChildrenPolicy.SET_AT_CONSTRUCTION + } else if (childrenPolicy == ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION) { + throw ConfigurationException("A custom constructor was provided, but child $name has no setter") + } } children[prefix + name] = ChildTransform(prefix + name, get, set, childType) @@ -195,7 +209,7 @@ class Transform( * fail – or worse, it will map an unrelated node. */ fun skipChildren(skip: Boolean = true): Transform { - this.skipChildren = skip + childrenPolicy = if (skip) ChildrenPolicy.SKIP else childrenPolicy return this } @@ -380,7 +394,8 @@ open class ASTTransformer if (transform != null) { nodes = makeNodes(transform, source, context) val parent = context.parent - if (!transform.skipChildren && !transform.childrenSetAtConstruction) { + if (transform.childrenPolicy == ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION || + transform.childrenPolicy == ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION) { nodes.forEach { node -> context.parent = node setChildren(transform, source, context) @@ -520,6 +535,7 @@ open class ASTTransformer factory: (S, TransformationContext, ASTTransformer, Transform) -> T?, ): Transform { val transform = Transform.single(factory) + transform.childrenPolicy = ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION transforms[kclass] = transform return transform } @@ -529,6 +545,7 @@ open class ASTTransformer factory: (S, TransformationContext, ASTTransformer, Transform) -> List, ): Transform { val transform = Transform(factory) + transform.childrenPolicy = ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION transforms[kclass] = transform return transform } @@ -593,6 +610,7 @@ open class ASTTransformer inline fun registerTransform(): Transform = registerTransform(S::class, T::class) + .apply { childrenPolicy = ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION } inline fun notTranslateDirectly(): Transform = registerTransform { @@ -699,7 +717,7 @@ open class ASTTransformer // so we should really check the value that `childrenSetAtConstruction` time has when we actually invoke // the factory. val instance = - if (thisTransform.childrenSetAtConstruction) { + if (thisTransform.childrenPolicy == ChildrenPolicy.SET_AT_CONSTRUCTION) { val constructor = target.preferredConstructor() val constructorParamValues = constructor.parameters @@ -740,7 +758,12 @@ open class ASTTransformer // If I do not have an emptyLikeConstructor, then I am forced to invoke a constructor with parameters and // therefore setting the children at construction time. // Note that we are assuming that either we set no children at construction time or we set all of them - childrenSetAtConstruction = emptyLikeConstructor == null, + childrenPolicy = + if (emptyLikeConstructor == null) { + ChildrenPolicy.SET_AT_CONSTRUCTION + } else { + ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION + }, ) transforms[source] = transform return transform diff --git a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt index a92fe916..a760fd8e 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt @@ -11,6 +11,7 @@ import com.strumenta.starlasu.testing.assertASTsAreEqual import com.strumenta.starlasu.traversing.walkDescendants import com.strumenta.starlasu.validation.Issue import com.strumenta.starlasu.validation.IssueSeverity +import org.junit.Assert.assertThrows import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -137,7 +138,7 @@ class ASTTransformerTest { val transformedCU = transformer.transform(cu)!! assertASTsAreEqual(cu, transformedCU, considerPosition = true) assertTrue { transformedCU.hasValidParents() } - assertEquals(transformedCU.origin, cu) + assertEquals( cu, transformedCU.origin) } /** @@ -374,7 +375,7 @@ class ASTTransformerTest { ) val transformedCU = transformer.transform(cu)!! as CU assertTrue { transformedCU.hasValidParents() } - assertEquals(transformedCU.origin, cu) + assertEquals( cu, transformedCU.origin) assertIs(transformedCU.statements[0].origin) } @@ -689,6 +690,15 @@ class ASTTransformerTest { (transformedAST.child.origin as FailingASTTransformation).message, ) } + + @Test + fun `children set at construction with lambda`() { + val transformer = ASTTransformer() + assertThrows(ConfigurationException::class.java) { + transformer.registerTransform(NoSetter::class) { _ -> NoSetter() } + .withChild(NoSetter::child, NoSetter::child) + } + } } data class BazRoot( @@ -744,3 +754,5 @@ class BB( class BD( d: String, ) : AD(d) + +class NoSetter(val child: NoSetter? = null) : BaseASTNode() \ No newline at end of file From f2fd22f8af9958bf98ce27a7a8d65e84f2226999 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Tue, 28 Oct 2025 17:22:05 +0100 Subject: [PATCH 08/11] #417 revisit fault tolerance flags --- .../mapping/ParseTreeToASTTransformer.kt | 8 ++-- .../starlasu/transformation/Transformation.kt | 41 +++++++++++++------ .../transformation/ASTTransformerTest.kt | 11 +++-- .../starlasu/javalib/ASTTransformer.java | 13 +++--- .../javalib/ParseTreeToASTTransformer.java | 9 ++-- .../starlasu/javalib/TransformerTest.java | 3 +- 6 files changed, 49 insertions(+), 36 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index 60eefb8f..7a68e08d 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -7,6 +7,7 @@ import com.strumenta.starlasu.parsing.ParseTreeOrigin import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.transformation.ASTTransformer import com.strumenta.starlasu.transformation.FailingASTTransformation +import com.strumenta.starlasu.transformation.FaultTolerance import com.strumenta.starlasu.transformation.Transform import com.strumenta.starlasu.transformation.TransformationContext import org.antlr.v4.runtime.ParserRuleContext @@ -20,9 +21,8 @@ import kotlin.reflect.KClass open class ParseTreeToASTTransformer @JvmOverloads constructor( - throwOnUnmappedNode: Boolean = true, - faultTolerant: Boolean = throwOnUnmappedNode, - ) : ASTTransformer(throwOnUnmappedNode, faultTolerant) { + faultTolerance: FaultTolerance = FaultTolerance.THROW_ONLY_ON_UNMAPPED, + ) : ASTTransformer(faultTolerance) { /** * Performs the transformation of a node and, recursively, its descendants. In addition to the overridden method, * it also assigns the parseTreeNode to the AST node so that it can keep track of its position. @@ -34,7 +34,7 @@ open class ParseTreeToASTTransformer expectedType: KClass, ): List { if (source is ParserRuleContext && source.exception != null) { - if (!faultTolerant) { + if (faultTolerance == FaultTolerance.STRICT) { throw RuntimeException("Failed to transform $source into $expectedType", source.exception) } val origin = diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index 08c4802a..b7a385a4 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -22,7 +22,10 @@ import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.memberProperties import kotlin.reflect.full.superclasses -class ConfigurationException(message: String, cause: Throwable? = null) : Exception(message, cause) +class ConfigurationException( + message: String, + cause: Throwable? = null, +) : Exception(message, cause) enum class ChildrenPolicy { SET_AT_CONSTRUCTION, @@ -316,6 +319,23 @@ open class TransformationContext } } +enum class FaultTolerance { + /** + * If a transformation fails, we'll throw an exception. + */ + STRICT, + + /** + * Same as [STRICT] when the failure is due to a node not being mapped; otherwise, same as [LOOSE]. + */ + THROW_ONLY_ON_UNMAPPED, + + /** + * In case a transformation fails, we will add a node with the origin FailingASTTransformation. + */ + LOOSE, +} + /** * 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 @@ -326,13 +346,7 @@ open class TransformationContext open class ASTTransformer @JvmOverloads constructor( - val throwOnUnmappedNode: Boolean = false, - /** - * When the fault-tolerant flag is set, in case a transformation fails, we will add a node - * with the origin FailingASTTransformation. If the flag is not set, then the transformation will just - * fail. - */ - val faultTolerant: Boolean = !throwOnUnmappedNode, + val faultTolerance: FaultTolerance = FaultTolerance.LOOSE, val defaultTransformation: ( ( source: Any?, @@ -394,8 +408,9 @@ open class ASTTransformer if (transform != null) { nodes = makeNodes(transform, source, context) val parent = context.parent - if (transform.childrenPolicy == ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION || - transform.childrenPolicy == ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION) { + if (transform.childrenPolicy == ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION || + transform.childrenPolicy == ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION + ) { nodes.forEach { node -> context.parent = node setChildren(transform, source, context) @@ -407,7 +422,7 @@ open class ASTTransformer node.parent = parent } } else { - if (defaultTransformation == null && throwOnUnmappedNode) { + if (defaultTransformation == null && faultTolerance != FaultTolerance.LOOSE) { throw IllegalStateException( "Unable to translate node $source (class ${source::class.qualifiedName})", ) @@ -576,7 +591,7 @@ open class ASTTransformer try { factory(input) } catch (t: NotImplementedError) { - if (faultTolerant) { + if (faultTolerance != FaultTolerance.STRICT) { val node = T::class.dummyInstance() node.origin = FailingASTTransformation( @@ -589,7 +604,7 @@ open class ASTTransformer throw RuntimeException("Failed to transform $input into $kclass", t) } } catch (e: Exception) { - if (faultTolerant) { + if (faultTolerance != FaultTolerance.STRICT) { val node = T::class.dummyInstance() node.origin = FailingASTTransformation( diff --git a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt index a760fd8e..80a61609 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt @@ -138,7 +138,7 @@ class ASTTransformerTest { val transformedCU = transformer.transform(cu)!! assertASTsAreEqual(cu, transformedCU, considerPosition = true) assertTrue { transformedCU.hasValidParents() } - assertEquals( cu, transformedCU.origin) + assertEquals(cu, transformedCU.origin) } /** @@ -375,7 +375,7 @@ class ASTTransformerTest { ) val transformedCU = transformer.transform(cu)!! as CU assertTrue { transformedCU.hasValidParents() } - assertEquals( cu, transformedCU.origin) + assertEquals(cu, transformedCU.origin) assertIs(transformedCU.statements[0].origin) } @@ -695,7 +695,8 @@ class ASTTransformerTest { fun `children set at construction with lambda`() { val transformer = ASTTransformer() assertThrows(ConfigurationException::class.java) { - transformer.registerTransform(NoSetter::class) { _ -> NoSetter() } + transformer + .registerTransform(NoSetter::class) { _ -> NoSetter() } .withChild(NoSetter::child, NoSetter::child) } } @@ -755,4 +756,6 @@ class BD( d: String, ) : AD(d) -class NoSetter(val child: NoSetter? = null) : BaseASTNode() \ No newline at end of file +class NoSetter( + val child: NoSetter? = null, +) : BaseASTNode() diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java index bd1facd3..af653e96 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java @@ -1,6 +1,7 @@ package com.strumenta.starlasu.javalib; import com.strumenta.starlasu.model.ASTNode; +import com.strumenta.starlasu.transformation.FaultTolerance; import com.strumenta.starlasu.transformation.Transform; import com.strumenta.starlasu.transformation.TransformationContext; import kotlin.Unit; @@ -22,16 +23,12 @@ public ASTTransformer() { super(); } - public ASTTransformer(boolean throwOnUnmappedNode) { - super(throwOnUnmappedNode); + public ASTTransformer(FaultTolerance faultTolerance) { + super(faultTolerance); } - public ASTTransformer(boolean throwOnUnmappedNode, boolean faultTolerant) { - super(throwOnUnmappedNode, faultTolerant); - } - - public ASTTransformer(boolean throwOnUnmappedNode, boolean faultTolerant, @Nullable Function4, ? super com.strumenta.starlasu.transformation.ASTTransformer, ? extends List> defaultTransformation) { - super(throwOnUnmappedNode, faultTolerant, defaultTransformation); + public ASTTransformer(FaultTolerance faultTolerance, @Nullable Function4, ? super com.strumenta.starlasu.transformation.ASTTransformer, ? extends List> defaultTransformation) { + super(faultTolerance, defaultTransformation); } protected @NotNull Transform registerNodeFactory(Class source, Class target) { diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index e07d56cb..492b4193 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -2,6 +2,7 @@ import com.strumenta.starlasu.model.ASTNode; import com.strumenta.starlasu.transformation.ASTTransformer; +import com.strumenta.starlasu.transformation.FaultTolerance; import com.strumenta.starlasu.transformation.Transform; import com.strumenta.starlasu.transformation.TransformationContext; import kotlin.jvm.functions.Function1; @@ -15,12 +16,8 @@ public ParseTreeToASTTransformer() { super(); } - public ParseTreeToASTTransformer(boolean throwOnUnmappedNode) { - super(throwOnUnmappedNode); - } - - public ParseTreeToASTTransformer(boolean throwOnUnmappedNode, boolean faultTolerant) { - super(throwOnUnmappedNode, faultTolerant); + public ParseTreeToASTTransformer(FaultTolerance faultTolerance) { + super(faultTolerance); } protected @NotNull Transform registerNodeFactory(Class source, Class target) { diff --git a/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java b/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java index 146a81ea..14657d1d 100644 --- a/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java +++ b/javalib/src/test/java/com/strumenta/starlasu/javalib/TransformerTest.java @@ -1,6 +1,7 @@ package com.strumenta.starlasu.javalib; import com.strumenta.starlasu.model.*; +import com.strumenta.starlasu.transformation.FaultTolerance; import com.strumenta.starlasu.transformation.TransformationContext; import com.strumenta.starlasu.validation.Issue; import org.junit.Test; @@ -15,7 +16,7 @@ public class TransformerTest { @Test public void testJavaNodes() { ArrayList issues = new ArrayList<>(); - ASTTransformer t = new ASTTransformer( false); + ASTTransformer t = new ASTTransformer( FaultTolerance.STRICT); t.registerNodeFactory(Node1.class, Node1.class) .withChild(Node1::getNode2, setter(Node1::setNode2), "node2"); t.registerNodeFactory(Node2.class, Node2.class); From d3443ede256636eca37d8780f6d463b1e179c0ea Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Wed, 29 Oct 2025 17:29:29 +0100 Subject: [PATCH 09/11] #417 documentation, refactoring --- .../starlasu/transformation/Transformation.kt | 90 +++++++------------ .../transformation/TransformationContext.kt | 48 ++++++++++ 2 files changed, 80 insertions(+), 58 deletions(-) create mode 100644 core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index b7a385a4..f5779201 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -2,15 +2,11 @@ package com.strumenta.starlasu.transformation import com.strumenta.starlasu.model.ASTNode import com.strumenta.starlasu.model.Origin -import com.strumenta.starlasu.model.Position import com.strumenta.starlasu.model.PropertyDescription -import com.strumenta.starlasu.model.Source import com.strumenta.starlasu.model.asContainment import com.strumenta.starlasu.model.children import com.strumenta.starlasu.model.processProperties import com.strumenta.starlasu.model.withOrigin -import com.strumenta.starlasu.validation.Issue -import com.strumenta.starlasu.validation.IssueSeverity import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KParameter @@ -27,28 +23,43 @@ class ConfigurationException( cause: Throwable? = null, ) : Exception(message, cause) +/** + * Policy to adopt for handling child nodes in an AST transformer. + */ enum class ChildrenPolicy { + /** + * Child nodes of an AST node are set upon creation, as constructor parameters. + */ SET_AT_CONSTRUCTION, - SET_AFTER_STANDARD_CONSTRUCTION, - SET_AFTER_CUSTOM_CONSTRUCTION, + + /** + * Child nodes of an AST node are set after the creation of the parent node, using a setter method. The parent node + * is either instantiated automatically by the AST transformer, or by user code. + */ + SET_AFTER_CONSTRUCTION, + + /** + * Child nodes of an AST node are not set by the AST transformer. + */ SKIP, } /** - * Factory that, given a tree node, will instantiate the corresponding transformed node. + * Strategy to instantiate the corresponding transformed AST node given the source node. */ class Transform( val constructor: (Source, TransformationContext, ASTTransformer, Transform) -> List, var children: MutableMap?> = mutableMapOf(), var finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, - var childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION, + var childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_CONSTRUCTION, + var customConstructor: Boolean = true, ) { companion object { fun single( singleConstructor: (Source, TransformationContext, ASTTransformer, Transform) -> Output?, children: MutableMap?> = mutableMapOf(), finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, - childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION, + childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_CONSTRUCTION, ): Transform = Transform({ source, ctx, at, nf -> val result = singleConstructor(source, ctx, at, nf) @@ -173,11 +184,11 @@ class Transform( } val prefix = if (scopedToType != null) scopedToType.qualifiedName + "#" else "" if (set == null) { - if (childrenPolicy == ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION) { + if (customConstructor) { + throw ConfigurationException("A custom constructor was provided, but child $name has no setter") + } else { // given we have no setter we MUST set the children at construction childrenPolicy = ChildrenPolicy.SET_AT_CONSTRUCTION - } else if (childrenPolicy == ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION) { - throw ConfigurationException("A custom constructor was provided, but child $name has no setter") } } @@ -212,7 +223,11 @@ class Transform( * fail – or worse, it will map an unrelated node. */ fun skipChildren(skip: Boolean = true): Transform { - childrenPolicy = if (skip) ChildrenPolicy.SKIP else childrenPolicy + if (skip) { + childrenPolicy = ChildrenPolicy.SKIP + } else if (childrenPolicy == ChildrenPolicy.SKIP) { + childrenPolicy = ChildrenPolicy.SET_AFTER_CONSTRUCTION + } return this } @@ -281,44 +296,6 @@ data class ChildTransform( */ private val NO_CHILD_NODE = ChildTransform("", { x -> x }, { _, _ -> }, ASTNode::class) -/** - * A context holding metadata relevant to the transformation process, such as parent node reference, associated source, - * and issues discovered during transformation. - * - * This is an open class so that specialized AST transformers can extend it to track additional information. - * - * @constructor Creates an instance of the transformation context. - * @param issues A mutable list of issues encountered during the transformation. - * Defaults to an empty list if not provided. - * @param parent The parent [ASTNode] in the hierarchy, if available. Defaults to null. - * @param source The [Source] object associated with this context, if any. Defaults to null. - */ -open class TransformationContext - @JvmOverloads - constructor( - /** - * Additional issues found during the transformation process. - */ - val issues: MutableList = mutableListOf(), - var parent: ASTNode? = null, - var source: Source? = null, - ) { - fun addIssue( - message: String, - severity: IssueSeverity = IssueSeverity.ERROR, - position: Position? = null, - ): Issue { - val issue = - Issue.semantic( - message, - severity, - position?.apply { source = this@TransformationContext.source }, - ) - issues.add(issue) - return issue - } - } - enum class FaultTolerance { /** * If a transformation fails, we'll throw an exception. @@ -408,9 +385,7 @@ open class ASTTransformer if (transform != null) { nodes = makeNodes(transform, source, context) val parent = context.parent - if (transform.childrenPolicy == ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION || - transform.childrenPolicy == ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION - ) { + if (transform.childrenPolicy == ChildrenPolicy.SET_AFTER_CONSTRUCTION) { nodes.forEach { node -> context.parent = node setChildren(transform, source, context) @@ -550,7 +525,6 @@ open class ASTTransformer factory: (S, TransformationContext, ASTTransformer, Transform) -> T?, ): Transform { val transform = Transform.single(factory) - transform.childrenPolicy = ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION transforms[kclass] = transform return transform } @@ -560,7 +534,6 @@ open class ASTTransformer factory: (S, TransformationContext, ASTTransformer, Transform) -> List, ): Transform { val transform = Transform(factory) - transform.childrenPolicy = ChildrenPolicy.SET_AFTER_CUSTOM_CONSTRUCTION transforms[kclass] = transform return transform } @@ -625,7 +598,7 @@ open class ASTTransformer inline fun registerTransform(): Transform = registerTransform(S::class, T::class) - .apply { childrenPolicy = ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION } + .apply { customConstructor = false } inline fun notTranslateDirectly(): Transform = registerTransform { @@ -777,9 +750,10 @@ open class ASTTransformer if (emptyLikeConstructor == null) { ChildrenPolicy.SET_AT_CONSTRUCTION } else { - ChildrenPolicy.SET_AFTER_STANDARD_CONSTRUCTION + ChildrenPolicy.SET_AFTER_CONSTRUCTION }, ) + transform.customConstructor = false transforms[source] = transform return transform } diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt new file mode 100644 index 00000000..66299e54 --- /dev/null +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt @@ -0,0 +1,48 @@ +package com.strumenta.starlasu.transformation + +import com.strumenta.starlasu.model.ASTNode +import com.strumenta.starlasu.model.Position +import com.strumenta.starlasu.model.Source +import com.strumenta.starlasu.validation.Issue +import com.strumenta.starlasu.validation.IssueSeverity + +/** + * A context holding metadata relevant to the transformation process, such as parent node reference, associated source, + * and issues discovered during transformation. + * + * This is an open class so that specialized AST transformers can extend it to track additional information. However, + * to keep type signatures reasonably simple, AST transformers are not generic on the context class. This is also + * because we expect that only few, if any, AST transformation rules ([Transform] instances) will require a custom + * context; those few can just cast the context to the desired subclass. + * + * @constructor Creates an instance of the transformation context. + * @param issues A mutable list of issues encountered during the transformation. + * Defaults to an empty list if not provided. + * @param parent The parent [com.strumenta.starlasu.model.ASTNode] in the hierarchy, if available. Defaults to null. + * @param source The [com.strumenta.starlasu.model.Source] object associated with this context, if any. Defaults to null. + */ +open class TransformationContext + @JvmOverloads + constructor( + /** + * Additional issues found during the transformation process. + */ + val issues: MutableList = mutableListOf(), + var parent: ASTNode? = null, + var source: Source? = null, + ) { + fun addIssue( + message: String, + severity: IssueSeverity = IssueSeverity.ERROR, + position: Position? = null, + ): Issue { + val issue = + Issue.semantic( + message, + severity, + position?.apply { source = this@TransformationContext.source }, + ) + issues.add(issue) + return issue + } + } From d0714c9500471e8eea64d80b0ab173d62e4bee10 Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 30 Oct 2025 16:16:19 +0100 Subject: [PATCH 10/11] #417 address review remarks --- .../mapping/ParseTreeToASTTransformer.kt | 6 +- .../starlasu/transformation/Transformation.kt | 178 +++++++++--------- .../transformation/TransformationContext.kt | 4 +- .../starlasu/javalib/ASTTransformer.java | 10 +- .../javalib/ParseTreeToASTTransformer.java | 10 +- 5 files changed, 109 insertions(+), 99 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index 7a68e08d..da8defc1 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -8,8 +8,8 @@ import com.strumenta.starlasu.parsing.withParseTreeNode import com.strumenta.starlasu.transformation.ASTTransformer import com.strumenta.starlasu.transformation.FailingASTTransformation import com.strumenta.starlasu.transformation.FaultTolerance -import com.strumenta.starlasu.transformation.Transform import com.strumenta.starlasu.transformation.TransformationContext +import com.strumenta.starlasu.transformation.TransformationRule import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.tree.ParseTree import kotlin.reflect.KClass @@ -92,7 +92,7 @@ open class ParseTreeToASTTransformer * wrapper. When there is only a ParserRuleContext child we can transform * that child and return that result. */ - fun

registerTransformUnwrappingChild(kclass: KClass

): Transform = + fun

registerTransformUnwrappingChild(kclass: KClass

): TransformationRule = registerTransform(kclass) { source, context, _ -> val nodeChildren = source.children.filterIsInstance() require(nodeChildren.size == 1) { @@ -105,6 +105,6 @@ open class ParseTreeToASTTransformer /** * Alternative to registerNodeFactoryUnwrappingChild(KClass) which is slightly more concise. */ - inline fun registerTransformUnwrappingChild(): Transform = + inline fun registerTransformUnwrappingChild(): TransformationRule = registerTransformUnwrappingChild(P::class) } diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt index f5779201..932c6e6b 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/Transformation.kt @@ -47,21 +47,31 @@ enum class ChildrenPolicy { /** * Strategy to instantiate the corresponding transformed AST node given the source node. */ -class Transform( - val constructor: (Source, TransformationContext, ASTTransformer, Transform) -> List, - var children: MutableMap?> = mutableMapOf(), +class TransformationRule( + val constructor: ( + Source, + TransformationContext, + ASTTransformer, + TransformationRule, + ) -> List, + var children: MutableMap?> = mutableMapOf(), var finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, var childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_CONSTRUCTION, var customConstructor: Boolean = true, ) { companion object { fun single( - singleConstructor: (Source, TransformationContext, ASTTransformer, Transform) -> Output?, - children: MutableMap?> = mutableMapOf(), + singleConstructor: ( + Source, + TransformationContext, + ASTTransformer, + TransformationRule, + ) -> Output?, + children: MutableMap?> = mutableMapOf(), finalizer: (Output, TransformationContext) -> Unit = { _, _ -> }, childrenPolicy: ChildrenPolicy = ChildrenPolicy.SET_AFTER_CONSTRUCTION, - ): Transform = - Transform({ source, ctx, at, nf -> + ): TransformationRule = + TransformationRule({ source, ctx, at, nf -> val result = singleConstructor(source, ctx, at, nf) if (result == null) emptyList() else listOf(result) }, children, finalizer, childrenPolicy) @@ -74,7 +84,7 @@ class Transform( * * Example using the scopedToType parameter: * ``` - * on.registerTransform(SASParser.DatasetOptionContext::class) { ctx -> + * on.registerRule(SASParser.DatasetOptionContext::class) { ctx -> * when { * ... * } @@ -93,7 +103,7 @@ class Transform( targetProperty: KMutableProperty1<*, *>, sourceAccessor: Source.() -> Any?, scopedToType: KClass<*>, - ): Transform = + ): TransformationRule = withChild( get = { source -> source.sourceAccessor() }, set = (targetProperty as KMutableProperty1)::set, @@ -119,7 +129,7 @@ class Transform( fun withChild( targetProperty: KMutableProperty1, sourceAccessor: Source.() -> Any?, - ): Transform = + ): TransformationRule = withChild( get = { source -> source.sourceAccessor() }, set = (targetProperty as KMutableProperty1)::set, @@ -136,7 +146,7 @@ class Transform( fun withChild( targetProperty: KProperty1, sourceAccessor: Source.() -> Any?, - ): Transform = + ): TransformationRule = withChild( get = { source -> source.sourceAccessor() }, null, @@ -157,7 +167,7 @@ class Transform( targetProperty: KProperty1, sourceAccessor: Source.() -> Any?, scopedToType: KClass<*>, - ): Transform = + ): TransformationRule = withChild( get = { source -> source.sourceAccessor() }, null, @@ -178,7 +188,7 @@ class Transform( name: String, scopedToType: KClass<*>? = null, childType: KClass = ASTNode::class, - ): Transform { + ): TransformationRule { if (childrenPolicy == ChildrenPolicy.SKIP) { throw ConfigurationException("Children are configured to be skipped, calling withChild is illegal") } @@ -192,16 +202,16 @@ class Transform( } } - children[prefix + name] = ChildTransform(prefix + name, get, set, childType) + children[prefix + name] = ChildTransformationRule(prefix + name, get, set, childType) return this } - fun withFinalizer(finalizer: (Output, TransformationContext) -> Unit): Transform { + fun withFinalizer(finalizer: (Output, TransformationContext) -> Unit): TransformationRule { this.finalizer = finalizer return this } - fun withFinalizer(finalizer: (Output) -> Unit): Transform { + fun withFinalizer(finalizer: (Output) -> Unit): TransformationRule { this.finalizer = { n, _ -> finalizer(n) } return this } @@ -213,7 +223,7 @@ class Transform( * we may configure the transformer as follows: * * ```kotlin - * transformer.registerTransform(XYZContext::class) { ctx -> transformer.transform(ctx.children[0]) } + * transformer.registerRule(XYZContext::class) { ctx -> transformer.transform(ctx.children[0]) } * ``` * * However, if the result of `transformer.transform(ctx.children[0])` is an instance of a ASTNode with a child @@ -222,7 +232,7 @@ class Transform( * be an instance of `XYZContext` that may not have a child with a corresponding name, and the transformation will * fail – or worse, it will map an unrelated node. */ - fun skipChildren(skip: Boolean = true): Transform { + fun skipChildren(skip: Boolean = true): TransformationRule { if (skip) { childrenPolicy = ChildrenPolicy.SKIP } else if (childrenPolicy == ChildrenPolicy.SKIP) { @@ -270,7 +280,7 @@ class Transform( * * @param type the property type if single, the collection's element type if multiple */ -data class ChildTransform( +data class ChildTransformationRule( val name: String, val get: (Source) -> Any?, val setter: ((Target, Child?) -> Unit)?, @@ -294,7 +304,7 @@ data class ChildTransform( /** * Sentinel value used to represent the information that a given property is not a child node. */ -private val NO_CHILD_NODE = ChildTransform("", { x -> x }, { _, _ -> }, ASTNode::class) +private val NO_CHILD_NODE = ChildTransformationRule("", { x -> x }, { _, _ -> }, ASTNode::class) enum class FaultTolerance { /** @@ -336,7 +346,7 @@ open class ASTTransformer /** * Factories that map from source tree node to target tree node. */ - val transforms = mutableMapOf, Transform<*, *>>() + val rules = mutableMapOf, TransformationRule<*, *>>() private val _knownClasses = mutableMapOf>>() val knownClasses: Map>> = _knownClasses @@ -380,7 +390,7 @@ open class ASTTransformer if (source is Collection<*>) { throw Error("Mapping error: received collection when value was expected") } - val transform = getTransform(source::class as KClass) + val transform = getTransformationRule(source::class as KClass) val nodes: List if (transform != null) { nodes = makeNodes(transform, source, context) @@ -436,19 +446,19 @@ open class ASTTransformer } protected open fun setChildren( - transform: Transform, + rule: TransformationRule, source: Any, context: TransformationContext, ) { val node = context.parent!! node.processProperties { pd -> - val childTransform = transform.getChildTransform(node, pd.name) + val childTransform = rule.getChildTransformationRule(node, pd.name) if (childTransform != null) { if (childTransform != NO_CHILD_NODE) { setChild(childTransform, source, context, pd) } } else { - transform.children[getChildKey(node.nodeType, pd.name)] = NO_CHILD_NODE + rule.children[getChildKey(node.nodeType, pd.name)] = NO_CHILD_NODE } } } @@ -459,13 +469,13 @@ open class ASTTransformer ): Origin? = source as? Origin protected open fun setChild( - childTransform: ChildTransform<*, *, *>, + childTransformationRule: ChildTransformationRule<*, *, *>, source: Any, context: TransformationContext, pd: PropertyDescription, ) { val node = context.parent!! - val childFactory = childTransform as ChildTransform + val childFactory = childTransformationRule as ChildTransformationRule val childrenSource = childFactory.get(getSource(node, source)) val child: Any? = if (pd.multiple) { @@ -477,9 +487,9 @@ open class ASTTransformer transform(childrenSource, context) } try { - childTransform.set(node, child) + childTransformationRule.set(node, child) } catch (e: IllegalArgumentException) { - throw Error("Could not set child $childTransform", e) + throw Error("Could not set child $childTransformationRule", e) } } @@ -489,11 +499,11 @@ open class ASTTransformer ): Any = source protected open fun makeNodes( - transform: Transform, + rule: TransformationRule, source: S, context: TransformationContext, ): List { - val nodes = transform.constructor(source, context, this, transform) + val nodes = rule.constructor(source, context, this, rule) nodes.forEach { node -> if (node.origin == null) { node.withOrigin(asOrigin(source, context)) @@ -502,65 +512,65 @@ open class ASTTransformer return nodes } - protected open fun getTransform(kClass: KClass): Transform? { - val transform = transforms[kClass] - if (transform != null) { - return transform as Transform + protected open fun getTransformationRule(kClass: KClass): TransformationRule? { + val rule = rules[kClass] + if (rule != null) { + return rule as TransformationRule } else { if (kClass == Any::class) { return null } for (superclass in kClass.superclasses) { - val transform = getTransform(superclass as KClass) - if (transform != null) { - return transform + val rule = getTransformationRule(superclass as KClass) + if (rule != null) { + return rule } } } return null } - fun registerTransform( + fun registerRule( kclass: KClass, - factory: (S, TransformationContext, ASTTransformer, Transform) -> T?, - ): Transform { - val transform = Transform.single(factory) - transforms[kclass] = transform - return transform + factory: (S, TransformationContext, ASTTransformer, TransformationRule) -> T?, + ): TransformationRule { + val transformationRule = TransformationRule.single(factory) + rules[kclass] = transformationRule + return transformationRule } fun registerMultipleTransform( kclass: KClass, - factory: (S, TransformationContext, ASTTransformer, Transform) -> List, - ): Transform { - val transform = Transform(factory) - transforms[kclass] = transform - return transform + factory: (S, TransformationContext, ASTTransformer, TransformationRule) -> List, + ): TransformationRule { + val transformationRule = TransformationRule(factory) + rules[kclass] = transformationRule + return transformationRule } - fun registerTransform( + fun registerRule( kclass: KClass, factory: (S, TransformationContext, ASTTransformer) -> T?, - ): Transform = - registerTransform(kclass) { source, context, transformer, _ -> factory(source, context, transformer) } + ): TransformationRule = + registerRule(kclass) { source, context, transformer, _ -> factory(source, context, transformer) } - fun registerTransform( + fun registerRule( kclass: KClass, factory: (S, TransformationContext) -> T?, - ): Transform = registerTransform(kclass) { source, context, _, _ -> factory(source, context) } + ): TransformationRule = registerRule(kclass) { source, context, _, _ -> factory(source, context) } - inline fun registerTransform( + inline fun registerRule( crossinline factory: S.(TransformationContext) -> T?, - ): Transform = registerTransform(S::class) { source, context, _, _ -> source.factory(context) } + ): TransformationRule = registerRule(S::class) { source, context, _, _ -> source.factory(context) } /** * We need T to be reified because we may need to install dummy classes of T. */ - inline fun registerTransform( + inline fun registerRule( kclass: KClass, crossinline factory: (S) -> T?, - ): Transform = - registerTransform(kclass) { input, context -> + ): TransformationRule = + registerRule(kclass) { input, context -> try { factory(input) } catch (t: NotImplementedError) { @@ -594,14 +604,14 @@ open class ASTTransformer fun registerMultipleTransform( kclass: KClass, factory: (S) -> List, - ): Transform = registerMultipleTransform(kclass) { input, _, _, _ -> factory(input) } + ): TransformationRule = registerMultipleTransform(kclass) { input, _, _, _ -> factory(input) } - inline fun registerTransform(): Transform = - registerTransform(S::class, T::class) + inline fun registerRule(): TransformationRule = + registerRule(S::class, T::class) .apply { customConstructor = false } - inline fun notTranslateDirectly(): Transform = - registerTransform { + inline fun notTranslateDirectly(): TransformationRule = + registerRule { throw java.lang.IllegalStateException( "A ASTNode of this type (${this.javaClass.canonicalName}) should never be translated directly. " + "It is expected that the container will not delegate the translation of this node but it " + @@ -612,10 +622,10 @@ open class ASTTransformer private fun parameterValue( kParameter: KParameter, source: S, - childTransform: ChildTransform, + childTransformationRule: ChildTransformationRule, context: TransformationContext, ): ParameterValue = - when (val childSource = childTransform.get.invoke(source)) { + when (val childSource = childTransformationRule.get.invoke(source)) { null -> { AbsentParameterValue } @@ -659,17 +669,17 @@ open class ASTTransformer * @param nodeType the [ASTNode.nodeType] of the target node. Normally, the node type is the same as the class name, * however, [ASTNode] subclasses may want to override it, and in that case, the parameter must be provided explicitly. */ - fun registerTransform( + fun registerRule( source: KClass, target: KClass, nodeType: String = target.qualifiedName!!, - ): Transform { + ): TransformationRule { registerKnownClass(target) // We are looking for any constructor with does not take parameters or have default // values for all its parameters val emptyLikeConstructor = target.constructors.find { it.parameters.all { param -> param.isOptional } } - val transform = - Transform.single( + val transformationRule = + TransformationRule.single( { source: S, context, _, thisTransform -> if (target.isSealed) { throw IllegalStateException("Unable to instantiate sealed class $target") @@ -678,7 +688,7 @@ open class ASTTransformer fun getConstructorParameterValue(kParameter: KParameter): ParameterValue { try { val childTransform = - thisTransform.getChildTransform( + thisTransform.getChildTransformationRule( nodeType, kParameter.name!!, ) @@ -753,17 +763,17 @@ open class ASTTransformer ChildrenPolicy.SET_AFTER_CONSTRUCTION }, ) - transform.customConstructor = false - transforms[source] = transform - return transform + transformationRule.customConstructor = false + rules[source] = transformationRule + return transformationRule } /** * Here the method needs to be inlined and the type parameter reified as in the invoked - * registerTransform we need to access the nodeClass + * registerRule we need to access the nodeClass */ inline fun registerIdentityTransformation(nodeClass: KClass) = - registerTransform(nodeClass) { node -> node }.skipChildren() + registerRule(nodeClass) { node -> node }.skipChildren() private fun registerKnownClass(target: KClass<*>) { val qualifiedName = target.qualifiedName @@ -783,21 +793,21 @@ open class ASTTransformer } } -private fun Transform<*, *>.getChildTransform( +private fun TransformationRule<*, *>.getChildTransformationRule( node: Target, parameterName: String, -): ChildTransform? = getChildTransform(node.nodeType, parameterName) +): ChildTransformationRule? = getChildTransformationRule(node.nodeType, parameterName) -private fun Transform<*, *>.getChildTransform( +private fun TransformationRule<*, *>.getChildTransformationRule( nodeType: String, parameterName: String, -): ChildTransform? { +): ChildTransformationRule? { val childKey = getChildKey(nodeType, parameterName) - var childTransform = this.children[childKey] - if (childTransform == null) { - childTransform = this.children[parameterName] + var childRule = this.children[childKey] + if (childRule == null) { + childRule = this.children[parameterName] } - return childTransform as ChildTransform? + return childRule as ChildTransformationRule? } private fun getChildKey( diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt index 66299e54..d5a50315 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TransformationContext.kt @@ -12,8 +12,8 @@ import com.strumenta.starlasu.validation.IssueSeverity * * This is an open class so that specialized AST transformers can extend it to track additional information. However, * to keep type signatures reasonably simple, AST transformers are not generic on the context class. This is also - * because we expect that only few, if any, AST transformation rules ([Transform] instances) will require a custom - * context; those few can just cast the context to the desired subclass. + * because we expect that only few, if any, AST transformation rules ([TransformationRule] instances) will require a + * custom context; those few can just cast the context to the desired subclass. * * @constructor Creates an instance of the transformation context. * @param issues A mutable list of issues encountered during the transformation. diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java index af653e96..02599c55 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java @@ -2,7 +2,7 @@ import com.strumenta.starlasu.model.ASTNode; import com.strumenta.starlasu.transformation.FaultTolerance; -import com.strumenta.starlasu.transformation.Transform; +import com.strumenta.starlasu.transformation.TransformationRule; import com.strumenta.starlasu.transformation.TransformationContext; import kotlin.Unit; import kotlin.jvm.functions.Function1; @@ -31,21 +31,21 @@ public ASTTransformer(FaultTolerance faultTolerance, @Nullable Function4 @NotNull Transform registerNodeFactory(Class source, Class target) { + protected @NotNull TransformationRule registerNodeFactory(Class source, Class target) { return registerNodeFactory(source, target, target.getName()); } - protected @NotNull Transform registerNodeFactory( + protected @NotNull TransformationRule registerNodeFactory( Class source, Class target, String nodeType ) { return registerTransform(getKotlinClass(source), getKotlinClass(target), nodeType); } - protected Transform registerNodeFactory(Class source, Function1 function) { + protected TransformationRule registerNodeFactory(Class source, Function1 function) { return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); } - protected Transform registerNodeFactory( + protected TransformationRule registerNodeFactory( Class source, Function3 function ) { diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index 492b4193..5cb458ee 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -3,7 +3,7 @@ import com.strumenta.starlasu.model.ASTNode; import com.strumenta.starlasu.transformation.ASTTransformer; import com.strumenta.starlasu.transformation.FaultTolerance; -import com.strumenta.starlasu.transformation.Transform; +import com.strumenta.starlasu.transformation.TransformationRule; import com.strumenta.starlasu.transformation.TransformationContext; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function3; @@ -20,21 +20,21 @@ public ParseTreeToASTTransformer(FaultTolerance faultTolerance) { super(faultTolerance); } - protected @NotNull Transform registerNodeFactory(Class source, Class target) { + protected @NotNull TransformationRule registerNodeFactory(Class source, Class target) { return registerNodeFactory(source, target, target.getName()); } - protected @NotNull Transform registerNodeFactory( + protected @NotNull TransformationRule registerNodeFactory( Class source, Class target, String nodeType ) { return registerTransform(getKotlinClass(source), getKotlinClass(target), nodeType); } - protected Transform registerNodeFactory(Class source, Function1 function) { + protected TransformationRule registerNodeFactory(Class source, Function1 function) { return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); } - protected Transform registerNodeFactory( + protected TransformationRule registerNodeFactory( Class source, Function3 function ) { From 06806c8b94cee0c9fd4b3bf7f09591d0740686cb Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Thu, 30 Oct 2025 16:34:09 +0100 Subject: [PATCH 11/11] #417 address review remarks --- .../mapping/ParseTreeToASTTransformer.kt | 8 ++-- ...TrivialFactoryOfParseTreeToASTTransform.kt | 4 +- .../mapping/ParseTreeToASTTransformerTest.kt | 18 ++++----- .../transformation/ASTTransformerTest.kt | 40 +++++++++---------- .../starlasu/javalib/ASTTransformer.java | 6 +-- .../javalib/ParseTreeToASTTransformer.java | 6 +-- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt index da8defc1..d9ed20d5 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformer.kt @@ -92,8 +92,8 @@ open class ParseTreeToASTTransformer * wrapper. When there is only a ParserRuleContext child we can transform * that child and return that result. */ - fun

registerTransformUnwrappingChild(kclass: KClass

): TransformationRule = - registerTransform(kclass) { source, context, _ -> + fun

registerRuleUnwrappingChild(kclass: KClass

): TransformationRule = + registerRule(kclass) { source, context, _ -> val nodeChildren = source.children.filterIsInstance() require(nodeChildren.size == 1) { "Node $source (${source.javaClass}) has ${nodeChildren.size} " + @@ -105,6 +105,6 @@ open class ParseTreeToASTTransformer /** * Alternative to registerNodeFactoryUnwrappingChild(KClass) which is slightly more concise. */ - inline fun registerTransformUnwrappingChild(): TransformationRule = - registerTransformUnwrappingChild(P::class) + inline fun registerRuleUnwrappingChild(): TransformationRule = + registerRuleUnwrappingChild(P::class) } diff --git a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt index 65964185..7c8d4d16 100644 --- a/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt +++ b/core/src/main/kotlin/com/strumenta/starlasu/transformation/TrivialFactoryOfParseTreeToASTTransform.kt @@ -133,7 +133,7 @@ object TrivialFactoryOfParseTreeToASTTransform { inline fun ASTTransformer.registerTrivialPTtoASTConversion( vararg nameConversions: Pair, ) { - this.registerTransform( + this.registerRule( S::class, TrivialFactoryOfParseTreeToASTTransform.trivialTransform(*nameConversions), ) @@ -148,7 +148,7 @@ inline fun ParseTreeToASTTransformer ) inline fun ParseTreeToASTTransformer.unwrap(wrappingMember: KCallable<*>) { - this.registerTransform(S::class) { parseTreeNode, context -> + this.registerRule(S::class) { parseTreeNode, context -> val wrapped = wrappingMember.call(parseTreeNode) transform(wrapped, context) as T? } diff --git a/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt index 71ff11da..ab280e43 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/mapping/ParseTreeToASTTransformerTest.kt @@ -223,9 +223,9 @@ class ParseTreeToASTTransformerTest { private fun configure(transformer: ASTTransformer) { transformer - .registerTransform(SimpleLangParser.CompilationUnitContext::class, CU::class) + .registerRule(SimpleLangParser.CompilationUnitContext::class, CU::class) .withChild(CU::statements, SimpleLangParser.CompilationUnitContext::statement) - transformer.registerTransform(SimpleLangParser.DisplayStmtContext::class) { ctx -> + transformer.registerRule(SimpleLangParser.DisplayStmtContext::class) { ctx -> if (ctx.exception != null || ctx.expression().exception != null) { // We throw a custom error so that we can check that it's recorded in the AST throw IllegalStateException("Parse error") @@ -239,7 +239,7 @@ class ParseTreeToASTTransformerTest { .toInt(), ) } - transformer.registerTransform(SimpleLangParser.SetStmtContext::class) { ctx -> + transformer.registerRule(SimpleLangParser.SetStmtContext::class) { ctx -> if (ctx.exception != null || ctx.expression().exception != null) { // We throw a custom error so that we can check that it's recorded in the AST throw IllegalStateException("Parse error") @@ -416,10 +416,10 @@ class ParseTreeToASTTransformerTest { transformer.registerTrivialPTtoASTConversion( AntlrScriptParser.Int_literal_expressionContext::INT_VALUE to SIntegerLiteral::value, ) - transformer.registerTransform(AntlrScriptParser.String_literal_expressionContext::class) { pt, t -> + transformer.registerRule(AntlrScriptParser.String_literal_expressionContext::class) { pt, t -> SStringLiteral(pt.text.removePrefix("'").removeSuffix("'")) } - transformer.registerTransform(AntlrScriptParser.Div_mult_expressionContext::class) { pt, c, t -> + transformer.registerRule(AntlrScriptParser.Div_mult_expressionContext::class) { pt, c, t -> when (pt.op.text) { "/" -> { TrivialFactoryOfParseTreeToASTTransform.trivialTransform< @@ -448,7 +448,7 @@ class ParseTreeToASTTransformerTest { else -> TODO() } } - transformer.registerTransform(AntlrScriptParser.Sum_sub_expressionContext::class) { pt, c, t -> + transformer.registerRule(AntlrScriptParser.Sum_sub_expressionContext::class) { pt, c, t -> when (pt.op.text) { "+" -> { TrivialFactoryOfParseTreeToASTTransform.trivialTransform< @@ -559,11 +559,11 @@ class ParseTreeToASTTransformerTest { class EntTransformer : ParseTreeToASTTransformer() { init { - registerTransform(EntCtx::class) { ctx -> Ent(ctx.name) } + registerRule(EntCtx::class) { ctx -> Ent(ctx.name) } .withChild(Ent::features, EntCtx::features) - registerTransform(EntCtxFeature::class) { ctx -> EntFeature(name = ctx.name) } + registerRule(EntCtxFeature::class) { ctx -> EntFeature(name = ctx.name) } .withChild(EntFeature::type, EntCtxFeature::type) - this.registerTransform(EntCtxStringType::class, EntStringType::class) + this.registerRule(EntCtxStringType::class, EntStringType::class) } } diff --git a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt index 80a61609..7dfaaa50 100644 --- a/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt +++ b/core/src/test/kotlin/com/strumenta/starlasu/transformation/ASTTransformerTest.kt @@ -122,7 +122,7 @@ class ASTTransformerTest { fun testIdentitiyTransformer() { val transformer = ASTTransformer() transformer - .registerTransform(CU::class, CU::class) + .registerRule(CU::class, CU::class) .withChild(CU::statements, CU::statements) transformer.registerIdentityTransformation(DisplayIntStatement::class) transformer.registerIdentityTransformation(SetStatement::class) @@ -148,7 +148,7 @@ class ASTTransformerTest { fun translateBinaryExpression() { val myTransformer = ASTTransformer().apply { - registerTransform(GenericBinaryExpression::class) { source: GenericBinaryExpression -> + registerRule(GenericBinaryExpression::class) { source: GenericBinaryExpression -> when (source.operator) { Operator.MULT -> Mult( @@ -182,14 +182,14 @@ class ASTTransformerTest { fun translateAcrossLanguages() { val myTransformer = ASTTransformer().apply { - registerTransform(ALangIntLiteral::class) { source: ALangIntLiteral -> BLangIntLiteral(source.value) } - registerTransform(ALangSum::class) { source: ALangSum -> + registerRule(ALangIntLiteral::class) { source: ALangIntLiteral -> BLangIntLiteral(source.value) } + registerRule(ALangSum::class) { source: ALangSum -> BLangSum( transform(source.left) as BLangExpression, transform(source.right) as BLangExpression, ) } - registerTransform(ALangMult::class) { source -> + registerRule(ALangMult::class) { source -> BLangMult( transform(source.left) as BLangExpression, transform(source.right) as BLangExpression, @@ -334,9 +334,9 @@ class ASTTransformerTest { fun testDroppingNodes() { val transformer = ASTTransformer() transformer - .registerTransform(CU::class, CU::class) + .registerRule(CU::class, CU::class) .withChild(CU::statements, CU::statements) - transformer.registerTransform(DisplayIntStatement::class) { _ -> null } + transformer.registerRule(DisplayIntStatement::class) { _ -> null } transformer.registerIdentityTransformation(SetStatement::class) val cu = @@ -360,9 +360,9 @@ class ASTTransformerTest { val transformer = ASTTransformer() transformer - .registerTransform(CU::class, CU::class) + .registerRule(CU::class, CU::class) .withChild(CU::statements, CU::statements) - transformer.registerTransform(DisplayIntStatement::class) { s -> + transformer.registerRule(DisplayIntStatement::class) { s -> s.withOrigin(GenericNode()) } @@ -383,7 +383,7 @@ class ASTTransformerTest { fun testTransformingOneNodeToMany() { val transformer = ASTTransformer() transformer - .registerTransform(BarRoot::class, BazRoot::class) + .registerRule(BarRoot::class, BazRoot::class) .withChild(BazRoot::stmts, BarRoot::stmts) transformer.registerMultipleTransform(BarStmt::class) { s -> listOf(BazStmt("${s.desc}-1"), BazStmt("${s.desc}-2")) @@ -417,7 +417,7 @@ class ASTTransformerTest { fun testUnmappedNode() { val transformer1 = ASTTransformer() transformer1 - .registerTransform(BarRoot::class, BazRoot::class) + .registerRule(BarRoot::class, BazRoot::class) .withChild(BazRoot::stmts) { stmts } val original = BarRoot( @@ -462,7 +462,7 @@ class ASTTransformerTest { @Test fun testPartialIdentityTransformation() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerTransform(BarRoot::class) { original: BarRoot, c -> + transformer1.registerRule(BarRoot::class) { original: BarRoot, c -> FooRoot( desc = "#children = ${original.children.size}", stmts = transformer1.translateList(original.stmts, c), @@ -492,7 +492,7 @@ class ASTTransformerTest { @Test fun testIdentityTransformationOfIntermediateNodes() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerTransform(BarRoot::class) { original: BarRoot, c -> + transformer1.registerRule(BarRoot::class) { original: BarRoot, c -> FooRoot( desc = "#children = ${original.children.size}", stmts = transformer1.translateList(original.stmts, c), @@ -538,7 +538,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA - transformer1.registerTransform(AA::class) { original, c -> + transformer1.registerRule(AA::class) { original, c -> BA("your_" + original.a.removePrefix("my_"), transformer1.translateCasted(original.child, c)) } assertASTsAreEqual( @@ -562,7 +562,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA and AB - transformer1.registerTransform(AB::class) { original, c, _ -> + transformer1.registerRule(AB::class) { original, c, _ -> BB("your_" + original.b.removePrefix("my_"), transformer1.translateCasted(original.child, c)) } assertASTsAreEqual( @@ -586,7 +586,7 @@ class ASTTransformerTest { transformer1.transform(original) as AA, ) // All identity besides AA and AB and AD - transformer1.registerTransform(AD::class) { original -> + transformer1.registerRule(AD::class) { original -> BD("your_" + original.d.removePrefix("my_")) } assertASTsAreEqual( @@ -614,7 +614,7 @@ class ASTTransformerTest { @Test fun testIdentityTransformationOfIntermediateNodesWithOrigin() { val transformer1 = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer1.registerTransform(AA::class) { original, c -> + transformer1.registerRule(AA::class) { original, c -> BA("your_" + original.a.removePrefix("my_"), transformer1.translateCasted(original.child, c)) } val original = @@ -651,7 +651,7 @@ class ASTTransformerTest { @Test fun `exception handling, root`() { val transformer = ASTTransformer() - transformer.registerTransform(AA::class) { aa -> + transformer.registerRule(AA::class) { aa -> // if (false) needed to detect the target type as AA if (false) aa else throw Exception("Something went wrong") } @@ -672,7 +672,7 @@ class ASTTransformerTest { @Test fun `exception handling, internal node`() { val transformer = ASTTransformer(defaultTransformation = IDENTTITY_TRANSFORMATION) - transformer.registerTransform(AB::class) { ab -> + transformer.registerRule(AB::class) { ab -> // if (false) needed to detect the target type as AA if (false) ab else throw Exception("Something went wrong") } @@ -696,7 +696,7 @@ class ASTTransformerTest { val transformer = ASTTransformer() assertThrows(ConfigurationException::class.java) { transformer - .registerTransform(NoSetter::class) { _ -> NoSetter() } + .registerRule(NoSetter::class) { _ -> NoSetter() } .withChild(NoSetter::child, NoSetter::child) } } diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java index 02599c55..01c4e586 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ASTTransformer.java @@ -38,18 +38,18 @@ public ASTTransformer(FaultTolerance faultTolerance, @Nullable Function4 @NotNull TransformationRule registerNodeFactory( Class source, Class target, String nodeType ) { - return registerTransform(getKotlinClass(source), getKotlinClass(target), nodeType); + return registerRule(getKotlinClass(source), getKotlinClass(target), nodeType); } protected TransformationRule registerNodeFactory(Class source, Function1 function) { - return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); + return registerRule(getKotlinClass(source), (s, t) -> function.invoke(s)); } protected TransformationRule registerNodeFactory( Class source, Function3 function ) { - return registerTransform(getKotlinClass(source), function); + return registerRule(getKotlinClass(source), function); } /** diff --git a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java index 5cb458ee..bc0feefe 100644 --- a/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java +++ b/javalib/src/main/java/com/strumenta/starlasu/javalib/ParseTreeToASTTransformer.java @@ -27,18 +27,18 @@ public ParseTreeToASTTransformer(FaultTolerance faultTolerance) { protected @NotNull TransformationRule registerNodeFactory( Class source, Class target, String nodeType ) { - return registerTransform(getKotlinClass(source), getKotlinClass(target), nodeType); + return registerRule(getKotlinClass(source), getKotlinClass(target), nodeType); } protected TransformationRule registerNodeFactory(Class source, Function1 function) { - return registerTransform(getKotlinClass(source), (s, t) -> function.invoke(s)); + return registerRule(getKotlinClass(source), (s, t) -> function.invoke(s)); } protected TransformationRule registerNodeFactory( Class source, Function3 function ) { - return registerTransform(getKotlinClass(source), function); + return registerRule(getKotlinClass(source), function); } }