Skip to content

Commit

Permalink
Merge pull request #704 from smeup/bugfix/LS25000567/movea-from-s-to-ds
Browse files Browse the repository at this point in the history
Bugfix/LS25000567/`MOVEA` from `S` to `DS`
  • Loading branch information
lanarimarco authored Feb 11, 2025
2 parents 46bde13 + de869b2 commit 63d7b27
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ fun movea(operationExtenter: String?, target: AssignableExpression, valueExpress

private fun moveaFullArray(operationExtenter: String?, target: DataRefExpr, value: Expression, startIndex: Int, interpreterCore: InterpreterCore): Value {
val targetType = target.type()
require(targetType is ArrayType || targetType is StringType) {
"Result must be an Array or a String"
require(targetType is ArrayType || targetType is StringType || targetType is DataStructureType) {
"Result must be an Array, String or a DS"
}
return if (value is FigurativeConstantRef) {
interpreterCore.assign(target, interpreterCore.eval(value))
Expand All @@ -43,6 +43,7 @@ private fun moveaFullArray(operationExtenter: String?, target: DataRefExpr, valu
val computedValue = when (type) {
is StringType -> moveaString(operationExtenter, target, startIndex, interpreterCore, value)
is NumberType -> moveaNumber(operationExtenter, target, startIndex, interpreterCore, value)
is DataStructureType -> moveaDataStructure(operationExtenter, target, startIndex, interpreterCore, value)
else -> TODO()
}
interpreterCore.assign(target, computedValue)
Expand Down Expand Up @@ -76,12 +77,68 @@ private fun moveaNumber(
IntValue.ZERO
}
}
internalCoercing(elementValue, targetArray.elementType, newValue.elementType)

if (newValue.elementType is NumberType && targetArray.elementType is NumberType) {
numberCoercing(elementValue, targetArray.elementType as NumberType, newValue.elementType as NumberType)
} else {
elementValue
}
}
}
return arrayValue
}

/**
* Moves data into a Data Structure.
*
* This function takes a portion of a string value (provided as an `Expression`) and inserts it into a
* `DataStructValue` at a specified position. It handles cases where the input string is longer than
* the available space in the target Data Structure by truncating the input string.
*
* The function performs the following steps:
* 1. Evaluates the `target` `DataRefExpr` to obtain the `DataStructValue` to modify.
* 2. Evaluates the `value` `Expression` to obtain the string value to insert (converting if necessary).
* 3. Checks for a specific type mismatch: If the `value` is a `DataRefExpr` referring to an array of numbers,
* an `IllegalStateException` is thrown. This likely represents a domain-specific constraint.
* 4. Determines the end index for the substring to be inserted, taking into account the length of the
* input string and the available space in the target `DataStructValue`.
* 5. Extracts the appropriate substring from the input string.
* 6. Uses the `setSubstring` method of the `DataStructValue` to insert the substring at the correct position.
* 7. Returns the modified `DataStructValue`.
*
* @param operationExtender (Optional) A string representing an operation extender. Its purpose is not clear from the code and requires further context.
* @param target The `DataRefExpr` representing the target `DataStructValue` to modify.
* @param startIndex The starting index (1-based) within the `DataStructValue` where the substring should be inserted.
* @param interpreterCore The `InterpreterCore` instance used to evaluate expressions.
* @param value The `Expression` representing the string value to be inserted.
* @return The modified `DataStructValue`.
* @throws IllegalStateException If the `value` is a `DataRefExpr` referring to an array of numbers.
* @see DataStructValue.setSubstring
*/
private fun moveaDataStructure(
operationExtender: String?,
target: DataRefExpr,
startIndex: Int,
interpreterCore: InterpreterCore,
value: Expression
): DataStructValue {
if (value is DataRefExpr && value.variable.referred?.type is ArrayType && (value.variable.referred?.type as ArrayType).element is NumberType) {
throw IllegalStateException("You cannot move a numeric array into a DS: ${value.render()} (${value.position})")
}

val targetValue: DataStructValue = interpreterCore.eval(target) as DataStructValue
var newValue: StringValue = interpreterCore.eval(value).asString()
var endIndex: Int = newValue.length()

if (endIndex > targetValue.len) {
endIndex = targetValue.len
newValue = newValue.getSubstring(startIndex - 1, endIndex)
}

targetValue.setSubstring(startIndex - 1, endIndex, newValue)
return targetValue
}

private fun InterpreterCore.toArray(expression: Expression, targetType: Type): ArrayValue =
when (expression) {
is ArrayAccessExpr -> {
Expand Down Expand Up @@ -167,9 +224,9 @@ private fun valueFromSourceExpression(interpreterCore: InterpreterCore, valueExp
* the conversion.
*
* @param sourceValue The `Value` to be coerced, generally a numeric type such as `DecimalValue` or `IntValue`.
* @param targetType The target `Type` for the coercion, used to guide the conversion, particularly
* @param targetType The target `NumberType` for the coercion, used to guide the conversion, particularly
* regarding the number of decimal places.
* @param sourceType The `Type` representing the original type of `sourceValue`, assisting in determining
* @param sourceType The `NumberType` representing the original type of `sourceValue`, assisting in determining
* the required scale and format for conversion.
*
* @return The resulting `Value` after coercion, transformed to align with `targetType`.
Expand All @@ -179,22 +236,22 @@ private fun valueFromSourceExpression(interpreterCore: InterpreterCore, valueExp
* @throws ClassCastException if `sourceValue` is not a `DecimalValue` or `IntValue`, or if `targetType`
* or `sourceType` are not of numeric types.
*/
private fun internalCoercing(
private fun numberCoercing(
sourceValue: Value,
targetType: Type,
sourceType: Type
targetType: NumberType,
sourceType: NumberType
): Value {
// Number of digits between source and target must be equals.
if (sourceType is NumberType && targetType is NumberType && sourceType.numberOfDigits != targetType.numberOfDigits) {
if (sourceType.numberOfDigits != targetType.numberOfDigits) {
throw IllegalStateException("Factor 2 and Result with different type and size.")
}

return when {
// Proper conversion between a left side as decimal to right side as integer
sourceValue is DecimalValue && sourceType is NumberType && targetType is NumberType && targetType.decimalDigits == 0 ->
sourceValue is DecimalValue && targetType.decimalDigits == 0 ->
DecimalValue(sourceValue.value * 10.0.pow(targetType.entireDigits - sourceType.entireDigits).toBigDecimal())
// Or integer to decimal
sourceValue is IntValue && targetType is NumberType && targetType.decimalDigits > 0 ->
sourceValue is IntValue && targetType.decimalDigits > 0 ->
DecimalValue((sourceValue.value / 10.0.pow(targetType.decimalDigits)).toBigDecimal())
else -> sourceValue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,8 @@ data class ConcreteArrayValue(val elements: MutableList<Value>, override val ele
override fun takeLast(n: Int): Value = takeAll().takeLast(n)

override fun takeFirst(n: Int): Value = takeAll().takeFirst(n)

override fun asString(): StringValue = takeAll().asString()
}

object BlanksValue : Value {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,18 @@ class JarikoCallbackTest : AbstractTest() {
executeSourceLineTest("ERROR51")
}

@Test
fun executeERROR52CallBackTest() {
executePgmCallBackTest("ERROR52", SourceReferenceType.Program, "ERROR52", mapOf(
13 to "You cannot move a numeric array into a DS: SCAATT (Position(start=Line 13, Column 35, end=Line 13, Column 41))"
))
}

@Test
fun executeERROR52SourceLineTest() {
executeSourceLineTest("ERROR52")
}

@Test
fun executeERROR53CallBackTest() {
executePgmCallBackTest("ERROR53", SourceReferenceType.Copy, "QILEGEN,£PDS", listOf(130))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -958,4 +958,34 @@ open class MULANGT10BaseCodopTest : MULANGTTest() {
val expected = listOf("0123456789", "", "0123456789", "0123456789")
assertEquals(expected, "smeup/MUDRNRAPU00197".outputOf())
}

/**
* MOVEA from S, defined as array, to DS.
* @see #LS25000567
*/
@Test
fun executeMUDRNRAPU00198() {
val expected = listOf("0123456789", "AAAAAAAAAA", "AAAAAAAAAA", "AAAAAAAAAA")
assertEquals(expected, "smeup/MUDRNRAPU00198".outputOf())
}

/**
* MOVEA from S, defined as array, to DS. The array size is lower than DS.
* @see #LS25000567
*/
@Test
fun executeMUDRNRAPU00199() {
val expected = listOf("0123456789", "AAAAAAAA", "AAAAAAAA89", "AAAAAAAA")
assertEquals(expected, "smeup/MUDRNRAPU00199".outputOf())
}

/**
* MOVEA from S, defined as array, to DS. The array size is greater than DS.
* @see #LS25000567
*/
@Test
fun executeMUDRNRAPU001100() {
val expected = listOf("0123456789", "AAAAAAAAAAAA", "AAAAAAAAAA", "AAAAAAAAAAAA")
assertEquals(expected, "smeup/MUDRNRAPU001100".outputOf())
}
}
15 changes: 15 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/ERROR52.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
V* ==============================================================
D* 06/02/25
D* Purpose: Must fire the following errors during execution of
D* `C MOVEA SCAATT SCAATTDS`:
D* line 13 - "You cannot move a numeric array into a DS"
D* because isn't possible to assign Standalone defined
D* as number to a DS.
V* ==============================================================
D SCAATTDS DS 10
D SCAATT S 1 0 DIM(10) INZ('A')

C EVAL SCAATTDS='0123456789'
C MOVEA SCAATT SCAATTDS

C SETON LR
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
V* ==============================================================
V* 06/02/2025 APU001 Creation
V* ==============================================================
O * PROGRAM GOAL
O * MOVEA from S, defined as array, to DS.
O * The array size is greater than DS.
V* ==============================================================
D SCAATTDS DS 10
D SCAATTSTR S 12
D SCAATT S 1 DIM(12) INZ('A')
D I S 2 0

C EVAL SCAATTDS='0123456789'
C EXSR SHOW

C MOVEA SCAATT SCAATTDS
C EXSR SHOW

C SETON LR



C SHOW BEGSR

C SCAATTDS DSPLY
C CLEAR SCAATTSTR
C FOR I = 1 TO 12
C EVAL SCAATTSTR=%TRIM(SCAATTSTR)+
C %CHAR(SCAATT(I))
C ENDFOR
C SCAATTSTR DSPLY
C ENDSR
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
V* ==============================================================
V* 06/02/2025 APU001 Creation
V* ==============================================================
O * PROGRAM GOAL
O * MOVEA from S, defined as array, to DS.
V* ==============================================================
O * JARIKO ANOMALY
O * Error occurred: 'Result must be an Array or a String'.
V* ==============================================================
D SCAATTDS DS 10
D SCAATTSTR S 10
D SCAATT S 1 DIM(10) INZ('A')
D I S 2 0

C EVAL SCAATTDS='0123456789'
C EXSR SHOW

C MOVEA SCAATT SCAATTDS #Result must be an Array or a String
C EXSR SHOW

C SETON LR



C SHOW BEGSR

C SCAATTDS DSPLY
C CLEAR SCAATTSTR
C FOR I = 1 TO 10
C EVAL SCAATTSTR=%TRIM(SCAATTSTR)+
C %CHAR(SCAATT(I))
C ENDFOR
C SCAATTSTR DSPLY
C ENDSR
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
V* ==============================================================
V* 06/02/2025 APU001 Creation
V* ==============================================================
O * PROGRAM GOAL
O * MOVEA from S, defined as array, to DS.
O * The array size is lower than DS.
V* ==============================================================
D SCAATTDS DS 10
D SCAATTSTR S 8
D SCAATT S 1 DIM(8) INZ('A')
D I S 1 0

C EVAL SCAATTDS='0123456789'
C EXSR SHOW

C MOVEA SCAATT SCAATTDS
C EXSR SHOW

C SETON LR



C SHOW BEGSR

C SCAATTDS DSPLY
C CLEAR SCAATTSTR
C FOR I = 1 TO 8
C EVAL SCAATTSTR=%TRIM(SCAATTSTR)+
C %CHAR(SCAATT(I))
C ENDFOR
C SCAATTSTR DSPLY
C ENDSR

0 comments on commit 63d7b27

Please sign in to comment.