- fixed CLI -x mode

- fixed a wrong error message when global symbol is not found
This commit is contained in:
Sergey Chernov 2026-03-31 20:52:31 +03:00
parent 6e9333844e
commit c097464750
13 changed files with 426 additions and 77 deletions

View File

@ -1,28 +1,28 @@
/* /*
Рассчитывает глубину провала по времени падения камня и прихода звука. Рассчитывает глубину провала по времени падения камня и прихода звука.
@param T измеренное полное время (с) @param T измеренное полное время (с)
@param m масса камня (кг) @param m масса камня (кг)
@param d диаметр камня (м) (предполагается сферическая форма) @param d диаметр камня (м) (предполагается сферическая форма)
@param rho плотность воздуха (кг/м³), по умолчанию 1.2 @param rho плотность воздуха (кг/м³), по умолчанию 1.2
@param c скорость звука (м/с), по умолчанию 340.0 @param c скорость звука (м/с), по умолчанию 340.0
@param g ускорение свободного падения (м/с²), по умолчанию 9.81 @param g ускорение свободного падения (м/с²), по умолчанию 9.81
@param Cd коэффициент лобового сопротивления, по умолчанию 0.5 @param Cd коэффициент лобового сопротивления, по умолчанию 0.5
@param epsilon относительная точность (м), по умолчанию 1e-3 @param epsilon относительная точность (м), по умолчанию 1e-3
@param maxIter максимальное число итераций, по умолчанию 20 @param maxIter максимальное число итераций, по умолчанию 20
@return глубина h (м), или null если расчёт не сошёлся @return глубина h (м), или null если расчёт не сошёлся
*/ */
fun calculateDepth( fun calculateDepth(
T: Real, T: Real,
m: Real, m: Real,
d: Real, d: Real,
rho: Real = 1.2, rho: Real = 1.2,
c: Real = 340.0, c: Real = 340.0,
g: Real = 9.81, g: Real = 9.81,
Cd: Real = 0.5, Cd: Real = 0.5,
epsilon: Real = 1e-3, epsilon: Real = 1e-3,
maxIter: Int = 20 maxIter: Int = 20
): Real? { ): Real? {
// Площадь миделя // Площадь миделя
val r = d / 2.0 val r = d / 2.0
@ -90,14 +90,14 @@ fun calculateDepth(
return null return null
} }
// Пример использования // Пример использования
val T = 6.0 // секунды val T = 6.0 // секунды
val m = 1.0 // кг val m = 1.0 // кг
val d = 0.1 // м (10 см) val d = 0.1 // м (10 см)
val depth = calculateDepth(T, m, d) val depth = calculateDepth(T, m, d)
if (depth != null) { if (depth != null) {
println("Глубина: %.2f м".format(depth)) println("Глубина: %.2f м".format(depth))
} else { } else {
println("Расчёт не сошёлся") println("Расчёт не сошёлся")
} }

View File

@ -158,6 +158,7 @@ private class Fmt : CliktCommand(name = "fmt") {
private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() { private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
override val invokeWithoutSubcommand = true
override val printHelpOnEmptyArgs = true override val printHelpOnEmptyArgs = true
val version by option("-v", "--version", help = "Print version and exit").flag() val version by option("-v", "--version", help = "Print version and exit").flag()

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -131,4 +131,18 @@ class CliFmtJvmTest {
Files.deleteIfExists(tmp) Files.deleteIfExists(tmp)
} }
} }
@Test
fun inlineExecuteWithDashXStillWorksAndPassesArgv() {
val r = runCli(
"-x",
"""println("INLINE"); println(ARGV[0]); println(ARGV[1])""",
"one",
"two"
)
assertTrue("Expected inline execution output", r.out.contains("INLINE"))
assertTrue("Expected ARGV to include first trailing arg", r.out.contains("one"))
assertTrue("Expected ARGV to include second trailing arg", r.out.contains("two"))
assertTrue("Did not expect CLI exit()", r.exitCode == null)
}
} }

View File

