- fixed CLI -x mode
- fixed a wrong error message when global symbol is not found
This commit is contained in:
parent
6e9333844e
commit
c097464750
@ -1,28 +1,28 @@
|
||||
|
||||
/*
|
||||
Рассчитывает глубину провала по времени падения камня и прихода звука.
|
||||
Рассчитывает глубину провала по времени падения камня и прихода звука.
|
||||
|
||||
@param T измеренное полное время (с)
|
||||
@param m масса камня (кг)
|
||||
@param d диаметр камня (м) (предполагается сферическая форма)
|
||||
@param rho плотность воздуха (кг/м³), по умолчанию 1.2
|
||||
@param c скорость звука (м/с), по умолчанию 340.0
|
||||
@param g ускорение свободного падения (м/с²), по умолчанию 9.81
|
||||
@param Cd коэффициент лобового сопротивления, по умолчанию 0.5
|
||||
@param epsilon относительная точность (м), по умолчанию 1e-3
|
||||
@param maxIter максимальное число итераций, по умолчанию 20
|
||||
@return глубина h (м), или null если расчёт не сошёлся
|
||||
*/
|
||||
@param T измеренное полное время (с)
|
||||
@param m масса камня (кг)
|
||||
@param d диаметр камня (м) (предполагается сферическая форма)
|
||||
@param rho плотность воздуха (кг/м³), по умолчанию 1.2
|
||||
@param c скорость звука (м/с), по умолчанию 340.0
|
||||
@param g ускорение свободного падения (м/с²), по умолчанию 9.81
|
||||
@param Cd коэффициент лобового сопротивления, по умолчанию 0.5
|
||||
@param epsilon относительная точность (м), по умолчанию 1e-3
|
||||
@param maxIter максимальное число итераций, по умолчанию 20
|
||||
@return глубина h (м), или null если расчёт не сошёлся
|
||||
*/
|
||||
fun calculateDepth(
|
||||
T: Real,
|
||||
m: Real,
|
||||
d: Real,
|
||||
rho: Real = 1.2,
|
||||
c: Real = 340.0,
|
||||
g: Real = 9.81,
|
||||
Cd: Real = 0.5,
|
||||
epsilon: Real = 1e-3,
|
||||
maxIter: Int = 20
|
||||
T: Real,
|
||||
m: Real,
|
||||
d: Real,
|
||||
rho: Real = 1.2,
|
||||
c: Real = 340.0,
|
||||
g: Real = 9.81,
|
||||
Cd: Real = 0.5,
|
||||
epsilon: Real = 1e-3,
|
||||
maxIter: Int = 20
|
||||
): Real? {
|
||||
// Площадь миделя
|
||||
val r = d / 2.0
|
||||
@ -90,14 +90,14 @@ fun calculateDepth(
|
||||
return null
|
||||
}
|
||||
|
||||
// Пример использования
|
||||
val T = 6.0 // секунды
|
||||
val m = 1.0 // кг
|
||||
val d = 0.1 // м (10 см)
|
||||
// Пример использования
|
||||
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("Расчёт не сошёлся")
|
||||
}
|
||||
val depth = calculateDepth(T, m, d)
|
||||
if (depth != null) {
|
||||
println("Глубина: %.2f м".format(depth))
|
||||
} else {
|
||||
println("Расчёт не сошёлся")
|
||||
}
|
||||
|
||||
@ -158,6 +158,7 @@ private class Fmt : CliktCommand(name = "fmt") {
|
||||
|
||||
private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
|
||||
|
||||
override val invokeWithoutSubcommand = true
|
||||
override val printHelpOnEmptyArgs = true
|
||||
|
||||
val version by option("-v", "--version", help = "Print version and exit").flag()
|
||||
|
||||
@ -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.
|
||||
@ -131,4 +131,18 @@ class CliFmtJvmTest {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +187,8 @@ class Compiler(
|
||||
private val externCallableNames: MutableSet<String> = mutableSetOf()
|
||||
private val externBindingNames: 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 fun moduleForcedLocalSlotInfo(): Map<String, ForcedLocalSlotInfo> {
|
||||
@ -393,6 +395,7 @@ class Compiler(
|
||||
}
|
||||
declareSlotNameIn(plan, nameToken.value, isMutable = t.value == "var", isDelegated = false)
|
||||
moduleDeclaredNames.add(nameToken.value)
|
||||
predeclaredTopLevelValueNames.add(nameToken.value)
|
||||
}
|
||||
"class", "object" -> {
|
||||
val nameToken = nextNonWs()
|
||||
@ -933,6 +936,7 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
captureLocalRef(name, slotLoc, pos)?.let { ref ->
|
||||
moduleReferencePosByName.putIfAbsent(name, pos)
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
@ -978,8 +982,11 @@ class Compiler(
|
||||
if (moduleLoc != null) {
|
||||
val moduleDeclaredNames = localNamesStack.firstOrNull()
|
||||
if (moduleDeclaredNames == null || !moduleDeclaredNames.contains(name)) {
|
||||
resolveImportBinding(name, pos)?.let { resolved ->
|
||||
registerImportBinding(name, resolved.binding, pos)
|
||||
val resolvedImport = resolveImportBinding(name, 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(
|
||||
@ -1008,8 +1015,11 @@ class Compiler(
|
||||
if (moduleEntry != null) {
|
||||
val moduleDeclaredNames = localNamesStack.firstOrNull()
|
||||
if (moduleDeclaredNames == null || !moduleDeclaredNames.contains(name)) {
|
||||
resolveImportBinding(name, pos)?.let { resolved ->
|
||||
registerImportBinding(name, resolved.binding, pos)
|
||||
val resolvedImport = resolveImportBinding(name, 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(
|
||||
@ -1020,6 +1030,7 @@ class Compiler(
|
||||
moduleEntry.isDelegated
|
||||
)
|
||||
captureLocalRef(name, moduleLoc, pos)?.let { ref ->
|
||||
moduleReferencePosByName.putIfAbsent(name, pos)
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
@ -1046,6 +1057,7 @@ class Compiler(
|
||||
strictSlotRefs
|
||||
)
|
||||
}
|
||||
moduleReferencePosByName.putIfAbsent(name, pos)
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
@ -1080,6 +1092,7 @@ class Compiler(
|
||||
val slot = lookupSlotLocation(name)
|
||||
if (slot != null) {
|
||||
captureLocalRef(name, slot, pos)?.let { ref ->
|
||||
moduleReferencePosByName.putIfAbsent(name, pos)
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
@ -1106,6 +1119,7 @@ class Compiler(
|
||||
strictSlotRefs
|
||||
)
|
||||
}
|
||||
moduleReferencePosByName.putIfAbsent(name, pos)
|
||||
resolutionSink?.reference(name, pos)
|
||||
return ref
|
||||
}
|
||||
@ -1757,6 +1771,8 @@ class Compiler(
|
||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||
callableReturnTypeByName = callableReturnTypeByName,
|
||||
externBindingNames = externBindingNames,
|
||||
preparedModuleBindingNames = importBindings.keys,
|
||||
scopeRefPosByName = moduleReferencePosByName,
|
||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||
) as BytecodeStatement
|
||||
unwrapped to bytecodeStmt.bytecodeFunction()
|
||||
@ -2085,6 +2101,8 @@ class Compiler(
|
||||
callableReturnTypeByName = callableReturnTypeByName,
|
||||
externCallableNames = externCallableNames,
|
||||
externBindingNames = externBindingNames,
|
||||
preparedModuleBindingNames = importBindings.keys,
|
||||
scopeRefPosByName = moduleReferencePosByName,
|
||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||
)
|
||||
}
|
||||
@ -2116,6 +2134,8 @@ class Compiler(
|
||||
callableReturnTypeByName = callableReturnTypeByName,
|
||||
externCallableNames = externCallableNames,
|
||||
externBindingNames = externBindingNames,
|
||||
preparedModuleBindingNames = importBindings.keys,
|
||||
scopeRefPosByName = moduleReferencePosByName,
|
||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||
)
|
||||
}
|
||||
@ -2172,6 +2192,8 @@ class Compiler(
|
||||
callableReturnTypeByName = callableReturnTypeByName,
|
||||
externCallableNames = externCallableNames,
|
||||
externBindingNames = externBindingNames,
|
||||
preparedModuleBindingNames = importBindings.keys,
|
||||
scopeRefPosByName = moduleReferencePosByName,
|
||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||
)
|
||||
}
|
||||
|
||||
@ -43,6 +43,8 @@ class BytecodeCompiler(
|
||||
private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
||||
private val externCallableNames: 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 useScopeSlots: Boolean = allowedScopeNames != null || scopeSlotNameSet != null
|
||||
@ -53,12 +55,15 @@ class BytecodeCompiler(
|
||||
private var scopeSlotIndices = IntArray(0)
|
||||
private var scopeSlotNames = emptyArray<String?>()
|
||||
private var scopeSlotIsModule = BooleanArray(0)
|
||||
private var scopeSlotRequiresPreparedBinding = BooleanArray(0)
|
||||
private var scopeSlotRefPos = emptyArray<Pos?>()
|
||||
private var scopeSlotMutables = BooleanArray(0)
|
||||
private var scopeKeyByIndex = emptyArray<ScopeSlotKey?>()
|
||||
private val scopeSlotMap = LinkedHashMap<ScopeSlotKey, Int>()
|
||||
private val scopeSlotNameMap = LinkedHashMap<ScopeSlotKey, String>()
|
||||
private val scopeSlotMutableMap = LinkedHashMap<ScopeSlotKey, Boolean>()
|
||||
private val scopeSlotIndexByName = LinkedHashMap<String, Int>()
|
||||
private val scopeSlotRefPosByKey = LinkedHashMap<ScopeSlotKey, Pos>()
|
||||
private val pendingScopeNameRefs = LinkedHashSet<String>()
|
||||
private val addrSlotByScopeSlot = LinkedHashMap<Int, Int>()
|
||||
private data class LocalSlotInfo(val name: String, val isMutable: Boolean, val isDelegated: Boolean)
|
||||
@ -119,6 +124,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -140,6 +147,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -158,6 +167,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -176,6 +187,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -194,6 +207,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -212,6 +227,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -230,6 +247,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -248,6 +267,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -266,6 +287,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -284,6 +307,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -302,6 +327,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -322,6 +349,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -340,6 +369,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -358,6 +389,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -376,6 +409,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -395,6 +430,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -416,6 +453,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -439,6 +478,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -459,6 +500,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -534,6 +577,7 @@ class BytecodeCompiler(
|
||||
resolved = SlotType.INT
|
||||
}
|
||||
if (mapped < scopeSlotCount && resolved != SlotType.UNKNOWN) {
|
||||
noteScopeSlotRef(mapped, ref.pos())
|
||||
val addrSlot = ensureScopeAddr(mapped)
|
||||
val local = allocSlot()
|
||||
emitLoadFromAddr(addrSlot, local, resolved)
|
||||
@ -544,6 +588,7 @@ class BytecodeCompiler(
|
||||
return CompiledValue(local, resolved)
|
||||
}
|
||||
if (mapped < scopeSlotCount && resolved == SlotType.UNKNOWN) {
|
||||
noteScopeSlotRef(mapped, ref.pos())
|
||||
val addrSlot = ensureScopeAddr(mapped)
|
||||
val local = allocSlot()
|
||||
emitLoadFromAddr(addrSlot, local, SlotType.OBJ)
|
||||
@ -563,6 +608,7 @@ class BytecodeCompiler(
|
||||
}
|
||||
if (allowLocalSlots) {
|
||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||
noteScopeSlotRef(slot, callSitePos())
|
||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||
return CompiledValue(slot, resolved)
|
||||
}
|
||||
@ -585,6 +631,7 @@ class BytecodeCompiler(
|
||||
return CompiledValue(slot, resolved)
|
||||
}
|
||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||
noteScopeSlotRef(slot, callSitePos())
|
||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||
return CompiledValue(slot, resolved)
|
||||
}
|
||||
@ -596,6 +643,7 @@ class BytecodeCompiler(
|
||||
val slot = ref.slotIndex()
|
||||
val resolved = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||
if (slot < scopeSlotCount && resolved != SlotType.UNKNOWN) {
|
||||
noteScopeSlotRef(slot, callSitePos())
|
||||
val addrSlot = ensureScopeAddr(slot)
|
||||
val local = allocSlot()
|
||||
emitLoadFromAddr(addrSlot, local, resolved)
|
||||
@ -5002,6 +5050,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -5021,6 +5071,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -5041,6 +5093,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -5061,6 +5115,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -5084,6 +5140,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -5103,6 +5161,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -5727,6 +5787,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
scopeSlotRequiresPreparedBinding,
|
||||
scopeSlotRefPos,
|
||||
localSlotNames,
|
||||
localSlotMutables,
|
||||
localSlotDelegated,
|
||||
@ -7599,6 +7661,12 @@ class BytecodeCompiler(
|
||||
private fun assignValue(ref: AssignRef): ObjRef = ref.value
|
||||
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? {
|
||||
loopSlotOverrides[ref.name]?.let { return it }
|
||||
val scopeId = refScopeId(ref)
|
||||
@ -7606,8 +7674,14 @@ class BytecodeCompiler(
|
||||
val key = ScopeSlotKey(scopeId, refSlot(ref))
|
||||
val localIndex = localSlotIndexByKey[key]
|
||||
if (localIndex != null) return scopeSlotCount + localIndex
|
||||
scopeSlotMap[key]?.let { return it }
|
||||
scopeSlotIndexByName[ref.name]?.let { return it }
|
||||
scopeSlotMap[key]?.let {
|
||||
scopeSlotRefPosByKey.putIfAbsent(key, ref.pos())
|
||||
return it
|
||||
}
|
||||
scopeSlotIndexByName[ref.name]?.let {
|
||||
scopeSlotRefPosByKey.putIfAbsent(key, ref.pos())
|
||||
return it
|
||||
}
|
||||
}
|
||||
if (ref.captureOwnerScopeId != null) {
|
||||
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
|
||||
@ -7617,7 +7691,11 @@ class BytecodeCompiler(
|
||||
return scopeSlotCount + localIndex
|
||||
}
|
||||
}
|
||||
return scopeSlotMap[scopeKey]
|
||||
val resolved = scopeSlotMap[scopeKey]
|
||||
if (resolved != null) {
|
||||
scopeSlotRefPosByKey.putIfAbsent(scopeKey, ref.pos())
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
if (ref.isDelegated) {
|
||||
val localKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
|
||||
@ -7628,7 +7706,11 @@ class BytecodeCompiler(
|
||||
val localIndex = localSlotIndexByKey[localKey]
|
||||
if (localIndex != null) return scopeSlotCount + localIndex
|
||||
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? {
|
||||
@ -7858,6 +7940,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices = IntArray(scopeSlotCount)
|
||||
scopeSlotNames = arrayOfNulls(scopeSlotCount)
|
||||
scopeSlotIsModule = BooleanArray(scopeSlotCount)
|
||||
scopeSlotRequiresPreparedBinding = BooleanArray(scopeSlotCount)
|
||||
scopeSlotRefPos = arrayOfNulls(scopeSlotCount)
|
||||
scopeSlotMutables = BooleanArray(scopeSlotCount) { true }
|
||||
scopeKeyByIndex = arrayOfNulls(scopeSlotCount)
|
||||
for ((key, index) in scopeSlotMap) {
|
||||
@ -7865,6 +7949,8 @@ class BytecodeCompiler(
|
||||
scopeSlotIndices[index] = key.slot
|
||||
scopeSlotNames[index] = name
|
||||
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 }
|
||||
scopeKeyByIndex[index] = key
|
||||
}
|
||||
|
||||
@ -88,6 +88,8 @@ class BytecodeStatement private constructor(
|
||||
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
|
||||
externCallableNames: Set<String> = emptySet(),
|
||||
externBindingNames: Set<String> = emptySet(),
|
||||
preparedModuleBindingNames: Set<String> = emptySet(),
|
||||
scopeRefPosByName: Map<String, Pos> = emptyMap(),
|
||||
lambdaCaptureEntriesByRef: Map<ValueFnRef, List<LambdaCaptureEntry>> = emptyMap(),
|
||||
slotTypeDeclByScopeId: Map<Int, Map<Int, TypeDecl>> = emptyMap(),
|
||||
): Statement {
|
||||
@ -124,6 +126,8 @@ class BytecodeStatement private constructor(
|
||||
callableReturnTypeByName = callableReturnTypeByName,
|
||||
externCallableNames = externCallableNames,
|
||||
externBindingNames = externBindingNames,
|
||||
preparedModuleBindingNames = preparedModuleBindingNames,
|
||||
scopeRefPosByName = scopeRefPosByName,
|
||||
lambdaCaptureEntriesByRef = lambdaCaptureEntriesByRef
|
||||
)
|
||||
val compiled = compiler.compileStatement(nameHint, statement)
|
||||
|
||||
@ -67,6 +67,8 @@ class CmdBuilder {
|
||||
scopeSlotIndices: IntArray = IntArray(0),
|
||||
scopeSlotNames: Array<String?> = emptyArray(),
|
||||
scopeSlotIsModule: BooleanArray = BooleanArray(0),
|
||||
scopeSlotRequiresPreparedBinding: BooleanArray = BooleanArray(0),
|
||||
scopeSlotRefPos: Array<net.sergeych.lyng.Pos?> = emptyArray(),
|
||||
localSlotNames: Array<String?> = emptyArray(),
|
||||
localSlotMutables: BooleanArray = BooleanArray(0),
|
||||
localSlotDelegated: BooleanArray = BooleanArray(0),
|
||||
@ -79,6 +81,12 @@ class CmdBuilder {
|
||||
require(scopeSlotIsModule.isEmpty() || scopeSlotIsModule.size == scopeSlotCount) {
|
||||
"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 == localSlotDelegated.size) { "local slot delegation size mismatch" }
|
||||
require(localSlotNames.size == localSlotCaptures.size) { "local slot capture size mismatch" }
|
||||
@ -113,6 +121,8 @@ class CmdBuilder {
|
||||
scopeSlotIndices = scopeSlotIndices,
|
||||
scopeSlotNames = if (scopeSlotNames.isEmpty()) Array(scopeSlotCount) { null } else scopeSlotNames,
|
||||
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,
|
||||
localSlotMutables = localSlotMutables,
|
||||
localSlotDelegated = localSlotDelegated,
|
||||
|
||||
@ -26,6 +26,8 @@ data class CmdFunction(
|
||||
val scopeSlotIndices: IntArray,
|
||||
val scopeSlotNames: Array<String?>,
|
||||
val scopeSlotIsModule: BooleanArray,
|
||||
val scopeSlotRequiresPreparedBinding: BooleanArray,
|
||||
val scopeSlotRefPos: Array<net.sergeych.lyng.Pos?>,
|
||||
val localSlotNames: Array<String?>,
|
||||
val localSlotMutables: BooleanArray,
|
||||
val localSlotDelegated: BooleanArray,
|
||||
@ -38,6 +40,8 @@ data class CmdFunction(
|
||||
require(scopeSlotIndices.size == scopeSlotCount) { "scopeSlotIndices size mismatch" }
|
||||
require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames 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 == localSlotDelegated.size) { "localSlot delegation size mismatch" }
|
||||
require(localSlotNames.size == localSlotCaptures.size) { "localSlot capture size mismatch" }
|
||||
|
||||
@ -4112,7 +4112,7 @@ class CmdFrame(
|
||||
}
|
||||
|
||||
fun ensureScope(): Scope {
|
||||
val pos = posForIp(ip - 1)
|
||||
val pos = currentErrorPos()
|
||||
if (pos != null && lastScopePosIp != ip) {
|
||||
scope.pos = pos
|
||||
lastScopePosIp = ip
|
||||
@ -4189,6 +4189,33 @@ class CmdFrame(
|
||||
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>) {
|
||||
if (scope.skipScopeCreation) {
|
||||
val snapshot = emptyMap<String, Int?>()
|
||||
@ -5045,8 +5072,12 @@ class CmdFrame(
|
||||
if (hadNamedBinding) return
|
||||
if (record.value !== ObjUnset) 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(
|
||||
ensureScope().pos,
|
||||
pos,
|
||||
"module binding '$name' is not available in the execution scope; prepare the script imports/module bindings explicitly"
|
||||
)
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
@ -5474,33 +5474,112 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun testFromCalcrus1() = runTest {
|
||||
// eval($$"""
|
||||
// import lyng.decimal
|
||||
// var x = 7.0.d
|
||||
// // глубина по звуку падения
|
||||
// val m = 1 // kg
|
||||
// val d = 0.06 // 6 cm
|
||||
// val c = 340 // скор. звука
|
||||
// val g = 9.82
|
||||
// var cnt = 0
|
||||
// var h = 0.0
|
||||
// var t = x
|
||||
// var message = ""
|
||||
//
|
||||
// while(true){
|
||||
// val h0 = 0
|
||||
// h = c*c/h*(1 + g*t/c -sqrt(1+2*g*t/c))
|
||||
// message = "iter ${cnt++}"
|
||||
// if( cnt > 100 ) {
|
||||
// message= "ошибка"
|
||||
// break 0
|
||||
// }
|
||||
// x = h
|
||||
// if( abs(h-h0)/h > 0.08 ) break h
|
||||
// }
|
||||
// println(x)
|
||||
// """.trimIndent())
|
||||
// }
|
||||
@Test
|
||||
fun testFromCalcurus1() = runTest {
|
||||
eval($$"""
|
||||
|
||||
/*
|
||||
Рассчитывает глубину провала по времени падения камня и прихода звука.
|
||||
|
||||
@param T измеренное полное время (с)
|
||||
@param m масса камня (кг)
|
||||
@param d диаметр камня (м) (предполагается сферическая форма)
|
||||
@param rho плотность воздуха (кг/м³), по умолчанию 1.2
|
||||
@param c скорость звука (м/с), по умолчанию 340.0
|
||||
@param g ускорение свободного падения (м/с²), по умолчанию 9.81
|
||||
@param Cd коэффициент лобового сопротивления, по умолчанию 0.5
|
||||
@param epsilon относительная точность (м), по умолчанию 1e-3
|
||||
@param maxIter максимальное число итераций, по умолчанию 20
|
||||
@return глубина h (м), или null если расчёт не сошёлся
|
||||
*/
|
||||
fun calculateDepth(
|
||||
T: Real,
|
||||
m: Real,
|
||||
d: Real,
|
||||
rho: Real = 1.2,
|
||||
c: Real = 340.0,
|
||||
g: Real = 9.81,
|
||||
Cd: Real = 0.5,
|
||||
epsilon: Real = 1e-3,
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.FrameSlotRef
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.bytecode.BytecodeConst
|
||||
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 net.sergeych.lyng.bytecode.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNull
|
||||
|
||||
@ -39,6 +36,8 @@ class SeedLocalsRegressionTest {
|
||||
scopeSlotIndices = intArrayOf(0),
|
||||
scopeSlotNames = arrayOf(null),
|
||||
scopeSlotIsModule = booleanArrayOf(false),
|
||||
scopeSlotRequiresPreparedBinding = booleanArrayOf(false),
|
||||
scopeSlotRefPos = arrayOfNulls(1),
|
||||
localSlotNames = arrayOf("x"),
|
||||
localSlotMutables = booleanArrayOf(true),
|
||||
localSlotDelegated = booleanArrayOf(false),
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user