From 16f5583af1706f1d9b11466f1c34bd155a3b901b Mon Sep 17 00:00:00 2001 From: domenico Date: Wed, 7 Aug 2024 15:09:13 +0200 Subject: [PATCH 1/5] Add test case --- .../rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt | 10 ++++++++++ .../src/test/resources/smeup/MUDRNRAPU00244.rpgle | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt index 44f16dbb7..b444e14d2 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt @@ -528,4 +528,14 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() { val expected = listOf("0") assertEquals(expected, "smeup/MUDRNRAPU00241".outputOf(configuration = smeupConfig)) } + + /** + * Access to an array detected as a function call recursively + * @see #LS24003753 + */ + @Test + fun executeMUDRNRAPU00244() { + val expected = listOf("ok") + assertEquals(expected, "smeup/MUDRNRAPU00244".outputOf(configuration = smeupConfig)) + } } \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle new file mode 100644 index 000000000..802523314 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle @@ -0,0 +1,12 @@ + D £DBG_Str S 2 + D £C6JDSO DS 512 + D £C6JO_TO 2 0 DIM(10) + D $X S 5 0 + D §§IMPO S 6 0 + D T$AG_I S 21 6 DIM(15) + C EVAL $X=2 + C EVAL £C6JO_TO($X)=2 + C EVAL T$AG_I(£C6JO_TO($X))= + C T$AG_I(£C6JO_TO($X))+§§IMPO + C EVAL £DBG_Str='ok' + C £DBG_Str DSPLY From 39d9722f430313ef36de13e6bbae3d9e79806cd0 Mon Sep 17 00:00:00 2001 From: domenico Date: Wed, 7 Aug 2024 15:09:57 +0200 Subject: [PATCH 2/5] Make Node.replace return the transformed value --- .../kotlin/com/strumenta/kolasu/model/Processing.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kolasu/src/main/kotlin/com/strumenta/kolasu/model/Processing.kt b/kolasu/src/main/kotlin/com/strumenta/kolasu/model/Processing.kt index 6e8933725..8ceef98d3 100644 --- a/kolasu/src/main/kotlin/com/strumenta/kolasu/model/Processing.kt +++ b/kolasu/src/main/kotlin/com/strumenta/kolasu/model/Processing.kt @@ -191,7 +191,7 @@ fun Node.transformChildren(operation: (Node) -> Node, inPlace: Boolean = false): } } var instanceToTransform = this - if (!changes.isEmpty()) { + if (changes.isNotEmpty()) { val constructor = this.javaClass.kotlin.primaryConstructor!! val params = HashMap() constructor.parameters.forEach { param -> @@ -206,9 +206,8 @@ fun Node.transformChildren(operation: (Node) -> Node, inPlace: Boolean = false): return instanceToTransform } -fun Node.replace(other: Node) { - if (this.parent == null) { - throw IllegalStateException("Parent not set") - } - this.parent!!.transformChildren(inPlace = true, operation = { if (it == this) other else it }) +fun Node.replace(other: Node): Node { + return this.parent?.let { + it.transformChildren(inPlace = true, operation = { node -> if (node == this) other else node }) + } ?: throw IllegalStateException("Parent not set") } From 4a8f1847bbac63876b694bed8bd1aa6ded2b7770 Mon Sep 17 00:00:00 2001 From: domenico Date: Wed, 7 Aug 2024 15:10:18 +0200 Subject: [PATCH 3/5] Fix immutability issue on CallPStmt --- .../main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt index 0cb477e38..1a57dce6c 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt @@ -816,7 +816,7 @@ data class CallStmt( @Serializable data class CallPStmt( - val functionCall: FunctionCall, + var functionCall: FunctionCall, val errorIndicator: IndicatorKey? = null, override val position: Position? = null ) : Statement(position), StatementThatCanDefineData { From f4433b54ee92e1a1dbab664f36550c7f0cba1654 Mon Sep 17 00:00:00 2001 From: domenico Date: Wed, 7 Aug 2024 15:10:40 +0200 Subject: [PATCH 4/5] Refactor Node.resolveFunctionCalls --- .../parsing/parsetreetoast/resolution.kt | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/resolution.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/resolution.kt index d350ab101..1b82aaa90 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/resolution.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/resolution.kt @@ -99,16 +99,35 @@ private fun Node.resolveDataRefs(cu: CompilationUnit) { private fun Node.resolveFunctionCalls(cu: CompilationUnit) { // replace FunctionCall with ArrayAccessExpr where it makes sense this.specificProcess(FunctionCall::class.java) { fc -> - if (fc.args.size == 1) { - val data = cu.allDataDefinitions.firstOrNull { it.name == fc.function.name } - if (data != null) { - fc.replace(ArrayAccessExpr( - array = DataRefExpr(ReferenceByName(fc.function.name, referred = data)), - index = fc.args[0], - position = fc.position)) - } + fc.tryReplaceWithArrayAccess(cu) + } +} + +private fun FunctionCall.tryReplaceWithArrayAccess(cu: CompilationUnit): Optional { + // Only said FunctionCalls with 1 arg can be ArrayAccessExpr + if (this.args.size != 1) return Optional.empty() + + // Replacement can only happen when there is a DataDefinition named like this 'FunctionCall' + val data = cu.allDataDefinitions.firstOrNull { it.name == this.function.name } + data ?: return Optional.empty() + + // Recursively try to process inner expressions + var indexExpr = this.args.first() + if (indexExpr is FunctionCall) { + indexExpr.tryReplaceWithArrayAccess(cu).ifPresent { + // Needed for type-checking + if (it is Expression) indexExpr = it } } + + val arrayAccessExpr = ArrayAccessExpr( + array = DataRefExpr(ReferenceByName(this.function.name, referred = data)), + index = indexExpr, + position = this.position + ) + + val newExpression = this.replace(arrayAccessExpr).children.first() + return Optional.of(newExpression) } fun MuteAnnotation.resolveAndValidate(cu: CompilationUnit) { From 9f01aa5ff909bece8f6449c384324dd29ebd5901 Mon Sep 17 00:00:00 2001 From: domenico Date: Wed, 14 Aug 2024 09:14:17 +0200 Subject: [PATCH 5/5] Add high-level comments in RPGLE test source --- .../src/test/resources/smeup/MUDRNRAPU00244.rpgle | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle index 802523314..26145fe3a 100644 --- a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00244.rpgle @@ -1,12 +1,23 @@ + * Helper declarations D £DBG_Str S 2 + + * Declarations needed for test purpose D £C6JDSO DS 512 D £C6JO_TO 2 0 DIM(10) D $X S 5 0 D §§IMPO S 6 0 D T$AG_I S 21 6 DIM(15) + + * Initializing array indices to avoid null value exception C EVAL $X=2 C EVAL £C6JO_TO($X)=2 + + * Trying to access an array while accessing another array (like £C6JO_TO($X) inside T$AG_I). + * £C6JO_TO($X) should not be seen as a function call but as an array access. + * This should be true when this kind of array access is contained in any type expression. C EVAL T$AG_I(£C6JO_TO($X))= C T$AG_I(£C6JO_TO($X))+§§IMPO + + * Test output C EVAL £DBG_Str='ok' C £DBG_Str DSPLY