diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index b57423c..4f9e88b 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -2247,7 +2247,7 @@ class Compiler( throw ScriptError(cc.currentPos(), "try block must have either catch or finally clause or both") val stmtPos = body.pos - return object : Statement() { + val tryStatement = object : Statement() { override val pos: Pos = stmtPos override suspend fun execute(scope: Scope): Obj { var result: Obj = ObjVoid @@ -2296,6 +2296,7 @@ class Compiler( return result } } + return TryStatement(tryStatement, stmtPos) } private fun parseEnumDeclaration(isExtern: Boolean = false): Statement { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/TryStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/TryStatement.kt new file mode 100644 index 0000000..5b52039 --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/TryStatement.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2026 Sergey S. Chernov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sergeych.lyng + +import net.sergeych.lyng.obj.Obj + +class TryStatement( + private val delegate: Statement, + private val startPos: Pos, +) : Statement() { + override val pos: Pos = startPos + + override suspend fun execute(scope: Scope): Obj { + return delegate.execute(scope) + } +} diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt index 30577b9..3bd6551 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -78,6 +78,23 @@ class BytecodeCompiler( is VarDeclStatement -> compileVarDecl(name, stmt) is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> compileExtensionPropertyDecl(name, stmt) + is net.sergeych.lyng.TryStatement -> { + val value = emitStatementEval(stmt) + builder.emit(Opcode.RET, value.slot) + val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount + builder.build( + name, + localCount, + addrCount = nextAddrSlot, + returnLabels = returnLabels, + scopeSlotDepths, + scopeSlotIndices, + scopeSlotNames, + localSlotNames, + localSlotMutables, + localSlotDepths + ) + } else -> null } } @@ -297,12 +314,7 @@ class BytecodeCompiler( builder.emit(Opcode.NEG_REAL, a.slot, out) CompiledValue(out, SlotType.REAL) } - else -> { - val obj = ensureObjSlot(a) - val methodId = builder.addConst(BytecodeConst.StringVal("negate")) - builder.emit(Opcode.CALL_VIRTUAL, obj.slot, methodId, 0, 0, out) - CompiledValue(out, SlotType.OBJ) - } + else -> compileEvalRef(ref) } UnaryOp.NOT -> { when (a.type) { @@ -312,13 +324,7 @@ class BytecodeCompiler( builder.emit(Opcode.INT_TO_BOOL, a.slot, tmp) builder.emit(Opcode.NOT_BOOL, tmp, out) } - SlotType.OBJ, SlotType.UNKNOWN -> { - val obj = ensureObjSlot(a) - val methodId = builder.addConst(BytecodeConst.StringVal("logicalNot")) - val tmpObj = allocSlot() - builder.emit(Opcode.CALL_VIRTUAL, obj.slot, methodId, 0, 0, tmpObj) - builder.emit(Opcode.OBJ_TO_BOOL, tmpObj, out) - } + SlotType.OBJ, SlotType.UNKNOWN -> return compileEvalRef(ref) else -> return null } CompiledValue(out, SlotType.BOOL) @@ -328,10 +334,7 @@ class BytecodeCompiler( builder.emit(Opcode.INV_INT, a.slot, out) return CompiledValue(out, SlotType.INT) } - val obj = ensureObjSlot(a) - val methodId = builder.addConst(BytecodeConst.StringVal("bitNot")) - builder.emit(Opcode.CALL_VIRTUAL, obj.slot, methodId, 0, 0, out) - CompiledValue(out, SlotType.OBJ) + return compileEvalRef(ref) } } } @@ -1825,6 +1828,7 @@ class BytecodeCompiler( is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target) is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target) is net.sergeych.lyng.EnumDeclStatement -> emitStatementEval(target) + is net.sergeych.lyng.TryStatement -> emitStatementEval(target) is net.sergeych.lyng.BreakStatement -> compileBreak(target) is net.sergeych.lyng.ContinueStatement -> compileContinue(target) is net.sergeych.lyng.ReturnStatement -> compileReturn(target) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt index bd2f132..99d741a 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt @@ -105,6 +105,7 @@ class BytecodeStatement private constructor( is net.sergeych.lyng.ClassDeclStatement -> false is net.sergeych.lyng.FunctionDeclStatement -> false is net.sergeych.lyng.EnumDeclStatement -> false + is net.sergeych.lyng.TryStatement -> false else -> true } } diff --git a/lynglib/src/commonTest/kotlin/EmbeddingExceptionTest.kt b/lynglib/src/commonTest/kotlin/EmbeddingExceptionTest.kt index 9caf520..c1d3248 100644 --- a/lynglib/src/commonTest/kotlin/EmbeddingExceptionTest.kt +++ b/lynglib/src/commonTest/kotlin/EmbeddingExceptionTest.kt @@ -27,7 +27,7 @@ import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertTrue -@Ignore("TODO(bytecode-only): uses fallback (try/catch)") +@Ignore("TODO(bytecode-only): exception rethrow mismatch") class EmbeddingExceptionTest { @Test diff --git a/lynglib/src/commonTest/kotlin/StdlibTest.kt b/lynglib/src/commonTest/kotlin/StdlibTest.kt index 5966636..a37c2ac 100644 --- a/lynglib/src/commonTest/kotlin/StdlibTest.kt +++ b/lynglib/src/commonTest/kotlin/StdlibTest.kt @@ -20,9 +20,9 @@ import net.sergeych.lyng.eval import kotlin.test.Ignore import kotlin.test.Test -@Ignore("TODO(bytecode-only): uses fallback") class StdlibTest { @Test + @Ignore("TODO(bytecode-only): iterable filter mismatch") fun testIterableFilter() = runTest { eval(""" assertEquals([2,4,6,8], (1..8).filter{ println("call2"); it % 2 == 0 }.toList() ) @@ -33,6 +33,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): range first/last mismatch") fun testFirstLast() = runTest { eval(""" assertEquals(1, (1..8).first ) @@ -41,6 +42,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): range take mismatch") fun testTake() = runTest { eval(""" val r = 1..8 @@ -50,6 +52,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): any/all mismatch") fun testAnyAndAll() = runTest { eval(""" assert( [1,2,3].any { it > 2 } ) @@ -87,6 +90,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): range drop mismatch") fun testDrop() = runTest { eval(""" assertEquals([7,8], (1..8).drop(6).toList() ) @@ -95,6 +99,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): flatten/filter mismatch") fun testFlattenAndFilter() = runTest { eval(""" assertEquals([1,2,3,4,5,6], [1,3,5].map { [it, it+1] }.flatten() ) @@ -110,6 +115,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): count mismatch") fun testCount() = runTest { eval(""" assertEquals(5, (1..10).toList().count { it % 2 == 1 } ) @@ -117,6 +123,7 @@ class StdlibTest { } @Test + @Ignore("TODO(bytecode-only): with mismatch") fun testWith() = runTest { eval(""" class Person(val name, var age) diff --git a/lynglib/src/jvmTest/kotlin/LynonTests.kt b/lynglib/src/jvmTest/kotlin/LynonTests.kt index db1ace2..e498e5e 100644 --- a/lynglib/src/jvmTest/kotlin/LynonTests.kt +++ b/lynglib/src/jvmTest/kotlin/LynonTests.kt @@ -343,7 +343,6 @@ class LynonTests { @Test - @Ignore("TODO(bytecode-only): unary minus regression") fun testUnaryMinus() = runTest { eval( """ @@ -354,7 +353,6 @@ class LynonTests { } @Test - @Ignore("TODO(bytecode-only): simple types regression") fun testSimpleTypes() = runTest { testScope().eval( """