@ -187,6 +187,8 @@ class Compiler(
private val externCallableNames: MutableSet<String> = mutableSetOf() private val externCallableNames: MutableSet<String> = mutableSetOf()
private val externBindingNames: MutableSet<String> = mutableSetOf() private val externBindingNames: MutableSet<String> = mutableSetOf()
private val moduleDeclaredNames: MutableSet<String> = mutableSetOf() private val moduleDeclaredNames: MutableSet<String> = mutableSetOf()
private val predeclaredTopLevelValueNames: MutableSet<String> = mutableSetOf()
private val moduleReferencePosByName: MutableMap<String, Pos> = mutableMapOf()
private var seedingSlotPlan: Boolean = false private var seedingSlotPlan: Boolean = false
private fun moduleForcedLocalSlotInfo(): Map<String, ForcedLocalSlotInfo> { private fun moduleForcedLocalSlotInfo(): Map<String, ForcedLocalSlotInfo> {
@ -393,6 +395,7 @@ class Compiler(
} }
declareSlotNameIn(plan, nameToken.value, isMutable = t.value == "var", isDelegated = false) declareSlotNameIn(plan, nameToken.value, isMutable = t.value == "var", isDelegated = false)
moduleDeclaredNames.add(nameToken.value) moduleDeclaredNames.add(nameToken.value)
predeclaredTopLevelValueNames.add(nameToken.value)
} }
"class", "object" -> { "class", "object" -> {
val nameToken = nextNonWs() val nameToken = nextNonWs()
@ -933,6 +936,7 @@ class Compiler(
} }
} }
captureLocalRef(name, slotLoc, pos)?.let { ref -> captureLocalRef(name, slotLoc, pos)?.let { ref ->
moduleReferencePosByName.putIfAbsent(name, pos)
resolutionSink?.reference(name, pos) resolutionSink?.reference(name, pos)
return ref return ref
} }
@ -978,8 +982,11 @@ class Compiler(
if (moduleLoc != null) { if (moduleLoc != null) {
val moduleDeclaredNames = localNamesStack.firstOrNull() val moduleDeclaredNames = localNamesStack.firstOrNull()
if (moduleDeclaredNames == null || !moduleDeclaredNames.contains(name)) { if (moduleDeclaredNames == null || !moduleDeclaredNames.contains(name)) {
resolveImportBinding(name, pos)?.let { resolved -> val resolvedImport = resolveImportBinding(name, pos)
registerImportBinding(name, resolved.binding, pos) if (resolvedImport != null) {
registerImportBinding(name, resolvedImport.binding, pos)
} else if (predeclaredTopLevelValueNames.contains(name)) {
throw ScriptError(pos, "symbol '$name' is not defined")
} }
} }
val ref = LocalSlotRef( val ref = LocalSlotRef(
@ -1008,8 +1015,11 @@ class Compiler(
if (moduleEntry != null) { if (moduleEntry != null) {
val moduleDeclaredNames = localNamesStack.firstOrNull() val moduleDeclaredNames = localNamesStack.firstOrNull()
if (moduleDeclaredNames == null || !moduleDeclaredNames.contains(name)) { if (moduleDeclaredNames == null || !moduleDeclaredNames.contains(name)) {
resolveImportBinding(name, pos)?.let { resolved -> val resolvedImport = resolveImportBinding(name, pos)
registerImportBinding(name, resolved.binding, pos) if (resolvedImport != null) {
registerImportBinding(name, resolvedImport.binding, pos)
} else if (predeclaredTopLevelValueNames.contains(name)) {
throw ScriptError(pos, "symbol '$name' is not defined")
} }
} }
val moduleLoc = SlotLocation( val moduleLoc = SlotLocation(
@ -1020,6 +1030,7 @@ class Compiler(
moduleEntry.isDelegated moduleEntry.isDelegated
) )
captureLocalRef(name, moduleLoc, pos)?.let { ref -> captureLocalRef(name, moduleLoc, pos)?.let { ref ->
moduleReferencePosByName.putIfAbsent(name, pos)
resolutionSink?.reference(name, pos) resolutionSink?.reference(name, pos)
return ref return ref
} }
@ -1046,6 +1057,7 @@ class Compiler(
strictSlotRefs strictSlotRefs
) )
} }
moduleReferencePosByName.putIfAbsent(name, pos)
resolutionSink?.reference(name, pos) resolutionSink?.reference(name, pos)
return ref return ref
} }
@ -1080,6 +1092,7 @@ class Compiler(
val slot = lookupSlotLocation(name) val slot = lookupSlotLocation(name)
if (slot != null) { if (slot != null) {
captureLocalRef(name, slot, pos)?.let { ref -> captureLocalRef(name, slot, pos)?.let { ref ->
moduleReferencePosByName.putIfAbsent(name, pos)
resolutionSink?.reference(name, pos) resolutionSink?.reference(name, pos)
return ref return ref
} }
@ -1106,6 +1119,7 @@ class Compiler(
strictSlotRefs strictSlotRefs
) )
} }
moduleReferencePosByName.putIfAbsent(name, pos)
resolutionSink?.reference(name, pos) resolutionSink?.reference(name, pos)
return ref return ref
} }
@ -1757,6 +1771,8 @@ class Compiler(
callableReturnTypeByScopeId = callableReturnTypeByScopeId, callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName, callableReturnTypeByName = callableReturnTypeByName,
externBindingNames = externBindingNames, externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
scopeRefPosByName = moduleReferencePosByName,
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
) as BytecodeStatement ) as BytecodeStatement
unwrapped to bytecodeStmt.bytecodeFunction() unwrapped to bytecodeStmt.bytecodeFunction()
@ -2085,6 +2101,8 @@ class Compiler(
callableReturnTypeByName = callableReturnTypeByName, callableReturnTypeByName = callableReturnTypeByName,
externCallableNames = externCallableNames, externCallableNames = externCallableNames,
externBindingNames = externBindingNames, externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
scopeRefPosByName = moduleReferencePosByName,
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
) )
} }
@ -2116,6 +2134,8 @@ class Compiler(
callableReturnTypeByName = callableReturnTypeByName, callableReturnTypeByName = callableReturnTypeByName,
externCallableNames = externCallableNames, externCallableNames = externCallableNames,
externBindingNames = externBindingNames, externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
scopeRefPosByName = moduleReferencePosByName,
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
) )
} }
@ -2172,6 +2192,8 @@ class Compiler(
callableReturnTypeByName = callableReturnTypeByName, callableReturnTypeByName = callableReturnTypeByName,
externCallableNames = externCallableNames, externCallableNames = externCallableNames,
externBindingNames = externBindingNames, externBindingNames = externBindingNames,
preparedModuleBindingNames = importBindings.keys,
scopeRefPosByName = moduleReferencePosByName,
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
) )
} }

