From dc9885c218a94cb196b9ddbf6334cbfa668805f3 Mon Sep 17 00:00:00 2001 From: sergeych Date: Wed, 18 Feb 2026 09:17:12 +0300 Subject: [PATCH] fixed samples test --- docs/samples/operator_overloading.lyng | 10 +- docs/samples/sum.lyng | 1 - .../kotlin/net/sergeych/lyng/Compiler.kt | 126 +++++++++++++++++- lynglib/src/jvmTest/kotlin/SamplesTest.kt | 11 +- 4 files changed, 134 insertions(+), 14 deletions(-) diff --git a/docs/samples/operator_overloading.lyng b/docs/samples/operator_overloading.lyng index b170c1b..2d42eff 100644 --- a/docs/samples/operator_overloading.lyng +++ b/docs/samples/operator_overloading.lyng @@ -1,23 +1,23 @@ // Sample: Operator Overloading in Lyng -class Vector(val x, val y) { +class Vector(val x: T, val y: T) { // Overload + - fun plus(other) = Vector(x + other.x, y + other.y) + fun plus(other: Vector) = Vector(x + other.x, y + other.y) // Overload - - fun minus(other) = Vector(x - other.x, y - other.y) + fun minus(other: Vector) = Vector(x - other.x, y - other.y) // Overload unary - fun negate() = Vector(-x, -y) // Overload == fun equals(other) { - if (other is Vector) x == other.x && y == other.y + if (other is Vector) x == other.x && y == other.y else false } // Overload * (scalar multiplication) - fun mul(scalar) = Vector(x * scalar, y * scalar) + fun mul(scalar: Int | Real) = Vector(x * scalar, y * scalar) override fun toString() = "Vector(${x}, ${y})" } diff --git a/docs/samples/sum.lyng b/docs/samples/sum.lyng index 97dd585..2625d97 100755 --- a/docs/samples/sum.lyng +++ b/docs/samples/sum.lyng @@ -13,7 +13,6 @@ fun findSumLimit(f) { println("limit reached after "+n+" rounds") break sum } - n++ } else { println("limit not reached") diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 7e60e22..d850148 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -6797,6 +6797,118 @@ class Compiler( return t.pos } + private data class SmartCastTarget( + val name: String? = null, + val scopeId: Int? = null, + val slot: Int? = null, + val typeDecl: TypeDecl, + ) + + private data class SmartCastRestore( + val name: String?, + val nameHad: Boolean, + val namePrev: TypeDecl?, + val scopeId: Int?, + val slot: Int?, + val slotHad: Boolean, + val slotPrev: TypeDecl?, + val slotMapCreated: Boolean, + ) + + private fun smartCastTargetFromRef(ref: ObjRef, typeDecl: TypeDecl): SmartCastTarget? = when (ref) { + is LocalSlotRef -> { + val ownerScopeId = ref.captureOwnerScopeId ?: ref.scopeId + val ownerSlot = ref.captureOwnerSlot ?: ref.slot + SmartCastTarget(scopeId = ownerScopeId, slot = ownerSlot, typeDecl = typeDecl) + } + is LocalVarRef -> SmartCastTarget(name = ref.name, typeDecl = typeDecl) + is FastLocalVarRef -> SmartCastTarget(name = ref.name, typeDecl = typeDecl) + else -> null + } + + private fun extractSmartCasts(condition: Statement): Pair, List> { + val ref = unwrapDirectRef(condition) ?: return emptyList() to emptyList() + if (ref !is BinaryOpRef) return emptyList() to emptyList() + if (ref.op != BinOp.IS && ref.op != BinOp.NOTIS) return emptyList() to emptyList() + val typeRef = ref.right as? net.sergeych.lyng.obj.TypeDeclRef ?: return emptyList() to emptyList() + val typeDecl = expandTypeAliases(typeRef.decl(), typeRef.pos()) + val target = smartCastTargetFromRef(ref.left, typeDecl) ?: return emptyList() to emptyList() + return if (ref.op == BinOp.IS) { + listOf(target) to emptyList() + } else { + emptyList() to listOf(target) + } + } + + private fun applySmartCasts(overrides: List): List { + if (overrides.isEmpty()) return emptyList() + val restores = ArrayList(overrides.size) + for (override in overrides) { + if (override.name != null) { + val had = nameTypeDecl.containsKey(override.name) + val prev = nameTypeDecl[override.name] + nameTypeDecl[override.name] = override.typeDecl + restores.add( + SmartCastRestore( + name = override.name, + nameHad = had, + namePrev = prev, + scopeId = null, + slot = null, + slotHad = false, + slotPrev = null, + slotMapCreated = false, + ) + ) + } else if (override.scopeId != null && override.slot != null) { + val map = slotTypeDeclByScopeId[override.scopeId] + val mapCreated = map == null + val targetMap = map ?: mutableMapOf().also { slotTypeDeclByScopeId[override.scopeId] = it } + val had = targetMap.containsKey(override.slot) + val prev = targetMap[override.slot] + targetMap[override.slot] = override.typeDecl + restores.add( + SmartCastRestore( + name = null, + nameHad = false, + namePrev = null, + scopeId = override.scopeId, + slot = override.slot, + slotHad = had, + slotPrev = prev, + slotMapCreated = mapCreated, + ) + ) + } + } + return restores + } + + private fun restoreSmartCasts(restores: List) { + if (restores.isEmpty()) return + for (restore in restores.asReversed()) { + if (restore.name != null) { + if (restore.nameHad) { + nameTypeDecl[restore.name] = restore.namePrev!! + } else { + nameTypeDecl.remove(restore.name) + } + } else if (restore.scopeId != null && restore.slot != null) { + val map = slotTypeDeclByScopeId[restore.scopeId] + if (map != null) { + if (restore.slotHad) { + map[restore.slot] = restore.slotPrev!! + } else { + map.remove(restore.slot) + } + if (restore.slotMapCreated && map.isEmpty()) { + slotTypeDeclByScopeId.remove(restore.scopeId) + } + } + } + } + } + private suspend fun parseIfStatement(): Statement { val start = ensureLparen() @@ -6805,7 +6917,13 @@ class Compiler( val pos = ensureRparen() - val ifBody = parseStatement() ?: throw ScriptError(pos, "Bad if statement: expected statement") + val (trueCasts, falseCasts) = extractSmartCasts(condition) + val ifRestores = applySmartCasts(trueCasts) + val ifBody = try { + parseStatement() ?: throw ScriptError(pos, "Bad if statement: expected statement") + } finally { + restoreSmartCasts(ifRestores) + } cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true) // could be else block: @@ -6813,8 +6931,12 @@ class Compiler( // we generate different statements: optimization val stmt = if (t2.type == Token.Type.ID && t2.value == "else") { - val elseBody = + val elseRestores = applySmartCasts(falseCasts) + val elseBody = try { parseStatement() ?: throw ScriptError(pos, "Bad else statement: expected statement") + } finally { + restoreSmartCasts(elseRestores) + } IfStatement(condition, ifBody, elseBody, start) } else { cc.previous() diff --git a/lynglib/src/jvmTest/kotlin/SamplesTest.kt b/lynglib/src/jvmTest/kotlin/SamplesTest.kt index ade7546..64553d6 100644 --- a/lynglib/src/jvmTest/kotlin/SamplesTest.kt +++ b/lynglib/src/jvmTest/kotlin/SamplesTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com + * Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import net.sergeych.lyng.Scope +import net.sergeych.lyng.Script +import net.sergeych.lyng.toSource import java.nio.file.Files import java.nio.file.Paths import kotlin.io.path.extension -import kotlin.test.Ignore import kotlin.test.Test import kotlin.time.Clock @@ -31,9 +31,9 @@ suspend fun executeSampleTests(fileName: String) { Files.readString(Paths.get(fileName)) } runBlocking { - val c = Scope() + val c = Script.newScope() val start = Clock.System.now() - c.eval(sample) + c.eval(sample.toSource(fileName)) val time = Clock.System.now() - start println("$time: $fileName") // delay(100) @@ -41,7 +41,6 @@ suspend fun executeSampleTests(fileName: String) { } } -@Ignore("TODO(compile-time-res): legacy tests disabled") class SamplesTest { @Test