From a4fc5ac6d5c220981fd38093b83d4009e8e0ac67 Mon Sep 17 00:00:00 2001 From: sergeych Date: Wed, 28 Jan 2026 22:35:14 +0300 Subject: [PATCH] Add list literal opcode and bytecode wrappers --- .../net/sergeych/lyng/ClassDeclStatement.kt | 30 ++++ .../kotlin/net/sergeych/lyng/Compiler.kt | 143 ++++++++++-------- .../net/sergeych/lyng/EnumDeclStatement.kt | 30 ++++ .../lyng/ExtensionPropertyDeclStatement.kt | 50 ++++++ .../sergeych/lyng/FunctionDeclStatement.kt | 30 ++++ .../sergeych/lyng/bytecode/BytecodeConst.kt | 1 + .../lyng/bytecode/BytecodeStatement.kt | 1 + .../net/sergeych/lyng/bytecode/CmdBuilder.kt | 3 + .../sergeych/lyng/bytecode/CmdDisassembler.kt | 3 + .../net/sergeych/lyng/bytecode/CmdRuntime.kt | 29 ++++ .../net/sergeych/lyng/bytecode/Opcode.kt | 1 + .../src/commonTest/kotlin/IfNullAssignTest.kt | 2 - .../kotlin/ScriptTest_OptionalAssign.kt | 2 - lynglib/stdlib/lyng/root.lyng | 28 ++-- 14 files changed, 271 insertions(+), 82 deletions(-) create mode 100644 lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt create mode 100644 lynglib/src/commonMain/kotlin/net/sergeych/lyng/EnumDeclStatement.kt create mode 100644 lynglib/src/commonMain/kotlin/net/sergeych/lyng/ExtensionPropertyDeclStatement.kt create mode 100644 lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt new file mode 100644 index 0000000..9c1f31c --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.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 ClassDeclStatement( + 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/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index c9dcebb..af314ea 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -251,6 +251,10 @@ class Compiler( val statements = mutableListOf() val start = cc.currentPos() // Track locals at script level for fast local refs + val needsSlotPlan = slotPlanStack.isEmpty() + if (needsSlotPlan) { + slotPlanStack.add(SlotPlan(mutableMapOf(), 0)) + } return try { withLocalNames(emptySet()) { // package level declarations @@ -360,6 +364,9 @@ class Compiler( ) } } finally { + if (needsSlotPlan) { + slotPlanStack.removeLast() + } } } @@ -387,9 +394,51 @@ class Compiler( private val currentRangeParamNames: Set get() = rangeParamNamesStack.lastOrNull() ?: emptySet() + private fun containsLoopControl(stmt: Statement, inLoop: Boolean = false): Boolean { + val target = if (stmt is BytecodeStatement) stmt.original else stmt + return when (target) { + is BreakStatement, is ContinueStatement -> !inLoop + is IfStatement -> { + containsLoopControl(target.ifBody, inLoop) || + (target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false) + } + is ForInStatement -> { + containsLoopControl(target.body, true) || + (target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false) + } + is WhileStatement -> { + containsLoopControl(target.body, true) || + (target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false) + } + is DoWhileStatement -> { + containsLoopControl(target.body, true) || + (target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false) + } + is BlockStatement -> target.statements().any { containsLoopControl(it, inLoop) } + is VarDeclStatement -> target.initializer?.let { containsLoopControl(it, inLoop) } ?: false + is ReturnStatement, is ThrowStatement, is ExpressionStatement -> false + else -> false + } + } + private fun wrapBytecode(stmt: Statement): Statement { if (!useBytecodeStatements) return stmt - val allowLocals = codeContexts.lastOrNull() is CodeContext.Function + if (codeContexts.lastOrNull() is CodeContext.ClassBody) { + return stmt + } + if (stmt is FunctionDeclStatement || + stmt is ClassDeclStatement || + stmt is EnumDeclStatement || + stmt is BreakStatement || + stmt is ContinueStatement || + stmt is ReturnStatement + ) { + return stmt + } + if (containsLoopControl(stmt)) { + return stmt + } + val allowLocals = codeContexts.lastOrNull() !is CodeContext.ClassBody val returnLabels = returnLabelStack.lastOrNull() ?: emptySet() return BytecodeStatement.wrap( stmt, @@ -738,11 +787,7 @@ class Compiler( isCall = true val lambda = parseLambdaExpression() val argPos = next.pos - val argStmt = object : Statement() { - override val pos: Pos = argPos - override suspend fun execute(scope: Scope): Obj = lambda.get(scope).value - } - val args = listOf(ParsedArgument(argStmt, next.pos)) + val args = listOf(ParsedArgument(ExpressionStatement(lambda, argPos), next.pos)) operand = when (left) { is LocalVarRef -> if (left.name == "this") { ThisMethodSlotCallRef(next.value, args, true, isOptional) @@ -1421,11 +1466,7 @@ class Compiler( if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) { val localVar = LocalVarRef(name, t1.pos) val argPos = t1.pos - val argStmt = object : Statement() { - override val pos: Pos = argPos - override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope) - } - return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name) + return ParsedArgument(ExpressionStatement(localVar, argPos), t1.pos, isSplat = false, name = name) } val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'") return ParsedArgument(rhs, t1.pos, isSplat = false, name = name) @@ -1468,11 +1509,7 @@ class Compiler( // last argument - callable val callableAccessor = parseLambdaExpression() args += ParsedArgument( - // transform ObjRef to the callable value - object : Statement() { - override val pos: Pos = end.pos - override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value - }, + ExpressionStatement(callableAccessor, end.pos), end.pos ) lastBlockArgument = true @@ -1499,11 +1536,7 @@ class Compiler( if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) { val localVar = LocalVarRef(name, t1.pos) val argPos = t1.pos - val argStmt = object : Statement() { - override val pos: Pos = argPos - override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope) - } - return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name) + return ParsedArgument(ExpressionStatement(localVar, argPos), t1.pos, isSplat = false, name = name) } val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'") return ParsedArgument(rhs, t1.pos, isSplat = false, name = name) @@ -1555,11 +1588,7 @@ class Compiler( // into the lambda body. This ensures expected order: // foo { ... }.bar() == (foo { ... }).bar() val callableAccessor = parseLambdaExpression() - val argStmt = object : Statement() { - override val pos: Pos = cc.currentPos() - override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value - } - listOf(ParsedArgument(argStmt, cc.currentPos())) + listOf(ParsedArgument(ExpressionStatement(callableAccessor, cc.currentPos()), cc.currentPos())) } else { val r = parseArgs() detectedBlockArgument = r.second @@ -2320,7 +2349,7 @@ class Compiler( ) val stmtPos = startPos - return object : Statement() { + val enumDeclStatement = object : Statement() { override val pos: Pos = stmtPos override suspend fun execute(scope: Scope): Obj { val enumClass = ObjEnumClass.createSimpleEnum(nameToken.value, names) @@ -2328,6 +2357,7 @@ class Compiler( return enumClass } } + return EnumDeclStatement(enumDeclStatement, stmtPos) } private suspend fun parseObjectDeclaration(isExtern: Boolean = false): Statement { @@ -2586,7 +2616,7 @@ class Compiler( return instance } } - object : Statement() { + val classDeclStatement = object : Statement() { override val pos: Pos = startPos override suspend fun execute(scope: Scope): Obj { // the main statement should create custom ObjClass instance with field @@ -2643,6 +2673,7 @@ class Compiler( return newClass } } + ClassDeclStatement(classDeclStatement, startPos) } } @@ -3279,11 +3310,12 @@ class Compiler( return annotatedFnBody } } + val declaredFn = FunctionDeclStatement(fnCreateStatement, start) if (isStatic) { - currentInitScope += fnCreateStatement + currentInitScope += declaredFn NopStatement } else - fnCreateStatement + declaredFn }.also { val bodyRange = lastParsedBlockRange // Also emit a post-parse MiniFunDecl to be robust in case early emission was skipped by some path @@ -3823,43 +3855,26 @@ class Compiler( } } + if (extTypeName != null) { + val prop = if (getter != null || setter != null) { + ObjProperty(name, getter, setter) + } else { + // Simple val extension with initializer + val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized") + ObjProperty(name, initExpr, null) + } + return ExtensionPropertyDeclStatement( + extTypeName = extTypeName, + property = prop, + visibility = visibility, + setterVisibility = setterVisibility, + startPos = start + ) + } + return object : Statement() { override val pos: Pos = start override suspend fun execute(context: Scope): Obj { - if (extTypeName != null) { - val prop = if (getter != null || setter != null) { - ObjProperty(name, getter, setter) - } else { - // Simple val extension with initializer - val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized") - ObjProperty( - name, - object : Statement() { - override val pos: Pos = initExpr.pos - override suspend fun execute(scp: Scope): Obj = initExpr.execute(scp) - }, - null - ) - } - - val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found") - if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance") - - context.addExtension( - type, - name, - ObjRecord( - prop, - isMutable = false, - visibility = visibility, - writeVisibility = setterVisibility, - declaringClass = null, - type = ObjRecord.Type.Property - ) - ) - - return prop - } // In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions // Do NOT infer declaring class from runtime thisObj here; only the compile-time captured // ClassBody qualifies for class-field storage. Otherwise, this is a plain local. diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/EnumDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/EnumDeclStatement.kt new file mode 100644 index 0000000..b90ca74 --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/EnumDeclStatement.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 EnumDeclStatement( + 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/ExtensionPropertyDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ExtensionPropertyDeclStatement.kt new file mode 100644 index 0000000..10c7b4e --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ExtensionPropertyDeclStatement.kt @@ -0,0 +1,50 @@ +/* + * 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 +import net.sergeych.lyng.obj.ObjClass +import net.sergeych.lyng.obj.ObjProperty +import net.sergeych.lyng.obj.ObjRecord + +class ExtensionPropertyDeclStatement( + val extTypeName: String, + val property: ObjProperty, + val visibility: Visibility, + val setterVisibility: Visibility?, + private val startPos: Pos, +) : Statement() { + override val pos: Pos = startPos + + override suspend fun execute(context: Scope): Obj { + val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found") + if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance") + context.addExtension( + type, + property.name, + ObjRecord( + property, + isMutable = false, + visibility = visibility, + writeVisibility = setterVisibility, + declaringClass = null, + type = ObjRecord.Type.Property + ) + ) + return property + } +} diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.kt new file mode 100644 index 0000000..cd49fd7 --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/FunctionDeclStatement.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 FunctionDeclStatement( + 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/BytecodeConst.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt index 9df6966..127659e 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt @@ -31,6 +31,7 @@ sealed class BytecodeConst { data class ObjRef(val value: Obj) : BytecodeConst() data class Ref(val value: net.sergeych.lyng.obj.ObjRef) : BytecodeConst() data class StatementVal(val statement: net.sergeych.lyng.Statement) : BytecodeConst() + data class ListLiteralPlan(val spreads: List) : BytecodeConst() data class SlotPlan(val plan: Map) : BytecodeConst() data class ExtensionPropertyDecl( val extTypeName: String, 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 426c922..9d38f78 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt @@ -100,6 +100,7 @@ class BytecodeStatement private constructor( target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false is net.sergeych.lyng.ThrowStatement -> containsUnsupportedStatement(target.throwExpr) + is net.sergeych.lyng.ExtensionPropertyDeclStatement -> false else -> true } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt index db003fc..285a98e 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt @@ -182,6 +182,8 @@ class CmdBuilder { listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) Opcode.SET_INDEX -> listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) + Opcode.LIST_LITERAL -> + listOf(OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT -> listOf(OperandKind.ID, OperandKind.SLOT) } @@ -371,6 +373,7 @@ class CmdBuilder { Opcode.GET_NAME -> CmdGetName(operands[0], operands[1]) Opcode.GET_INDEX -> CmdGetIndex(operands[0], operands[1], operands[2]) Opcode.SET_INDEX -> CmdSetIndex(operands[0], operands[1], operands[2]) + Opcode.LIST_LITERAL -> CmdListLiteral(operands[0], operands[1], operands[2], operands[3]) Opcode.EVAL_FALLBACK -> CmdEvalFallback(operands[0], operands[1]) Opcode.EVAL_REF -> CmdEvalRef(operands[0], operands[1]) Opcode.EVAL_STMT -> CmdEvalStmt(operands[0], operands[1]) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt index 979573e..d6d327d 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt @@ -183,6 +183,7 @@ object CmdDisassembler { is CmdGetName -> Opcode.GET_NAME to intArrayOf(cmd.nameId, cmd.dst) is CmdGetIndex -> Opcode.GET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.dst) is CmdSetIndex -> Opcode.SET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.valueSlot) + is CmdListLiteral -> Opcode.LIST_LITERAL to intArrayOf(cmd.planId, cmd.baseSlot, cmd.count, cmd.dst) is CmdEvalFallback -> Opcode.EVAL_FALLBACK to intArrayOf(cmd.id, cmd.dst) is CmdEvalRef -> Opcode.EVAL_REF to intArrayOf(cmd.id, cmd.dst) is CmdEvalStmt -> Opcode.EVAL_STMT to intArrayOf(cmd.id, cmd.dst) @@ -265,6 +266,8 @@ object CmdDisassembler { listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) Opcode.SET_INDEX -> listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) + Opcode.LIST_LITERAL -> + listOf(OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT) Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT -> listOf(OperandKind.ID, OperandKind.SLOT) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt index 9e9524f..8fc7387 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -1223,6 +1223,35 @@ class CmdGetName( } } +class CmdListLiteral( + internal val planId: Int, + internal val baseSlot: Int, + internal val count: Int, + internal val dst: Int, +) : Cmd() { + override suspend fun perform(frame: CmdFrame) { + val plan = frame.fn.constants.getOrNull(planId) as? BytecodeConst.ListLiteralPlan + ?: error("LIST_LITERAL expects ListLiteralPlan at $planId") + val list = ArrayList(count) + for (i in 0 until count) { + val value = frame.slotToObj(baseSlot + i) + if (plan.spreads.getOrNull(i) == true) { + when (value) { + is ObjList -> { + list.ensureCapacity(list.size + value.list.size) + list.addAll(value.list) + } + else -> frame.scope.raiseError("Spread element must be list") + } + } else { + list.add(value) + } + } + frame.storeObjResult(dst, ObjList(list)) + return + } +} + class CmdSetField( internal val recvSlot: Int, internal val fieldId: Int, diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt index c8ed1ee..2d252a8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt @@ -130,6 +130,7 @@ enum class Opcode(val code: Int) { GET_INDEX(0xA2), SET_INDEX(0xA3), GET_NAME(0xA4), + LIST_LITERAL(0xA5), EVAL_FALLBACK(0xB0), RESOLVE_SCOPE_SLOT(0xB1), diff --git a/lynglib/src/commonTest/kotlin/IfNullAssignTest.kt b/lynglib/src/commonTest/kotlin/IfNullAssignTest.kt index 21fddaa..a8d61ab 100644 --- a/lynglib/src/commonTest/kotlin/IfNullAssignTest.kt +++ b/lynglib/src/commonTest/kotlin/IfNullAssignTest.kt @@ -1,10 +1,8 @@ import kotlinx.coroutines.test.runTest import net.sergeych.lyng.eval -import kotlin.test.Ignore import kotlin.test.Test -@Ignore("TODO(bytecode-only): uses fallback") class IfNullAssignTest { @Test diff --git a/lynglib/src/commonTest/kotlin/ScriptTest_OptionalAssign.kt b/lynglib/src/commonTest/kotlin/ScriptTest_OptionalAssign.kt index a22bb96..f285367 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest_OptionalAssign.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest_OptionalAssign.kt @@ -21,10 +21,8 @@ import kotlinx.coroutines.test.runTest import net.sergeych.lyng.eval -import kotlin.test.Ignore import kotlin.test.Test -@Ignore("TODO(bytecode-only): uses fallback") class ScriptTest_OptionalAssign { @Test diff --git a/lynglib/stdlib/lyng/root.lyng b/lynglib/stdlib/lyng/root.lyng index 1f0d2e9..1aaf280 100644 --- a/lynglib/stdlib/lyng/root.lyng +++ b/lynglib/stdlib/lyng/root.lyng @@ -249,20 +249,6 @@ fun List.sort() { sortWith { a, b -> a <=> b } } -/* Represents a single stack trace element. */ -class StackTraceEntry( - val sourceName: String, - val line: Int, - val column: Int, - val sourceString: String -) { - val at by lazy { "%s:%s:%s"(sourceName,line+1,column+1) } - /* Formatted representation: source:line:column: text. */ - override fun toString() { - "%s: %s"(at, sourceString.trim()) - } -} - /* Print this exception and its stack trace to standard output. */ fun Exception.printStackTrace() { println(this) @@ -337,3 +323,17 @@ class lazy(creatorParam) : Delegate { value } } + +/* Represents a single stack trace element. */ +class StackTraceEntry( + val sourceName: String, + val line: Int, + val column: Int, + val sourceString: String +) { + val at by lazy { "%s:%s:%s"(sourceName,line+1,column+1) } + /* Formatted representation: source:line:column: text. */ + override fun toString() { + "%s: %s"(at, sourceString.trim()) + } +}