Add explicit script import prep APIs and VM regression tests

This commit is contained in:
sergeych committed 2026-03-27 15:51:17 +03:00
1 parent 3d8bfe8863
commit ecc4d8b9ed
8 files changed
+271 -1

No files matched your search

@@ -46,6 +46,28 @@ class Script(
// private val catchReturn: Boolean = false,
) : Statement() {
fun statements(): List<Statement> = statements
/**
* Explicitly apply this script's import/module bindings to [scope] without executing the script.
* This is intended for embedding scenarios where the host owns scope lifecycle and wants
* script-specific imports to be a separate, opt-in preparation step.
*/
suspend fun importInto(scope: Scope, seedScope: Scope = scope): Scope {
prepareScriptScope(scope, seedScope)
return scope
}
/**
* Create a fresh raw module scope and apply this script's import/module bindings to it.
* If [seedScope] is provided, its import provider is reused and seed-bound imports resolve from it.
*/
suspend fun instantiateModule(seedScope: Scope? = null, pos: Pos = this.pos): ModuleScope {
val provider = seedScope?.currentImportProvider ?: defaultImportManager
val module = provider.newModuleAt(pos)
prepareScriptScope(module, seedScope ?: module)
return module
}
override suspend fun execute(scope: Scope): Obj {
scope.pos = pos
val execScope = resolveModuleScope(scope) ?: scope
@@ -75,6 +97,19 @@ class Script(
return ObjVoid
}
private suspend fun prepareScriptScope(scope: Scope, seedScope: Scope) {
if (importBindings.isNotEmpty() || importedModules.isNotEmpty()) {
seedImportBindings(scope, seedScope)
}
if (moduleSlotPlan.isNotEmpty()) {
scope.applySlotPlan(moduleSlotPlan)
for (name in moduleSlotPlan.keys) {
val record = scope.objects[name] ?: scope.localBindings[name] ?: continue
scope.updateSlotFor(name, record)
}
}
}
private suspend fun seedModuleSlots(scope: Scope, seedScope: Scope) {
if (importBindings.isEmpty() && importedModules.isEmpty()) return
seedImportBindings(scope, seedScope)
@@ -36,7 +36,7 @@ internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) {
} else {
record.value
}
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, base + i)) {
if (value is net.sergeych.lyng.FrameSlotRef && value.refersTo(frame.frame, i)) {
continue
}
frame.setObjUnchecked(base + i, value)