Skip to content

Commit 37754c3

Browse files
authored
Merge pull request #654 from smeup/bugfix/LS24004772/MOVEA-ProjectedArrayValue-to-ConcreteArrayValue
Bugfix/LS24004772/`MOVEA` from `ProjectedArrayValue` to `ConcreteArrayValue`
2 parents f348f40 + 048951c commit 37754c3

File tree

11 files changed

+337
-5
lines changed

11 files changed

+337
-5
lines changed

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/coercing.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ fun coerce(value: Value, type: Type): Value {
250250
}
251251
is ArrayType -> {
252252
val coercedValue = coerce(value, type.element)
253-
ConcreteArrayValue(MutableList(type.element.size) { coercedValue }, type.element)
253+
ConcreteArrayValue(MutableList(type.nElements) { coercedValue }, type.element)
254254
}
255255
else -> TODO("Converting DecimalValue to $type")
256256
}

rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/move_a.kt

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.smeup.rpgparser.interpreter
22

33
import com.smeup.rpgparser.parsing.ast.*
4+
import kotlin.math.pow
45

56
fun movea(operationExtenter: String?, target: AssignableExpression, valueExpression: Expression, interpreterCore: InterpreterCore): Value {
67
return when (target) {
@@ -62,21 +63,22 @@ private fun moveaNumber(
6263
targetArray.getElement(it + 1)
6364
} else {
6465
val newValueIndex = it - startIndex + 1
65-
if (newValueIndex < newValue.elements.size) {
66-
newValue.elements[newValueIndex]
66+
var elementValue = if (newValueIndex < newValue.arrayLength()) {
67+
newValue.getElement(newValueIndex + 1)
6768
} else {
6869
if (operationExtenter == null) {
6970
targetArray.getElement(it + 1)
7071
} else {
7172
IntValue.ZERO
7273
}
7374
}
75+
internalCoercing(elementValue, targetArray.elementType, newValue.elementType)
7476
}
7577
}
7678
return arrayValue
7779
}
7880

79-
private fun InterpreterCore.toArray(expression: Expression, targetType: Type): ConcreteArrayValue =
81+
private fun InterpreterCore.toArray(expression: Expression, targetType: Type): ArrayValue =
8082
when (expression) {
8183
is ArrayAccessExpr -> {
8284
val arrayValueRaw = eval(expression.array)
@@ -90,7 +92,7 @@ private fun InterpreterCore.toArray(expression: Expression, targetType: Type): C
9092
}
9193
is DataRefExpr -> {
9294
if (expression.type() is ArrayType) {
93-
eval(expression) as ConcreteArrayValue
95+
eval(expression) as ArrayValue
9496
} else {
9597
ConcreteArrayValue(mutableListOf(eval(expression)), expression.type())
9698
}
@@ -152,4 +154,44 @@ private fun valueFromSourceExpression(interpreterCore: InterpreterCore, valueExp
152154
} else {
153155
interpreterCore.eval(valueExpression)
154156
}
157+
}
158+
159+
/**
160+
* Coerces `sourceValue` to match `targetType` based on specified numeric conversion rules, especially
161+
* for conversions between decimal and integer types with varying decimal precision. This function
162+
* validates that the number of digits between `sourceType` and `targetType` are equal before performing
163+
* the conversion.
164+
*
165+
* @param sourceValue The `Value` to be coerced, generally a numeric type such as `DecimalValue` or `IntValue`.
166+
* @param targetType The target `Type` for the coercion, used to guide the conversion, particularly
167+
* regarding the number of decimal places.
168+
* @param sourceType The `Type` representing the original type of `sourceValue`, assisting in determining
169+
* the required scale and format for conversion.
170+
*
171+
* @return The resulting `Value` after coercion, transformed to align with `targetType`.
172+
*
173+
* @throws IllegalStateException if the total number of digits between `sourceType` and `targetType`
174+
* do not match, indicating incompatible numeric sizes for conversion.
175+
* @throws ClassCastException if `sourceValue` is not a `DecimalValue` or `IntValue`, or if `targetType`
176+
* or `sourceType` are not of numeric types.
177+
*/
178+
private fun internalCoercing(
179+
sourceValue: Value,
180+
targetType: Type,
181+
sourceType: Type
182+
): Value {
183+
// Number of digits between source and target must be equals.
184+
if (sourceType is NumberType && targetType is NumberType && sourceType.numberOfDigits != targetType.numberOfDigits) {
185+
throw IllegalStateException("Factor 2 and Result with different type and size.")
186+
}
187+
188+
return when {
189+
// Proper conversion between a left side as decimal to right side as integer
190+
sourceValue is DecimalValue && sourceType is NumberType && targetType is NumberType && targetType.decimalDigits == 0 ->
191+
DecimalValue(sourceValue.value * 10.0.pow(targetType.entireDigits - sourceType.entireDigits).toBigDecimal())
192+
// Or integer to decimal
193+
sourceValue is IntValue && targetType is NumberType && targetType.decimalDigits > 0 ->
194+
DecimalValue((sourceValue.value / 10.0.pow(targetType.decimalDigits)).toBigDecimal())
195+
else -> sourceValue
196+
}
155197
}

rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/interpreter/JarikoCallbackTest.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,30 @@ class JarikoCallbackTest : AbstractTest() {
960960
assertEquals(rpgProgramTraces.filter { it.description == "TRACETST2" }.size, 1)
961961
}
962962

963+
@Test
964+
fun executeERROR45CallBackTest() {
965+
executePgmCallBackTest("ERROR45", SourceReferenceType.Program, "ERROR45", mapOf(
966+
18 to "Factor 2 and Result with different type and size."
967+
))
968+
}
969+
970+
@Test
971+
fun executeERROR45SourceLineTest() {
972+
executeSourceLineTest("ERROR45")
973+
}
974+
975+
@Test
976+
fun executeERROR46CallBackTest() {
977+
executePgmCallBackTest("ERROR46", SourceReferenceType.Program, "ERROR46", mapOf(
978+
18 to "Factor 2 and Result with different type and size."
979+
))
980+
}
981+
982+
@Test
983+
fun executeERROR46SourceLineTest() {
984+
executeSourceLineTest("ERROR46")
985+
}
986+
963987
@Test
964988
fun bypassSyntaxErrorTest() {
965989
val configuration = Configuration().apply {

rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,4 +558,60 @@ open class MULANGT10BaseCodopTest : MULANGTTest() {
558558
val expected = listOf("1", "1", "1", "1", "1", "1", "2", "2")
559559
assertEquals(expected, "smeup/MUDRNRAPU00148".outputOf(configuration = smeupConfig))
560560
}
561+
562+
/**
563+
* MOVEA between a DS field declared as array and a standalone array. Both as integer.
564+
* @see #LS24004772
565+
*/
566+
@Test
567+
fun executeMUDRNRAPU00155() {
568+
val expected = listOf("2", "2", "2", "2", "2", "1", "1", "1", "1", "1")
569+
assertEquals(expected, "smeup/MUDRNRAPU00155".outputOf(configuration = smeupConfig))
570+
}
571+
572+
/**
573+
* MOVEA between a DS field declared as array and a standalone array. Both as integer.
574+
* Size of DS field as array is lower than standalone.
575+
* @see #LS24004772
576+
*/
577+
@Test
578+
fun executeMUDRNRAPU00156() {
579+
val expected = listOf("2", "2", "2", "2", "2", "1", "1", "1", "0", "0")
580+
assertEquals(expected, "smeup/MUDRNRAPU00156".outputOf(configuration = smeupConfig))
581+
}
582+
583+
/**
584+
* MOVEA between a DS field declared as array and a standalone array. Both as integer.
585+
* Size of DS field as array is greater than standalone.
586+
* @see #LS24004772
587+
*/
588+
@Test
589+
fun executeMUDRNRAPU00157() {
590+
val expected = listOf("2", "2", "2", "1", "1", "1")
591+
assertEquals(expected, "smeup/MUDRNRAPU00157".outputOf(configuration = smeupConfig))
592+
}
593+
594+
/**
595+
* MOVEA between a DS field declared as array and a standalone array. DS field array is declared as decimal;
596+
* standalone array as integer.
597+
* Size of DS field as array is lower than standalone.
598+
* @see #LS24004772
599+
*/
600+
@Test
601+
fun executeMUDRNRAPU00158() {
602+
val expected = listOf("2", "2", "2", "2", "2", "1200", "1200", "1200", "0", "0")
603+
assertEquals(expected, "smeup/MUDRNRAPU00158".outputOf(configuration = smeupConfig))
604+
}
605+
606+
/**
607+
* MOVEA between a DS field declared as array and a standalone array. DS field array is declared as integer;
608+
* standalone array as decimal.
609+
* Size of DS field as array is lower than standalone.
610+
* @see #LS24004772
611+
*/
612+
@Test
613+
fun executeMUDRNRAPU00159() {
614+
val expected = listOf("2.200", "2.200", "2.200", "2.200", "2.200", ".001", ".001", ".001", ".000", ".000")
615+
assertEquals(expected, "smeup/MUDRNRAPU00159".outputOf(configuration = smeupConfig))
616+
}
561617
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
V* ==============================================================
2+
D* 05/11/24
3+
D* Purpose: Must fire the following errors during execution of
4+
D* C MOVEA(P) DS1_ARR1 ARR1
5+
D* line 18 - "Factor 2 and Result with different type and size."
6+
V* ==============================================================
7+
D DS1 DS 100
8+
D DS1_ARR1 5 3 DIM(3) INZ(12.345)
9+
D ARR1 S 3 0 DIM(5) INZ(9)
10+
D TMP S 7
11+
D COUNT S 2 0 INZ(1)
12+
13+
C FOR COUNT=1 TO %ELEM(ARR1)
14+
C EVAL TMP=%CHAR(ARR1(COUNT))
15+
C TMP DSPLY
16+
C ENDFOR
17+
18+
C MOVEA(P) DS1_ARR1 ARR1
19+
20+
C FOR COUNT=1 TO %ELEM(ARR1)
21+
C EVAL TMP=%CHAR(ARR1(COUNT))
22+
C TMP DSPLY
23+
C ENDFOR
24+
25+
C SETON LR
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
V* ==============================================================
2+
D* 05/11/24
3+
D* Purpose: Must fire the following errors during execution of
4+
D* C MOVEA(P) DS1_ARR1 ARR1
5+
D* line 18 - "Factor 2 and Result with different type and size."
6+
V* ==============================================================
7+
D DS1 DS 100
8+
D DS1_ARR1 3 0 DIM(3) INZ(123)
9+
D ARR1 S 5 3 DIM(5) INZ(9.9)
10+
D TMP S 7
11+
D COUNT S 2 0 INZ(1)
12+
13+
C FOR COUNT=1 TO %ELEM(ARR1)
14+
C EVAL TMP=%CHAR(ARR1(COUNT))
15+
C TMP DSPLY
16+
C ENDFOR
17+
18+
C MOVEA(P) DS1_ARR1 ARR1
19+
20+
C FOR COUNT=1 TO %ELEM(ARR1)
21+
C EVAL TMP=%CHAR(ARR1(COUNT))
22+
C TMP DSPLY
23+
C ENDFOR
24+
25+
C SETON LR
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
V* ==============================================================
2+
V* 05/11/2024 APU001 Creation
3+
V* ==============================================================
4+
O * PROGRAM GOAL
5+
O * MOVEA between a DS field declared as array and a standalone
6+
O * array. Both as integer.
7+
V* ==============================================================
8+
O * JARIKO ANOMALY
9+
O * Before the fix:
10+
O * `ProjectedArrayValue cannot be cast to
11+
O * class ConcreteArrayValue`
12+
V* ==============================================================
13+
D DS1 DS 100
14+
D DS1_ARR1 2 0 DIM(5) INZ(1)
15+
D ARR1 S 2 0 DIM(5) INZ(2)
16+
D TMP S 2
17+
D COUNT S 2 0 INZ(1)
18+
19+
C FOR COUNT=1 TO %ELEM(ARR1)
20+
C EVAL TMP=%CHAR(ARR1(COUNT))
21+
C TMP DSPLY
22+
C ENDFOR
23+
24+
C MOVEA(P) DS1_ARR1 ARR1 #ProjectedArrayValue cannot be cast to class ConcreteArrayValue
25+
26+
C FOR COUNT=1 TO %ELEM(ARR1)
27+
C EVAL TMP=%CHAR(ARR1(COUNT))
28+
C TMP DSPLY
29+
C ENDFOR
30+
31+
C SETON LR
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
V* ==============================================================
2+
V* 05/11/2024 APU001 Creation
3+
V* ==============================================================
4+
O * PROGRAM GOAL
5+
O * MOVEA between a DS field declared as array and a standalone
6+
O * array. Both as integer.
7+
O * Size of DS field as array is lower than standalone.
8+
V* ==============================================================
9+
O * JARIKO ANOMALY
10+
O * Before the fix:
11+
O * `ProjectedArrayValue cannot be cast to
12+
O * class ConcreteArrayValue`
13+
V* ==============================================================
14+
D DS1 DS 100
15+
D DS1_ARR1 2 0 DIM(3) INZ(1)
16+
D ARR1 S 2 0 DIM(5) INZ(2)
17+
D TMP S 2
18+
D COUNT S 2 0 INZ(1)
19+
20+
C FOR COUNT=1 TO %ELEM(ARR1)
21+
C EVAL TMP=%CHAR(ARR1(COUNT))
22+
C TMP DSPLY
23+
C ENDFOR
24+
25+
C MOVEA(P) DS1_ARR1 ARR1 #ProjectedArrayValue cannot be cast to class ConcreteArrayValue
26+
27+
C FOR COUNT=1 TO %ELEM(ARR1)
28+
C EVAL TMP=%CHAR(ARR1(COUNT))
29+
C TMP DSPLY
30+
C ENDFOR
31+
32+
C SETON LR
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
V* ==============================================================
2+
V* 05/11/2024 APU001 Creation
3+
V* ==============================================================
4+
O * PROGRAM GOAL
5+
O * MOVEA between a DS field declared as array and a standalone
6+
O * array. Both as integer.
7+
O * Size of DS field as array is greater than standalone.
8+
V* ==============================================================
9+
O * JARIKO ANOMALY
10+
O * Before the fix:
11+
O * `ProjectedArrayValue cannot be cast to
12+
O * class ConcreteArrayValue`
13+
V* ==============================================================
14+
D DS1 DS 100
15+
D DS1_ARR1 2 0 DIM(5) INZ(1)
16+
D ARR1 S 2 0 DIM(3) INZ(2)
17+
D TMP S 2
18+
D COUNT S 2 0 INZ(1)
19+
20+
C FOR COUNT=1 TO %ELEM(ARR1)
21+
C EVAL TMP=%CHAR(ARR1(COUNT))
22+
C TMP DSPLY
23+
C ENDFOR
24+
25+
C MOVEA(P) DS1_ARR1 ARR1 #ProjectedArrayValue cannot be cast to class ConcreteArrayValue
26+
27+
C FOR COUNT=1 TO %ELEM(ARR1)
28+
C EVAL TMP=%CHAR(ARR1(COUNT))
29+
C TMP DSPLY
30+
C ENDFOR
31+
32+
C SETON LR
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
V* ==============================================================
2+
V* 05/11/2024 APU001 Creation
3+
V* ==============================================================
4+
O * PROGRAM GOAL
5+
O * MOVEA between a DS field declared as array and a standalone
6+
O * array. DS field array is declared as decimal;
7+
O * standalone array as integer.
8+
O * Size of DS field as array is lower than standalone.
9+
V* ==============================================================
10+
O * JARIKO ANOMALY
11+
O * Before the fix:
12+
O * `Issue arose while setting field DS1_ARR1`
13+
V* ==============================================================
14+
D DS1 DS 100
15+
D DS1_ARR1 5 3 DIM(3) INZ(1.2)
16+
D ARR1 S 5 0 DIM(5) INZ(2)
17+
D TMP S 5
18+
D COUNT S 2 0 INZ(1)
19+
20+
C FOR COUNT=1 TO %ELEM(ARR1)
21+
C EVAL TMP=%CHAR(ARR1(COUNT))
22+
C TMP DSPLY
23+
C ENDFOR
24+
25+
C MOVEA(P) DS1_ARR1 ARR1 #Issue arose while setting field DS1_ARR1
26+
27+
C FOR COUNT=1 TO %ELEM(ARR1)
28+
C EVAL TMP=%CHAR(ARR1(COUNT))
29+
C TMP DSPLY
30+
C ENDFOR
31+
32+
C SETON LR

0 commit comments

Comments
 (0)