diff --git a/rpgJavaInterpreter-core/src/main/antlr/RpgParser.g4 b/rpgJavaInterpreter-core/src/main/antlr/RpgParser.g4 index c94e52bc6..a6c37a809 100644 --- a/rpgJavaInterpreter-core/src/main/antlr/RpgParser.g4 +++ b/rpgJavaInterpreter-core/src/main/antlr/RpgParser.g4 @@ -1496,8 +1496,8 @@ csMULT: csMVR: CS_FIXED BlankIndicator - BlankFlag - BlankIndicator + (BlankFlag | NoFlag) + (BlankIndicator | GeneralIndicator) CS_BlankFactor operation=OP_MVR cspec_fixed_standard_parts; 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 16d630be7..1def3b8a8 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 @@ -1719,7 +1719,7 @@ data class ScanStmt( val left: Expression, val leftLength: Int?, val right: Expression, - val startPosition: Int, + val startPosition: Expression?, val target: AssignableExpression?, val rightIndicators: WithRightIndicators, @Derived val dataDefinition: InStatementDataDefinition? = null, @@ -1727,13 +1727,15 @@ data class ScanStmt( ) : Statement(position), WithRightIndicators by rightIndicators, StatementThatCanDefineData { override fun execute(interpreter: InterpreterCore) { + val start = startPosition?.let { interpreter.eval(it).asString().value.toInt() } ?: 1 + val stringToSearch = interpreter.eval(left).asString().value.substringOfLength(leftLength) - val searchInto = interpreter.eval(right).asString().value.substring(startPosition - 1) + val searchInto = interpreter.eval(right).asString().value.substring(start - 1) val occurrences = mutableListOf() var index = -1 do { index = searchInto.indexOf(stringToSearch, index + 1) - if (index >= 0) occurrences.add(IntValue((index + startPosition).toLong())) + if (index >= 0) occurrences.add(IntValue((index + start).toLong())) } while (index >= 0) if (occurrences.isEmpty()) { interpreter.setIndicators(this, BooleanValue.FALSE, BooleanValue.FALSE, BooleanValue.FALSE) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/misc.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/misc.kt index e9cc0ddf4..20f9d02fa 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/misc.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/misc.kt @@ -1316,20 +1316,30 @@ internal fun CsDELETEContext.toAst(conf: ToAstConfiguration): Statement { internal fun CsSCANContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): ScanStmt { val position = toPosition(conf.considerPosition) + val (compareExpression, compareLength) = this.factor1Context().toIndexedExpression(conf) - val (baseExpression, startPosition) = this.cspec_fixed_standard_parts().factor2.toIndexedExpression(conf) + + val factor2 = this.cspec_fixed_standard_parts().factor2 + + val result = this.cspec_fixed_standard_parts().result + val dataDefinition = this.cspec_fixed_standard_parts().toDataDefinition(result.text, position, conf) + val rightIndicators = cspec_fixed_standard_parts().rightIndicators() - val result = this.cspec_fixed_standard_parts().result.text - val dataDefinition = this.cspec_fixed_standard_parts().toDataDefinition(result, position, conf) - val target = when { - result.isNotBlank() -> this.cspec_fixed_standard_parts()!!.result!!.toAst(conf) - else -> null - } + val target = if (result.text.isNotBlank()) result.toAst(conf) else null + + val baseExpression = factor2.factorContent(0).toAst(conf) + val positionExpression = + if (factor2.factorContent().size > 1) { + factor2.factorContent(1).toAst(conf) + } else { + null + } + return ScanStmt( left = compareExpression, leftLength = compareLength, right = baseExpression, - startPosition = startPosition ?: 1, + startPosition = positionExpression, target = target, rightIndicators = rightIndicators, dataDefinition = dataDefinition, @@ -1378,8 +1388,6 @@ private fun FactorContext.toIndexedExpression(conf: ToAstConfiguration): Pair { - fun String.isLiteral(): Boolean { return (startsWith('\'') && endsWith('\'')) } - val baseStringTokens = this.split(":") val startPosition = when (baseStringTokens.size) { @@ -1389,7 +1397,10 @@ private fun String.toIndexedExpression(position: Position?): Pair StringLiteral(reference.trim('\''), position) + reference.isStringLiteral() -> StringLiteral(reference.trim('\''), position) + reference.contains('(') && reference.endsWith(")") -> { + annidatedReferenceExpression(this, position) + } else -> DataRefExpr(ReferenceByName(reference), position) } to startPosition } @@ -2037,3 +2048,5 @@ private fun List.removeUnnecessaryRecordFormat() dataDef.type is RecordFormatType && this.any { it.type is DataStructureType && it.name.uppercase() == dataDef.name.uppercase() } } } + +private fun String.isStringLiteral(): Boolean = startsWith('\'') && endsWith('\'') \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/SmeupInterpreterTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/SmeupInterpreterTest.kt index 0d845883a..afeae5b9c 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/SmeupInterpreterTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/evaluation/SmeupInterpreterTest.kt @@ -467,6 +467,12 @@ open class SmeupInterpreterTest : AbstractTest() { assertEquals(expected, "smeup/T52_A07_P01".outputOf(configuration = smeupConfig)) } + @Test + fun executeT60_A10_P01_02() { + val expected = listOf() + assertEquals(expected, "smeup/T60_A10_P01-02".outputOf(configuration = smeupConfig)) + } + @Test fun executeT10_A20_P35_38() { val expected = listOf( @@ -547,6 +553,12 @@ open class SmeupInterpreterTest : AbstractTest() { assertEquals(expected, "smeup/T02_A50_P09".outputOf()) } + @Test + fun executeT60_A10_P01() { + val expected = listOf("KA(0)KB(0)KC(0)KD(0)KE(0)KF(0)KG(0)KH(0)KI(0)KJ(0)KK(0)KL(0)KM(0)KN(0)KP(0)KQ(0)KR(0)KS(0)KT(0)KU(0)KV(0)KW(0)KX(0)KY(0)") + assertEquals(expected, "smeup/T60_A10_P01".outputOf(configuration = smeupConfig)) + } + @Test fun executeT10_A45_P01() { val expected = listOf("NUM(0)") 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 e4df6941f..34107ff0b 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 @@ -13,4 +13,14 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() { val expected = listOf("CNCLICNCLIAAAABBBBBAAAABBBBBCNFORCNFORCCCCDDDDDCCCCDDDDDCNCOLCNCOLEEEEFFFFFEEEEFFFFF") assertEquals(expected, "smeup/T02_A40_P03".outputOf()) } + + /** + * Data reference - Inline definition + * @see #250 + */ + @Test + fun executeT02_A80_P01() { + val expected = listOf("ABCDEFGHIJ12345") + assertEquals(expected, "smeup/T02_A80_P01".outputOf()) + } } \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt index 9d46c6f12..5084bdf75 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt @@ -1,3 +1,36 @@ package com.smeup.rpgparser.smeup -open class MULANGT10BaseCodopTest : MULANGTTest() \ No newline at end of file +import org.junit.Test +import kotlin.test.assertEquals + +open class MULANGT10BaseCodopTest : MULANGTTest() { + /** + * DIV and MVR with indicators + * @see #245 + */ + @Test + fun executeT10_A20_P52() { + val expected = listOf("A20_N73(2.272) A20_N70(0) A20_N112(11.11) A20_N110(0) A20_N309(4.889964788)") + assertEquals(expected, "smeup/T10_A20_P52".outputOf(configuration = smeupConfig)) + } + + /** + * SCAN with array in input + * @see #218 + */ + @Test + fun executeT10_A35_P08() { + val expected = listOf("A35_AR1(2)(123&5) IN45(1)") + assertEquals(expected, "smeup/T10_A35_P08".outputOf(configuration = smeupConfig)) + } + + /** + * SCAN with array in result + * @see #244 + */ + @Test + fun executeT10_A35_P10() { + val expected = listOf("A35_AR2(01)(5) A35_AR2(02)(6) A35_AR2(03)(0) IN20(1)") + assertEquals(expected, "smeup/T10_A35_P10".outputOf(configuration = smeupConfig)) + } +} \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T02_A80_P01.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T02_A80_P01.rpgle new file mode 100644 index 000000000..6ae94378e --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T02_A80_P01.rpgle @@ -0,0 +1,10 @@ + D A80_A10 S 10 + D A80_N50 S 5 0 + D £DBG_Str S 50 VARYING + + C CLEAR A80_A10 10 + C CLEAR A80_N50 5 0 + C EVAL A80_A10 = 'ABCDEFGHIJ' + C EVAL A80_N50 = 12345 + C EVAL £DBG_Str=%TRIM(A80_A10)+%CHAR(A80_N50) + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A20_P52.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A20_P52.rpgle new file mode 100644 index 000000000..932244957 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A20_P52.rpgle @@ -0,0 +1,28 @@ + D £DBG_Str S 150 VARYING + + D A20_N70 S 20P 0 + D A20_N73 S 20P 3 + D A20_N110 S 11P 0 + D A20_N112 S 11P 2 + D A20_N309 S 30P 9 + + C EXSR SUB_A20_I + C EVAL £DBG_Str='A20_N73('+%CHAR(A20_N73)+')' + C +' A20_N70('+%CHAR(A20_N70)+')' + C +' A20_N112('+%CHAR(A20_N112)+')' + C +' A20_N110('+%CHAR(A20_N110)+')' + C +' A20_N309('+%CHAR(A20_N309)+')' + + C £DBG_Str DSPLY + + *--------------------------------------------------------------------- + C SUB_A20_I BEGSR + * + C SETOFF 22 + C SETON 23 + C N2225 DIV 11 A20_N73 + C N22 MVR A20_N70 + C 23100 DIV 9 A20_N112 + C 23 MVR A20_N110 + C A20_N112 DIV A20_N73 A20_N309 + C ENDSR diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A35_P08.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A35_P08.rpgle new file mode 100644 index 000000000..e37b2aaa5 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A35_P08.rpgle @@ -0,0 +1,18 @@ + D £DBG_Str S 150 VARYING + D A35_AR1 S 70 DIM(10) + D A35_A1 S 6 + D A35_A2 S 1 + D A35_AR2 S 2 0 DIM(5) + D $A S 1 0 + D I S 2 0 + + C EVAL A35_AR1(1)='ABCDEF' + C EVAL A35_AR1(2)='123&5' + C EVAL A35_AR1(3)='TEST&' + C EVAL I = 2 + C '&' SCAN A35_AR1(I):1 45 + C EVAL £DBG_Str= + C 'A35_AR1(2)('+%TRIMR(A35_AR1(2))+') ' + C +'IN45('+%CHAR(*IN45)+')' + + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A35_P10.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A35_P10.rpgle new file mode 100644 index 000000000..976940a27 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A35_P10.rpgle @@ -0,0 +1,20 @@ + D £DBG_Str S 150 VARYING + D A35_AR1 S 70 DIM(10) + D A35_A1 S 6 + D A35_A2 S 1 + D A35_AR2 S 2 0 DIM(5) + D $A S 1 0 + D I S 2 0 + + C SETOFF 20 + C EVAL A35_A1='YARRYY' + C EVAL A35_A2='Y' + C EVAL $A=3 + C A35_A2 SCAN A35_A1:$A A35_AR2 20 + C EVAL £DBG_Str= + C 'A35_AR2(01)('+%CHAR(A35_AR2(01))+') ' + C +'A35_AR2(02)('+%CHAR(A35_AR2(02))+') ' + C +'A35_AR2(03)('+%CHAR(A35_AR2(03))+') ' + C +'IN20('+%CHAR(*IN20)+')' + + C £DBG_Str DSPLY \ No newline at end of file