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/interpreter/ExpressionEvaluation.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/ExpressionEvaluation.kt index 58d9f94d5..a419a0e74 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 @@ -206,21 +206,19 @@ class ExpressionEvaluation( override fun eval(expression: CharExpr): Value { val value = expression.value.evalWith(this) - return if (expression.value is DivExpr) { - // are always return 10 decimal digits - // fill with 0 if necessary - if (value.asDecimal().value.scale() != 0) { - val numeDecimals = value.asDecimal().value.scale() - if (numeDecimals < 10) { - StringValue(value.stringRepresentation(expression.format) + "0".repeat(10 - numeDecimals)) - } else { - StringValue(value.stringRepresentation(expression.format).trim()) - } - } else { - StringValue(value.stringRepresentation(expression.format) + ".0000000000") - } - } else { - StringValue(value.stringRepresentation(expression.format)) + val representation = value.stringRepresentation(expression.format) + + if (expression.value !is DivExpr) { + return StringValue(representation) + } + + // Decimals are always returned with 10 decimal digits + // fill with 0 if necessary + val numDecimals = value.asDecimal().value.scale() + return when { + numDecimals == 0 -> StringValue("$representation.0000000000") + numDecimals < 10 -> StringValue(representation + "0".repeat(10 - numDecimals)) + else -> StringValue(representation.trim()) } } diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/logs.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/logs.kt index e5302877c..73c63f9a4 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/logs.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/logs.kt @@ -411,6 +411,17 @@ class CasOtherExecutionLogEntry(programName: String, val other: CaseOtherClause, } } +class MonitorExecutionLogEntry(programName: String, val statement: MonitorStmt) : LogEntry(programName) { + override fun toString(): String { + return "executing MONITOR" + } + + override fun renderStatement(channel: String, filename: String, sep: String): String { + val data = "MONITOR" + return renderHeader(channel, filename, statement.startLine(), sep) + data + } +} + class IfExecutionLogEntry(programName: String, val statement: IfStmt, val result: Value) : LogEntry(programName) { override fun toString(): String { return "executing IF" @@ -442,6 +453,16 @@ class ElseExecutionLogEntry(programName: String, val statement: ElseClause, val } } +class OnErrorExecutionLogEntry(programName: String, val statement: OnErrorClause) : LogEntry(programName) { + override fun toString(): String { + return "executing ON-ERROR" + } + override fun renderStatement(channel: String, filename: String, sep: String): String { + val data = "ON-ERROR" + return renderHeader(channel, filename, statement.startLine(), sep) + data + } +} + class EvaluationLogEntry(programName: String, val statement: EvalStmt, val value: Value?) : LogEntry(programName) { override fun toString(): String { return "evaluating $statement as $value" 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 c404b7f31..d9b6cee70 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 @@ -314,6 +314,10 @@ data class IntValue(val value: Long) : NumberValue() { return StringValue(render()) } + override fun asBoolean(): BooleanValue { + return BooleanValue(value > 0) + } + operator fun plus(other: IntValue) = IntValue(this.bigDecimal.plus(other.bigDecimal).longValueExact()) operator fun minus(other: IntValue) = IntValue(this.bigDecimal.minus(other.bigDecimal).longValueExact()) 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 f58318903..ed2fb4c07 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 @@ -68,6 +68,7 @@ private val modules = SerializersModule { subclass(LeaveSrStmt::class) subclass(LeaveStmt::class) subclass(LookupStmt::class) + subclass(MonitorStmt::class) subclass(MoveAStmt::class) subclass(MoveLStmt::class) subclass(MoveStmt::class) 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..d75909109 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 @@ -814,6 +814,62 @@ private constructor(val name: String, val fields: List, override val pos } } +@Serializable +data class MonitorStmt( + @SerialName("body") + val monitorBody: List, + val onErrorClauses: List = emptyList(), + override val position: Position? = null +) : Statement(position), CompositeStatement { + + // Since that this property is a collection achieved from thenBody + elseIfClauses + elseClause, this annotation + // is necessary to avoid that the same node is processed more than ones, thing that it would cause that the same + // ErrorEvent is fired more times + @Derived + @Transient + override val body: List = mutableListOf().apply { + addAll(monitorBody) + onErrorClauses.forEach { addAll(it.body) } + } + + override fun accept(mutes: MutableMap, start: Int, end: Int): MutableList { + // check if the annotation is just before the ELSE + val muteAttached: MutableList = mutableListOf() + + // Process the body statements + muteAttached.addAll( + acceptBody(monitorBody, mutes, this.position!!.start.line, this.position.end.line) + ) + + // Process the ON ERROR + onErrorClauses.forEach { + muteAttached.addAll( + acceptBody(it.body, mutes, it.position!!.start.line, it.position.end.line) + ) + } + + return muteAttached + } + + override fun execute(interpreter: InterpreterCore) { + interpreter.log { + MonitorExecutionLogEntry( + interpreter.getInterpretationContext().currentProgramName, + this + ) + } + + try { + interpreter.execute(this.monitorBody) + } catch (_: Exception) { + onErrorClauses.forEach { + interpreter.log { OnErrorExecutionLogEntry(interpreter.getInterpretationContext().currentProgramName, it) } + interpreter.execute(it.body) + } + } + } +} + @Serializable data class IfStmt( val condition: Expression, @@ -889,6 +945,9 @@ data class IfStmt( } } +@Serializable +data class OnErrorClause(override val body: List, override val position: Position? = null) : Node(position), CompositeStatement + @Serializable data class ElseClause(override val body: List, override val position: Position? = null) : Node(position), CompositeStatement @@ -1061,7 +1120,7 @@ data class DefineStmt( } else { val inStatementDataDefinition = containingCU.main.stmts - .filterIsInstance(StatementThatCanDefineData::class.java) + .filterIsInstance() .filter { it != this } .asSequence() .map(StatementThatCanDefineData::dataDefinition) @@ -1719,7 +1778,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 +1786,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) @@ -1968,22 +2029,27 @@ data class XlateStmt( @Serializable data class ResetStmt( val name: String, + @Derived val dataDefinition: InStatementDataDefinition? = null, override val position: Position? = null -) : Statement(position) { +) : Statement(position), StatementThatCanDefineData { override fun execute(interpreter: InterpreterCore) { - val dataDefinition = interpreter.dataDefinitionByName(name) - require(dataDefinition != null) { - this.error("Data definition $name not found") - } - require(dataDefinition is DataDefinition) { - this.error("Data definition $name is not an instance of DataDefinition") - } - require(dataDefinition.defaultValue != null) { - this.error("Data definition $name has no default value") + when (val dataDefinition = interpreter.dataDefinitionByName(name)) { + null -> this.error("Data definition $name not found") + is DataDefinition -> { + require(dataDefinition.defaultValue != null) { + this.error("Data definition $name has no default value") + } + interpreter.assign(dataDefinition, dataDefinition.defaultValue!!) + } + is InStatementDataDefinition -> { + interpreter.assign(dataDefinition, dataDefinition.type.blank()) + } + else -> this.error("Data definition $name is not a valid instance of DataDefinition") } - interpreter.assign(dataDefinition, dataDefinition.defaultValue!!) } + + override fun dataDefinition(): List = dataDefinition?.let { listOf(it) } ?: emptyList() } @Serializable 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..d959f321d 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 @@ -1031,10 +1031,10 @@ internal fun ResultTypeContext.toAst(conf: ToAstConfiguration = ToAstConfigurati // what kind of expression is this val position = toPosition(conf.considerPosition) - if (text.contains('.')) { - return handleParsingOfTargets(text, position) + return if (text.contains('.')) { + handleParsingOfTargets(text, position) } else { - return annidatedReferenceExpression(text, position) + annidatedReferenceExpression(text, position) } } @@ -1121,10 +1121,7 @@ internal fun Cspec_fixed_standard_partsContext.toDataDefinition( position: Position?, conf: ToAstConfiguration ): InStatementDataDefinition? { - val len = this.len.asInt() - if (len == null) { - return null - } + val len = this.len.asInt() ?: return null val decimals = this.decimalPositions.asInt() val initialValue = this.factor2Expression(conf) return InStatementDataDefinition(name, dataType(len, decimals), position, initializationValue = initialValue) @@ -1316,20 +1313,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, @@ -1345,11 +1352,19 @@ internal fun CsCHECKContext.toAst(conf: ToAstConfiguration): Statement { val result = this.cspec_fixed_standard_parts().result val dataDefinition = this.cspec_fixed_standard_parts().toDataDefinition(result.text, position, conf) + val eqIndicator = this.cspec_fixed_standard_parts().resultIndicator(2)?.text + + val wrongCharExpression = when { + result != null && result.text.isNotBlank() -> result.toAst(conf) + !eqIndicator.isNullOrBlank() -> IndicatorExpr(eqIndicator.toIndicatorKey(), position) + else -> null + } + return CheckStmt( factor1, expression, startPosition ?: 1, - this.cspec_fixed_standard_parts()?.result?.toAst(conf), + wrongCharExpression, dataDefinition, position ) @@ -1378,8 +1393,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 +1402,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 } @@ -1940,6 +1956,7 @@ internal fun CsCLOSEContext.toAst(conf: ToAstConfiguration = ToAstConfiguration( internal fun CsRESETContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): Statement { val position = toPosition(conf.considerPosition) + require(this.cspec_fixed_standard_parts().factor().text.isEmptyTrim()) { "RESET operation does not support factor1" } @@ -1950,8 +1967,12 @@ internal fun CsRESETContext.toAst(conf: ToAstConfiguration = ToAstConfiguration( require(!result.isEmptyTrim()) { "RESET operation requires result" } + + val dataDefinition = this.cspec_fixed_standard_parts().toDataDefinition(result, position, conf) + return ResetStmt( name = result, + dataDefinition = dataDefinition, position = position ) } @@ -2037,3 +2058,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/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/statements.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/statements.kt index 3dd55e631..e8bb11dc6 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/statements.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/statements.kt @@ -56,6 +56,12 @@ internal fun BlockContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()) this.csDOWxx() != null -> this.csDOWxx().toAst(blockContext = this, conf = conf) this.forstatement() != null -> this.forstatement().toAst(conf) this.begindou() != null -> this.begindou().toAst(blockContext = this, conf = conf) + this.monitorstatement() != null -> this.monitorstatement().let { + it.beginmonitor().csMONITOR().cspec_fixed_standard_parts().validate( + stmt = it.toAst(conf = conf), + conf = conf + ) + } else -> todo(message = "Missing composite statement implementation for this block: ${this.text}", conf = conf) } } @@ -358,6 +364,18 @@ internal fun toAst(conf: ToAstConfiguration = ToAstConfiguration()): SelectOther TODO("OtherContext.toAst with $conf") } +internal fun RpgParser.MonitorstatementContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): MonitorStmt { + val position = toPosition(conf.considerPosition) + val statements = this.statement().mapNotNull { + it.toAst(conf) + } + val onErrorClauses = this.onError().mapNotNull { + it.toAst(conf) + } + + return MonitorStmt(statements, onErrorClauses, position) +} + internal fun RpgParser.IfstatementContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): IfStmt { val position = toPosition(conf.considerPosition) return if (this.beginif().fixedexpression != null) { @@ -387,6 +405,12 @@ internal fun RpgParser.IfstatementContext.toAst(conf: ToAstConfiguration = ToAst } } +internal fun RpgParser.OnErrorContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): OnErrorClause { + val body = this.statement().mapNotNull { kotlin.runCatching { it.toAst(conf) }.getOrNull() } + val position = toPosition(conf.considerPosition) + return OnErrorClause(body, position) +} + internal fun RpgParser.ElseClauseContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): ElseClause { return ElseClause(this.statement().mapNotNull { kotlin.runCatching { it.toAst(conf) }.getOrNull() }, toPosition(conf.considerPosition)) } 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 47245d519..2c4984c3c 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 @@ -4,6 +4,16 @@ import org.junit.Test import kotlin.test.assertEquals open class MULANGT02ConstAndDSpecTest : MULANGTTest() { + /** + * Data reference - Inline definition + * @see #250 + */ + @Test + fun executeT02_A80_P01() { + val expected = listOf("ABCDEFGHIJ12345") + assertEquals(expected, "smeup/T02_A80_P01".outputOf()) + } + /** * Definition with Like to a variable defined also with like. * @see #160 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..32b403025 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,66 @@ 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)) + } + + /** + * Positive Check with indicator + * @see #246 + */ + @Test + fun executeT10_A45_P04() { + val expected = listOf("IND(0)") + assertEquals(expected, "smeup/T10_A45_P04".outputOf()) + } + + /** + * Negative Check with indicator + * @see #246 + */ + @Test + fun executeT10_A45_P05() { + val expected = listOf("IND(1)") + assertEquals(expected, "smeup/T10_A45_P05".outputOf()) + } + + /** + * Check with error indicator + * @see #246 + */ + @Test + fun executeT10_A45_P06() { + val expected = listOf("IND(0)") + assertEquals(expected, "smeup/T10_A45_P06".outputOf()) + } +} \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT11Codop2Test.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT11Codop2Test.kt index f45bd304b..8bda0a157 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT11Codop2Test.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT11Codop2Test.kt @@ -1,3 +1,46 @@ package com.smeup.rpgparser.smeup -open class MULANGT11Codop2Test : MULANGTTest() \ No newline at end of file +import org.junit.Test +import kotlin.test.assertEquals + +open class MULANGT11Codop2Test : MULANGTTest() { + /** + * MONITOR and error catching + * @see #241 + */ + @Test + fun executeT11_A10_P01() { + val expected = listOf("BLOCCO; ERR_ZERO_DIV;") + assertEquals(expected, "smeup/T11_A10_P01".outputOf()) + } + + /** + * Nested MONITOR statements + * @see #241 + */ + @Test + fun executeT11_A10_P02() { + val expected = listOf("BLOCCO1; BLOCCO2; BLOCCO3; ERR_ZERO_DIV; FINE_BLOCCO3;; ERR_ZERO_DIV; FINE_BLOCCO2;; ERR_ZERO_DIV; FINE_BLOCCO1;") + assertEquals(expected, "smeup/T11_A10_P02".outputOf()) + } + + /** + * MONITOR nested in IF, DO and SELECT + * @see #241 + */ + @Test + fun executeT11_A10_P03() { + val expected = listOf("DENTRO_IF(BLOCCO; ERR_ZERO_DIV;) DENTRO_DO(BLOCCO; ERR_ZERO_DIV;BLOCCO; ERR_ZERO_DIV;) DENTRO_WHEN(BLOCCO; ERR_ZERO_DIV;) DENTRO_OTHER(BLOCCO; ERR_ZERO_DIV;)") + assertEquals(expected, "smeup/T11_A10_P03".outputOf()) + } + + /** + * IF, DO and SELECT in MONITOR + * @see #241 + */ + @Test + fun executeT11_A10_P04() { + val expected = listOf("DENTRO_IF(BLOCCO; ERR_ZERO_DIV;) DENTRO_DO(BLOCCO; ERR_ZERO_DIV;) DENTRO_WHEN(BLOCCO; ERR_ZERO_DIV;) DENTRO_OTHER(BLOCCO; ERR_ZERO_DIV;)") + assertEquals(expected, "smeup/T11_A10_P04".outputOf()) + } +} \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT40ArrayAndDSTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT40ArrayAndDSTest.kt index 53c400fe2..5a740c0e4 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT40ArrayAndDSTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT40ArrayAndDSTest.kt @@ -1,3 +1,16 @@ package com.smeup.rpgparser.smeup -open class MULANGT40ArrayAndDSTest : MULANGTTest() \ No newline at end of file +import org.junit.Test +import kotlin.test.assertEquals + +open class MULANGT40ArrayAndDSTest : MULANGTTest() { + /** + * Reset with inline declaration + * @see #242 + */ + @Test + fun executeT40_A10_P10() { + val expected = listOf("Contenuto Pre-RESET: AAA - Contenuto Post-RESET:") + assertEquals(expected, "smeup/T40_A10_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 diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P04.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P04.rpgle new file mode 100644 index 000000000..f2fcff064 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P04.rpgle @@ -0,0 +1,12 @@ + D £DBG_Str S 150 VARYING + + D A45_A10 C '0123456789' + D A45_A04 S 4 INZ(' ') + D A45_N10 S 1 0 + + C SETOFF 40 + C EVAL A45_A04='2019' + C A45_A10 CHECK A45_A04 40 + C EVAL £DBG_Str='IND('+%CHAR(*IN40)+')' + + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P05.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P05.rpgle new file mode 100644 index 000000000..b1a963b1f --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P05.rpgle @@ -0,0 +1,12 @@ + D £DBG_Str S 150 VARYING + + D A45_A10 C '0123456789' + D A45_A04 S 4 INZ(' ') + D A45_N10 S 1 0 + + C SETOFF 40 + C EVAL A45_A04='201A' + C A45_A10 CHECK A45_A04 40 + C EVAL £DBG_Str='IND('+%CHAR(*IN40)+')' + + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P06.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P06.rpgle new file mode 100644 index 000000000..ac102a928 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T10_A45_P06.rpgle @@ -0,0 +1,12 @@ + D £DBG_Str S 150 VARYING + + D A45_A10 C '0123456789' + D A45_A04 S 4 INZ(' ') + D A45_N10 S 1 0 + + C SETOFF 40 + C EVAL A45_A04='201A' + C A45_A10 CHECK A45_A04 40 + C EVAL £DBG_Str='IND('+%CHAR(*IN40)+')' + + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P01.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P01.rpgle new file mode 100644 index 000000000..16f593448 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P01.rpgle @@ -0,0 +1,13 @@ + D £DBG_Str S 150 VARYING + D T11_A10_A20A S 2 0 INZ(10) + D T11_A10_A20B S 2 0 INZ(0) + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; FINE_BLOCCO;' + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P02.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P02.rpgle new file mode 100644 index 000000000..4386ee2e5 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P02.rpgle @@ -0,0 +1,31 @@ + D £DBG_Str S 150 VARYING + D T11_A10_A20A S 2 0 INZ(10) + D T11_A10_A20B S 2 0 INZ(0) + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO1;' + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' BLOCCO2;' + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' BLOCCO3' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' FINE_BLOCCO3;' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' FINE_BLOCCO2;' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' FINE_BLOCCO1;' + C £DBG_Str DSPLY diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P03.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P03.rpgle new file mode 100644 index 000000000..ddebc84b8 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P03.rpgle @@ -0,0 +1,36 @@ + D £DBG_Str S 180 VARYING + D T11_A10_A20A S 2 0 INZ(10) + D T11_A10_A20B S 2 0 INZ(0) + C EVAL £DBG_Str='DENTRO_IF(' + C IF 'A'='A' + C EXSR SUB_SEZ_A10 + C ENDIF + C EVAL £DBG_Str=%TRIM(£DBG_Str)+') DENTRO_DO(' + C DO 2 + C EXSR SUB_SEZ_A10 + C ENDDO + C EVAL £DBG_Str=%TRIM(£DBG_Str)+') DENTRO_WHEN(' + C SELECT + C WHEN 'A'='A' + C EXSR SUB_SEZ_A10 + C ENDSL + C EVAL £DBG_Str=%TRIM(£DBG_Str)+') DENTRO_OTHER(' + C SELECT + C WHEN 'A'='B' + C OTHER + C EXSR SUB_SEZ_A10 + C ENDSL + C EVAL £DBG_Str=%TRIM(£DBG_Str)+')' + C £DBG_Str DSPLY + + C SUB_SEZ_A10 BEGSR + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; FINE_BLOCCO;' + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C ENDSR \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P04.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P04.rpgle new file mode 100644 index 000000000..af3c386c2 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T11_A10_P04.rpgle @@ -0,0 +1,62 @@ + D £DBG_Str S 150 VARYING + D T11_A10_A20A S 2 0 INZ(10) + D T11_A10_A20B S 2 0 INZ(0) + C MONITOR + C EVAL £DBG_Str='DENTRO_IF(' + C IF 'A'='A' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; FINE_BLOCCO;' + C ENDIF + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+')' + * + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' DENTRO_DO(' + C DO 2 + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; FINE_BLOCCO;' + C ENDDO + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+')' + * + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' DENTRO_WHEN(' + C SELECT + C WHEN 'A'='A' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; FINE_BLOCCO;' + C ENDSL + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+')' + * + C MONITOR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+' DENTRO_OTHER(' + C SELECT + C WHEN 'A'='B' + C OTHER + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'BLOCCO' + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; '+ + C %CHAR(T11_A10_A20A/T11_A10_A20B) + C EVAL £DBG_Str=%TRIM(£DBG_Str)+'; FINE_BLOCCO;' + C ENDSL + C ON-ERROR + C EVAL £DBG_Str=%TRIM(£DBG_Str)+ + C '; ERR_ZERO_DIV;' + C ENDMON + C EVAL £DBG_Str=%TRIM(£DBG_Str)+')' + C £DBG_Str DSPLY \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/T40_A10_P10.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/T40_A10_P10.rpgle new file mode 100644 index 000000000..fc9ee5879 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/T40_A10_P10.rpgle @@ -0,0 +1,7 @@ + D £DBG_Str S 150 VARYING + C EVAL A10_D1='AAA' + C EVAL £DBG_Str='Contenuto Pre-RESET: '+A10_D1 + C RESET A10_D1 10 + C EVAL £DBG_Str=%TRIMR(£DBG_Str) + C +' - Contenuto Post-RESET: '+A10_D1 + C £DBG_Str DSPLY