diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt index e017acbf1..a34e05aa3 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt @@ -19,6 +19,7 @@ package com.smeup.rpgparser.interpreter import com.smeup.rpgparser.RpgParser import com.smeup.rpgparser.RpgParser.Cspec_fixedContext import com.smeup.rpgparser.RpgParser.Parm_fixedContext +import com.smeup.rpgparser.RpgParser.StatementContext import com.smeup.rpgparser.execution.MainExecutionContext import com.smeup.rpgparser.parsing.ast.* import com.smeup.rpgparser.parsing.facade.findAllDescendants @@ -34,8 +35,8 @@ import com.strumenta.kolasu.model.tryToResolve */ interface CompileTimeInterpreter { fun evaluate(rContext: RpgParser.RContext, expression: Expression): Value - fun evaluateElementSizeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration): Int - fun evaluateTypeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration): Type + fun evaluateElementSizeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String? = null): Int + fun evaluateTypeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String? = null): Type fun evaluateNumberOfElementsOf(rContext: RpgParser.RContext, declName: String): Int } @@ -50,12 +51,12 @@ class InjectableCompileTimeInterpreter( return mockedDecls[declName]?.numberOfElements() ?: super.evaluateNumberOfElementsOf(rContext, declName) } - override fun evaluateElementSizeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration): Int { - return mockedDecls[declName]?.elementSize() ?: super.evaluateElementSizeOf(rContext, declName, conf) + override fun evaluateElementSizeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Int { + return mockedDecls[declName]?.elementSize() ?: super.evaluateElementSizeOf(rContext, declName, conf, procedureName) } - override fun evaluateTypeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration): Type { - return mockedDecls[declName] ?: super.evaluateTypeOf(rContext, declName, conf) + override fun evaluateTypeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Type { + return mockedDecls[declName] ?: super.evaluateTypeOf(rContext, declName, conf, procedureName) } private val mockedDecls = HashMap() @@ -145,7 +146,7 @@ open class BaseCompileTimeInterpreter( throw NotFoundAtCompileTimeException(declName) } - open fun evaluateElementSizeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration): Int { + open fun evaluateElementSizeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Int { knownDataDefinitions.forEach { if (it.name.equals(declName, ignoreCase = true)) { return it.elementSize() @@ -154,7 +155,7 @@ open class BaseCompileTimeInterpreter( if (field != null) return (field.elementSize() /*/ field.declaredArrayInLine!!*/) } - return findSize(rContext.statement() + rContext.subroutine().flatMap { it.statement() }, declName, conf, false)!! + return findSize(rContext.getStatements(procedureName), declName, conf, false)!! } private fun findSize(statements: List, declName: String, conf: ToAstConfiguration, innerBlock: Boolean = true): Int? { @@ -198,14 +199,14 @@ open class BaseCompileTimeInterpreter( throw NotFoundAtCompileTimeException(declName) } - override fun evaluateElementSizeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration): Int { + override fun evaluateElementSizeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String?): Int { return when (expression) { is DataRefExpr -> { try { - evaluateElementSizeOf(rContext, expression.variable.name, conf) + evaluateElementSizeOf(rContext, expression.variable.name, conf, procedureName) } catch (e: NotFoundAtCompileTimeException) { if (delegatedCompileTimeInterpreter != null) { - return delegatedCompileTimeInterpreter.evaluateElementSizeOf(rContext, expression, conf) + return delegatedCompileTimeInterpreter.evaluateElementSizeOf(rContext, expression, conf, procedureName) } else { expression.error(message = e.message, cause = e) } @@ -215,14 +216,14 @@ open class BaseCompileTimeInterpreter( } } - override fun evaluateTypeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration): Type { + override fun evaluateTypeOf(rContext: RpgParser.RContext, expression: Expression, conf: ToAstConfiguration, procedureName: String?): Type { return when (expression) { is DataRefExpr -> { try { - evaluateTypeOf(rContext, expression.variable.name, conf) + evaluateTypeOf(rContext, expression.variable.name, conf, procedureName) } catch (e: NotFoundAtCompileTimeException) { if (delegatedCompileTimeInterpreter != null) { - return delegatedCompileTimeInterpreter.evaluateTypeOf(rContext, expression, conf) + return delegatedCompileTimeInterpreter.evaluateTypeOf(rContext, expression, conf, procedureName) } else { throw RuntimeException(e) } @@ -232,7 +233,7 @@ open class BaseCompileTimeInterpreter( } } - open fun evaluateTypeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration): Type { + open fun evaluateTypeOf(rContext: RpgParser.RContext, declName: String, conf: ToAstConfiguration, procedureName: String?): Type { knownDataDefinitions.forEach { if (it.name.equals(declName, ignoreCase = true)) { return it.type @@ -243,7 +244,7 @@ open class BaseCompileTimeInterpreter( } } - return findType(rContext.statement() + rContext.subroutine().flatMap { it.statement() }, declName, conf, false)!! + return findType(rContext.getStatements(procedureName), declName, conf, false)!! } private fun findType(statements: List, declName: String, conf: ToAstConfiguration, innerBlock: Boolean = true): Type? { @@ -321,4 +322,19 @@ open class BaseCompileTimeInterpreter( private fun Parm_fixedContext.findType(conf: ToAstConfiguration): Type? { return this.toAst(conf, emptyList()).type } + + private fun RpgParser.RContext.getStatements(procedureName: String?): List { + val statements: MutableList = mutableListOf() + if (procedureName != null) { + val procedureContext: RpgParser.ProcedureContext? = this.procedure().firstOrNull { it.beginProcedure().psBegin().ps_name().text.equals(procedureName, ignoreCase = true) } + if (procedureContext != null) { + statements.addAll( + procedureContext.subprocedurestatement().mapNotNull { it.subroutine() }.flatMap { it.statement() } + + procedureContext.subprocedurestatement().mapNotNull { it.statement() }) + } + } + statements.addAll(this.statement() + this.subroutine().flatMap { it.statement() }) + + return statements.toList() + } } diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt index 7126a78a8..2a21f1be6 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt @@ -220,7 +220,7 @@ internal fun RpgParser.Parm_fixedContext.toAst( val elementSize = when { like != null -> { - compileTimeInterpreter.evaluateElementSizeOf(this.rContext(), like!!, conf) + compileTimeInterpreter.evaluateElementSizeOf(this.rContext(), like!!, conf, null) } else -> this.TO_POSITION().text.trim().let { if (it.isBlank()) null else it.toInt() } } @@ -297,9 +297,9 @@ internal fun RpgParser.DspecContext.toAst( conf: ToAstConfiguration = ToAstConfiguration(), knownDataDefinitions: List, parentDataDefinitions: List? = null, - fileDefinitions: Map>? = null + fileDefinitions: Map>? = null, + procedureName: String? = null ): DataDefinition { - if (dspecConstant() != null) return dspecConstant().toAst(conf = conf) val compileTimeInterpreter = InjectableCompileTimeInterpreter( knownDataDefinitions = knownDataDefinitions, @@ -381,7 +381,7 @@ internal fun RpgParser.DspecContext.toAst( val elementSize = when { like != null -> { - compileTimeInterpreter.evaluateElementSizeOf(this.rContext(), like!!, conf) + compileTimeInterpreter.evaluateElementSizeOf(this.rContext(), like!!, conf, procedureName) } else -> this.TO_POSITION().text.trim().let { if (it.isBlank()) null else it.toInt() } } @@ -394,7 +394,7 @@ internal fun RpgParser.DspecContext.toAst( NumberType(elementSize!! - decimalPositions, decimalPositions) } else { if (like != null) { - compileTimeInterpreter.evaluateTypeOf(this.rContext(), like!!, conf) + compileTimeInterpreter.evaluateTypeOf(this.rContext(), like!!, conf, procedureName) } else { StringType.createInstance(elementSize!!, varying) } 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 32bbe61b8..1f0f985e6 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 @@ -98,7 +98,8 @@ private fun List.getDataDefinition( fileDefinitions: Map>? = null, inputSpecifications: List = emptyList(), parentDataDefinitions: List? = null, - useKnownDataDefinitionInstance: Boolean = false + useKnownDataDefinitionInstance: Boolean = false, + procedureName: String? = null ): Pair, KnownDataDefinitionInstance> { // We need to calculate first all the data definitions which do not contain the LIKE DS directives // then we calculate the ones with the LIKE DS clause, as they could have references to DS declared @@ -133,7 +134,11 @@ private fun List.getDataDefinition( when { it.dspec() != null -> { it.dspec() - .toAst(conf, knownDataDefinitions.values.toList(), parentDataDefinitions) + .toAst( + conf = conf, + knownDataDefinitions = knownDataDefinitions.values.toList(), + parentDataDefinitions = parentDataDefinitions, + procedureName = procedureName) .updateKnownDataDefinitionsAndGetHolder(knownDataDefinitions) } @@ -490,14 +495,17 @@ internal fun SubroutineContext.toAst(conf: ToAstConfiguration = ToAstConfigurati } internal fun ProcedureContext.toAst(conf: ToAstConfiguration = ToAstConfiguration(), parentDataDefinitions: List): CompilationUnit { - val procedureName = this.beginProcedure().psBegin().ps_name().text MainExecutionContext.getParsingProgramStack().peek().parsingFunctionNameStack.push(procedureName) // TODO FileDefinitions // DataDefinitions - val dataDefinitions = getDataDefinitions(conf, parentDataDefinitions) + val dataDefinitions = getDataDefinitions( + conf = conf, + parentDataDefinitions = parentDataDefinitions, + procedureName = procedureName + ) // Procedure Parameters DataDefinitions val proceduresParamsDataDefinitions = getProceduresParamsDataDefinitions(dataDefinitions) @@ -610,12 +618,13 @@ private fun StatementContext.toDataDefinitionProvider( } } -private fun ProcedureContext.getDataDefinitions(conf: ToAstConfiguration = ToAstConfiguration(), parentDataDefinitions: List): List { +private fun ProcedureContext.getDataDefinitions(conf: ToAstConfiguration = ToAstConfiguration(), parentDataDefinitions: List, procedureName: String): List { val (providers, knownDataDefinitions) = this.subprocedurestatement() .map { it.statement() } .getDataDefinition( conf = conf, - parentDataDefinitions = parentDataDefinitions + parentDataDefinitions = parentDataDefinitions, + procedureName = procedureName ) // PROCEDURE PARAMETERS pass diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT18ProcedureTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT18ProcedureTest.kt index d30e699ea..e90713cd0 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT18ProcedureTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT18ProcedureTest.kt @@ -1,3 +1,16 @@ package com.smeup.rpgparser.smeup -open class MULANGT18ProcedureTest : MULANGTTest() \ No newline at end of file +import org.junit.Test +import kotlin.test.assertEquals + +open class MULANGT18ProcedureTest : MULANGTTest() { + /** + * LIKE to variable defined into a COPY. This one is also declared inner of procedure. + * @see #LS24003187 + */ + @Test + fun executeMU181003() { + val expected = listOf("O: HT -P:HT_P") + assertEquals(expected, "smeup/MU181003".outputOf(configuration = smeupConfig)) + } +} \ No newline at end of file diff --git "a/rpgJavaInterpreter-core/src/test/resources/QILEGEN/\302\243RISBS.rpgle" "b/rpgJavaInterpreter-core/src/test/resources/QILEGEN/\302\243RISBS.rpgle" new file mode 100644 index 000000000..fbe2d35a5 --- /dev/null +++ "b/rpgJavaInterpreter-core/src/test/resources/QILEGEN/\302\243RISBS.rpgle" @@ -0,0 +1,53 @@ + *==================================================================== + * smeup V6R1.023DV + * Nome sorgente : £RISBS + * Sorgente di origine : SMEUP_DEV/QILEGEN(£RISBS) + * Esportato il : 20240613 143719 + *==================================================================== + V* ============================================================== + V* MODIFICHE Ril. T Au Descrizione + V* gg/mm/aa nn.mm i xx Breve descrizione + V* ============================================================== + V* 13/03/01 04.00 GG Sostituito £RITST con £RISA5 + V* 22/10/04 V2R1 PV Aggiunto livello di chiamata + D*---------------------------------------------------------------- + D* OBIETTIVO + D* Eseguire la ricerca alfabetica e/o il controllo validità/deco- + D* difica di subsettori tabelle SMEUP + D* + D* FLUSSO + D* Input : £COSET : Codice settore + D* £COSBS : Codice subsettore + D* Output: £DESBS : Descrizione subsettore + D* *IN35 : Se ON subsettore errato + D* *IN36 : Se ON eseguita ricerca + D* + D* ESEMPIO DI CHIAMATA + D*C MOVEL £COSET + D*C MOVEL£COSBS + D*C EXSR £RISBS + D*C MOVEL£DESBS + D*C 35 N60 MOVE 'BAS0001' £MSGCO + D*C 35 SETON 60 +2 D*C N35 36 SETON 10 + D*C N35 36 MOVEL£COSBS + D*---------------------------------------------------------------- + C £RISBS BEGSR + C 'A' IFEQ 'B' + C MOVEL £COSET £COSET 3 + C MOVE £COSBS £COSBS 2 + C MOVE £RSSLC £RSSLC 1 + C ENDIF + C MOVEL £COSET £RISA5 5 + C MOVE £COSBS £RISA5 + C 'B£AR80' CAT(P) £RSSLC:0 £RSSPG 10 + C CALL £RSSPG + C PARM £RISA5 + C PARM £DESBS 30 + C PARM £IN35 1 + C PARM £IN36 1 + C £IN35 COMP '1' 35 + C £IN36 COMP '1' 36 + C MOVEL £RISA5 £COSET + C MOVE £RISA5 £COSBS + C ENDSR diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MU181003.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MU181003.rpgle new file mode 100644 index 000000000..2dde31393 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MU181003.rpgle @@ -0,0 +1,50 @@ + V* ============================================================== + V* MODIFICHE Ril. T Au Descrizione + V* gg/mm/aa nn.mm i xx Breve descrizione + V* ============================================================== + V* 03/07/24 MUTEST APU001 Creazione + V*===================================================================== + O * OBIETTIVO + O * Testare il funzionamento della definizione di una variabile, + O * all'interno di una procedura, mediante LIKE verso una presente + O * in una COPY. + O * Questa COPY sarà presente all'interno della procedura stessa. + O * A tal proposito, quest'ultima non dovrà contenere specifiche D, + O * ma dichiarazioni inline di specifiche C. + V* ============================================================== + D A10_S10 S 10 + * -------------------------------------------------------------- + /COPY QILEGEN,MULANG_D_D + *--------------------------------------------------------------------- + RD* M A I N + *--------------------------------------------------------------------- + C EVAL £DBG_Pgm = 'MU181003' + C EVAL £DBG_Sez = 'A10' + C EVAL £DBG_Fun = '*INZ' + C EXSR £DBG + C EXSR SEZ_A10 + C EXSR £DBG + C EVAL £DBG_Fun = '*END' + C EXSR £DBG + C SETON LR + *--------------------------------------------------------------------- + RD* Test atomico LIKE ad una variabile in COPY + *--------------------------------------------------------------------- + C SEZ_A10 BEGSR + OA* A£.CDOP(LIKE) + C EVAL £DBG_Pas='P03' + C EVAL A10_S10 = ' HT ' + C EVAL £DBG_Str = 'O:' + + C A10_S10 + '-P:' + + C %TRIMR(A10_PR03()) + C ENDSR + /COPY QILEGEN,MULANG_D_C + *--------------------------------------------------------------------- + P A10_PR03 B + D A10_PR03 PI 10 + D A10_PR03_V1 S LIKE(£RSSPG) + C EVAL A10_PR03_V1 = A10_S10 + C RETURN %TRIM(A10_PR03_V1) + '_P' + /COPY QILEGEN,£RISBS + P A10_PR03 E + *--------------------------------------------------------------------- \ No newline at end of file