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/LS24004606/MOVEL Array to Array #647

Merged
merged 16 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
33b073e
Implemented tests
davidepalladino-apuliasoft Oct 24, 2024
64f89b1
First refactoring about `movel` for next implementation about array t…
davidepalladino-apuliasoft Oct 24, 2024
6b0695c
Applied fix for `MUDRNRAPU00135`, `MUDRNRAPU00136`
davidepalladino-apuliasoft Oct 25, 2024
e4f8ccb
Applied fix for `MUDRNRAPU00137`
davidepalladino-apuliasoft Oct 25, 2024
e5d8c4d
Typo result for `executeMUDRNRAPU00138`
davidepalladino-apuliasoft Oct 25, 2024
b93219f
Typo exception for `ArrayType` in `move` function
davidepalladino-apuliasoft Oct 25, 2024
da623f4
Applied refactoring for more readable and maintenance
davidepalladino-apuliasoft Oct 25, 2024
96efce6
Applied important fix for `if` in `movel`
davidepalladino-apuliasoft Oct 25, 2024
b48e6a0
Merge branch 'develop' into bugfix/LS24004606/movel-array-to-array
davidepalladino-apuliasoft Oct 25, 2024
0ed4298
Prepared `executeMUDRNRAPU00147` where is tested a target as DS array.
davidepalladino-apuliasoft Oct 28, 2024
6bfc4a4
Fixed case od DS array
davidepalladino-apuliasoft Oct 28, 2024
d7a8278
Merge branch 'develop' into bugfix/LS24004606/movel-array-to-array
lanarimarco Oct 29, 2024
eb770ef
Improved tests
davidepalladino-apuliasoft Oct 29, 2024
e1cb5ed
Merge remote-tracking branch 'origin/bugfix/LS24004606/movel-array-to…
davidepalladino-apuliasoft Oct 29, 2024
a36686a
Updated header of tests
davidepalladino-apuliasoft Oct 29, 2024
f507148
Merge branch 'develop' into bugfix/LS24004606/movel-array-to-array
davidepalladino-apuliasoft Oct 29, 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 @@ -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