Skip to content

Commit

Permalink
Merge pull request #647 from smeup/bugfix/LS24004606/movel-array-to-a…
Browse files Browse the repository at this point in the history
…rray

Bugfix/LS24004606/MOVEL Array to Array
  • Loading branch information
lanarimarco authored Oct 29, 2024
2 parents b69246f + f507148 commit 200f126
Show file tree
Hide file tree
Showing 10 changed files with 441 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ fun coerce(value: Value, type: Type): Value {
return DecimalValue(value.value.setScale(type.decimalDigits))
}
}
is ArrayType -> {
val coercedValue = coerce(value, type.element)
ConcreteArrayValue(MutableList(type.element.size) { coercedValue }, type.element)
}
else -> TODO("Converting DecimalValue to $type")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.smeup.rpgparser.parsing.parsetreetoast.isDecimal
import com.smeup.rpgparser.parsing.parsetreetoast.isInt
import com.smeup.rpgparser.parsing.parsetreetoast.toDecimal
import java.math.BigDecimal
import kotlin.math.min

private fun clear(value: String, type: Type): String {
return when (type) {
Expand All @@ -25,42 +26,148 @@ private fun clear(value: String, type: Type): String {
}
}

/**
* Performs RPGLE `MOVEL` operation by assigning `value` (Factor 2) to `target`,
* handling both scalar and array types for `value`.
*
* If `value` is of type `ArrayType`, it delegates to `movelFactorAsArray` to handle array
* operations. If `value` is a scalar, it delegates to `movelFactorAsScalar`. For figurative constants,
* it directly assigns the evaluated constant to the `target`.
*
* @param operationExtender Optional modifier specifying an extender for the `MOVEL` operation.
* When non-null, enables the "clear" functionality, which clears existing target values
* before assignment.
* @param target The target `AssignableExpression` where the result of the `MOVEL` operation
* will be assigned.
* @param value The `Expression` representing Factor 2 in RPGLE. This can be a figurative constant,
* scalar, or array.
* @param dataAttributes Optional `Expression` containing additional attributes, in example for date format.
* Can be null.
* @param interpreterCore The `InterpreterCore` instance handling expression evaluation and assignment.
*
* @return The result `Value` of the `MOVEL` operation after assigning `value` to `target`.
*
* @throws IllegalArgumentException if the type of `target` or `value` does not support the `MOVEL` operation.
*/
fun movel(
operationExtender: String?,
target: AssignableExpression,
value: Expression,
dataAttributes: Expression?,
interpreterCore: InterpreterCore
): Value {
if (value !is FigurativeConstantRef) {
return if (value !is FigurativeConstantRef) {
if (value.type() is ArrayType) {
throw UnsupportedOperationException("Cannot set an array as factor 2 in MOVEL/MOVEL(P) statement")
movelFactorAsArray(operationExtender, target, value, dataAttributes, interpreterCore)
} else {
movelFactorAsScalar(operationExtender, target, value, dataAttributes, interpreterCore)
}
} else {
interpreterCore.assign(target, interpreterCore.eval(value))
}
}

/**
* Handles `MOVEL` operation when `source` (Factor 2) is an array type, assigning its elements
* to `target` (Result). Elements are transferred up to the minimum length between `source` and
* `target` arrays, applying any `operationExtender` if specified.
*
* @param operationExtender Optional modifier specifying an extender for the `MOVEL` operation.
* When non-null, enables the "clear" functionality, which clears existing target values
* before assignment.
* @param target The `AssignableExpression` target array where elements from `source` will be
* assigned.
* @param source The `Expression` source array (Factor 2) providing elements to transfer to `target`.
* @param dataAttributes Optional `Expression` containing additional attributes, in example for date format.
* Can be null.
* @param interpreterCore The `InterpreterCore` instance for evaluating, retrieving, and assigning
* expressions.
*
* @return The resulting `Value` after assigning elements from `source` to `target`.
*
* @throws UnsupportedOperationException If `target` is not an array type or if `source` and `target`
* have incompatible types for `MOVEL` operation.
*/
fun movelFactorAsArray(
operationExtender: String?,
target: AssignableExpression,
source: Expression,
dataAttributes: Expression?,
interpreterCore: InterpreterCore
): Value {
return when (target.type()) {
is ArrayType -> {
val arraySourceValue: ArrayValue = interpreterCore.eval(source) as ArrayValue
val arrayTargetValue: ArrayValue = interpreterCore.eval(target) as ArrayValue
val arraySourceType: Type = (source.type() as ArrayType).element
val arrayTargetType: Type = (target.type() as ArrayType).element

if (value.type() is DateType) {
return interpreterCore.assign(target, dateToString(value, dataAttributes, interpreterCore))
val maxSize = min(arraySourceValue.arrayLength(), arrayTargetValue.arrayLength())
for (i in 1 until maxSize + 1) {
arrayTargetValue.setElement(
i,
valueToString(arraySourceValue.getElement(i), arraySourceType),
valueToString(arrayTargetValue.getElement(i), arrayTargetType),
arrayTargetType,
operationExtender
)
}
interpreterCore.assign(target, arrayTargetValue)
}
else -> {
throw UnsupportedOperationException("Not implemented MOVEL/MOVEL(P) statement between Factor 2 as ${source.type()} to Result as ${target.type()}.")
}
}
}

val valueToMove: String = getStringOfValue(target, interpreterCore, value)
if (target.type() is ArrayType) {
// for each element of array apply move
/**
* Performs `MOVEL` operation when `value` (Factor 2) is a scalar, assigning it to `target`.
* Handles specific transformations if `value` is a date type, and applies the value to each
* element if `target` is an array. If `operationExtender` is specified, enables clearing
* behavior during assignment.
*
* @param operationExtender Optional modifier specifying an extender for the `MOVEL` operation.
* When non-null, enables the "clear" functionality, which clears existing target values
* before assignment.
* @param target The `AssignableExpression` where the transformed `value` will be assigned.
* @param value The scalar `Expression` (Factor 2) to be moved to `target`.
* @param dataAttributes Optional `Expression` containing additional attributes, in example for date format.
* Can be null.
* @param interpreterCore The `InterpreterCore` instance used for expression evaluation and assignment.
*
* @return The resulting `Value` after the `MOVEL` operation, representing the assigned target value.
*
* @throws UnsupportedOperationException if `target` is of an incompatible type for `MOVEL`.
*/
fun movelFactorAsScalar(
operationExtender: String?,
target: AssignableExpression,
value: Expression,
dataAttributes: Expression?,
interpreterCore: InterpreterCore
): Value {
if (value.type() is DateType) {
return interpreterCore.assign(target, dateToString(value, dataAttributes, interpreterCore))
}

val valueToMove: String = getStringOfValueBaseOfTarget(target, interpreterCore, value)
return when (target.type()) {
is ArrayType -> {
// For each element of array apply MOVEL
val arrayValue: ConcreteArrayValue = interpreterCore.eval(target) as ConcreteArrayValue
val valueToApplyMoveElementType: Type = (target.type() as ArrayType).element
arrayValue.elements.forEachIndexed { index, el ->
arrayValue.setElement(
index + 1, stringToValue(
movel(
valueToMove,
valueToString(el, valueToApplyMoveElementType),
valueToApplyMoveElementType,
operationExtender != null
),
valueToApplyMoveElementType
)
index + 1,
valueToMove,
valueToString(el, valueToApplyMoveElementType),
valueToApplyMoveElementType,
operationExtender
)
}
return interpreterCore.assign(target, arrayValue)
} else {
interpreterCore.assign(target, arrayValue)
}
else -> {
val valueToApplyMove: String = valueToString(interpreterCore.eval(target), target.type())
return interpreterCore.assign(
target,
Expand All @@ -70,8 +177,6 @@ fun movel(
)
)
}
} else {
return interpreterCore.assign(target, interpreterCore.eval(value))
}
}

Expand All @@ -83,9 +188,9 @@ fun move(
): Value {
if (value !is FigurativeConstantRef) {
if (value.type() is ArrayType) {
throw UnsupportedOperationException("Cannot set an array as factor 2 in MOVE/MOVE(P) statement")
throw UnsupportedOperationException("Not implemented MOVEL/MOVEL(P) statement between Factor 2 as ${value.type()} to Result as ${target.type()}.")
}
val valueToMove: String = getStringOfValue(target, interpreterCore, value)
val valueToMove: String = getStringOfValueBaseOfTarget(target, interpreterCore, value)
if (target.type() is ArrayType) {
// for each element of array apply move
val arrayValue: ConcreteArrayValue = interpreterCore.eval(target) as ConcreteArrayValue
Expand Down Expand Up @@ -119,7 +224,7 @@ fun move(
}
}

private fun getStringOfValue(
private fun getStringOfValueBaseOfTarget(
target: AssignableExpression,
interpreterCore: InterpreterCore,
value: Expression
Expand Down Expand Up @@ -274,4 +379,33 @@ private fun stringToValue(value: String, type: Type): Value {

else -> throw UnsupportedOperationException("MOVE/MOVEL not supported for the type: $type")
}
}

/**
* Sets an element in the `ArrayValue` array at the specified `index`, transforming
* `sourceValueAsString` and applying it to the target based on `MOVEL` operation rules and
* `targetType`. If `operationExtender` is provided, it enables the "clear" functionality in the
* `MOVEL` operation, allowing existing values to be cleared before setting the new value.
*
* @param index The position in the array where the element will be set.
* @param sourceValueAsString The new source value as a string to be assigned after transformation.
* @param targetValueAsString The current value at `index`, used as a basis for `MOVEL` transformation.
* @param targetType The `Type` of the target element, ensuring type compatibility during the assignment.
* @param operationExtender Optional flag that, if non-null, enables the "clear" functionality within
* `MOVEL`, allowing `targetValueAsString` to be cleared before `sourceValueAsString` is assigned.
*
* @throws IndexOutOfBoundsException if `index` is outside the bounds of the array.
*/
private fun ArrayValue.setElement(index: Int, sourceValueAsString: String, targetValueAsString: String, targetType: Type, operationExtender: String?) {
this.setElement(
index, stringToValue(
movel(
sourceValueAsString,
targetValueAsString,
targetType,
operationExtender != null
),
targetType
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,75 @@ open class MULANGT10BaseCodopTest : MULANGTTest() {
val expected = listOf("OK")
assertEquals(expected, "smeup/MUDRNRAPU00134".outputOf(configuration = smeupConfig))
}

/**
* MOVEL an integer array to another. The size of first is lower than destination.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00135() {
val expected = listOf("1", "1", "1", "1", "1", "1", "2", "2")
assertEquals(expected, "smeup/MUDRNRAPU00135".outputOf(configuration = smeupConfig))
}

/**
* MOVEL an integer array to another. The size of first is greater than destination.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00136() {
val expected = listOf("1", "1", "1", "1", "1", "1", "1", "1")
assertEquals(expected, "smeup/MUDRNRAPU00136".outputOf(configuration = smeupConfig))
}

/**
* MOVEL a decimal array to integer.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00137() {
val expected = listOf("1.200", "1.200", "1.200", "1200", "1200", "1200", "2", "2")
assertEquals(expected, "smeup/MUDRNRAPU00137".outputOf(configuration = smeupConfig))
}

/**
* MOVEL an integer array to decimal.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00138() {
val expected = listOf("1", "1", "1", ".001", ".001", ".001", "2.200", "2.200")
assertEquals(expected, "smeup/MUDRNRAPU00138".outputOf(configuration = smeupConfig))
}

/**
* MOVEL a decimal array to integer. The number of digits of first are greater than second.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00139() {
val expected = listOf("12.345", "12.345", "12.345", "123", "123", "123", "9", "9")
assertEquals(expected, "smeup/MUDRNRAPU00139".outputOf(configuration = smeupConfig))
}

/**
* MOVEL a decimal array to integer. The number of digits of first are greater than second.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00140() {
val expected = listOf("123", "123", "123", "12.300", "12.300", "12.300", "9.900", "9.900")
assertEquals(expected, "smeup/MUDRNRAPU00140".outputOf(configuration = smeupConfig))
}

/**
* MOVEL an integer array to another. The size of first is lower than destination. In this case the target
* is a DS array.
* @see #LS24004606
*/
@Test
fun executeMUDRNRAPU00147() {
val expected = listOf("1", "1", "1", "1", "1", "1", "2", "2")
assertEquals(expected, "smeup/MUDRNRAPU00147".outputOf(configuration = smeupConfig))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
V* ==============================================================
V* 24/10/2024 APU001 Creation
V* 29/10/2024 APU001 Improvements
V* ==============================================================
O * PROGRAM GOAL
O * MOVEL an integer array to another. The size of first is lower
O * than destination.
V* ==============================================================
O * JARIKO ANOMALY
O * Before the fix, the error occurred was
O * `Cannot set an array as factor 2 in MOVEL/MOVEL(P) statement'
V* ==============================================================
D ARR_1 S 1 0 DIM(3) INZ(1)
D ARR_2 S 1 0 DIM(5) INZ(2)
D TMP S 2
D COUNT S 2 0 INZ(1)

C FOR COUNT=1 TO %ELEM(ARR_1)
C EVAL TMP=%CHAR(ARR_1(COUNT))
C TMP DSPLY
C ENDFOR

C MOVEL ARR_1 ARR_2 #Cannot set an array as factor 2 in MOVEL/MOVEL(P) statement

C FOR COUNT=1 TO %ELEM(ARR_2)
C EVAL TMP=%CHAR(ARR_2(COUNT))
C TMP DSPLY
C ENDFOR

C SETON LR
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
V* ==============================================================
V* 24/10/2024 APU001 Creation
V* 29/10/2024 APU001 Improvements
V* ==============================================================
O * PROGRAM GOAL
O * MOVEL an integer array to another. The size of first is
O * greater than destination.
V* ==============================================================
O * JARIKO ANOMALY
O * Before the fix, the error occurred was
O * `Cannot set an array as factor 2 in MOVEL/MOVEL(P) statement'
V* ==============================================================
D ARR_1 S 1 0 DIM(5) INZ(1)
D ARR_2 S 1 0 DIM(3) INZ(2)
D TMP S 2
D COUNT S 2 0 INZ(1)

C FOR COUNT=1 TO %ELEM(ARR_1)
C EVAL TMP=%CHAR(ARR_1(COUNT))
C TMP DSPLY
C ENDFOR

C MOVEL ARR_1 ARR_2 #Cannot set an array as factor 2 in MOVEL/MOVEL(P) statement

C FOR COUNT=1 TO %ELEM(ARR_2)
C EVAL TMP=%CHAR(ARR_2(COUNT))
C TMP DSPLY
C ENDFOR

C SETON LR
Loading

0 comments on commit 200f126

Please sign in to comment.