Compare commits

..

No commits in common. "master" and "fix/scope-parent-cycle" have entirely different histories.

11 changed files with 27 additions and 147 deletions

View File

@ -1,28 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tests in 'lyng.lynglib.jvmTest'" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":lynglib:cleanJvmTest" />
<option value=":lynglib:jvmTest" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<ExternalSystemDebugDisabled>false</ExternalSystemDebugDisabled>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<GradleProfilingDisabled>false</GradleProfilingDisabled>
<GradleCoverageDisabled>false</GradleCoverageDisabled>
<method v="2" />
</configuration>
</component>

View File

@ -68,20 +68,6 @@ These, again, does the thing:
>>> void >>> void
## map and mapNotNull
Used to transform either the whole iterable stream or also skipping som elements from it:
val source = [1,2,3,4]
// transform every element to string or null:
assertEquals(["n1", "n2", null, "n4"], source.map { if( it == 3 ) null else "n"+it } )
// transform every element to stirng, skipping 3:
assertEquals(["n1", "n2", "n4"], source.mapNotNull { if( it == 3 ) null else "n"+it } )
>>> void
## Instance methods: ## Instance methods:
| fun/method | description | | fun/method | description |

View File

@ -516,23 +516,6 @@ open class Scope(
open fun applyClosure(closure: Scope): Scope = ClosureScope(this, closure) open fun applyClosure(closure: Scope): Scope = ClosureScope(this, closure)
/**
* Resolve and evaluate a qualified identifier exactly as compiled code would.
* For input like `A.B.C`, it builds the same ObjRef chain the compiler emits:
* `LocalVarRef("A", Pos.builtIn)` followed by `FieldRef` for each segment, then evaluates it.
* This mirrors `eval("A.B.C")` resolution semantics without invoking the compiler.
*/
suspend fun resolveQualifiedIdentifier(qualifiedName: String): Obj {
val trimmed = qualifiedName.trim()
if (trimmed.isEmpty()) raiseSymbolNotFound("empty identifier")
val parts = trimmed.split('.')
var ref: ObjRef = LocalVarRef(parts[0], Pos.builtIn)
for (i in 1 until parts.size) {
ref = FieldRef(ref, parts[i], false)
}
return ref.evalValue(this)
}
companion object { companion object {
fun new(): Scope = fun new(): Scope =

View File

@ -155,65 +155,52 @@ class Script(
sqrt(args.firstAndOnly().toDouble()) sqrt(args.firstAndOnly().toDouble())
) )
} }
addFn("abs") { addFn( "abs" ) {
val x = args.firstAndOnly() val x = args.firstAndOnly()
if (x is ObjInt) ObjInt(x.value.absoluteValue) else ObjReal(x.toDouble().absoluteValue) if( x is ObjInt) ObjInt( x.value.absoluteValue ) else ObjReal( x.toDouble().absoluteValue )
} }
addVoidFn("assert") { addVoidFn("assert") {
val cond = requiredArg<ObjBool>(0) val cond = requiredArg<ObjBool>(0)
val message = if (args.size > 1) val message = if( args.size > 1 )
": " + (args[1] as Statement).execute(this).toString(this).value ": " + (args[1] as Statement).execute(this).toString(this).value
else "" else ""
if (!cond.value == true) if( !cond.value == true )
raiseError(ObjAssertionFailedException(this, "Assertion failed$message")) raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
} }
addVoidFn("assertEquals") { addVoidFn("assertEquals") {
val a = requiredArg<Obj>(0) val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1) val b = requiredArg<Obj>(1)
if (a.compareTo(this, b) != 0) if( a.compareTo(this, b) != 0 )
raiseError( raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
)
)
} }
// alias used in tests // alias used in tests
addVoidFn("assertEqual") { addVoidFn("assertEqual") {
val a = requiredArg<Obj>(0) val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1) val b = requiredArg<Obj>(1)
if (a.compareTo(this, b) != 0) if( a.compareTo(this, b) != 0 )
raiseError( raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
)
)
} }
addVoidFn("assertNotEquals") { addVoidFn("assertNotEquals") {
val a = requiredArg<Obj>(0) val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1) val b = requiredArg<Obj>(1)
if (a.compareTo(this, b) == 0) if( a.compareTo(this, b) == 0 )
raiseError( raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"))
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"
)
)
} }
addFn("assertThrows") { addFn("assertThrows") {
val code = requireOnlyArg<Statement>() val code = requireOnlyArg<Statement>()
val result = try { val result =try {
code.execute(this) code.execute(this)
null null
} catch (e: ExecutionError) { }
catch( e: ExecutionError ) {
e.errorObject e.errorObject
} catch (_: ScriptError) { }
catch (_: ScriptError) {
ObjNull ObjNull
} }
result ?: raiseError(ObjAssertionFailedException(this, "Expected exception but nothing was thrown")) result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown"))
} }
addFn("dynamic") { addFn("dynamic") {
@ -222,7 +209,7 @@ class Script(
addFn("require") { addFn("require") {
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if (!condition.value) { if( !condition.value ) {
val message = args.list.getOrNull(1)?.toString() ?: "requirement not met" val message = args.list.getOrNull(1)?.toString() ?: "requirement not met"
raiseIllegalArgument(message) raiseIllegalArgument(message)
} }
@ -230,7 +217,7 @@ class Script(
} }
addFn("check") { addFn("check") {
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if (!condition.value) { if( !condition.value ) {
val message = args.list.getOrNull(1)?.toString() ?: "check failed" val message = args.list.getOrNull(1)?.toString() ?: "check failed"
raiseIllegalState(message) raiseIllegalState(message)
} }
@ -353,7 +340,7 @@ class Script(
doc = "Suspend for the given time. Accepts Duration, Int seconds, or Real seconds." doc = "Suspend for the given time. Accepts Duration, Int seconds, or Real seconds."
) { ) {
val a = args.firstAndOnly() val a = args.firstAndOnly()
when (a) { when(a) {
is ObjInt -> delay(a.value * 1000) is ObjInt -> delay(a.value * 1000)
is ObjReal -> delay((a.value * 1000).roundToLong()) is ObjReal -> delay((a.value * 1000).roundToLong())
is ObjDuration -> delay(a.duration) is ObjDuration -> delay(a.duration)

View File

@ -131,7 +131,7 @@ object CompletionEngineLight {
} }
is MiniClassDecl -> add(CompletionItem(d.name, Kind.Class_)) is MiniClassDecl -> add(CompletionItem(d.name, Kind.Class_))
is MiniValDecl -> add(CompletionItem(d.name, Kind.Value, typeText = typeOf(d.type))) is MiniValDecl -> add(CompletionItem(d.name, Kind.Value, typeText = typeOf(d.type)))
// else -> add(CompletionItem(d.name, Kind.Value)) else -> add(CompletionItem(d.name, Kind.Value))
} }
} }

View File

@ -197,20 +197,9 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
!it.key.contains("::") && it.value.visibility.isPublic && it.value.type.serializable !it.key.contains("::") && it.value.visibility.isPublic && it.value.type.serializable
} }
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString { override fun toString(): String {
return ObjString(buildString { val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",")
append("${objClass.className}(") return "${objClass.className}($fields)"
var first = true
for ((name, value) in publicFields) {
if (first) first = false else append(",")
append("$name=${value.value.toString(scope)}")
}
append(")")
})
}
override suspend fun inspect(scope: Scope): String {
return toString(scope).value
} }
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) { override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {

View File

@ -158,23 +158,6 @@ val ObjIterable by lazy {
ObjList(result) ObjList(result)
} }
addFnDoc(
name = "mapNotNull",
doc = "Transform elements by applying the given lambda unless it returns null.",
params = listOf(ParamDoc("transform")),
returns = type("lyng.List"),
isOpen = true,
moduleName = "lyng.stdlib"
) {
val fn = requiredArg<Statement>(0)
val result = mutableListOf<Obj>()
thisObj.toFlow(this).collect {
val transformed = fn.call(this, it)
if( transformed != ObjNull) result += transformed
}
ObjList(result)
}
addFnDoc( addFnDoc(
name = "take", name = "take",
doc = "Take the first N elements and return them as a list.", doc = "Take the first N elements and return them as a list.",

View File

@ -198,18 +198,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
return JsonArray(list.map { it.toJson(scope) }) return JsonArray(list.map { it.toJson(scope) })
} }
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString {
return ObjString(buildString {
append("[")
var first = true
for (v in list) {
if (first) first = false else append(",")
append(v.toString(scope).value)
}
append("]")
})
}
companion object { companion object {
val type = object : ObjClass("List", ObjArray) { val type = object : ObjClass("List", ObjArray) {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj { override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {

View File

@ -52,8 +52,8 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
else -> scope.raiseIndexOutOfBounds() else -> scope.raiseIndexOutOfBounds()
} }
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString { override fun toString(): String {
return ObjString("(${key.toString(scope).value} => ${value.toString(scope).value})") return "$key=>$value"
} }
override val objClass = type override val objClass = type

View File

@ -1625,7 +1625,7 @@ class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
when (elements) { when (elements) {
is ObjList -> { is ObjList -> {
// Grow underlying array once when possible // Grow underlying array once when possible
list.ensureCapacity(list.size + elements.list.size) if (list is ArrayList) list.ensureCapacity(list.size + elements.list.size)
list.addAll(elements.list) list.addAll(elements.list)
} }
else -> scope.raiseError("Spread element must be list") else -> scope.raiseError("Spread element must be list")

View File

@ -82,17 +82,9 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
if (it !is ObjClass) if (it !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}") scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}")
it it
} ?: run { } ?: scope.raiseSymbolNotFound("can't deserialize: not found type $className")
// Use Scope API that mirrors compiler-emitted ObjRef chain for qualified identifiers
val evaluated = scope.resolveQualifiedIdentifier(className.value)
if (evaluated !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${evaluated::class.simpleName}")
evaluated
}
} }
// helper moved to Scope as resolveQualifiedIdentifier
suspend fun decodeAnyList(scope: Scope, fixedSize: Int? = null): MutableList<Obj> { suspend fun decodeAnyList(scope: Scope, fixedSize: Int? = null): MutableList<Obj> {
return if (bin.getBit() == 1) { return if (bin.getBit() == 1) {
// homogenous // homogenous