fix #39 correct implementation of ++ and -- with indexing access

This commit is contained in:
Sergey Chernov 2025-07-10 15:54:53 +03:00
parent f1ae4b2d23
commit 5848adca61
8 changed files with 90 additions and 36 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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() {

View File

@ -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())
}
}