diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index d5f5f27..46dfe92 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -1235,12 +1235,12 @@ class Compiler( return when (t.type) { Token.Type.INT, Token.Type.HEX -> { val n = t.value.replace("_", "").toLong(if (t.type == Token.Type.HEX) 16 else 10) - if (isPlus) ObjInt(n) else ObjInt(-n) + if (isPlus) ObjInt.of(n) else ObjInt.of(-n) } Token.Type.REAL -> { val d = t.value.toDouble() - if (isPlus) ObjReal(d) else ObjReal(-d) + if (isPlus) ObjReal.of(d) else ObjReal.of(-d) } else -> { @@ -2940,22 +2940,22 @@ class Compiler( // Arithmetic for ints only (keep semantics simple at compile time) BinOp.PLUS -> when { - a is ObjInt && b is ObjInt -> ObjInt(a.value + b.value) + a is ObjInt && b is ObjInt -> ObjInt.of(a.value + b.value) a is ObjString && b is ObjString -> ObjString(a.value + b.value) else -> null } - BinOp.MINUS -> if (a is ObjInt && b is ObjInt) ObjInt(a.value - b.value) else null - BinOp.STAR -> if (a is ObjInt && b is ObjInt) ObjInt(a.value * b.value) else null - BinOp.SLASH -> if (a is ObjInt && b is ObjInt && b.value != 0L) ObjInt(a.value / b.value) else null - BinOp.PERCENT -> if (a is ObjInt && b is ObjInt && b.value != 0L) ObjInt(a.value % b.value) else null + BinOp.MINUS -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value - b.value) else null + BinOp.STAR -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value * b.value) else null + BinOp.SLASH -> if (a is ObjInt && b is ObjInt && b.value != 0L) ObjInt.of(a.value / b.value) else null + BinOp.PERCENT -> if (a is ObjInt && b is ObjInt && b.value != 0L) ObjInt.of(a.value % b.value) else null // Bitwise for ints - BinOp.BAND -> if (a is ObjInt && b is ObjInt) ObjInt(a.value and b.value) else null - BinOp.BXOR -> if (a is ObjInt && b is ObjInt) ObjInt(a.value xor b.value) else null - BinOp.BOR -> if (a is ObjInt && b is ObjInt) ObjInt(a.value or b.value) else null - BinOp.SHL -> if (a is ObjInt && b is ObjInt) ObjInt(a.value shl (b.value.toInt() and 63)) else null - BinOp.SHR -> if (a is ObjInt && b is ObjInt) ObjInt(a.value shr (b.value.toInt() and 63)) else null + BinOp.BAND -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value and b.value) else null + BinOp.BXOR -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value xor b.value) else null + BinOp.BOR -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value or b.value) else null + BinOp.SHL -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value shl (b.value.toInt() and 63)) else null + BinOp.SHR -> if (a is ObjInt && b is ObjInt) ObjInt.of(a.value shr (b.value.toInt() and 63)) else null // Non-folded / side-effecting or type-dependent ops BinOp.EQARROW, BinOp.REF_EQ, BinOp.REF_NEQ, BinOp.MATCH, BinOp.NOTMATCH, @@ -2968,12 +2968,12 @@ class Compiler( return when (op) { UnaryOp.NOT -> if (a is ObjBool) if (!a.value) ObjTrue else ObjFalse else null UnaryOp.NEGATE -> when (a) { - is ObjInt -> ObjInt(-a.value) - is ObjReal -> ObjReal(-a.value) + is ObjInt -> ObjInt.of(-a.value) + is ObjReal -> ObjReal.of(-a.value) else -> null } - UnaryOp.BITNOT -> if (a is ObjInt) ObjInt(a.value.inv()) else null + UnaryOp.BITNOT -> if (a is ObjInt) ObjInt.of(a.value.inv()) else null } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt index d573c53..d75531a 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjInt.kt @@ -28,7 +28,7 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu override val longValue get() = value override val doubleValue get() = value.toDouble() override val toObjInt get() = this - override val toObjReal = ObjReal(doubleValue) + override val toObjReal = ObjReal.of(doubleValue) override fun byValueCopy(): Obj = this @@ -65,28 +65,28 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu if (other is ObjInt) of(this.value + other.value) else - ObjReal(this.doubleValue + other.toDouble()) + ObjReal.of(this.doubleValue + other.toDouble()) override suspend fun minus(scope: Scope, other: Obj): Obj = if (other is ObjInt) of(this.value - other.value) else - ObjReal(this.doubleValue - other.toDouble()) + ObjReal.of(this.doubleValue - other.toDouble()) override suspend fun mul(scope: Scope, other: Obj): Obj = if (other is ObjInt) { of(this.value * other.value) - } else ObjReal(this.value * other.toDouble()) + } else ObjReal.of(this.value * other.toDouble()) override suspend fun div(scope: Scope, other: Obj): Obj = if (other is ObjInt) of(this.value / other.value) - else ObjReal(this.value / other.toDouble()) + else ObjReal.of(this.value / other.toDouble()) override suspend fun mod(scope: Scope, other: Obj): Obj = if (other is ObjInt) of(this.value % other.value) - else ObjReal(this.value.toDouble() % other.toDouble()) + else ObjReal.of(this.value.toDouble() % other.toDouble()) /** * Numbers are now immutable, so we can't do in-place assignment. @@ -107,31 +107,31 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu } override suspend fun negate(scope: Scope): Obj { - return ObjInt(-value) + return of(-value) } // Bitwise operations override suspend fun bitAnd(scope: Scope, other: Obj): Obj = - if (other is ObjInt) ObjInt(this.value and other.value) + if (other is ObjInt) of(this.value and other.value) else scope.raiseIllegalArgument("bitwise and '&' requires Int, got ${other.objClass.className}") override suspend fun bitOr(scope: Scope, other: Obj): Obj = - if (other is ObjInt) ObjInt(this.value or other.value) + if (other is ObjInt) of(this.value or other.value) else scope.raiseIllegalArgument("bitwise or '|' requires Int, got ${other.objClass.className}") override suspend fun bitXor(scope: Scope, other: Obj): Obj = - if (other is ObjInt) ObjInt(this.value xor other.value) + if (other is ObjInt) of(this.value xor other.value) else scope.raiseIllegalArgument("bitwise xor '^' requires Int, got ${other.objClass.className}") override suspend fun shl(scope: Scope, other: Obj): Obj = - if (other is ObjInt) ObjInt(this.value shl (other.value.toInt() and 63)) + if (other is ObjInt) of(this.value shl (other.value.toInt() and 63)) else scope.raiseIllegalArgument("shift left '<<' requires Int, got ${other.objClass.className}") override suspend fun shr(scope: Scope, other: Obj): Obj = - if (other is ObjInt) ObjInt(this.value shr (other.value.toInt() and 63)) + if (other is ObjInt) of(this.value shr (other.value.toInt() and 63)) else scope.raiseIllegalArgument("shift right '>>' requires Int, got ${other.objClass.className}") - override suspend fun bitNot(scope: Scope): Obj = ObjInt(this.value.inv()) + override suspend fun bitNot(scope: Scope): Obj = of(this.value.inv()) override suspend fun lynonType(): LynonType = when (value) { 0L -> LynonType.Int0 @@ -170,11 +170,11 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu val type = object : ObjClass("Int") { override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = when (lynonType) { - null -> ObjInt(decoder.unpackSigned()) + null -> of(decoder.unpackSigned()) LynonType.Int0 -> Zero - LynonType.IntPositive -> ObjInt(decoder.unpackUnsigned().toLong()) - LynonType.IntNegative -> ObjInt(-decoder.unpackUnsigned().toLong()) - LynonType.IntSigned -> ObjInt(decoder.unpackSigned()) + LynonType.IntPositive -> of(decoder.unpackUnsigned().toLong()) + LynonType.IntNegative -> of(-decoder.unpackUnsigned().toLong()) + LynonType.IntSigned -> of(decoder.unpackSigned()) else -> scope.raiseIllegalState("illegal type code for Int: $lynonType") } }.apply { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjReal.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjReal.kt index 8d5b7a1..a84ae41 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjReal.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjReal.kt @@ -32,10 +32,10 @@ import kotlin.math.floor import kotlin.math.roundToLong data class ObjReal(val value: Double) : Obj(), Numeric { - override val longValue: Long by lazy { floor(value).toLong() } - override val doubleValue: Double by lazy { value } - override val toObjInt: ObjInt by lazy { ObjInt(longValue) } - override val toObjReal: ObjReal by lazy { ObjReal(value) } + override val longValue: Long get() = floor(value).toLong() + override val doubleValue: Double get() = value + override val toObjInt: ObjInt get() = ObjInt.of(longValue) + override val toObjReal: ObjReal get() = this override val objClass: ObjClass = type @@ -65,19 +65,19 @@ data class ObjReal(val value: Double) : Obj(), Numeric { } override suspend fun plus(scope: Scope, other: Obj): Obj = - ObjReal(this.value + other.toDouble()) + of(this.value + other.toDouble()) override suspend fun minus(scope: Scope, other: Obj): Obj = - ObjReal(this.value - other.toDouble()) + of(this.value - other.toDouble()) override suspend fun mul(scope: Scope, other: Obj): Obj = - ObjReal(this.value * other.toDouble()) + of(this.value * other.toDouble()) override suspend fun div(scope: Scope, other: Obj): Obj = - ObjReal(this.value / other.toDouble()) + of(this.value / other.toDouble()) override suspend fun mod(scope: Scope, other: Obj): Obj = - ObjReal(this.value % other.toDouble()) + of(this.value % other.toDouble()) /** * Returns unboxed Double value @@ -96,7 +96,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric { } override suspend fun negate(scope: Scope): Obj { - return ObjReal(-value) + return of(-value) } override suspend fun lynonType(): LynonType = LynonType.Real @@ -110,9 +110,18 @@ data class ObjReal(val value: Double) : Obj(), Numeric { } companion object { + val Zero = ObjReal(0.0) + val One = ObjReal(1.0) + + fun of(value: Double): ObjReal = when (value) { + 0.0 -> Zero + 1.0 -> One + else -> ObjReal(value) + } + val type: ObjClass = object : ObjClass("Real") { override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = - ObjReal(decoder.unpackDouble()) + of(decoder.unpackDouble()) }.apply { // roundToInt: number rounded to the nearest integer addConstDoc( @@ -130,7 +139,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric { returns = type("lyng.Int"), moduleName = "lyng.stdlib" ) { - ObjInt(thisAs().value.toLong()) + ObjInt.of(thisAs().value.toLong()) } } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt index 965dc7e..96122d9 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt @@ -132,16 +132,16 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r val av = a.value val bv = b.value val r: Obj? = when (op) { - BinOp.PLUS -> ObjInt(av + bv) - BinOp.MINUS -> ObjInt(av - bv) - BinOp.STAR -> ObjInt(av * bv) - BinOp.SLASH -> if (bv != 0L) ObjInt(av / bv) else null - BinOp.PERCENT -> if (bv != 0L) ObjInt(av % bv) else null - BinOp.BAND -> ObjInt(av and bv) - BinOp.BXOR -> ObjInt(av xor bv) - BinOp.BOR -> ObjInt(av or bv) - BinOp.SHL -> ObjInt(av shl (bv.toInt() and 63)) - BinOp.SHR -> ObjInt(av shr (bv.toInt() and 63)) + BinOp.PLUS -> ObjInt.of(av + bv) + BinOp.MINUS -> ObjInt.of(av - bv) + BinOp.STAR -> ObjInt.of(av * bv) + BinOp.SLASH -> if (bv != 0L) ObjInt.of(av / bv) else null + BinOp.PERCENT -> if (bv != 0L) ObjInt.of(av % bv) else null + BinOp.BAND -> ObjInt.of(av and bv) + BinOp.BXOR -> ObjInt.of(av xor bv) + BinOp.BOR -> ObjInt.of(av or bv) + BinOp.SHL -> ObjInt.of(av shl (bv.toInt() and 63)) + BinOp.SHR -> ObjInt.of(av shr (bv.toInt() and 63)) BinOp.EQ -> if (av == bv) ObjTrue else ObjFalse BinOp.NEQ -> if (av != bv) ObjTrue else ObjFalse BinOp.LT -> if (av < bv) ObjTrue else ObjFalse @@ -216,11 +216,11 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r val ad: Double = if (a is ObjInt) a.doubleValue else (a as ObjReal).value val bd: Double = if (b is ObjInt) b.doubleValue else (b as ObjReal).value val rNum: Obj? = when (op) { - BinOp.PLUS -> ObjReal(ad + bd) - BinOp.MINUS -> ObjReal(ad - bd) - BinOp.STAR -> ObjReal(ad * bd) - BinOp.SLASH -> ObjReal(ad / bd) - BinOp.PERCENT -> ObjReal(ad % bd) + BinOp.PLUS -> ObjReal.of(ad + bd) + BinOp.MINUS -> ObjReal.of(ad - bd) + BinOp.STAR -> ObjReal.of(ad * bd) + BinOp.SLASH -> ObjReal.of(ad / bd) + BinOp.PERCENT -> ObjReal.of(ad % bd) BinOp.LT -> if (ad < bd) ObjTrue else ObjFalse BinOp.LTE -> if (ad <= bd) ObjTrue else ObjFalse BinOp.GT -> if (ad > bd) ObjTrue else ObjFalse diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt index 6fd747a..8c9eb91 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt @@ -134,7 +134,7 @@ data class ObjString(val value: String) : Obj() { returns = type("lyng.Int"), moduleName = "lyng.stdlib" ) { - ObjInt( + ObjInt.of( thisAs().value.toLongOrNull() ?: raiseIllegalArgument("can't convert to int: $thisObj") ) @@ -159,7 +159,7 @@ data class ObjString(val value: String) : Obj() { } addConstDoc( name = "length", - value = statement { ObjInt(thisAs().value.length.toLong()) }, + value = statement { ObjInt.of(thisAs().value.length.toLong()) }, doc = "Number of UTF-16 code units in this string.", type = type("lyng.Int"), moduleName = "lyng.stdlib" @@ -269,14 +269,14 @@ data class ObjString(val value: String) : Obj() { doc = "Alias for length: the number of characters (code units) in this string.", returns = type("lyng.Int"), moduleName = "lyng.stdlib" - ) { ObjInt(thisAs().value.length.toLong()) } + ) { ObjInt.of(thisAs().value.length.toLong()) } addFnDoc( name = "toReal", doc = "Parse this string as a real number (floating point).", returns = type("lyng.Real"), moduleName = "lyng.stdlib" ) { - ObjReal(thisAs().value.toDouble()) + ObjReal.of(thisAs().value.toDouble()) } addFnDoc( name = "trim",