View File

@ -43,6 +43,8 @@ class BytecodeCompiler(
private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(), private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
private val externCallableNames: Set<String> = emptySet(), private val externCallableNames: Set<String> = emptySet(),
private val externBindingNames: Set<String> = emptySet(), private val externBindingNames: Set<String> = emptySet(),
private val preparedModuleBindingNames: Set<String> = emptySet(),
private val scopeRefPosByName: Map<String, Pos> = emptyMap(),
private val lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(), private val lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
) { ) {
private val useScopeSlots: Boolean = allowedScopeNames != null || scopeSlotNameSet != null private val useScopeSlots: Boolean = allowedScopeNames != null || scopeSlotNameSet != null
@ -53,12 +55,15 @@ class BytecodeCompiler(
private var scopeSlotIndices = IntArray(0) private var scopeSlotIndices = IntArray(0)
private var scopeSlotNames = emptyArray<String?>() private var scopeSlotNames = emptyArray<String?>()
private var scopeSlotIsModule = BooleanArray(0) private var scopeSlotIsModule = BooleanArray(0)
private var scopeSlotRequiresPreparedBinding = BooleanArray(0)
private var scopeSlotRefPos = emptyArray<Pos?>()
private var scopeSlotMutables = BooleanArray(0) private var scopeSlotMutables = BooleanArray(0)
private var scopeKeyByIndex = emptyArray<ScopeSlotKey?>() private var scopeKeyByIndex = emptyArray<ScopeSlotKey?>()
private val scopeSlotMap = LinkedHashMap<ScopeSlotKey, Int>() private val scopeSlotMap = LinkedHashMap<ScopeSlotKey, Int>()
private val scopeSlotNameMap = LinkedHashMap<ScopeSlotKey, String>() private val scopeSlotNameMap = LinkedHashMap<ScopeSlotKey, String>()
private val scopeSlotMutableMap = LinkedHashMap<ScopeSlotKey, Boolean>() private val scopeSlotMutableMap = LinkedHashMap<ScopeSlotKey, Boolean>()
private val scopeSlotIndexByName = LinkedHashMap<String, Int>() private val scopeSlotIndexByName = LinkedHashMap<String, Int>()
private val scopeSlotRefPosByKey = LinkedHashMap<ScopeSlotKey, Pos>()
private val pendingScopeNameRefs = LinkedHashSet<String>() private val pendingScopeNameRefs = LinkedHashSet<String>()
private val addrSlotByScopeSlot = LinkedHashMap<Int, Int>() private val addrSlotByScopeSlot = LinkedHashMap<Int, Int>()
private data class LocalSlotInfo(val name: String, val isMutable: Boolean, val isDelegated: Boolean) private data class LocalSlotInfo(val name: String, val isMutable: Boolean, val isDelegated: Boolean)
@ -119,6 +124,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -140,6 +147,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -158,6 +167,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -176,6 +187,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -194,6 +207,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -212,6 +227,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -230,6 +247,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -248,6 +267,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -266,6 +287,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -284,6 +307,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -302,6 +327,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -322,6 +349,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -340,6 +369,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -358,6 +389,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -376,6 +409,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -395,6 +430,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -416,6 +453,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -439,6 +478,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -459,6 +500,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -534,6 +577,7 @@ class BytecodeCompiler(
resolved = SlotType.INT resolved = SlotType.INT
} }
if (mapped < scopeSlotCount && resolved != SlotType.UNKNOWN) { if (mapped < scopeSlotCount && resolved != SlotType.UNKNOWN) {
noteScopeSlotRef(mapped, ref.pos())
val addrSlot = ensureScopeAddr(mapped) val addrSlot = ensureScopeAddr(mapped)
val local = allocSlot() val local = allocSlot()
emitLoadFromAddr(addrSlot, local, resolved) emitLoadFromAddr(addrSlot, local, resolved)
@ -544,6 +588,7 @@ class BytecodeCompiler(
return CompiledValue(local, resolved) return CompiledValue(local, resolved)
} }
if (mapped < scopeSlotCount && resolved == SlotType.UNKNOWN) { if (mapped < scopeSlotCount && resolved == SlotType.UNKNOWN) {
noteScopeSlotRef(mapped, ref.pos())
val addrSlot = ensureScopeAddr(mapped) val addrSlot = ensureScopeAddr(mapped)
val local = allocSlot() val local = allocSlot()
emitLoadFromAddr(addrSlot, local, SlotType.OBJ) emitLoadFromAddr(addrSlot, local, SlotType.OBJ)
@ -563,6 +608,7 @@ class BytecodeCompiler(
} }
if (allowLocalSlots) { if (allowLocalSlots) {
scopeSlotIndexByName[ref.name]?.let { slot -> scopeSlotIndexByName[ref.name]?.let { slot ->
noteScopeSlotRef(slot, callSitePos())
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
return CompiledValue(slot, resolved) return CompiledValue(slot, resolved)
} }
@ -585,6 +631,7 @@ class BytecodeCompiler(
return CompiledValue(slot, resolved) return CompiledValue(slot, resolved)
} }
scopeSlotIndexByName[ref.name]?.let { slot -> scopeSlotIndexByName[ref.name]?.let { slot ->
noteScopeSlotRef(slot, callSitePos())
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
return CompiledValue(slot, resolved) return CompiledValue(slot, resolved)
} }
@ -596,6 +643,7 @@ class BytecodeCompiler(
val slot = ref.slotIndex() val slot = ref.slotIndex()
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
if (slot < scopeSlotCount && resolved != SlotType.UNKNOWN) { if (slot < scopeSlotCount && resolved != SlotType.UNKNOWN) {
noteScopeSlotRef(slot, callSitePos())
val addrSlot = ensureScopeAddr(slot) val addrSlot = ensureScopeAddr(slot)
val local = allocSlot() val local = allocSlot()
emitLoadFromAddr(addrSlot, local, resolved) emitLoadFromAddr(addrSlot, local, resolved)
@ -5002,6 +5050,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -5021,6 +5071,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -5041,6 +5093,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -5061,6 +5115,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -5084,6 +5140,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -5103,6 +5161,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -5727,6 +5787,8 @@ class BytecodeCompiler(
scopeSlotIndices, scopeSlotIndices,
scopeSlotNames, scopeSlotNames,
scopeSlotIsModule, scopeSlotIsModule,
scopeSlotRequiresPreparedBinding,
scopeSlotRefPos,
localSlotNames, localSlotNames,
localSlotMutables, localSlotMutables,
localSlotDelegated, localSlotDelegated,
@ -7599,6 +7661,12 @@ class BytecodeCompiler(
private fun assignValue(ref: AssignRef): ObjRef = ref.value private fun assignValue(ref: AssignRef): ObjRef = ref.value
private fun refPos(ref: BinaryOpRef): Pos = Pos.builtIn private fun refPos(ref: BinaryOpRef): Pos = Pos.builtIn
private fun noteScopeSlotRef(slot: Int, pos: Pos) {
if (slot >= scopeSlotCount) return
val key = scopeKeyByIndex.getOrNull(slot) ?: return
scopeSlotRefPosByKey.putIfAbsent(key, pos)
}
private fun resolveSlot(ref: LocalSlotRef): Int? { private fun resolveSlot(ref: LocalSlotRef): Int? {
loopSlotOverrides[ref.name]?.let { return it } loopSlotOverrides[ref.name]?.let { return it }
val scopeId = refScopeId(ref) val scopeId = refScopeId(ref)
@ -7606,8 +7674,14 @@ class BytecodeCompiler(
val key = ScopeSlotKey(scopeId, refSlot(ref)) val key = ScopeSlotKey(scopeId, refSlot(ref))
val localIndex = localSlotIndexByKey[key] val localIndex = localSlotIndexByKey[key]
if (localIndex != null) return scopeSlotCount + localIndex if (localIndex != null) return scopeSlotCount + localIndex
scopeSlotMap[key]?.let { return it } scopeSlotMap[key]?.let {
scopeSlotIndexByName[ref.name]?.let { return it } scopeSlotRefPosByKey.putIfAbsent(key, ref.pos())
return it
}
scopeSlotIndexByName[ref.name]?.let {
scopeSlotRefPosByKey.putIfAbsent(key, ref.pos())
return it
}
} }
if (ref.captureOwnerScopeId != null) { if (ref.captureOwnerScopeId != null) {
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
@ -7617,7 +7691,11 @@ class BytecodeCompiler(
return scopeSlotCount + localIndex return scopeSlotCount + localIndex
} }
} }
return scopeSlotMap[scopeKey] val resolved = scopeSlotMap[scopeKey]
if (resolved != null) {
scopeSlotRefPosByKey.putIfAbsent(scopeKey, ref.pos())
}
return resolved
} }
if (ref.isDelegated) { if (ref.isDelegated) {
val localKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) val localKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
@ -7628,7 +7706,11 @@ class BytecodeCompiler(
val localIndex = localSlotIndexByKey[localKey] val localIndex = localSlotIndexByKey[localKey]
if (localIndex != null) return scopeSlotCount + localIndex if (localIndex != null) return scopeSlotCount + localIndex
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
return scopeSlotMap[scopeKey] val resolved = scopeSlotMap[scopeKey]
if (resolved != null) {
scopeSlotRefPosByKey.putIfAbsent(scopeKey, ref.pos())
}
return resolved
} }
private fun resolveCapturedOwnerScopeSlot(ref: LocalSlotRef): Int? { private fun resolveCapturedOwnerScopeSlot(ref: LocalSlotRef): Int? {
@ -7858,6 +7940,8 @@ class BytecodeCompiler(
scopeSlotIndices = IntArray(scopeSlotCount) scopeSlotIndices = IntArray(scopeSlotCount)
scopeSlotNames = arrayOfNulls(scopeSlotCount) scopeSlotNames = arrayOfNulls(scopeSlotCount)
scopeSlotIsModule = BooleanArray(scopeSlotCount) scopeSlotIsModule = BooleanArray(scopeSlotCount)
scopeSlotRequiresPreparedBinding = BooleanArray(scopeSlotCount)
scopeSlotRefPos = arrayOfNulls(scopeSlotCount)
scopeSlotMutables = BooleanArray(scopeSlotCount) { true } scopeSlotMutables = BooleanArray(scopeSlotCount) { true }
scopeKeyByIndex = arrayOfNulls(scopeSlotCount) scopeKeyByIndex = arrayOfNulls(scopeSlotCount)
for ((key, index) in scopeSlotMap) { for ((key, index) in scopeSlotMap) {
@ -7865,6 +7949,8 @@ class BytecodeCompiler(
scopeSlotIndices[index] = key.slot scopeSlotIndices[index] = key.slot
scopeSlotNames[index] = name scopeSlotNames[index] = name
scopeSlotIsModule[index] = moduleScopeId != null && key.scopeId == moduleScopeId scopeSlotIsModule[index] = moduleScopeId != null && key.scopeId == moduleScopeId
scopeSlotRequiresPreparedBinding[index] = name != null && preparedModuleBindingNames.contains(name)
scopeSlotRefPos[index] = name?.let { scopeRefPosByName[it] } ?: scopeSlotRefPosByKey[key]
scopeSlotMutableMap[key]?.let { scopeSlotMutables[index] = it } scopeSlotMutableMap[key]?.let { scopeSlotMutables[index] = it }
scopeKeyByIndex[index] = key scopeKeyByIndex[index] = key
} }

View File

@ -88,6 +88,8 @@ class BytecodeStatement private constructor(
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(), callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
externCallableNames: Set<String> = emptySet(), externCallableNames: Set<String> = emptySet(),
externBindingNames: Set<String> = emptySet(), externBindingNames: Set<String> = emptySet(),
preparedModuleBindingNames: Set<String> = emptySet(),
scopeRefPosByName: Map<String, Pos> = emptyMap(),
lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(), lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
slotTypeDeclByScopeId: Map<Int, Map<Int, TypeDecl>> = emptyMap(), slotTypeDeclByScopeId: Map<Int, Map<Int, TypeDecl>> = emptyMap(),
): Statement { ): Statement {
@ -124,6 +126,8 @@ class BytecodeStatement private constructor(
callableReturnTypeByName = callableReturnTypeByName, callableReturnTypeByName = callableReturnTypeByName,
externCallableNames = externCallableNames, externCallableNames = externCallableNames,
externBindingNames = externBindingNames, externBindingNames = externBindingNames,
preparedModuleBindingNames = preparedModuleBindingNames,
scopeRefPosByName = scopeRefPosByName,
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
) )
val compiled = compiler.compileStatement(nameHint, statement) val compiled = compiler.compileStatement(nameHint, statement)

View File

@ -67,6 +67,8 @@ class CmdBuilder {
scopeSlotIndices: IntArray = IntArray(0), scopeSlotIndices: IntArray = IntArray(0),
scopeSlotNames: Array<String?> = emptyArray(), scopeSlotNames: Array<String?> = emptyArray(),
scopeSlotIsModule: BooleanArray = BooleanArray(0), scopeSlotIsModule: BooleanArray = BooleanArray(0),
scopeSlotRequiresPreparedBinding: BooleanArray = BooleanArray(0),
scopeSlotRefPos: Array<net.sergeych.lyng.Pos?> = emptyArray(),
localSlotNames: Array<String?> = emptyArray(), localSlotNames: Array<String?> = emptyArray(),
localSlotMutables: BooleanArray = BooleanArray(0), localSlotMutables: BooleanArray = BooleanArray(0),
localSlotDelegated: BooleanArray = BooleanArray(0), localSlotDelegated: BooleanArray = BooleanArray(0),
@ -79,6 +81,12 @@ class CmdBuilder {
require(scopeSlotIsModule.isEmpty() || scopeSlotIsModule.size == scopeSlotCount) { require(scopeSlotIsModule.isEmpty() || scopeSlotIsModule.size == scopeSlotCount) {
"scope slot module mapping size mismatch" "scope slot module mapping size mismatch"
} }
require(scopeSlotRequiresPreparedBinding.isEmpty() || scopeSlotRequiresPreparedBinding.size == scopeSlotCount) {
"scope slot prepared-binding mapping size mismatch"
}
require(scopeSlotRefPos.isEmpty() || scopeSlotRefPos.size == scopeSlotCount) {
"scope slot position mapping size mismatch"
}
require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" } require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" }
require(localSlotNames.size == localSlotDelegated.size) { "local slot delegation size mismatch" } require(localSlotNames.size == localSlotDelegated.size) { "local slot delegation size mismatch" }
require(localSlotNames.size == localSlotCaptures.size) { "local slot capture size mismatch" } require(localSlotNames.size == localSlotCaptures.size) { "local slot capture size mismatch" }
@ -113,6 +121,8 @@ class CmdBuilder {
scopeSlotIndices = scopeSlotIndices, scopeSlotIndices = scopeSlotIndices,
scopeSlotNames = if (scopeSlotNames.isEmpty()) Array(scopeSlotCount) { null } else scopeSlotNames, scopeSlotNames = if (scopeSlotNames.isEmpty()) Array(scopeSlotCount) { null } else scopeSlotNames,
scopeSlotIsModule = if (scopeSlotIsModule.isEmpty()) BooleanArray(scopeSlotCount) else scopeSlotIsModule, scopeSlotIsModule = if (scopeSlotIsModule.isEmpty()) BooleanArray(scopeSlotCount) else scopeSlotIsModule,
scopeSlotRequiresPreparedBinding = if (scopeSlotRequiresPreparedBinding.isEmpty()) BooleanArray(scopeSlotCount) else scopeSlotRequiresPreparedBinding,
scopeSlotRefPos = if (scopeSlotRefPos.isEmpty()) Array(scopeSlotCount) { null } else scopeSlotRefPos,
localSlotNames = localSlotNames, localSlotNames = localSlotNames,
localSlotMutables = localSlotMutables, localSlotMutables = localSlotMutables,
localSlotDelegated = localSlotDelegated, localSlotDelegated = localSlotDelegated,

View File

@ -26,6 +26,8 @@ data class CmdFunction(
val scopeSlotIndices: IntArray, val scopeSlotIndices: IntArray,
val scopeSlotNames: Array<String?>, val scopeSlotNames: Array<String?>,
val scopeSlotIsModule: BooleanArray, val scopeSlotIsModule: BooleanArray,
val scopeSlotRequiresPreparedBinding: BooleanArray,
val scopeSlotRefPos: Array<net.sergeych.lyng.Pos?>,
val localSlotNames: Array<String?>, val localSlotNames: Array<String?>,
val localSlotMutables: BooleanArray, val localSlotMutables: BooleanArray,
val localSlotDelegated: BooleanArray, val localSlotDelegated: BooleanArray,
@ -38,6 +40,8 @@ data class CmdFunction(
require(scopeSlotIndices.size == scopeSlotCount) { "scopeSlotIndices size mismatch" } require(scopeSlotIndices.size == scopeSlotCount) { "scopeSlotIndices size mismatch" }
require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames size mismatch" } require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames size mismatch" }
require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" } require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" }
require(scopeSlotRequiresPreparedBinding.size == scopeSlotCount) { "scopeSlotRequiresPreparedBinding size mismatch" }
require(scopeSlotRefPos.size == scopeSlotCount) { "scopeSlotRefPos size mismatch" }
require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" } require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" }
require(localSlotNames.size == localSlotDelegated.size) { "localSlot delegation size mismatch" } require(localSlotNames.size == localSlotDelegated.size) { "localSlot delegation size mismatch" }
require(localSlotNames.size == localSlotCaptures.size) { "localSlot capture size mismatch" } require(localSlotNames.size == localSlotCaptures.size) { "localSlot capture size mismatch" }

View File

@ -4112,7 +4112,7 @@ class CmdFrame(
} }
fun ensureScope(): Scope { fun ensureScope(): Scope {
val pos = posForIp(ip - 1) val pos = currentErrorPos()
if (pos != null && lastScopePosIp != ip) { if (pos != null && lastScopePosIp != ip) {
scope.pos = pos scope.pos = pos
lastScopePosIp = ip lastScopePosIp = ip
@ -4189,6 +4189,33 @@ class CmdFrame(
return fn.posByIp.getOrNull(ip) return fn.posByIp.getOrNull(ip)
} }
private fun currentErrorPos(): Pos? {
val center = ip - 1
if (center < 0) return null
var fallback: Pos? = null
val maxRadius = maxOf(center, fn.posByIp.size - 1 - center)
for (radius in 0..maxRadius) {
val before = center - radius
if (before >= 0) {
val pos = posForIp(before)
if (pos != null) {
if (pos.source !== Source.builtIn && pos.source !== Source.UNKNOWN) return pos
if (fallback == null) fallback = pos
}
}
if (radius == 0) continue
val after = center + radius
if (after < fn.posByIp.size) {
val pos = posForIp(after)
if (pos != null) {
if (pos.source !== Source.builtIn && pos.source !== Source.UNKNOWN) return pos
if (fallback == null) fallback = pos
}
}
}
return fallback
}
fun pushScope(plan: Map<String, Int>, captures: List<String>) { fun pushScope(plan: Map<String, Int>, captures: List<String>) {
if (scope.skipScopeCreation) { if (scope.skipScopeCreation) {
val snapshot = emptyMap<String, Int?>() val snapshot = emptyMap<String, Int?>()
@ -5045,8 +5072,12 @@ class CmdFrame(
if (hadNamedBinding) return if (hadNamedBinding) return
if (record.value !== ObjUnset) return if (record.value !== ObjUnset) return
if (fn.scopeSlotIsModule.getOrNull(slot) != true) return if (fn.scopeSlotIsModule.getOrNull(slot) != true) return
val pos = fn.scopeSlotRefPos.getOrNull(slot) ?: currentErrorPos() ?: ensureScope().pos
if (fn.scopeSlotRequiresPreparedBinding.getOrNull(slot) != true) {
throw ScriptError(pos, "symbol '$name' is not defined")
}
throw ScriptError( throw ScriptError(
ensureScope().pos, pos,
"module binding '$name' is not available in the execution scope; prepare the script imports/module bindings explicitly" "module binding '$name' is not available in the execution scope; prepare the script imports/module bindings explicitly"
) )
} }

View File

@ -0,0 +1,55 @@
/*
* 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.
* 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.
*
*/
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.ScriptError
import net.sergeych.lyng.eval
import kotlin.test.Test
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class CalculusDiagnosticRegressionTest {
@Test
fun undefinedHInCalculusSnippetReportsPreciseDiagnostic() = runTest {
val ex = assertFailsWith<ScriptError> {
eval(
"""
var x = 7.0
// глубина по звуку падения
val m = 1 // kg
val d = 0.06 // 6 cm
val c = 340 // скор. звука
val g = 9.82
var cnt = 0
var t = x
var message = ""
val hinv = 1/h
var h = c*c/g*(1 + g*t/c -sqrt(1+2*g*t/c))
assert(h is Real)
assert(!h.isNaN())
""".trimIndent()
)
}
assertEquals(9, ex.pos.line)
assertEquals(13, ex.pos.column)
assertContains(ex.errorMessage, "symbol 'h' is not defined")
assertContains(ex.message ?: "", "val hinv = 1/h")
}
}

View File

@ -5474,33 +5474,112 @@ class ScriptTest {
) )
} }
// @Test @Test
// fun testFromCalcrus1() = runTest { fun testFromCalcurus1() = runTest {
// eval($$""" eval($$"""
// import lyng.decimal
// var x = 7.0.d /*
// // глубина по звуку падения Рассчитывает глубину провала по времени падения камня и прихода звука.
// val m = 1 // kg
// val d = 0.06 // 6 cm @param T измеренное полное время (с)
// val c = 340 // скор. звука @param m масса камня (кг)
// val g = 9.82 @param d диаметр камня (м) (предполагается сферическая форма)
// var cnt = 0 @param rho плотность воздуха (кг/м³), по умолчанию 1.2
// var h = 0.0 @param c скорость звука (м/с), по умолчанию 340.0
// var t = x @param g ускорение свободного падения (м/с²), по умолчанию 9.81
// var message = "" @param Cd коэффициент лобового сопротивления, по умолчанию 0.5
// @param epsilon относительная точность (м), по умолчанию 1e-3
// while(true){ @param maxIter максимальное число итераций, по умолчанию 20
// val h0 = 0 @return глубина h (м), или null если расчёт не сошёлся
// h = c*c/h*(1 + g*t/c -sqrt(1+2*g*t/c)) */
// message = "iter ${cnt++}" fun calculateDepth(
// if( cnt > 100 ) { T: Real,
// message= "ошибка" m: Real,
// break 0 d: Real,
// } rho: Real = 1.2,
// x = h c: Real = 340.0,
// if( abs(h-h0)/h > 0.08 ) break h g: Real = 9.81,
// } Cd: Real = 0.5,
// println(x) epsilon: Real = 1e-3,
// """.trimIndent()) maxIter: Int = 20
// } ): Real? {
// Площадь миделя
val r = d / 2.0
val A = π * r * r
// Коэффициент сопротивления
val k = 0.5 * Cd * rho * A
// Предельная скорость
val vTerm = sqrt(m * g / k)
// Функция времени падения с высоты h
fun tFall(h: Real): Real {
val arg = exp(g * h / (vTerm * vTerm))
// arcosh(x) = ln(x + sqrt(x^2 - 1))
val arcosh = ln(arg + sqrt(arg * arg - 1.0))
return vTerm / g * arcosh
}
// Производная времени падения по h
fun dtFall_dh(h: Real): Real {
val expArg = exp(2.0 * g * h / (vTerm * vTerm))
return 1.0 / (vTerm * sqrt(expArg - 1.0))
}
// Полное расчётное время T_calc(h) = tFall(h) + h/c
fun Tcalc(h: Real): Real = tFall(h) + h / c
// Производная T_calc по h
fun dTcalc_dh(h: Real): Real = dtFall_dh(h) + 1.0 / c
// Начальное приближение (без сопротивления)
val term = 1.0 + g * T / c
val sqrtTerm = sqrt(1.0 + 2.0 * g * T / c)
var h = (c * c / g) * (term - sqrtTerm)
// Проверка на валидность начального приближения
if (h.isNaN() || h <= 0.0) {
// Если формула дала некорректный результат, используем оценку по свободному падению
h = 0.5 * g * T * T // грубая оценка, всё равно будет уточняться
if (h.isNaN() || h <= 0.0) return null
}
// Итерации Ньютона
var iter = 0
while (iter < maxIter) {
val f = Tcalc(h) - T
val df = dTcalc_dh(h)
// Если производная близка к нулю, выходим
if (abs(df) < 1e-12) return null
val hNew = h - f / df
// Проверка сходимости
if (abs(hNew - h) < epsilon) {
return hNew
}
h = hNew
iter++
}
// Не сошлось за maxIter
return null
}
// Пример использования
val T = 6.0 // секунды
val m = 1.0 // кг
val d = 0.1 // м (10 см)
val depth = calculateDepth(T, m, d)
if (depth != null) {
println("Глубина: %.2f м".format(depth))
} else {
println("Расчёт не сошёлся")
}
""".trimIndent())
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2026 Sergey S. Chernov * Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -12,17 +12,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.FrameSlotRef import net.sergeych.lyng.FrameSlotRef
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.bytecode.BytecodeConst import net.sergeych.lyng.bytecode.*
import net.sergeych.lyng.bytecode.CmdFrame
import net.sergeych.lyng.bytecode.CmdFunction
import net.sergeych.lyng.bytecode.CmdVm
import net.sergeych.lyng.bytecode.seedFrameLocalsFromScope
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertNull import kotlin.test.assertNull
@ -39,6 +36,8 @@ class SeedLocalsRegressionTest {
scopeSlotIndices = intArrayOf(0), scopeSlotIndices = intArrayOf(0),
scopeSlotNames = arrayOf(null), scopeSlotNames = arrayOf(null),
scopeSlotIsModule = booleanArrayOf(false), scopeSlotIsModule = booleanArrayOf(false),
scopeSlotRequiresPreparedBinding = booleanArrayOf(false),
scopeSlotRefPos = arrayOfNulls(1),
localSlotNames = arrayOf("x"), localSlotNames = arrayOf("x"),
localSlotMutables = booleanArrayOf(true), localSlotMutables = booleanArrayOf(true),
localSlotDelegated = booleanArrayOf(false), localSlotDelegated = booleanArrayOf(false),

View File

@ -0,0 +1,44 @@
/*
* 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.
* 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.
*
*/
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.ScriptError
import net.sergeych.lyng.eval
import kotlin.test.Test
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class UndefinedSymbolDiagnosticTest {
@Test
fun laterDeclaredTopLevelSymbolReportsUndefinedNameAtUseSite() = runTest {
val ex = assertFailsWith<ScriptError> {
eval(
"""
var x = 7.0
val hinv = 1/h
var h = x
""".trimIndent()
)
}
assertEquals(1, ex.pos.line)
assertEquals(13, ex.pos.column)
assertContains(ex.errorMessage, "symbol 'h' is not defined")
}
}