From 5848adca61c0c8a09a29c2decbe9314b8244ab14 Mon Sep 17 00:00:00 2001 From: sergeych Date: Thu, 10 Jul 2025 15:54:53 +0300 Subject: [PATCH] fix #39 correct implementation of ++ and -- with indexing access --- docs/Buffer.md | 2 + .../kotlin/net/sergeych/lyng/Compiler.kt | 60 ++++++++++++------- .../kotlin/net/sergeych/lyng/Obj.kt | 6 ++ .../kotlin/net/sergeych/lyng/ObjClass.kt | 1 - .../kotlin/net/sergeych/lyng/ObjInt.kt | 6 +- .../net/sergeych/lyng/pacman/ImportManager.kt | 1 - .../kotlin/net/sergeych/lyng/statements.kt | 2 +- lynglib/src/commonTest/kotlin/ScriptTest.kt | 48 +++++++++++---- 8 files changed, 90 insertions(+), 36 deletions(-) diff --git a/docs/Buffer.md b/docs/Buffer.md index d754815..f303411 100644 --- a/docs/Buffer.md +++ b/docs/Buffer.md @@ -54,6 +54,8 @@ Buffer provides concatenation with another Buffer: assertEquals( Buffer(101, 102, 1, 2), b + [1,2]) >>> void +Please note that indexed bytes are _readonly projection_, e.g. you can't modify these with + ## Comparing Buffers are comparable with other buffers: diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index c99eaf8..85d0186 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -12,10 +12,6 @@ class Compiler( settings: Settings = Settings() ) { - init { - println("Compiler initialized: $importManager") - } - var packageName: String? = null class Settings @@ -27,7 +23,7 @@ class Compiler( // package level declarations do { val t = cc.current() - if(t.type == Token.Type.NEWLINE || t.type == Token.Type.SINLGE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) { + if (t.type == Token.Type.NEWLINE || t.type == Token.Type.SINLGE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) { cc.next() continue } @@ -44,6 +40,7 @@ class Compiler( packageName = name continue } + "import" -> { cc.next() val pos = cc.currentPos() @@ -349,17 +346,26 @@ class Compiler( left.setter(startPos) operand = Accessor { cxt -> val x = left.getter(cxt) - if (x.isMutable) - x.value.getAndIncrement(cxt).asReadonly - else cxt.raiseError("Cannot increment immutable value") + if (x.isMutable) { + if (x.value.isConst) { + x.value.plus(cxt, ObjInt.One).also { + left.setter(startPos)(cxt, it) + }.asReadonly + } else + x.value.getAndIncrement(cxt).asReadonly + } else cxt.raiseError("Cannot increment immutable value") } } ?: run { // no lvalue means pre-increment, expression to increment follows - val next = parseAccessor() ?: throw ScriptError(t.pos, "Expecting expression") + val next = parseTerm() ?: throw ScriptError(t.pos, "Expecting expression") operand = Accessor { ctx -> - next.getter(ctx).also { + val x = next.getter(ctx).also { if (!it.isMutable) ctx.raiseError("Cannot increment immutable value") - }.value.incrementAndGet(ctx).asReadonly + }.value + if (x.isConst) { + next.setter(startPos)(ctx, x.plus(ctx, ObjInt.One)) + x.asReadonly + } else x.incrementAndGet(ctx).asReadonly } } } @@ -369,18 +375,28 @@ class Compiler( operand?.let { left -> // post decrement left.setter(startPos) - operand = Accessor { ctx -> - left.getter(ctx).also { - if (!it.isMutable) ctx.raiseError("Cannot decrement immutable value") - }.value.getAndDecrement(ctx).asReadonly + operand = Accessor { cxt -> + val x = left.getter(cxt) + if (!x.isMutable) cxt.raiseError("Cannot decrement immutable value") + if (x.value.isConst) { + x.value.minus(cxt, ObjInt.One).also { + left.setter(startPos)(cxt, it) + }.asReadonly + } else + x.value.getAndDecrement(cxt).asReadonly } } ?: run { // no lvalue means pre-decrement, expression to decrement follows - val next = parseAccessor() ?: throw ScriptError(t.pos, "Expecting expression") - operand = Accessor { ctx -> - next.getter(ctx).also { - if (!it.isMutable) ctx.raiseError("Cannot decrement immutable value") - }.value.decrementAndGet(ctx).asReadonly + val next = parseTerm() ?: throw ScriptError(t.pos, "Expecting expression") + operand = Accessor { cxt -> + val x = next.getter(cxt) + if (!x.isMutable) cxt.raiseError("Cannot decrement immutable value") + if (x.value.isConst) { + x.value.minus(cxt, ObjInt.One).also { + next.setter(startPos)(cxt, it) + }.asReadonly + } else + x.value.decrementAndGet(cxt).asReadonly } } } @@ -1623,8 +1639,8 @@ class Compiler( companion object { - suspend fun compile(source: Source,importManager: ImportProvider): Script { - return Compiler(CompilerContext(parseLyng(source)),importManager).parseScript() + suspend fun compile(source: Source, importManager: ImportProvider): Script { + return Compiler(CompilerContext(parseLyng(source)), importManager).parseScript() } private var lastPriority = 0 diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt index 85cca83..d783a13 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Obj.kt @@ -48,6 +48,12 @@ data class Accessor( open class Obj { + open val isConst: Boolean = false + + fun ensureNotConst(scope: Scope) { + if( isConst ) scope.raiseError("can't assign to constant") + } + val isNull by lazy { this === ObjNull } var isFrozen: Boolean = false diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt index 3cac0a0..a4944d5 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjClass.kt @@ -82,7 +82,6 @@ open class ObjClass( override suspend fun readField(scope: Scope, name: String): ObjRecord { classMembers[name]?.let { - println("class field $it") return it } return super.readField(scope, name) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjInt.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjInt.kt index 22e3ec8..8f59877 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjInt.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ObjInt.kt @@ -1,6 +1,6 @@ package net.sergeych.lyng -class ObjInt(var value: Long,val isConst: Boolean = false) : Obj(), Numeric { +class ObjInt(var value: Long,override val isConst: Boolean = false) : Obj(), Numeric { override val asStr get() = ObjString(value.toString()) override val longValue get() = value override val doubleValue get() = value.toDouble() @@ -14,18 +14,22 @@ class ObjInt(var value: Long,val isConst: Boolean = false) : Obj(), Numeric { } override suspend fun getAndIncrement(scope: Scope): Obj { + ensureNotConst(scope) return ObjInt(value).also { value++ } } override suspend fun getAndDecrement(scope: Scope): Obj { + ensureNotConst(scope) return ObjInt(value).also { value-- } } override suspend fun incrementAndGet(scope: Scope): Obj { + ensureNotConst(scope) return ObjInt(++value) } override suspend fun decrementAndGet(scope: Scope): Obj { + ensureNotConst(scope) return ObjInt(--value) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/pacman/ImportManager.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/pacman/ImportManager.kt index 3271065..26bc9de 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/pacman/ImportManager.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/pacman/ImportManager.kt @@ -101,7 +101,6 @@ class ImportManager( */ private suspend fun doImport(packageName: String, pos: Pos): ModuleScope { val entry = imports[packageName] ?: throw ImportException(pos, "package not found: $packageName") - println("import enrty found: $packageName") return entry.getScope(pos) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/statements.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/statements.kt index d1bcf58..4860d1e 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/statements.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/statements.kt @@ -11,7 +11,7 @@ sealed class ObjType { @Suppress("unused") abstract class Statement( val isStaticConst: Boolean = false, - val isConst: Boolean = false, + override val isConst: Boolean = false, val returnType: ObjType = ObjType.Any ) : Obj() { diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 24fd5b8..0e9ccf8 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -707,19 +707,13 @@ class ScriptTest { var t1 = 10 outer@ while( t1 > 0 ) { var t2 = 10 - println("starting t2 = " + t2) while( t2 > 0 ) { t2 = t2 - 1 - println("t2 " + t2 + " t1 " + t1) if( t2 == 3 && t1 == 7) { - println("will break") break@outer "ok2:"+t2+":"+t1 } } - println("next t1") - t1 = t1 - 1 - println("t1 now "+t1) - t1 + --t1 } """.trimIndent() ).toString() @@ -734,8 +728,6 @@ class ScriptTest { """ val count = 3 val res = if( count > 10 ) "too much" else "just " + count - println(count) - println(res) res """.trimIndent() ) @@ -771,7 +763,7 @@ class ScriptTest { fun testDecr() = runTest { val c = Scope() c.eval("var x = 9") - assertEquals(9, c.eval("x--").toInt()) + assertEquals(9, c.eval("println(x); val a = x--; println(x); println(a); a").toInt()) assertEquals(8, c.eval("x--").toInt()) assertEquals(7, c.eval("x--").toInt()) assertEquals(6, c.eval("x--").toInt()) @@ -2612,4 +2604,40 @@ class ScriptTest { } + @Test + fun testIndexIntIncrements() = runTest { + eval(""" + val x = [1,2,3] + x[1]++ + ++x[0] + assertEquals( [2,3,3], x ) + + import lyng.buffer + + val b = Buffer(1,2,3) + b[1]++ + assert( b == Buffer(1,3,3) ) + ++b[0] + assertEquals( b, Buffer(2,3,3) ) + """.trimIndent()) + } + + @Test + fun testIndexIntDecrements() = runTest { + eval(""" + val x = [1,2,3] + x[1]-- + --x[0] + assertEquals( [0,1,3], x ) + + import lyng.buffer + + val b = Buffer(1,2,3) + b[1]-- + assert( b == Buffer(1,1,3) ) + --b[0] + assertEquals( b, Buffer(0,1,3) ) + """.trimIndent()) + } + } \ No newline at end of file