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
## 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:
| fun/method | description |

View File

@ -516,23 +516,6 @@ open class Scope(
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 {
fun new(): Scope =

View File

@ -173,44 +173,31 @@ class Script(
val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1)
if( a.compareTo(this, b) != 0 )
raiseError(
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
)
)
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
}
// alias used in tests
addVoidFn("assertEqual") {
val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1)
if( a.compareTo(this, b) != 0 )
raiseError(
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
)
)
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
}
addVoidFn("assertNotEquals") {
val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1)
if( a.compareTo(this, b) == 0 )
raiseError(
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"
)
)
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"))
}
addFn("assertThrows") {
val code = requireOnlyArg<Statement>()
val result =try {
code.execute(this)
null
} catch (e: ExecutionError) {
}
catch( e: ExecutionError ) {
e.errorObject
} catch (_: ScriptError) {
}
catch (_: ScriptError) {
ObjNull
}
result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown"))

View File

@ -131,7 +131,7 @@ object CompletionEngineLight {
}
is MiniClassDecl -> add(CompletionItem(d.name, Kind.Class_))
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
}
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString {
return ObjString(buildString {
append("${objClass.className}(")
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 fun toString(): String {
val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",")
return "${objClass.className}($fields)"
}
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {

View File

@ -158,23 +158,6 @@ val ObjIterable by lazy {
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(
name = "take",
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) })
}
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 {
val type = object : ObjClass("List", ObjArray) {
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()
}
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString {
return ObjString("(${key.toString(scope).value} => ${value.toString(scope).value})")
override fun toString(): String {
return "$key=>$value"
}
override val objClass = type

View File

@ -1625,7 +1625,7 @@ class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
when (elements) {
is ObjList -> {
// 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)
}
else -> scope.raiseError("Spread element must be list")

View File

@ -82,16 +82,8 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
if (it !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}")
it
} ?: run {
// 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
} ?: scope.raiseSymbolNotFound("can't deserialize: not found type $className")
}
}
// helper moved to Scope as resolveQualifiedIdentifier
suspend fun decodeAnyList(scope: Scope, fixedSize: Int? = null): MutableList<Obj> {
return if (bin.getBit() == 1) {