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

Feature/ls24003289/bif dec with time #563

Merged
merged 7 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -39,7 +39,8 @@ interface Evaluator {
fun eval(expression: LessEqualThanExpr): Value
fun eval(expression: LessThanExpr): Value
fun eval(expression: BlanksRefExpr): BlanksValue
fun eval(expression: DecExpr): Value
fun eval(expression: DecNumericExpr): Value
fun eval(expression: DecTimeExpr): Value
fun eval(expression: PlusExpr): Value
fun eval(expression: MinusExpr): Value
fun eval(expression: MultExpr): Value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class ExpressionEvaluation(

override fun eval(expression: BlanksRefExpr) = proxyLogging(expression) { BlanksValue } as BlanksValue

override fun eval(expression: DecExpr): Value = proxyLogging(expression) {
override fun eval(expression: DecNumericExpr): Value = proxyLogging(expression) {
val decDigits = expression.decDigits.evalWith(this).asInt().value
val valueAsString = expression.value.evalWith(this).asString().value
val valueAsBigDecimal = valueAsString.asBigDecimal()
Expand All @@ -165,6 +165,23 @@ class ExpressionEvaluation(
}
}

override fun eval(expression: DecTimeExpr): Value = proxyLogging(expression) {
val value = expression.timestamp.evalWith(this)
val format = expression.format

return@proxyLogging when (value) {
is TimeStampValue -> {
if (format != null) error("%DEC(time:format) is allowed only when time is DateValue")
value.toDecimal()
}
is DateValue -> {
if (format != null) TODO("%DEC(time{:format}) with custom format is not implemented yet")
value.toDecimal()
}
else -> error("%DEC(time{:format}) is allowed only when time is TimeStampValue or DateValue")
}
}

override fun eval(expression: PlusExpr): Value = proxyLogging(expression) {
val left = expression.left.evalWith(this)
val right = expression.right.evalWith(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ data class StringValue(var value: String, var varying: Boolean = false) : Abstra
return BooleanValue.FALSE
}

override fun asInt() = value.toInt().asValue()
override fun asDecimal() = value.toBigDecimal().asValue()

override fun asTimeStamp(): TimeStampValue = TimeStampValue.of(value)

fun setSubstring(startOffset: Int, endOffset: Int, substringValue: StringValue) {
Expand Down Expand Up @@ -459,6 +462,7 @@ data class TimeStampValue(@Contextual val value: LocalDateTime) : Value {

companion object {
val DEFAULT_FORMAT = "yyyy-MM-dd-HH.mm.ss.SSSSSS"
val DECIMAL_FORMAT = "yyyyMMddHHmmssSSSSSS"
val LOVAL: TimeStampValue by lazy {
TimeStampValue(LocalDateTime.parse("0001-01-01-00.00.00.000000", DateTimeFormatter.ofPattern(DEFAULT_FORMAT)))
}
Expand All @@ -480,6 +484,15 @@ data class TimeStampValue(@Contextual val value: LocalDateTime) : Value {
override fun asString(): StringValue {
return StringValue(DateTimeFormatter.ofPattern(DEFAULT_FORMAT).format(value))
}

fun format(pattern: String): StringValue {
val formatter = DateTimeFormatter.ofPattern(pattern)
return format(formatter)
}

fun format(formatter: DateTimeFormatter) = formatter.format(value).asValue()

fun toDecimal() = format(DECIMAL_FORMAT).asDecimal()
}

/**
Expand All @@ -489,6 +502,10 @@ data class TimeStampValue(@Contextual val value: LocalDateTime) : Value {
*/
@Serializable
data class DateValue(val value: Long, val format: DateFormat) : Value {
private val isoDate: String by lazy {
SimpleDateFormat("YYYY-MM-dd").format(Date(value))
}

override fun assignableTo(expectedType: Type): Boolean {
return expectedType is DateType
}
Expand All @@ -504,13 +521,27 @@ data class DateValue(val value: Long, val format: DateFormat) : Value {
* @return String with date formatted.
*/
fun adapt(format: DateFormat): String {
val dateISO = SimpleDateFormat("YYYY-MM-dd").format(Date(value))
return when (format) {
DateFormat.JUL -> {
LocalDate.parse(dateISO).format(DateTimeFormatter.ISO_ORDINAL_DATE)
LocalDate.parse(isoDate).format(DateTimeFormatter.ISO_ORDINAL_DATE)
.let { "${it.substring(2, 4)}/${it.substring(5)}" }
}
DateFormat.ISO -> dateISO
DateFormat.ISO -> isoDate
}
}

fun format(pattern: String): StringValue {
val formatter = DateTimeFormatter.ofPattern(pattern)
return format(formatter)
}

fun format(formatter: DateTimeFormatter) = formatter.format(LocalDate.parse(isoDate)).asValue()

fun toDecimal(): DecimalValue {
return when (format) {
DateFormat.JUL -> format("yyDDD").asDecimal()
DateFormat.ISO -> format("yyyyMMdd").asDecimal()
else -> TODO()
}
}
}
Expand Down Expand Up @@ -1077,6 +1108,7 @@ data class DataStructValue(var value: String, private val optionalExternalLen: I

fun Int.asValue() = IntValue(this.toLong())
fun Boolean.asValue() = BooleanValue(this)
fun BigDecimal.asValue() = DecimalValue(this)

fun areEquals(value1: Value, value2: Value): Boolean {
return when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.smeup.rpgparser.interpreter.Evaluator
import com.smeup.rpgparser.interpreter.Value
import com.strumenta.kolasu.model.Position
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient

/**
* For expressions with this interface there isn't resolution but will be called the callback `onMockExpression` and returned a null value.
Expand Down Expand Up @@ -254,22 +255,40 @@ data class RemExpr(
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %DEC
// Abstract %DEC definition
@Serializable
data class DecExpr(
var value: Expression,
var intDigits: Expression,
val decDigits: Expression,
abstract class DecExpr(
override val position: Position? = null
) : Expression(position) {
override val loggableEntityName: String
get() = "%DEC"
}

// %DEC with numeric value
@Serializable
data class DecNumericExpr(
var value: Expression,
var intDigits: Expression,
val decDigits: Expression,
@Transient override val position: Position? = null
) : DecExpr(position) {
override fun render(): String {
return this.value.render()
}
override fun evalWith(evaluator: Evaluator): Value = evaluator.eval(this)
}

// %DEC with timestamp value
@Serializable
data class DecTimeExpr(
var timestamp: Expression,
var format: Expression?,
@Transient override val position: Position? = null
) : DecExpr(position) {
override fun render() = timestamp.render()
override fun evalWith(evaluator: Evaluator) = evaluator.eval(this)
}

// %INT
@Serializable
data class IntExpr(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ private val modules = SerializersModule {
subclass(CharExpr::class)
subclass(CheckExpr::class)
subclass(DataRefExpr::class)
subclass(DecExpr::class)
subclass(DecNumericExpr::class)
subclass(DecTimeExpr::class)
subclass(DiffExpr::class)
subclass(DifferentThanExpr::class)
subclass(DivExpr::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,20 @@ internal fun RpgParser.Bif_charContext.toAst(conf: ToAstConfiguration = ToAstCon
}

internal fun RpgParser.Bif_decContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): DecExpr {
return DecExpr(
this.expression(0).toAst(conf),
this.expression(1).toAst(conf),
this.expression(2).toAst(conf),
toPosition(conf.considerPosition))
val position = toPosition(conf.considerPosition)

// Target is mandatory
val target = this.expression(0).toAst(conf)
return if (this.expression().size < 3) {
// %DEC(date time or timestamp expression {:format})
val format = kotlin.runCatching { this.expression(1) }.getOrNull()?.toAst(conf)
DecTimeExpr(target, format, position)
} else {
// %DEC(Numeric expression :digits : dec pos)
val digits = this.expression(1).toAst(conf)
val decPos = this.expression(2).toAst(conf)
DecNumericExpr(target, digits, decPos, position)
}
}

internal fun RpgParser.Bif_intContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): IntExpr {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,14 @@ open class MULANGT04EssentialsCodopAndBifTest : MULANGTTest() {
)
assertEquals(expected, "smeup/MUDRNRAPU00228".outputOf())
}

/**
* %DEC with dates and timestamps
* @see #LS24003289
*/
@Test
fun executeMUDRNRAPU00229() {
val expected = listOf("19700101000000000000", "19700101")
assertEquals(expected, "smeup/MUDRNRAPU00229".outputOf())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
D NTIMES S 20 0
D timestamp S Z INZ(Z'1970-01-01-00.00.00.000000')
D date S D INZ(D'1970-01-01')
C EVAL NTIMES=%DEC(timestamp)
C NTIMES DSPLY
C EVAL NTIMES=%DEC(date)
C NTIMES DSPLY