Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/LS24005158/Fix params assignment at the end of program called #675

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
32f007f
Implemented a test which performs the same error in real scenario
davidepalladino-apuliasoft Nov 29, 2024
b91d93b
Applied a refactoring for `PlistParam` by renaming rightly the name o…
davidepalladino-apuliasoft Nov 29, 2024
ea51e2f
Applied the fix for the test `MUDRNRAPU00172`
davidepalladino-apuliasoft Nov 29, 2024
84e5121
Removed instruction commented
davidepalladino-apuliasoft Nov 29, 2024
ea2de8f
Improved construction of `PlistParam` in accord of official official …
davidepalladino-apuliasoft Nov 29, 2024
49bbe94
Updated the expected result of `ERROR08` because now exist the Factor…
davidepalladino-apuliasoft Nov 29, 2024
726deaa
Added new test for testing the utilization of params between caller a…
davidepalladino-apuliasoft Nov 29, 2024
2709856
Typo
davidepalladino-apuliasoft Nov 29, 2024
95a84a1
Improved the creation of `PlistParam` based of new test
davidepalladino-apuliasoft Nov 29, 2024
78d3568
Added to program execute the capability to initialize Factor 1, if it…
davidepalladino-apuliasoft Nov 29, 2024
671a1df
Restored if condition
davidepalladino-apuliasoft Nov 29, 2024
2e5ca93
Added a new test where the result, in called program, changes to anot…
davidepalladino-apuliasoft Dec 2, 2024
6c3261a
Typo
davidepalladino-apuliasoft Dec 2, 2024
f22732d
Improved comments
davidepalladino-apuliasoft Dec 2, 2024
81f1226
Added a new test made by Nicolò Bernardi
davidepalladino-apuliasoft Dec 3, 2024
6cc91bf
Typo
davidepalladino-apuliasoft Dec 3, 2024
4cf301b
Added a new test made by Nicolò Bernardi
davidepalladino-apuliasoft Dec 3, 2024
d1edabe
Moved the initialization of Param 1 from Result, from `program` to `i…
davidepalladino-apuliasoft Dec 3, 2024
61284de
Applied refactoring
davidepalladino-apuliasoft Dec 4, 2024
906422d
Merge branch 'develop' into feature/LS24005158/fix-params-assignment-…
davidepalladino-apuliasoft Dec 4, 2024
a65a68b
Merged develop
davidepalladino-apuliasoft Dec 5, 2024
dff263f
Merge branch 'develop' into feature/LS24005158/fix-params-assignment-…
davidepalladino-apuliasoft Dec 5, 2024
6f07a92
Added `MUDRNRAPU00177` test
davidepalladino-apuliasoft Dec 5, 2024
a39a73d
Merge remote-tracking branch 'origin/feature/LS24005158/fix-params-as…
davidepalladino-apuliasoft Dec 5, 2024
19d12e4
Added `MUDRNRAPU00178` test
davidepalladino-apuliasoft Dec 5, 2024
01d4ae5
Typo
davidepalladino-apuliasoft Dec 5, 2024
a72d0a0
Removed unused COPY
davidepalladino-apuliasoft Dec 5, 2024
763ff75
Added `MUDRNRAPU00179` test
davidepalladino-apuliasoft Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,19 @@ open class InternalInterpreter(
}
initialValue
}
/*
* In accord to documentation (see https://www.ibm.com/docs/en/i/7.5?topic=codes-plist-identify-parameter-list):
* when control transfers to called program, at the beginning, the contents of the Result field is placed in
* the Factor 1 field.
*/
it.isInPlist(compilationUnit) -> {
val resultName = it.getResultNameByFactor1(compilationUnit)
if (resultName == null || initialValues[resultName] is NullValue) {
blankValue(it)
} else {
initialValues[resultName]
}
}

it.initializationValue != null -> eval(it.initializationValue)
it.isCompileTimeArray() -> toArrayValue(
Expand Down Expand Up @@ -384,6 +397,45 @@ open class InternalInterpreter(
}
}

/**
* Retrieves the result name associated with the current `AbstractDataDefinition` instance
* from the parameter list (PList) of the specified `CompilationUnit`.
*
* This function searches the PList for the first parameter where `factor1` is of type `DataRefExpr`
* and its variable name matches the name of the current `AbstractDataDefinition` (case-insensitively).
* If such a parameter is found, its associated result name is returned.
*
* @param compilationUnit the compilation unit whose entry PList is to be checked
* @return the result name associated with the matching parameter, or `null` if no match is found
*/
private fun AbstractDataDefinition.getResultNameByFactor1(compilationUnit: CompilationUnit): String? {
val resultName = compilationUnit.entryPlist?.params
?.filter { plistParam -> plistParam.factor1 is DataRefExpr }
?.firstOrNull { plistParamFiltered ->
(plistParamFiltered.factor1 as DataRefExpr).variable.name.equals(
this.name,
true
)
}
?.result?.name
return resultName
}

/**
* Checks if the current `AbstractDataDefinition` instance is present in the parameter list (PList)
* of the specified `CompilationUnit`.
*
* This function evaluates whether the `AbstractDataDefinition` matches any parameter in the PList
* by comparing their names (case-insensitively). Parameters in the PList are filtered to include
* only those with a `factor1` of type `DataRefExpr`.
*
* @param compilationUnit the compilation unit whose entry PList is to be checked
* @return `true` if the `AbstractDataDefinition` is present in the PList, otherwise `false`
*/
private fun AbstractDataDefinition.isInPlist(compilationUnit: CompilationUnit) = compilationUnit.entryPlist?.params
?.filter { plistParam -> plistParam.factor1 is DataRefExpr }
?.any { plistParamFiltered -> (plistParamFiltered.factor1 as DataRefExpr).variable.name.equals(this.name, true) } == true

private fun toArrayValue(compileTimeArray: CompileTimeArray, arrayType: ArrayType): Value {
// It is not clear why the compileTimeRecordsPerLine on the array type is null
// probably it is an error during the ast processing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
val plistParams = cu.entryPlist
// TODO derive proper type from the data specification
return plistParams?.params?.map {
val type = cu.getAnyDataDefinition(it.param.name) {
"Cannot resolve PARAM: ${it.param.name} in *ENTRY PLIST of the program: $name"
val type = cu.getAnyDataDefinition(it.result.name) {
"Cannot resolve RESULT: ${it.result.name} in *ENTRY PLIST of the program: $name"
}.type
ProgramParam(it.param.name, type)
ProgramParam(it.result.name, type)
}
?: emptyList()
}
Expand Down Expand Up @@ -135,6 +135,7 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
"param ${pv.key} was expected to have type $expectedType. It has value: $coercedValue"
}
}

if (!initialized) {
initialized = true

Expand Down Expand Up @@ -185,7 +186,14 @@ class RpgProgram(val cu: CompilationUnit, val name: String = "<UNNAMED RPG PROGR
null
)
params.keys.forEach { params[it] = interpreter[it] }
changedInitialValues = params().map { interpreter[it.name] }

/* In accord to documentation (see https://www.ibm.com/docs/en/i/7.5?topic=codes-plist-identify-parameter-list):
* at the end, if the Factor 2 is declared, replaces the Result with the value of Factor 2.
*/
changedInitialValues = params().map { param -> this.cu.entryPlist?.params
?.firstOrNull { plistParamCu -> plistParamCu.result.name.equals(param.name, true) }
.let { it?.factor2?.let { factor2 -> interpreter.eval(factor2) } } ?: interpreter[param.name] }

// here clear symbol table if needed
interpreter.doSomethingAfterExecution()
}.nanoseconds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -935,16 +935,16 @@ data class CallStmt(
if (it.dataDefinition != null) {
// handle declaration of new variable
if (it.dataDefinition.initializationValue != null) {
if (!interpreter.exists(it.param.name)) {
if (!interpreter.exists(it.result.name)) {
interpreter.assign(it.dataDefinition, interpreter.eval(it.dataDefinition.initializationValue))
} else {
interpreter.assign(
interpreter.dataDefinitionByName(it.param.name)!!,
interpreter.dataDefinitionByName(it.result.name)!!,
interpreter.eval(it.dataDefinition.initializationValue)
)
}
} else {
if (!interpreter.exists(it.param.name)) {
if (!interpreter.exists(it.result.name)) {
interpreter.assign(it.dataDefinition, interpreter.eval(BlanksRefExpr(it.position)))
}
}
Expand All @@ -953,12 +953,12 @@ data class CallStmt(
// change the value of parameter with initialization value
if (it.initializationValue != null) {
interpreter.assign(
interpreter.dataDefinitionByName(it.param.name)!!,
interpreter.dataDefinitionByName(it.result.name)!!,
interpreter.eval(it.initializationValue)
)
}
}
targetProgramParams[index].name to interpreter[it.param.name]
targetProgramParams[index].name to interpreter[it.result.name]
}.toMap(LinkedHashMap())

val paramValuesAtTheEnd =
Expand Down Expand Up @@ -989,10 +989,10 @@ data class CallStmt(
paramValuesAtTheEnd?.forEachIndexed { index, value ->
if (this.params.size > index) {
val currentParam = this.params[index]
interpreter.assign(currentParam.param.referred!!, value)
interpreter.assign(currentParam.result.referred!!, value)

// If we also have a result field, assign to it
currentParam.result?.let { interpreter.assign(it, value) }
currentParam.factor1?.let { interpreter.assign(it, value) }
}
}

Expand Down Expand Up @@ -1344,8 +1344,8 @@ data class PlistStmt(

override fun execute(interpreter: InterpreterCore) {
params.forEach {
if (interpreter.getGlobalSymbolTable().contains(it.param.name)) {
interpreter.getGlobalSymbolTable()[it.param.name]
if (interpreter.getGlobalSymbolTable().contains(it.result.name)) {
interpreter.getGlobalSymbolTable()[it.result.name]
}
}
}
Expand All @@ -1360,8 +1360,9 @@ data class PlistStmt(

@Serializable
data class PlistParam(
val result: AssignableExpression?,
val param: ReferenceByName<AbstractDataDefinition>,
val factor1: AssignableExpression?,
val factor2: Expression?,
val result: ReferenceByName<AbstractDataDefinition>,
// TODO @Derived????
@Derived val dataDefinition: InStatementDataDefinition? = null,
override val position: Position? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1157,29 +1157,41 @@ internal fun CsPLISTContext.toAst(conf: ToAstConfiguration = ToAstConfiguration(
}

internal fun CsPARMContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): PlistParam {
var paramName = this.cspec_fixed_standard_parts().result.text
if (paramName.contains(".")) {
val parts = paramName.split(".")
var resultName = this.cspec_fixed_standard_parts().result.text
if (resultName.contains(".")) {
val parts = resultName.split(".")
require(parts.isNotEmpty())
paramName = parts.last()
}
// initialization value valid only if there isn't a variable declaration
val initializationValue = if (this.cspec_fixed_standard_parts().len.asInt() == null) {
this.cspec_fixed_standard_parts().factor2Expression(conf)
} else {
null
resultName = parts.last()
}
val resultPosition = this.cspec_fixed_standard_parts().result.toPosition()

val factor1Text = this.factor1.text.trim()
val factor1Position = this.factor1.toPosition(conf.considerPosition)
val result = if (factor1Text.isNotEmpty()) annidatedReferenceExpression(factor1Text, factor1Position) else null

val factor1Expression = if (factor1Text.isNotEmpty()) annidatedReferenceExpression(factor1Text, factor1Position) else null
val factor2Expression = this.cspec_fixed_standard_parts().factor2Expression(conf)

/*
* In accord to documentation (see https://www.ibm.com/docs/en/i/7.5?topic=codes-plist-identify-parameter-list):
* - when `CALL` is processed, the content of Factor 2 is placed in the Result field. So. is considered Factor 2 value;
* - when control transfers to called program, the contents of the Result field is placed in
* the Factor 1 field. So, is considered Result Value.
*/
val initializationValue = if (this.parent is CsCALLContext && this.cspec_fixed_standard_parts().len.asInt() == null) {
factor2Expression
} else if (this.parent is CsPLISTContext && this.cspec_fixed_standard_parts().len.asInt() == null) {
annidatedReferenceExpression(resultName, resultPosition)
} else {
null
}

val position = toPosition(conf.considerPosition)
return PlistParam(
result,
ReferenceByName(paramName),
factor1Expression,
factor2Expression,
ReferenceByName(resultName),
this.cspec_fixed_standard_parts().toDataDefinition(
paramName,
resultName,
position,
conf
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ private fun CompilationUnit.resolve() {
}

this.specificProcess(PlistParam::class.java) { pp ->
if (!pp.param.resolved) {
pp.param.tryToResolveRecursively(position = pp.position, cu = this)
if (!pp.result.resolved) {
pp.result.tryToResolveRecursively(position = pp.position, cu = this)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ class JarikoCallbackTest : AbstractTest() {
@Test
fun executeERROR08CallBackTest() {
// Errors in block statements
executePgmCallBackTest("ERROR08", SourceReferenceType.Program, "ERROR08", listOf(7, 8, 9, 14, 15, 16, 21, 22))
executePgmCallBackTest("ERROR08", SourceReferenceType.Program, "ERROR08", listOf(7, 8, 8, 9, 9, 14, 15, 15, 16, 16, 21, 22, 22))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -673,4 +673,111 @@ open class MULANGT10BaseCodopTest : MULANGTTest() {
val expected = listOf("ok")
assertEquals(expected, "smeup/MUDRNRAPU00274".outputOf(configuration = smeupConfig))
}

/**
* This program tests the moving of value from Factor 2 to Result. This operation is performed at the end of execution of
* program called. In this case, MUDRNRAPU00172_P turn on indicator 35. Later, `DO` block ends its execution.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00172() {
val expected = listOf("Sub program", "1")
assertEquals(expected, "smeup/MUDRNRAPU00172".outputOf(configuration = smeupConfig))
}

/**
* This program tests the behaviour of `CALL` and `PLIST` when is used the Params for both between caller and called.
* In accord to documentation:
* - when `CALL` is processed, the content of Factor 2 is placed in the Result field;
* - when control transfers to called program, the contents of the Result field is placed in the Factor 1 field.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00173() {
val expected = listOf("BAR")
assertEquals(expected, "smeup/MUDRNRAPU00173".outputOf(configuration = smeupConfig))
}

/**
* This program tests the behaviour of `CALL` and `PLIST` when is used the Params for both between caller and called.
* Also, the called program change the result (`RES`) to another value.
* In accord to documentation:
* - when `CALL` is processed, the content of Factor 2 is placed in the Result field;
* - when control transfers to called program, the contents of the Result field is placed in the Factor 1 field.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00174() {
val expected = listOf("BAR")
assertEquals(expected, "smeup/MUDRNRAPU00174".outputOf(configuration = smeupConfig))
}

/**
* This program is more complex. Tests the assignment of value between waterfall calls and by parameters. Each subprogram is
* called max 101 times (last is Jariko error), thanks `DO` statement; this main program calls `MUDRNRAPU00175_P1` which
* calls recursively `MUDRNRAPU00175_P2`.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00175() {
val expected = listOf("HELLO")
assertEquals(expected, "smeup/MUDRNRAPU00175".outputOf(configuration = smeupConfig))
}

/**
* This program is more complex. Tests the assignment of value between waterfall calls and by using `EVAL`.
* Each subprogram is called max 101 times (last is Jariko error), thanks `DO` statement; this main program calls
* `MUDRNRAPU00176_P1` which calls recursively `MUDRNRAPU00176_P2`.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00176() {
val expected = listOf("HELLO")
assertEquals(expected, "smeup/MUDRNRAPU00176".outputOf(configuration = smeupConfig))
}

/**
* This program call a sub program by using pre-initialized variables as factors and result. The called program
* doesn't make any assignment.
* This tests the full behaviour between a CALLER and CALLED, where:
* - caller (at the beginning) move Factor 2 to Result;
* - called (at the beginning) move Result to Factor 1;
* - called (at the end) move Factor 2 to Result;
* - caller (at the end) move Result to Factor 1.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00177() {
val expected = listOf("CALLED", "BAR", "", "BAR", "CALLER", "", "BAR", "")
assertEquals(expected, "smeup/MUDRNRAPU00177".outputOf(configuration = smeupConfig))
}

/**
* This program call a sub program by using pre-initialized variables as factors and result. The called program
* doesn't make any assignment.
* This tests the full behaviour between a CALLER and CALLED, where:
* - caller (at the beginning) move Factor 2 to Result;
* - called (at the beginning) move Result to Factor 1;
* - caller (at the end) move Result to Factor 1.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00178() {
val expected = listOf("CALLED", "BAR", "BAR", "CALLER", "BAR", "BAR", "BAR")
assertEquals(expected, "smeup/MUDRNRAPU00178".outputOf(configuration = smeupConfig))
}

/**
* This program call a sub program by using pre-initialized variables as factors and result. The called program
* doesn't make any assignment.
* This tests the full behaviour between a CALLER and CALLED, where:
* - called (at the beginning) move Result to Factor 1;
* - caller (at the end) move Result to Factor 1.
* @see #LS24005158
*/
@Test
fun executeMUDRNRAPU00179() {
val expected = listOf("CALLED", "BAZ", "BAZ", "CALLER", "BAZ", "BAZ")
assertEquals(expected, "smeup/MUDRNRAPU00179".outputOf(configuration = smeupConfig))
}
}
Loading
Loading