From 032362597ee2a786f1b9a48d3c8bd4b1e05140ac Mon Sep 17 00:00:00 2001 From: Domenico Date: Tue, 4 Jun 2024 16:13:53 +0200 Subject: [PATCH 01/14] Add test cases --- .../rpgparser/smeup/MULANGT15BaseBif1Test.kt | 23 +++++++ .../test/resources/smeup/MUDRNRAPU00210.rpgle | 60 +++++++++++++++++++ .../test/resources/smeup/MUDRNRAPU00211.rpgle | 60 +++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle create mode 100644 rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00211.rpgle diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt index f7699a704..e511163ba 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt @@ -1,7 +1,9 @@ package com.smeup.rpgparser.smeup +import com.smeup.rpgparser.parsing.parsetreetoast.AstResolutionError import org.junit.Test import kotlin.test.assertEquals +import kotlin.test.assertFailsWith open class MULANGT15BaseBif1Test : MULANGTTest() { /** @@ -53,4 +55,25 @@ open class MULANGT15BaseBif1Test : MULANGTTest() { val expected = listOf("1(10 - North York) 2(5 - North) 3(15 - North )") assertEquals(expected, "smeup/T15_A80_P09".outputOf()) } + + /** + * %LOOKUPxx tests + * @see #LS24002887 + */ + @Test + fun executeMUDRNRAPU00210() { + val expected = listOf("3", "0", "0", "3", "3", "0", "0", "3", "2", "2", "3", "3") + assertEquals(expected, "smeup/MUDRNRAPU00210".outputOf()) + } + + /** + * %LOOKUPxx fails with arrays that are not sequenced + * @see #LS24002887 + */ + @Test + fun executeMUDRNRAPU00211() { + assertFailsWith(RuntimeException::class) { + "smeup/MUDRNRAPU00211".outputOf(configuration = smeupConfig) + } + } } \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle new file mode 100644 index 000000000..9654c08d7 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle @@ -0,0 +1,60 @@ + D ARRAY S 5 DIM(10) PERRCD(1) CTDATA ASCEND _NOTXT + D INDEX S 2 0 + *--------------------------------------------------------------- + D* M A I N + *--------------------------------------------------------------- + C EXSR ROUTINE + C SETON LR + *--------------------------------------------------------------- + D* Routine + *--------------------------------------------------------------- + C ROUTINE BEGSR + * GT - last item is greater + C EVAL INDEX=%LOOKUPGT('AAAAA':ARRAY:1:3) + C INDEX DSPLY + * GT - last item is equal + C EVAL INDEX=%LOOKUPGT('CCCCC':ARRAY:1:3) + C INDEX DSPLY + * GT - no item found + C EVAL INDEX=%LOOKUPGT('DDDDD':ARRAY:1:3) + C INDEX DSPLY + * GE - last item is greater + C EVAL INDEX=%LOOKUPGE('BBBBB':ARRAY:1:3) + C INDEX DSPLY + * GE - last item is equal + C EVAL INDEX=%LOOKUPGE('CCCCC':ARRAY:1:3) + C INDEX DSPLY + * GE - no item found + C EVAL INDEX=%LOOKUPGE('DDDDD':ARRAY:1:3) + C INDEX DSPLY + * LT - no item found + C EVAL INDEX=%LOOKUPLT('BBBBB':ARRAY:2:2) + C INDEX DSPLY + * LT - one item found + C EVAL INDEX=%LOOKUPLT('DDDDD':ARRAY:2:2) + C INDEX DSPLY + * LT - one item equal + C EVAL INDEX=%LOOKUPLT('CCCCC':ARRAY:2:2) + C INDEX DSPLY + * LE - no item found + C EVAL INDEX=%LOOKUPLE('BBBBB':ARRAY:2:2) + C INDEX DSPLY + * LE - one item found + C EVAL INDEX=%LOOKUPLE('DDDDD':ARRAY:2:2) + C INDEX DSPLY + * LE - one item equal + C EVAL INDEX=%LOOKUPLE('CCCCC':ARRAY:2:2) + C INDEX DSPLY + * + C ENDSR + *--------------------------------------------------------------- +** TXT1 +AAAAA +BBBBB +CCCCC +DDDDD +EEEEE +FFFFF +GGGGG +HHHHH +IIIII \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00211.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00211.rpgle new file mode 100644 index 000000000..6c2b781b4 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00211.rpgle @@ -0,0 +1,60 @@ + D ARRAY S 5 DIM(10) PERRCD(1) CTDATA _NOTXT + D INDEX S 2 0 + *--------------------------------------------------------------- + D* M A I N + *--------------------------------------------------------------- + C EXSR ROUTINE + C SETON LR + *--------------------------------------------------------------- + D* Routine + *--------------------------------------------------------------- + C ROUTINE BEGSR + * GT - last item is greater + C EVAL INDEX=%LOOKUPGT('AAAAA':ARRAY:1:3) + C INDEX DSPLY + * GT - last item is equal + C EVAL INDEX=%LOOKUPGT('CCCCC':ARRAY:1:3) + C INDEX DSPLY + * GT - no item found + C EVAL INDEX=%LOOKUPGT('DDDDD':ARRAY:1:3) + C INDEX DSPLY + * GE - last item is greater + C EVAL INDEX=%LOOKUPGE('BBBBB':ARRAY:1:3) + C INDEX DSPLY + * GE - last item is equal + C EVAL INDEX=%LOOKUPGE('CCCCC':ARRAY:1:3) + C INDEX DSPLY + * GE - no item found + C EVAL INDEX=%LOOKUPGE('DDDDD':ARRAY:1:3) + C INDEX DSPLY + * LT - no item found + C EVAL INDEX=%LOOKUPLT('BBBBB':ARRAY:2:2) + C INDEX DSPLY + * LT - one item found + C EVAL INDEX=%LOOKUPLT('DDDDD':ARRAY:2:2) + C INDEX DSPLY + * LT - one item equal + C EVAL INDEX=%LOOKUPLT('CCCCC':ARRAY:2:2) + C INDEX DSPLY + * LE - no item found + C EVAL INDEX=%LOOKUPLE('BBBBB':ARRAY:2:2) + C INDEX DSPLY + * LE - one item found + C EVAL INDEX=%LOOKUPLE('DDDDD':ARRAY:2:2) + C INDEX DSPLY + * LE - one item equal + C EVAL INDEX=%LOOKUPLE('CCCCC':ARRAY:2:2) + C INDEX DSPLY + * + C ENDSR + *--------------------------------------------------------------- +** TXT1 +AAAAA +BBBBB +CCCCC +DDDDD +EEEEE +FFFFF +GGGGG +HHHHH +IIIII \ No newline at end of file From d1831c74d67b8fe75e427ac009c4ac022f41b533 Mon Sep 17 00:00:00 2001 From: Domenico Date: Tue, 4 Jun 2024 16:14:38 +0200 Subject: [PATCH 02/14] Add BIF ast construction --- .../parsing/ast/builtin_functions.kt | 61 +++++++++++++++++-- .../rpgparser/parsing/parsetreetoast/bif.kt | 44 +++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/builtin_functions.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/builtin_functions.kt index 08a44cda5..da35707fd 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/builtin_functions.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/builtin_functions.kt @@ -22,11 +22,6 @@ import com.strumenta.kolasu.model.Position import kotlinx.serialization.Serializable // %LOOKUP -// To be supported: -// * %LOOKUPLT -// * %LOOKUPLE -// * %LOOKUPGT -// * %LOOKUPGE @Serializable data class LookupExpr( var searchedValued: Expression, @@ -40,6 +35,62 @@ data class LookupExpr( override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this) } +// %LOOKUPGT +@Serializable +data class LookupGtExpr( + var searchedValue: Expression, + val array: Expression, + val start: Expression? = null, + val length: Expression? = null, + override val position: Position? = null +) : Expression(position) { + override val loggableEntityName: String + get() = "%LOOKUPGT" + override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this) +} + +// %LOOKUPGE +@Serializable +data class LookupGeExpr( + var searchedValue: Expression, + val array: Expression, + val start: Expression? = null, + val length: Expression? = null, + override val position: Position? = null +) : Expression(position) { + override val loggableEntityName: String + get() = "%LOOKUPGE" + override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this) +} + +// %LOOKUPLT +@Serializable +data class LookupLtExpr( + var searchedValue: Expression, + val array: Expression, + val start: Expression? = null, + val length: Expression? = null, + override val position: Position? = null +) : Expression(position) { + override val loggableEntityName: String + get() = "%LOOKUPLT" + override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this) +} + +// %LOOKUPLE +@Serializable +data class LookupLeExpr( + var searchedValue: Expression, + val array: Expression, + val start: Expression? = null, + val length: Expression? = null, + override val position: Position? = null +) : Expression(position) { + override val loggableEntityName: String + get() = "%LOOKUPLE" + override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this) +} + // %SCAN @Serializable data class ScanExpr( diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/bif.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/bif.kt index e77d9cae1..e0c6e95cd 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/bif.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/bif.kt @@ -29,6 +29,10 @@ internal fun RpgParser.BifContext.toAst(conf: ToAstConfiguration = ToAstConfigur position = position ) this.bif_lookup() != null -> this.bif_lookup().toAst(conf) + this.bif_lookupge() != null -> this.bif_lookupge().toAst(conf) + this.bif_lookupgt() != null -> this.bif_lookupgt().toAst(conf) + this.bif_lookuple() != null -> this.bif_lookuple().toAst(conf) + this.bif_lookuplt() != null -> this.bif_lookuplt().toAst(conf) this.bif_xlate() != null -> this.bif_xlate().toAst(conf) this.bif_scan() != null -> this.bif_scan().toAst(conf) this.bif_check() != null -> this.bif_check().toAst(conf) @@ -249,6 +253,46 @@ internal fun RpgParser.Bif_lookupContext.toAst(conf: ToAstConfiguration = ToAstC toPosition(conf.considerPosition)) } +internal fun RpgParser.Bif_lookupgeContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): LookupGeExpr { + return LookupGeExpr( + this.bif_lookupargs().arg.toAst(conf), + this.bif_lookupargs().array.toAst(conf), + this.bif_lookupargs().startindex?.toAst(conf), + this.bif_lookupargs().numberelements?.toAst(conf), + toPosition(conf.considerPosition) + ) +} + +internal fun RpgParser.Bif_lookupgtContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): LookupGtExpr { + return LookupGtExpr( + this.bif_lookupargs().arg.toAst(conf), + this.bif_lookupargs().array.toAst(conf), + this.bif_lookupargs().startindex?.toAst(conf), + this.bif_lookupargs().numberelements?.toAst(conf), + toPosition(conf.considerPosition) + ) +} + +internal fun RpgParser.Bif_lookupleContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): LookupLeExpr { + return LookupLeExpr( + this.bif_lookupargs().arg.toAst(conf), + this.bif_lookupargs().array.toAst(conf), + this.bif_lookupargs().startindex?.toAst(conf), + this.bif_lookupargs().numberelements?.toAst(conf), + toPosition(conf.considerPosition) + ) +} + +internal fun RpgParser.Bif_lookupltContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): LookupLtExpr { + return LookupLtExpr( + this.bif_lookupargs().arg.toAst(conf), + this.bif_lookupargs().array.toAst(conf), + this.bif_lookupargs().startindex?.toAst(conf), + this.bif_lookupargs().numberelements?.toAst(conf), + toPosition(conf.considerPosition) + ) +} + internal fun RpgParser.Bif_parmsContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): ParmsExpr { return ParmsExpr( this.text, From b6f64576c2c4bd229697e22afe191e8d778234e5 Mon Sep 17 00:00:00 2001 From: Domenico Date: Tue, 4 Jun 2024 16:15:00 +0200 Subject: [PATCH 03/14] Record classes in serialization.kt --- .../kotlin/com/smeup/rpgparser/parsing/ast/serialization.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/serialization.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/serialization.kt index 4ab21c31b..6f50238ae 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/serialization.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/serialization.kt @@ -153,6 +153,10 @@ private val modules = SerializersModule { subclass(LogicalCondition::class) subclass(LogicalOrExpr::class) subclass(LookupExpr::class) + subclass(LookupGtExpr::class) + subclass(LookupGeExpr::class) + subclass(LookupLtExpr::class) + subclass(LookupLeExpr::class) subclass(LowValExpr::class) subclass(MinusExpr::class) subclass(MultExpr::class) From c7a149f62a0cc1c177c2e153fd41a0392da053a9 Mon Sep 17 00:00:00 2001 From: Domenico Date: Tue, 4 Jun 2024 16:15:08 +0200 Subject: [PATCH 04/14] Add utility functions --- .../kotlin/com/smeup/rpgparser/interpreter/values.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt index f6d220f4a..bdbd72b07 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt @@ -27,6 +27,7 @@ import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.* +import kotlin.math.abs const val PAD_CHAR = ' ' const val PAD_STRING = PAD_CHAR.toString() @@ -1008,6 +1009,14 @@ data class DataStructValue(var value: String, private val optionalExternalLen: I fun Int.asValue() = IntValue(this.toLong()) fun Boolean.asValue() = BooleanValue(this) +infix fun Value.distanceFrom(other: Value): Long { + val a = this.asInt() + val b = other.asInt() + return (b - a).value +} + +infix fun Value.absoluteDistanceFrom(other: Value): Long = abs(this distanceFrom other) + fun areEquals(value1: Value, value2: Value): Boolean { return when { value1 is DecimalValue && value2 is IntValue || From 7bd095852eb041cce8b50133fc57bd2c638d9dbb Mon Sep 17 00:00:00 2001 From: Domenico Date: Tue, 4 Jun 2024 16:15:20 +0200 Subject: [PATCH 05/14] Implement evaluation logic --- .../smeup/rpgparser/interpreter/Evaluator.kt | 4 + .../interpreter/ExpressionEvaluation.kt | 135 ++++++++++++++---- 2 files changed, 115 insertions(+), 24 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/Evaluator.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/Evaluator.kt index 4d50fd053..e9116a1c3 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/Evaluator.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/Evaluator.kt @@ -42,6 +42,10 @@ interface Evaluator { fun eval(expression: MultExpr): Value fun eval(expression: CharExpr): Value fun eval(expression: LookupExpr): Value + fun eval(expression: LookupGtExpr): Value + fun eval(expression: LookupGeExpr): Value + fun eval(expression: LookupLtExpr): Value + fun eval(expression: LookupLeExpr): Value fun eval(expression: ArrayAccessExpr): Value fun eval(expression: HiValExpr): HiValValue fun eval(expression: LowValExpr): LowValValue diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt index 019d5be08..54613956d 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt @@ -20,10 +20,7 @@ import com.smeup.rpgparser.execution.MainExecutionContext import com.smeup.rpgparser.logging.ProgramUsageType import com.smeup.rpgparser.parsing.ast.* import com.smeup.rpgparser.parsing.parsetreetoast.LogicalCondition -import com.smeup.rpgparser.utils.asBigDecimal -import com.smeup.rpgparser.utils.asLong -import com.smeup.rpgparser.utils.divideAtIndex -import com.smeup.rpgparser.utils.moveEndingString +import com.smeup.rpgparser.utils.* import com.strumenta.kolasu.model.Position import com.strumenta.kolasu.model.specificProcess import java.math.BigDecimal @@ -33,10 +30,7 @@ import java.time.LocalDate import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit -import kotlin.math.abs -import kotlin.math.pow -import kotlin.math.roundToLong -import kotlin.math.sqrt +import kotlin.math.* import kotlin.time.Duration.Companion.nanoseconds class ExpressionEvaluation( @@ -248,25 +242,58 @@ class ExpressionEvaluation( } override fun eval(expression: LookupExpr): Value = proxyLogging(expression) { - val searchValued = expression.searchedValued.evalWith(this) - val array = expression.array.evalWith(this) as ArrayValue - var index = array.elements().indexOfFirst { - areEquals(it, searchValued) - } - // If 'start' and/or 'length' specified (both optional) - // Start: is the index of array element to start search - // Length: is the limit number of elements to search forward - var start = expression.start?.evalWith(this)?.asInt() - start = start?.minus(1.asValue()) ?: 0.asValue() + lookup( + array = expression.array.evalWith(this) as ArrayValue, + arrayType = expression.array.type() as ArrayType, + searchedValue = expression.searchedValued.evalWith(this), + start = expression.start?.evalWith(this)?.asInt(), + length = expression.length?.evalWith(this)?.asInt(), + operator = ComparisonOperator.EQ + ) + } - val arrayElements = expression.array.type().numberOfElements().asValue() - val length = expression.length?.evalWith(this)?.asInt() ?: arrayElements + override fun eval(expression: LookupGtExpr): Value = proxyLogging(expression) { + lookup( + array = expression.array.evalWith(this) as ArrayValue, + arrayType = expression.array.type() as ArrayType, + searchedValue = expression.searchedValue.evalWith(this), + start = expression.start?.evalWith(this)?.asInt(), + length = expression.length?.evalWith(this)?.asInt(), + operator = ComparisonOperator.GT + ) + } + + override fun eval(expression: LookupGeExpr): Value = proxyLogging(expression) { + lookup( + array = expression.array.evalWith(this) as ArrayValue, + arrayType = expression.array.type() as ArrayType, + searchedValue = expression.searchedValue.evalWith(this), + start = expression.start?.evalWith(this)?.asInt(), + length = expression.length?.evalWith(this)?.asInt(), + operator = ComparisonOperator.GE + ) + } - val lowerLimit = start.value - val upperLimit = start.plus(length).value - 1 - if (lowerLimit > index || upperLimit < index) index = -1 + override fun eval(expression: LookupLtExpr): Value = proxyLogging(expression) { + lookup( + array = expression.array.evalWith(this) as ArrayValue, + arrayType = expression.array.type() as ArrayType, + searchedValue = expression.searchedValue.evalWith(this), + start = expression.start?.evalWith(this)?.asInt(), + length = expression.length?.evalWith(this)?.asInt(), + operator = ComparisonOperator.LT + ) + } - return@proxyLogging if (index == -1) 0.asValue() else (index + 1).asValue() + override fun eval(expression: LookupLeExpr): Value = proxyLogging(expression) { + lookup( + array = expression.array.evalWith(this) as ArrayValue, + arrayType = expression.array.type() as ArrayType, + searchedValue = expression.searchedValue.evalWith(this), + start = expression.start?.evalWith(this)?.asInt(), + length = expression.length?.evalWith(this)?.asInt(), + operator = ComparisonOperator.LE + ) } override fun eval(expression: ArrayAccessExpr): Value = proxyLogging(expression) { @@ -789,6 +816,66 @@ class ExpressionEvaluation( } } + private inline fun lookupLinearSearch(values: List, predicate: (Value) -> Boolean, operator: ComparisonOperator): Int { + val stopOnFirstMatch = when (operator) { + ComparisonOperator.EQ, ComparisonOperator.NE -> true + ComparisonOperator.GE, ComparisonOperator.GT, ComparisonOperator.LE, ComparisonOperator.LT -> false + } + + var selectedIndex = -1 + for ((index, value) in values.withIndex()) { + val matchesCondition = predicate(value) + if (!matchesCondition) continue + if (stopOnFirstMatch) return index + selectedIndex = index + } + + return selectedIndex + } + + private fun lookup( + array: ArrayValue, + arrayType: ArrayType, + searchedValue: Value, + start: IntValue?, + length: IntValue?, + operator: ComparisonOperator + ): Value { + val arrayLength = arrayType.numberOfElements() + val isSequenced = arrayType.ascend != null + + when (operator) { + ComparisonOperator.GE, ComparisonOperator.GT, ComparisonOperator.LE, ComparisonOperator.LT -> { + if (!isSequenced) + throw UnsupportedOperationException("Array must be defined with ASCEND or DESCEND keyword") + } + else -> {} + } + + val computedLength = length?.value?.toInt() ?: arrayLength + val lowerBound: Int = start?.value?.let { it - 1 }?.toInt() ?: 0 + val upperBound = min(lowerBound + computedLength, arrayLength) + val searchRange = array.elements().slice(lowerBound until upperBound) + + val searchFn: (Value) -> Boolean = when (operator) { + ComparisonOperator.EQ -> { it: Value -> areEquals(it, searchedValue) } + ComparisonOperator.NE -> { it: Value -> !areEquals(it, searchedValue) } + ComparisonOperator.LT -> { it: Value -> it < searchedValue } + ComparisonOperator.GT -> { it: Value -> it > searchedValue } + ComparisonOperator.GE -> { it: Value -> it >= searchedValue } + ComparisonOperator.LE -> { it: Value -> it <= searchedValue } + } + + /* + * TODO: Consider implementing binary search strategy for sequenced arrays + * see https://www.ibm.com/docs/en/i/7.5?topic=functions-lookupxx-look-up-array-element + */ + val index = lookupLinearSearch(searchRange, searchFn, operator) + + val offsetIndex = if (index == -1) 0 else index + lowerBound + 1 + return offsetIndex.asValue() + } + private fun cleanNumericString(s: String): String { val result = s.moveEndingString("-") return when { From af99596206a02dcebd34773fd217fb7e3189216c Mon Sep 17 00:00:00 2001 From: Domenico Date: Tue, 4 Jun 2024 16:21:39 +0200 Subject: [PATCH 06/14] Fix ktlintCheck issue --- .../kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt index e511163ba..00cae517a 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt @@ -1,6 +1,5 @@ package com.smeup.rpgparser.smeup -import com.smeup.rpgparser.parsing.parsetreetoast.AstResolutionError import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith From 1a9126279cb7aba436ee9ae7bf5a2946c5b21c50 Mon Sep 17 00:00:00 2001 From: Domenico Date: Wed, 5 Jun 2024 10:48:44 +0200 Subject: [PATCH 07/14] Implement binary search strategy --- .../interpreter/ExpressionEvaluation.kt | 108 ++++++++++++++---- .../rpgparser/smeup/MULANGT15BaseBif1Test.kt | 2 +- .../test/resources/smeup/MUDRNRAPU00210.rpgle | 46 ++++---- 3 files changed, 107 insertions(+), 49 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt index 54613956d..f3c182bdd 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt @@ -816,21 +816,90 @@ class ExpressionEvaluation( } } - private inline fun lookupLinearSearch(values: List, predicate: (Value) -> Boolean, operator: ComparisonOperator): Int { - val stopOnFirstMatch = when (operator) { - ComparisonOperator.EQ, ComparisonOperator.NE -> true - ComparisonOperator.GE, ComparisonOperator.GT, ComparisonOperator.LE, ComparisonOperator.LT -> false + private fun lookupLinearSearch(values: List, target: Value): Int { + for ((index, value) in values.withIndex()) { + if (areEquals(value, target)) + return index } - var selectedIndex = -1 - for ((index, value) in values.withIndex()) { - val matchesCondition = predicate(value) - if (!matchesCondition) continue - if (stopOnFirstMatch) return index - selectedIndex = index + return -1 + } + + private inline fun lookupBinarySearchWithComparator( + values: List, + predicate: (Value) -> Boolean, + isAscending: Boolean, + searchingForLower: Boolean + ): Int { + var (left, right) = Pair(0, values.lastIndex) + var bestCandidateIndex = -1 + + while (left <= right) { + val mid = left + (right - left) / 2 + val currentValue = values[mid] + + if (predicate(currentValue)) { + bestCandidateIndex = mid + when { + isAscending && searchingForLower -> left = mid + 1 + isAscending && !searchingForLower -> right = mid - 1 + !isAscending && searchingForLower -> right = mid - 1 + !isAscending && !searchingForLower -> left = mid + 1 + } + } else { + when { + isAscending && searchingForLower -> right = mid - 1 + isAscending && !searchingForLower -> left = mid + 1 + !isAscending && searchingForLower -> left = mid + 1 + !isAscending && !searchingForLower -> right = mid - 1 + } + } } - return selectedIndex + return bestCandidateIndex + } + + private inline fun lookupBinarySearchWithCondition(values: List, predicate: (Value) -> Boolean, target: Value): Int { + var (left, right) = Pair(0, values.lastIndex) + + while (left <= right) { + val mid = left + (right - left) / 2 + val currentValue = values[mid] + + if (predicate(currentValue)) { + return mid + } + + if (currentValue < target) { + right = mid - 1 + continue + } + + // This means currentValue > target + left = mid + 1 + } + + return -1 + } + + private fun lookupBinarySearch( + values: List, + target: Value, + operator: ComparisonOperator, + isAscending: Boolean + ): Int { + return when (operator) { + ComparisonOperator.LE -> + lookupBinarySearchWithComparator(values, { it <= target }, isAscending, true) + ComparisonOperator.LT -> + lookupBinarySearchWithComparator(values, { it < target }, isAscending, true) + ComparisonOperator.GE -> + lookupBinarySearchWithComparator(values, { it >= target }, isAscending, false) + ComparisonOperator.GT -> + lookupBinarySearchWithComparator(values, { it > target }, isAscending, false) + ComparisonOperator.EQ -> lookupBinarySearchWithCondition(values, { areEquals(it, target) }, target) + else -> -1 + } } private fun lookup( @@ -857,20 +926,9 @@ class ExpressionEvaluation( val upperBound = min(lowerBound + computedLength, arrayLength) val searchRange = array.elements().slice(lowerBound until upperBound) - val searchFn: (Value) -> Boolean = when (operator) { - ComparisonOperator.EQ -> { it: Value -> areEquals(it, searchedValue) } - ComparisonOperator.NE -> { it: Value -> !areEquals(it, searchedValue) } - ComparisonOperator.LT -> { it: Value -> it < searchedValue } - ComparisonOperator.GT -> { it: Value -> it > searchedValue } - ComparisonOperator.GE -> { it: Value -> it >= searchedValue } - ComparisonOperator.LE -> { it: Value -> it <= searchedValue } - } - - /* - * TODO: Consider implementing binary search strategy for sequenced arrays - * see https://www.ibm.com/docs/en/i/7.5?topic=functions-lookupxx-look-up-array-element - */ - val index = lookupLinearSearch(searchRange, searchFn, operator) + val index = if (isSequenced) + lookupBinarySearch(searchRange, searchedValue, operator, arrayType.ascend!!) + else lookupLinearSearch(searchRange, searchedValue) val offsetIndex = if (index == -1) 0 else index + lowerBound + 1 return offsetIndex.asValue() diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt index 00cae517a..84dddf98d 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT15BaseBif1Test.kt @@ -61,7 +61,7 @@ open class MULANGT15BaseBif1Test : MULANGTTest() { */ @Test fun executeMUDRNRAPU00210() { - val expected = listOf("3", "0", "0", "3", "3", "0", "0", "3", "2", "2", "3", "3") + val expected = listOf("4", "0", "0", "3", "0", "6", "0", "3", "0", "2", "4", "0") assertEquals(expected, "smeup/MUDRNRAPU00210".outputOf()) } diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle index 9654c08d7..65966e5ce 100644 --- a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00210.rpgle @@ -9,41 +9,41 @@ D* Routine *--------------------------------------------------------------- C ROUTINE BEGSR - * GT - last item is greater - C EVAL INDEX=%LOOKUPGT('AAAAA':ARRAY:1:3) + * GT - some items are greater + C EVAL INDEX=%LOOKUPGT('CCCCC':ARRAY:1:6) + C INDEX DSPLY + * GT - no item is greater + C EVAL INDEX=%LOOKUPGT('IIIII':ARRAY:1:6) C INDEX DSPLY * GT - last item is equal - C EVAL INDEX=%LOOKUPGT('CCCCC':ARRAY:1:3) + C EVAL INDEX=%LOOKUPGT('FFFFF':ARRAY:1:6) C INDEX DSPLY - * GT - no item found - C EVAL INDEX=%LOOKUPGT('DDDDD':ARRAY:1:3) + * GE - some items are greater + C EVAL INDEX=%LOOKUPGE('CCCCC':ARRAY:1:6) C INDEX DSPLY - * GE - last item is greater - C EVAL INDEX=%LOOKUPGE('BBBBB':ARRAY:1:3) + * GE - no item is greater + C EVAL INDEX=%LOOKUPGE('IIIII':ARRAY:1:6) C INDEX DSPLY * GE - last item is equal - C EVAL INDEX=%LOOKUPGE('CCCCC':ARRAY:1:3) - C INDEX DSPLY - * GE - no item found - C EVAL INDEX=%LOOKUPGE('DDDDD':ARRAY:1:3) + C EVAL INDEX=%LOOKUPGE('FFFFF':ARRAY:1:6) C INDEX DSPLY - * LT - no item found - C EVAL INDEX=%LOOKUPLT('BBBBB':ARRAY:2:2) + * LT - first item is equal + C EVAL INDEX=%LOOKUPLT('BBBBB':ARRAY:2:4) C INDEX DSPLY - * LT - one item found - C EVAL INDEX=%LOOKUPLT('DDDDD':ARRAY:2:2) + * LT - some items are less + C EVAL INDEX=%LOOKUPLT('DDDDD':ARRAY:2:4) C INDEX DSPLY - * LT - one item equal - C EVAL INDEX=%LOOKUPLT('CCCCC':ARRAY:2:2) + * LT - no item is less + C EVAL INDEX=%LOOKUPLT('AAAAA':ARRAY:2:4) C INDEX DSPLY - * LE - no item found - C EVAL INDEX=%LOOKUPLE('BBBBB':ARRAY:2:2) + * LE - first item is equal + C EVAL INDEX=%LOOKUPLE('BBBBB':ARRAY:2:4) C INDEX DSPLY - * LE - one item found - C EVAL INDEX=%LOOKUPLE('DDDDD':ARRAY:2:2) + * LE - some items are less + C EVAL INDEX=%LOOKUPLE('DDDDD':ARRAY:2:4) C INDEX DSPLY - * LE - one item equal - C EVAL INDEX=%LOOKUPLE('CCCCC':ARRAY:2:2) + * LE - no item is less + C EVAL INDEX=%LOOKUPLE('AAAAA':ARRAY:2:4) C INDEX DSPLY * C ENDSR From 7272d0c530eb75c4a4d348fa78027dc683b7d5ce Mon Sep 17 00:00:00 2001 From: Domenico Date: Wed, 5 Jun 2024 12:38:30 +0200 Subject: [PATCH 08/14] Simplify binary search logic --- .../interpreter/ExpressionEvaluation.kt | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt index f3c182bdd..181314905 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt @@ -837,23 +837,16 @@ class ExpressionEvaluation( while (left <= right) { val mid = left + (right - left) / 2 val currentValue = values[mid] - - if (predicate(currentValue)) { - bestCandidateIndex = mid - when { - isAscending && searchingForLower -> left = mid + 1 - isAscending && !searchingForLower -> right = mid - 1 - !isAscending && searchingForLower -> right = mid - 1 - !isAscending && !searchingForLower -> left = mid + 1 - } - } else { - when { - isAscending && searchingForLower -> right = mid - 1 - isAscending && !searchingForLower -> left = mid + 1 - !isAscending && searchingForLower -> left = mid + 1 - !isAscending && !searchingForLower -> right = mid - 1 - } - } + val matchesCondition = predicate(currentValue) + if (matchesCondition) bestCandidateIndex = mid + + /* + * Detect if sorting order follows search direction in order to decide which endpoint to move + * - if it follows direction: move left on match and move right else-wise + * - if it does not follow direction: move right on match and move left else-wise + */ + val shouldSearchRight = if (isAscending == searchingForLower) matchesCondition else !matchesCondition + if (shouldSearchRight) left = mid + 1 else right = mid - 1 } return bestCandidateIndex From 15a646704a75f5a7f685173fb087d48b489c648d Mon Sep 17 00:00:00 2001 From: lanarimarco Date: Wed, 5 Jun 2024 14:32:48 +0200 Subject: [PATCH 09/14] Update dependencies to fix some vulnerabilities --- dspfparser/build.gradle | 2 +- examples/build.gradle | 2 +- kolasu/build.gradle | 2 +- rpgJavaInterpreter-core/build.gradle | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dspfparser/build.gradle b/dspfparser/build.gradle index 8a718c345..1613f714b 100644 --- a/dspfparser/build.gradle +++ b/dspfparser/build.gradle @@ -32,7 +32,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" - testImplementation 'junit:junit:4.12' + testImplementation 'unit:junit:4.13.1' implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion" implementation "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serializationVersion" diff --git a/examples/build.gradle b/examples/build.gradle index 59494a228..bc0fa675a 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -22,7 +22,7 @@ dependencies { implementation project(":rpgJavaInterpreter-core") testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.1' } task javadocJar(type: Jar) { diff --git a/kolasu/build.gradle b/kolasu/build.gradle index 333c5d6ed..9fca56e30 100644 --- a/kolasu/build.gradle +++ b/kolasu/build.gradle @@ -39,7 +39,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.1' implementation 'com.fifesoft:rsyntaxtextarea:2.5.8' implementation 'com.fifesoft:autocomplete:2.5.8' diff --git a/rpgJavaInterpreter-core/build.gradle b/rpgJavaInterpreter-core/build.gradle index bc0c9c30a..0bbc18f97 100644 --- a/rpgJavaInterpreter-core/build.gradle +++ b/rpgJavaInterpreter-core/build.gradle @@ -56,10 +56,10 @@ dependencies { api project(":kolasu") implementation "org.apache.logging.log4j:log4j-api-kotlin:1.0.0" - implementation "org.apache.logging.log4j:log4j-api:2.15.0" - implementation "org.apache.logging.log4j:log4j-core:2.15.0" + implementation "org.apache.logging.log4j:log4j-api:2.23.0" + implementation 'org.apache.logging.log4j:log4j-core:2.23.0' - implementation 'commons-io:commons-io:2.6' + implementation 'commons-io:commons-io:2.7' implementation 'com.github.ajalt:clikt:2.1.0' api "io.github.smeup.reload:base:$reloadVersion" @@ -75,8 +75,8 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" - testImplementation 'junit:junit:4.12' - testImplementation 'org.hsqldb:hsqldb:2.5.0' + testImplementation 'junit:junit:4.13.1' + testImplementation 'org.hsqldb:hsqldb:2.7.1' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.3.1' } From f3107641509d4f9930309d7ceabd51db77a23104 Mon Sep 17 00:00:00 2001 From: lanarimarco Date: Wed, 5 Jun 2024 14:55:16 +0200 Subject: [PATCH 10/14] Fix wrong testImplementation artifact name --- dspfparser/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspfparser/build.gradle b/dspfparser/build.gradle index 1613f714b..fa1c90501 100644 --- a/dspfparser/build.gradle +++ b/dspfparser/build.gradle @@ -32,7 +32,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" - testImplementation 'unit:junit:4.13.1' + testImplementation 'junit:junit:4.13.1' implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion" implementation "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serializationVersion" From a1c16358607829033bc97ceb5511e245f217bbe0 Mon Sep 17 00:00:00 2001 From: Domenico Date: Thu, 6 Jun 2024 12:21:00 +0200 Subject: [PATCH 11/14] Remove unused code --- .../main/kotlin/com/smeup/rpgparser/interpreter/values.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt index 52e93c3e9..ae0627835 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt @@ -1072,14 +1072,6 @@ data class DataStructValue(var value: String, private val optionalExternalLen: I fun Int.asValue() = IntValue(this.toLong()) fun Boolean.asValue() = BooleanValue(this) -infix fun Value.distanceFrom(other: Value): Long { - val a = this.asInt() - val b = other.asInt() - return (b - a).value -} - -infix fun Value.absoluteDistanceFrom(other: Value): Long = abs(this distanceFrom other) - fun areEquals(value1: Value, value2: Value): Boolean { return when { value1 is DecimalValue && value2 is IntValue || From b453a15f1318f862227ca365ac097c9c7ccb518f Mon Sep 17 00:00:00 2001 From: Domenico Date: Thu, 6 Jun 2024 12:25:34 +0200 Subject: [PATCH 12/14] Fix ktlintCheck issue --- .../src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt index ae0627835..675a2986a 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/values.kt @@ -28,7 +28,6 @@ import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.* -import kotlin.math.abs const val PAD_CHAR = ' ' const val PAD_STRING = PAD_CHAR.toString() From 33c18dc16f66dcfaf8a5a5a45faa5967c68e811e Mon Sep 17 00:00:00 2001 From: Domenico Date: Fri, 7 Jun 2024 09:18:41 +0200 Subject: [PATCH 13/14] Add test case --- .../smeup/MULANGT02ConstAndDSpecTest.kt | 10 ++++++ .../test/resources/smeup/MUDRNRAPU00213.rpgle | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00213.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 328c549d6..4344cbfa9 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 @@ -312,4 +312,14 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() { val expected = listOf("A40DS1(ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ) DS1_FL1(1)(BCDEFGHIJK) DS1_FL1(2)(LMNOPQRSTU) | A40DS1(A88 LMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ) DS1_FL1(1)(88 ) DS1_FL1(2)(LMNOPQRSTU) | A40DS1(A88 00 VWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ) DS1_FL1(1)(88 ) DS1_FL1(2)(00 )") assertEquals(expected, "smeup/MU024014".outputOf(configuration = smeupConfig)) } + + /** + * DefineStmt on instatement data definitions + * @see #LS24002930 + */ + @Test + fun executeMUDRNRAPU00213() { + val expected = listOf("ok") + assertEquals(expected, "smeup/MUDRNRAPU00213".outputOf(configuration = smeupConfig)) + } } \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00213.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00213.rpgle new file mode 100644 index 000000000..c1afe69bd --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00213.rpgle @@ -0,0 +1,34 @@ + D £DBG_Str S 2 + C £G00 BEGSR + * + C Z-ADD $1 £G00$1 + C Z-ADD $2 £G00$2 + C Z-ADD $3 £G00$3 + + C Z-ADD 01 £G00A1 2 0 + C Z-ADD 02 £G00A2 2 0 + C Z-ADD 03 £G00A3 2 0 + C Z-ADD 04 £G00D1 2 0 + C Z-ADD 05 £G00D2 2 0 + C Z-ADD 06 £G00D3 2 0 + C Z-ADD 07 £G00E2 2 0 + C Z-ADD 08 £G00E3 2 0 + C Z-ADD 10 £G00R1 2 0 + C Z-ADD 60 £G00R2 2 0 + * + C 0 IFEQ 0 + C Z-ADD £G00A1 $1 5 0 + C Z-ADD £G00A2 $2 5 0 + C Z-ADD £G00A3 $3 5 0 + C ENDIF + * + C Z-ADD £G00$1 $1 + C Z-ADD £G00$2 $2 + C Z-ADD £G00$3 $3 + C *LIKE DEFINE $1 £G00$1 + C *LIKE DEFINE $2 £G00$2 + C *LIKE DEFINE $2 £G00$3 + * + C ENDSR + C EVAL £DBG_Str='ok' + C £DBG_Str DSPLY \ No newline at end of file From 631219cf1d1f9f481f8c829a42c6b4547ebc4110 Mon Sep 17 00:00:00 2001 From: Domenico Date: Fri, 7 Jun 2024 09:18:52 +0200 Subject: [PATCH 14/14] Fix resolution issue --- .../kotlin/com/smeup/rpgparser/parsing/ast/cu_components.kt | 2 ++ .../main/kotlin/com/smeup/rpgparser/parsing/ast/statements.kt | 3 +++ 2 files changed, 5 insertions(+) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/cu_components.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/cu_components.kt index 281bd7276..213d00926 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/cu_components.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/ast/cu_components.kt @@ -75,6 +75,8 @@ data class CompilationUnit( private val inStatementsDataDefinitions = mutableListOf() + fun getInStatementDataDefinitions() = inStatementsDataDefinitions + fun addInStatementDataDefinitions(dataDefinitions: List) { inStatementsDataDefinitions.addAll(dataDefinitions) } 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 e6db6b823..75c8f8d7e 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 @@ -1188,7 +1188,10 @@ data class DefineStmt( val containingCU = this.ancestor(CompilationUnit::class.java) ?: return emptyList() + // Search standalone 'D spec' or InStatement definition val originalDataDefinition = containingCU.dataDefinitions.find { it.name == originalName } + ?: containingCU.getInStatementDataDefinitions().find { it.name == originalName } + // If definition was not found as a 'standalone' 'D spec' declaration, // maybe it can be found as a sub-field of DS in 'D specs' declarations containingCU.dataDefinitions.forEach {