diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 55b8a2d..e36794a 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -4217,10 +4217,20 @@ class Compiler( extTypeName == null && !isStatic && !isProperty && - !isDelegate && !actualExtern && !isAbstract ) { + if (isDelegate) { + val initExpr = initialExpression ?: throw ScriptError(start, "Delegate must be initialized") + return DelegatedVarDeclStatement( + name, + isMutable, + visibility, + initExpr, + isTransient, + start + ) + } val slotPlan = slotPlanStack.lastOrNull() val slotIndex = slotPlan?.slots?.get(name)?.index val scopeId = slotPlan?.id diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/DelegatedVarDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/DelegatedVarDeclStatement.kt new file mode 100644 index 0000000..099465a --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/DelegatedVarDeclStatement.kt @@ -0,0 +1,54 @@ +/* + * 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.ObjNull +import net.sergeych.lyng.obj.ObjRecord +import net.sergeych.lyng.obj.ObjString + +class DelegatedVarDeclStatement( + val name: String, + val isMutable: Boolean, + val visibility: Visibility, + val initializer: Statement, + val isTransient: Boolean, + private val startPos: Pos, +) : Statement() { + override val pos: Pos = startPos + + override suspend fun execute(context: Scope): Obj { + val initValue = initializer.execute(context) + val accessTypeStr = if (isMutable) "Var" else "Val" + val accessType = context.resolveQualifiedIdentifier("DelegateAccess.$accessTypeStr") + val finalDelegate = try { + initValue.invokeInstanceMethod(context, "bind", Arguments(ObjString(name), accessType, ObjNull)) + } catch (e: Exception) { + initValue + } + val rec = context.addItem( + name, + isMutable, + ObjNull, + visibility, + recordType = ObjRecord.Type.Delegated, + isTransient = isTransient + ) + rec.delegate = finalDelegate + return finalDelegate + } +} 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 bab1f82..4102d53 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -17,6 +17,7 @@ package net.sergeych.lyng.bytecode import net.sergeych.lyng.BlockStatement +import net.sergeych.lyng.DelegatedVarDeclStatement import net.sergeych.lyng.DestructuringVarDeclStatement import net.sergeych.lyng.ExpressionStatement import net.sergeych.lyng.IfStatement @@ -98,6 +99,22 @@ class BytecodeCompiler( } is BlockStatement -> compileBlock(name, stmt) is VarDeclStatement -> compileVarDecl(name, stmt) + is DelegatedVarDeclStatement -> { + 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, + scopeSlotIndices, + scopeSlotNames, + scopeSlotIsModule, + localSlotNames, + localSlotMutables + ) + } is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> compileExtensionPropertyDecl(name, stmt) is net.sergeych.lyng.TryStatement -> { @@ -196,7 +213,7 @@ class BytecodeCompiler( return compileNameLookup(ref.name) } if (!allowLocalSlots) return null - if (ref.isDelegated) return null + if (ref.isDelegated) return compileEvalRef(ref) if (ref.name.isEmpty()) return null if (ref.captureOwnerScopeId == null && refScopeId(ref) == 0) { val byName = scopeSlotIndexByName[ref.name] @@ -2284,6 +2301,7 @@ class BytecodeCompiler( } is BlockStatement -> emitBlock(target, true) is VarDeclStatement -> emitVarDecl(target) + is DelegatedVarDeclStatement -> emitStatementEval(target) is DestructuringVarDeclStatement -> emitStatementEval(target) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target) is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target) @@ -2310,6 +2328,7 @@ class BytecodeCompiler( } } is VarDeclStatement -> emitVarDecl(target) + is DelegatedVarDeclStatement -> emitStatementEval(target) is IfStatement -> compileIfStatement(target) is net.sergeych.lyng.ForInStatement -> { val resultSlot = emitForIn(target, false) ?: return null 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 6124d93..a44a5f2 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeStatement.kt @@ -101,6 +101,8 @@ class BytecodeStatement private constructor( target.statements().any { containsUnsupportedStatement(it) } is net.sergeych.lyng.VarDeclStatement -> target.initializer?.let { containsUnsupportedStatement(it) } ?: false + is net.sergeych.lyng.DelegatedVarDeclStatement -> + containsUnsupportedStatement(target.initializer) is net.sergeych.lyng.DestructuringVarDeclStatement -> containsUnsupportedStatement(target.initializer) is net.sergeych.lyng.BreakStatement -> diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 634a593..d095a4b 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -3029,7 +3029,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testMapAsDelegate() = runTest { eval( @@ -3330,7 +3329,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testInstantComponents() = runTest { eval( @@ -3364,7 +3362,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testDoubleImports() = runTest { val s = Scope.new() @@ -3409,7 +3406,6 @@ class ScriptTest { } - @Ignore("incremental enable") @Test fun testIndexIntIncrements() = runTest { eval( @@ -3430,7 +3426,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testIndexIntDecrements() = runTest { eval( @@ -3451,7 +3446,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testRangeToList() = runTest { val x = eval("""(1..10).toList()""") as ObjList @@ -3460,7 +3454,6 @@ class ScriptTest { println(y.list) } - @Ignore("incremental enable") @Test fun testMultilineStrings() = runTest { assertEquals( @@ -3502,7 +3495,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun tesFunAnnotation() = runTest { eval( @@ -3647,7 +3639,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testNewlinesAnsCommentsInExpressions() = runTest { assertEquals( @@ -3669,7 +3660,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testNotExpressionWithoutWs() = runTest { eval( @@ -3686,7 +3676,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testMultilineFnDeclaration() = runTest { eval(