diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/coercing.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/coercing.kt index 9d79a9e5f..854691d9a 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/coercing.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/coercing.kt @@ -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") } } diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/movel.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/movel.kt index f928e6e10..2e94ca541 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/movel.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/movel.kt @@ -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) { @@ -25,6 +26,29 @@ 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, @@ -32,35 +56,118 @@ fun movel( 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, @@ -70,8 +177,6 @@ fun movel( ) ) } - } else { - return interpreterCore.assign(target, interpreterCore.eval(value)) } } @@ -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 @@ -119,7 +224,7 @@ fun move( } } -private fun getStringOfValue( +private fun getStringOfValueBaseOfTarget( target: AssignableExpression, interpreterCore: InterpreterCore, value: Expression @@ -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 + ) + ) } \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt index bc5ac505b..54550c9ff 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT10BaseCodopTest.kt @@ -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)) + } } \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00135.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00135.rpgle new file mode 100644 index 000000000..5da211d2f --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00135.rpgle @@ -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 \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00136.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00136.rpgle new file mode 100644 index 000000000..4b3d205ff --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00136.rpgle @@ -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 \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00137.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00137.rpgle new file mode 100644 index 000000000..6e1a7940c --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00137.rpgle @@ -0,0 +1,29 @@ + V* ============================================================== + V* 24/10/2024 APU001 Creation + V* 29/10/2024 APU001 Improvements + V* ============================================================== + O * PROGRAM GOAL + O * MOVEL a decimal array to integer. + 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 5 3 DIM(3) INZ(1.2) + D ARR_2 S 5 0 DIM(5) INZ(2) + D TMP S 5 + 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 \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00138.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00138.rpgle new file mode 100644 index 000000000..9a222a8a8 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00138.rpgle @@ -0,0 +1,29 @@ + V* ============================================================== + V* 24/10/2024 APU001 Creation + V* 29/10/2024 APU001 Improvements + V* ============================================================== + O * PROGRAM GOAL + O * MOVEL a decimal array to integer. + 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 5 0 DIM(3) INZ(1) + D ARR_2 S 5 3 DIM(5) INZ(2.2) + D TMP S 5 + 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 \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00139.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00139.rpgle new file mode 100644 index 000000000..b44f226cf --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00139.rpgle @@ -0,0 +1,30 @@ + V* ============================================================== + V* 24/10/2024 APU001 Creation + V* 29/10/2024 APU001 Improvements + V* ============================================================== + O * PROGRAM GOAL + O * MOVEL a decimal array to integer. The number of digits + O * of first are greater than second. + 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 5 3 DIM(3) INZ(12.345) + D ARR_2 S 3 0 DIM(5) INZ(9) + D TMP S 7 + 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 \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00140.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00140.rpgle new file mode 100644 index 000000000..35db2ef30 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00140.rpgle @@ -0,0 +1,30 @@ + V* ============================================================== + V* 24/10/2024 APU001 Creation + V* 29/10/2024 APU001 Improvements + V* ============================================================== + O * PROGRAM GOAL + O * MOVEL a integer array to decimal. The number of digits + O * of first are lower than second. + 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 3 0 DIM(3) INZ(123) + D ARR_2 S 5 3 DIM(5) INZ(9.9) + D TMP S 7 + 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 \ No newline at end of file diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00147.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00147.rpgle new file mode 100644 index 000000000..5e36c45b4 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00147.rpgle @@ -0,0 +1,31 @@ + V* ============================================================== + V* 28/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. In this case the target is a DS array. + 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 DS_1 DS + D DS_ARR_1 1 0 DIM(5) INZ(2) + D TMP S 7 + 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 DS_ARR_1 + + C FOR COUNT=1 TO %ELEM(DS_ARR_1) + C EVAL TMP=%CHAR(DS_ARR_1(COUNT)) + C TMP DSPLY + C ENDFOR + + C SETON LR \ No newline at end of file