Skip to content

Commit

Permalink
Merge pull request #600 from smeup/bugfix/LS24003807/s-to-ds
Browse files Browse the repository at this point in the history
Bugfix/ls24003807/S to DS with positive number
  • Loading branch information
lanarimarco authored Aug 30, 2024
2 parents b1b0dba + dab20f1 commit fb9f97f
Show file tree
Hide file tree
Showing 14 changed files with 358 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.smeup.rpgparser.interpreter

import com.smeup.rpgparser.parsing.parsetreetoast.RpgType
import com.smeup.rpgparser.parsing.parsetreetoast.isNumber
import com.smeup.rpgparser.utils.repeatWithMaxSize
import java.math.BigDecimal
import java.math.RoundingMode
Expand Down Expand Up @@ -133,6 +134,9 @@ private fun coerceString(value: StringValue, type: Type): Value {
DecimalValue(BigDecimal.ZERO)
}
}
type.rpgType == RpgType.PACKED.rpgType && value.value.isNumber() -> {
throw UnsupportedOperationException("Cannot coerce `${value.value}` to $type.")
}
else -> {
if (!value.isBlank()) {
val intValue = decodeFromDS(value.value.trim(), type.entireDigits, type.decimalDigits)
Expand All @@ -144,12 +148,18 @@ private fun coerceString(value: StringValue, type: Type): Value {
}
} else {
if (!value.isBlank()) {
if (type.rpgType == RpgType.ZONED.rpgType) {
val decimalValue = decodeFromZoned(value.value.trim(), type.entireDigits, type.decimalDigits)
DecimalValue(decimalValue)
} else {
val decimalValue = decodeFromDS(value.value.trim(), type.entireDigits, type.decimalDigits)
DecimalValue(decimalValue)
when {
type.rpgType == RpgType.ZONED.rpgType -> {
val decimalValue = decodeFromZoned(value.value.trim(), type.entireDigits, type.decimalDigits)
DecimalValue(decimalValue)
}
type.rpgType == RpgType.PACKED.rpgType && value.value.isNumber() -> {
throw UnsupportedOperationException("Cannot coerce `${value.value}` to $type.")
}
else -> {
val decimalValue = decodeFromDS(value.value.trim(), type.entireDigits, type.decimalDigits)
DecimalValue(decimalValue)
}
}
} else {
DecimalValue(BigDecimal.ZERO)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ private fun valueToString(value: Value, type: Type): String {

is BooleanType -> return s

is DataStructureType -> return s

else -> throw UnsupportedOperationException("MOVE/MOVEL not supported for the type: $type")
}
}
Expand Down Expand Up @@ -256,6 +258,10 @@ private fun stringToValue(value: String, type: Type): Value {

is BooleanType -> return StringValue(value)

is DataStructureType -> {
return DataStructValue(value)
}

else -> throw UnsupportedOperationException("MOVE/MOVEL not supported for the type: $type")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,29 @@ data class DataStructValue(var value: String, private val optionalExternalLen: I
} else if (data.declaredArrayInLine != null) {
ProjectedArrayValue.forData(this, data)
} else {
coerce(this.getSubstring(data.startOffset, data.endOffset), data.type)
val substring = this.getSubstring(data.startOffset, data.endOffset)
if (data.type is NumberType && !checkNumberSyntax(substring.value, data.type)) {
throw UnsupportedOperationException("Cannot coerce sub-string `${substring.value}` to ${data.type}.")
}
coerce(substring, data.type)
}
}

/**
* On AS400 for DS there are some syntax rule for number. In example,
* ZONED number with at least space at the end is not allowed.
* This function provides to check if the number follow these requirements.
* @param value to check.
* @param type necessary for checking based of `RpgType`
* @return true if the `value` follows the syntax.
*/
fun checkNumberSyntax(
value: String,
type: NumberType
): Boolean {
return when {
type.rpgType == RpgType.ZONED.rpgType -> value.trimEnd().length == value.length
else -> true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,8 @@ internal fun String.isDecimal() = this.toDoubleOrNull() != null

internal fun String.toDecimal() = this.toDouble()

internal fun String.isNumber() = this.isInt() || this.isDecimal()

internal fun ParserRuleContext.rContext(): RContext {
return if (this.parent == null) {
this as RContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,9 @@ abstract class AbstractTest {
Pair(it.value, it.contains(lines))
}
Assert.assertTrue(
"Errors doesn't correspond:\n" + found.joinToString(separator = "\n") { it.first },
"Errors don't correspond.\n" +
"Actual: ${found.joinToString(separator = "\n\\") { it.first }}\n" +
"Expected: ${lines.map { it.value }.joinToString(separator = "\n\\") { it } }\n",
found.size == found.filter { it.second }.size && found.size == lines.size
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,66 @@ class JarikoCallbackTest : AbstractTest() {
executeSourceLineTest(pgm = "ERROR38")
}

@Test
fun executeERROR39CallBackTest() {
executePgmCallBackTest("ERROR39", SourceReferenceType.Program, "ERROR39", mapOf(
24 to "Cannot coerce `00520` to NumberType(entireDigits=7, decimalDigits=2, rpgType=P)."
))
}

@Test
fun executeERROR39SourceLineTest() {
executeSourceLineTest("ERROR39")
}

@Test
fun executeERROR40CallBackTest() {
executePgmCallBackTest("ERROR40", SourceReferenceType.Program, "ERROR40", mapOf(
24 to "Cannot coerce `00520` to NumberType(entireDigits=7, decimalDigits=2, rpgType=P)."
))
}

@Test
fun executeERROR40SourceLineTest() {
executeSourceLineTest("ERROERROR40R41")
}

@Test
fun executeERROR41CallBackTest() {
executePgmCallBackTest("ERROR41", SourceReferenceType.Program, "ERROR41", mapOf(
24 to "Cannot coerce sub-string `0052 ` to NumberType(entireDigits=3, decimalDigits=2, rpgType=S)."
))
}

@Test
fun executeERROR41SourceLineTest() {
executeSourceLineTest("ERROR41")
}

@Test
fun executeERROR42CallBackTest() {
executePgmCallBackTest("ERROR42", SourceReferenceType.Program, "ERROR42", mapOf(
24 to "Cannot coerce sub-string `0052 ` to NumberType(entireDigits=3, decimalDigits=2, rpgType=S)."
))
}

@Test
fun executeERROR42SourceLineTest() {
executeSourceLineTest("ERROR42")
}

@Test
fun executeERROR43CallBackTest() {
executePgmCallBackTest("ERROR43", SourceReferenceType.Program, "ERROR43", mapOf(
23 to "Cannot coerce sub-string `0005 ` to NumberType(entireDigits=5, decimalDigits=0, rpgType=S)."
))
}

@Test
fun executeERROR43SourceLineTest() {
executeSourceLineTest("ERROR43")
}

@Test
fun bypassSyntaxErrorTest() {
val configuration = Configuration().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ open class MULANGT04EssentialsCodopAndBifTest : MULANGTTest() {
}

/**
*Assigns content of DS to a String not VARYING in EVAL
* Assigns content of DS to a String not VARYING in EVAL
* @see #LS24003679
*/
@Test
Expand All @@ -45,7 +45,7 @@ open class MULANGT04EssentialsCodopAndBifTest : MULANGTTest() {
}

/**
*Assigns content of DS to a String VARYING in EVAL
* Assigns content of DS to a String VARYING in EVAL
* @see #LS24003679
*/
@Test
Expand All @@ -55,7 +55,7 @@ open class MULANGT04EssentialsCodopAndBifTest : MULANGTTest() {
}

/**
*Assigns content of DS to a String not VARYING in EVAL where, size of DS is greater than String
* Assigns content of DS to a String not VARYING in EVAL where, size of DS is greater than String
* @see #LS24003755
*/
@Test
Expand All @@ -65,7 +65,7 @@ open class MULANGT04EssentialsCodopAndBifTest : MULANGTTest() {
}

/**
*Assigns content of DS to a String not VARYING in EVAL where, size of DS is greater than String
* Assigns content of DS to a String not VARYING in EVAL where, size of DS is greater than String
* @see #LS24003755
*/
@Test
Expand All @@ -74,6 +74,28 @@ open class MULANGT04EssentialsCodopAndBifTest : MULANGTTest() {
assertEquals(expected, "smeup/MUDRNRAPU00107".outputOf())
}

/**
* Assigns content of String to a DS with type check and coercion between substring and destination field.
* In this test is used `MOVEL`
* @see #LS24003807
*/
@Test
fun executeMUDRNRAPU00108() {
val expected = listOf("Lorem ipsum dolor si", "t amet, consectetuer", "5", "5.20", "Lorem ipsum dolor si", "t amet, consectetuer", "5", "5.20")
assertEquals(expected, "smeup/MUDRNRAPU00108".outputOf())
}

/**
* Assigns content of String to a DS with type check and coercion between substring and destination field.
* In this test is used `EVAL`
* @see #LS24003807
*/
@Test
fun executeMUDRNRAPU00109() {
val expected = listOf("Lorem ipsum dolor si", "t amet, consectetuer", "5", "5.20", "Lorem ipsum dolor si", "t amet, consectetuer", "5", "5.20")
assertEquals(expected, "smeup/MUDRNRAPU00109".outputOf())
}

/**
* %DIFF with several DurationCodes
* @see #LS24003282
Expand Down
26 changes: 26 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/ERROR39.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
V* ==============================================================
D* 26/08/24
D* Purpose: Must fire the following errors during execution of
D* `MOVEL` from `S` to `DS`.
D* line 24 - Wrong value.
V* ==============================================================
D A40_A50 S 50
D A40_DS1 DS
D A40_DS1_F1 1 20
D A40_DS1_F2 21 40
D A40_DS1_F3 41 45 0
D A40_DS1_F4 46 50P 2

D A40_DS2_F4_S S 20

C EVAL A40_A50 = 'Lorem ipsum dolor si'
C + 't amet, consectetuer'
C + '0000500520'

C MOVEL A40_A50 A40_DS1
C A40_DS1_F1 DSPLY
C A40_DS1_F2 DSPLY
C A40_DS1_F3 DSPLY
C EVAL A40_DS2_F4_S=%CHAR(A40_DS1_F4)
V* Cannot assign Zoned number as Packed
C A40_DS2_F4_S DSPLY
26 changes: 26 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/ERROR40.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
V* ==============================================================
D* 26/08/24
D* Purpose: Must fire the following errors during execution of
D* `EVAL` from `S` to `DS`.
D* line 24 - Wrong value.
V* ==============================================================
D A40_A50 S 50
D A40_DS1 DS
D A40_DS1_F1 1 20
D A40_DS1_F2 21 40
D A40_DS1_F3 41 45 0
D A40_DS1_F4 46 50P 2

D A40_DS2_F4_S S 20

C EVAL A40_A50 = 'Lorem ipsum dolor si'
C + 't amet, consectetuer'
C + '0000500520'

C EVAL A40_DS1=A40_A50
C A40_DS1_F1 DSPLY
C A40_DS1_F2 DSPLY
C A40_DS1_F3 DSPLY
C EVAL A40_DS2_F4_S=%CHAR(A40_DS1_F4)
V* Cannot assign Zoned number as Packed
C A40_DS2_F4_S DSPLY
26 changes: 26 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/ERROR41.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
V* ==============================================================
D* 26/08/24
D* Purpose: Must fire the following errors during execution of
D* `MOVEL` from `S` to `DS`.
D* line 24 - Wrong value.
V* ==============================================================
D A40_A50 S 50
D A40_DS1 DS
D A40_DS1_F1 1 20
D A40_DS1_F2 21 40
D A40_DS1_F3 41 45 0
D A40_DS1_F4 46 50S 2

D A40_DS2_F4_S S 20

C EVAL A40_A50 = 'Lorem ipsum dolor si'
C + 't amet, consectetuer'
C + ' 0050052 '

C MOVEL A40_A50 A40_DS1
C A40_DS1_F1 DSPLY
C A40_DS1_F2 DSPLY
C A40_DS1_F3 DSPLY
C EVAL A40_DS2_F4_S=%CHAR(A40_DS1_F4)
V* Is not possible to assign a number with at least one blank char at the end.
C A40_DS2_F4_S DSPLY
26 changes: 26 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/ERROR42.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
V* ==============================================================
D* 26/08/24
D* Purpose: Must fire the following errors during execution of
D* `EVAL` from `S` to `DS`.
D* line 24 - Wrong value.
V* ==============================================================
D A40_A50 S 50
D A40_DS1 DS
D A40_DS1_F1 1 20
D A40_DS1_F2 21 40
D A40_DS1_F3 41 45 0
D A40_DS1_F4 46 50S 2

D A40_DS2_F4_S S 20

C EVAL A40_A50 = 'Lorem ipsum dolor si'
C + 't amet, consectetuer'
C + ' 0050052 '

C EVAL A40_DS1=A40_A50
C A40_DS1_F1 DSPLY
C A40_DS1_F2 DSPLY
C A40_DS1_F3 DSPLY
C EVAL A40_DS2_F4_S=%CHAR(A40_DS1_F4)
V* Is not possible to assign a number with at least one blank char at the end.
C A40_DS2_F4_S DSPLY
26 changes: 26 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/ERROR43.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
V* ==============================================================
D* 26/08/24
D* Purpose: Must fire the following errors during execution of
D* `EVAL` from `S` to `DS`.
D* line 24 - Wrong value.
V* ==============================================================
D A40_A50 S 50
D A40_DS1 DS
D A40_DS1_F1 1 20
D A40_DS1_F2 21 40
D A40_DS1_F3 41 45 0
D A40_DS1_F4 46 50S 2

D A40_DS2_F4_S S 20

C EVAL A40_A50 = 'Lorem ipsum dolor si'
C + 't amet, consectetuer'
C + '0005 00520'

C EVAL A40_DS1=A40_A50
C A40_DS1_F1 DSPLY
C A40_DS1_F2 DSPLY
C A40_DS1_F3 DSPLY
V* Is not possible to assign a number with at least one blank char at the end.
C EVAL A40_DS2_F4_S=%CHAR(A40_DS1_F4)
C A40_DS2_F4_S DSPLY
Loading

0 comments on commit fb9f97f

Please sign in to comment.