fixed Decimal conversion bug

This commit is contained in:
Sergey Chernov 2026-03-29 03:35:53 +03:00
parent aa9565b40b
commit c3c0a3292b
3 changed files with 109 additions and 8 deletions

View File

@ -88,7 +88,7 @@ object ObjDecimalSupport {
}
decimalClass.addClassFn("fromInt") {
val value = requiredArg<ObjInt>(0).value
newInstance(decimalClass, IonBigDecimal.fromLongAsSignificand(value))
newInstance(decimalClass, IonBigDecimal.fromLong(value))
}
decimalClass.addClassFn("fromReal") {
val value = requiredArg<ObjReal>(0).value
@ -204,7 +204,7 @@ object ObjDecimalSupport {
}
private fun coerceArg(scope: Scope, value: Obj): IonBigDecimal = when (value) {
is ObjInt -> IonBigDecimal.fromLongAsSignificand(value.value)
is ObjInt -> IonBigDecimal.fromLong(value.value)
is ObjReal -> IonBigDecimal.fromDouble(value.value, realConversionMode)
is ObjInstance -> {
if (value.objClass.className != "Decimal") {
@ -303,7 +303,7 @@ object ObjDecimalSupport {
doc = "Convert this integer to a Decimal.",
type = type("lyng.decimal.Decimal"),
moduleName = "lyng.decimal",
getter = { newInstance(decimalClass, IonBigDecimal.fromLongAsSignificand(thisAs<ObjInt>().value)) }
getter = { newInstance(decimalClass, IonBigDecimal.fromLong(thisAs<ObjInt>().value)) }
)
ObjInt.type.members["d"] = ObjInt.type.members.getValue("d").copy(typeDecl = decimalTypeDecl)
ObjReal.type.addPropertyDoc(
@ -347,7 +347,7 @@ object ObjDecimalSupport {
),
leftToCommon = ObjExternCallable.fromBridge {
val value = requiredArg<ObjInt>(0).value
newInstance(decimalClass, IonBigDecimal.fromLongAsSignificand(value))
newInstance(decimalClass, IonBigDecimal.fromLong(value))
},
rightToCommon = ObjExternCallable.fromBridge {
requiredArg<Obj>(0)

View File

@ -17,14 +17,15 @@
package net.sergeych.lyng
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.bridge.bindGlobalFun1
import net.sergeych.lyng.bridge.bindGlobalFun3
import net.sergeych.lyng.bridge.bindGlobalVar
import net.sergeych.lyng.bridge.globalBinder
import net.sergeych.lyng.bridge.*
import net.sergeych.lyng.obj.ObjInstance
import net.sergeych.lyng.obj.ObjInt
import net.sergeych.lyng.obj.ObjReal
import net.sergeych.lyng.obj.ObjString
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class GlobalBindingTest {
@ -136,4 +137,87 @@ class GlobalBindingTest {
assertTrue(readonlySetter)
}
}
@Test
fun rawDecimalExternBindingDoesNotBreakDecimalLiteralRendering() = runTest {
val scope = Script.newScope()
var x = BigDecimal.ZERO
scope.eval(
"""
import lyng.decimal
extern var X: Decimal
""".trimIndent()
)
scope.globalBinder().bindGlobalVarRaw(
name = "X",
get = { it.newDecimal(x) },
set = { _, value ->
x = when (value) {
is ObjInt -> BigDecimal.fromLong(value.value)
is ObjReal -> BigDecimal.fromDouble(value.value)
is ObjInstance -> value.data as BigDecimal
else -> error("unexpected value: $value")
}
}
)
scope.eval(
"""
fun main() {
assertEquals("42", 42.d.toStringExpanded())
}
main()
""".trimIndent()
)
assertEquals(BigDecimal.ZERO, x)
}
@Test
fun externDecimalDeclarationAloneDoesNotBreakDecimalLiteralRendering() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
extern var X: Decimal
fun main() {
assertEquals("42", 42.d.toStringExpanded())
}
main()
""".trimIndent()
)
}
@Test
fun parserKeeps42DotDAsIntDotIdentifierAfterExternDecimalDeclaration() = runTest {
val tokens = parseLyng(
Source(
"test",
"""
import lyng.decimal
extern var X: Decimal
fun main() {
42.d.toStringExpanded()
}
""".trimIndent()
)
)
val tokenTexts = tokens.map { it.type to it.value }
val needle = listOf(
Token.Type.INT to "42",
Token.Type.DOT to ".",
Token.Type.ID to "d",
Token.Type.DOT to ".",
Token.Type.ID to "toStringExpanded",
)
val found = tokenTexts.windowed(needle.size).any { it == needle }
assertTrue(found, tokenTexts.joinToString())
}
}

View File

@ -32,8 +32,10 @@ class DecimalModuleTest {
assertEquals("12.34", Decimal.fromString("12.34").toStringExpanded())
assertEquals("1", Decimal.fromInt(1).toStringExpanded())
assertEquals("42", Decimal.fromInt(42).toStringExpanded())
assertEquals("2.5", "2.5".d.toStringExpanded())
assertEquals("1", 1.d.toStringExpanded())
assertEquals("42", 42.d.toStringExpanded())
assertEquals("2.2", 2.2.d.toStringExpanded())
assertEquals("3", (1 + 2).d.toStringExpanded())
assertEquals("1.5", (1 + 0.5).d.toStringExpanded())
@ -229,6 +231,21 @@ class DecimalModuleTest {
""")
}
@Test
fun decimalPropertyWorksInsideFunctionBody() = runTest {
eval("""
import lyng.decimal
fun main() {
val x = 42.d
assertEquals(42.d, x)
assertEquals(53.d, x + 11)
}
main()
""".trimIndent())
}
@Test
fun kotlinHelperCanWrapIonBigDecimal() = runTest {
val scope = Script.newScope()