Remove interpreter paths and enforce bytecode-only execution

This commit is contained in:
Sergey Chernov 2026-02-14 01:20:13 +03:00
parent e73fe0d3c5
commit 34678068ac
45 changed files with 434 additions and 1878 deletions

View File

@ -23,6 +23,8 @@ package net.sergeych.lyng.io.fs
import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager import net.sergeych.lyng.pacman.ImportManager
@ -437,7 +439,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
moduleName = module.packageName moduleName = module.packageName
) { ) {
fsGuard { fsGuard {
val chunkIt = thisObj.invokeInstanceMethod(this, "readUtf8Chunks") val chunkIt = thisObj.invokeInstanceMethod(requireScope(), "readUtf8Chunks")
ObjFsLinesIterator(chunkIt) ObjFsLinesIterator(chunkIt)
} }
} }
@ -463,7 +465,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
// --- Helper classes and utilities --- // --- Helper classes and utilities ---
private fun parsePathArg(scope: Scope, self: ObjPath, arg: Obj): LyngPath { private fun parsePathArg(scope: ScopeFacade, self: ObjPath, arg: Obj): LyngPath {
return when (arg) { return when (arg) {
is ObjString -> arg.value.toPath() is ObjString -> arg.value.toPath()
is ObjPath -> arg.path is ObjPath -> arg.path
@ -472,11 +474,11 @@ private fun parsePathArg(scope: Scope, self: ObjPath, arg: Obj): LyngPath {
} }
// Map Fs access denials to Lyng runtime exceptions for script-friendly errors // Map Fs access denials to Lyng runtime exceptions for script-friendly errors
private suspend inline fun Scope.fsGuard(crossinline block: suspend () -> Obj): Obj { private suspend inline fun ScopeFacade.fsGuard(crossinline block: suspend () -> Obj): Obj {
return try { return try {
block() block()
} catch (e: AccessDeniedException) { } catch (e: AccessDeniedException) {
raiseError(ObjIllegalOperationException(this, e.reasonDetail ?: "access denied")) raiseError(ObjIllegalOperationException(requireScope(), e.reasonDetail ?: "access denied"))
} }
} }
@ -668,16 +670,17 @@ class ObjFsLinesIterator(
} }
} }
private suspend fun ensureBufferFilled(scope: Scope) { private suspend fun ensureBufferFilled(scope: ScopeFacade) {
if (buffer.contains('\n') || exhausted) return if (buffer.contains('\n') || exhausted) return
val actualScope = scope.requireScope()
// Pull next chunk from the underlying iterator // Pull next chunk from the underlying iterator
val it = chunksIterator.invokeInstanceMethod(scope, "iterator") val it = chunksIterator.invokeInstanceMethod(actualScope, "iterator")
val hasNext = it.invokeInstanceMethod(scope, "hasNext").toBool() val hasNext = it.invokeInstanceMethod(actualScope, "hasNext").toBool()
if (!hasNext) { if (!hasNext) {
exhausted = true exhausted = true
return return
} }
val next = it.invokeInstanceMethod(scope, "next") val next = it.invokeInstanceMethod(actualScope, "next")
buffer += next.toString() buffer += next.toString()
} }
} }

View File

@ -20,6 +20,8 @@ package net.sergeych.lyng.io.process
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager import net.sergeych.lyng.pacman.ImportManager
@ -204,20 +206,21 @@ class ObjRunningProcess(
override fun toString(): String = "RunningProcess($process)" override fun toString(): String = "RunningProcess($process)"
} }
private suspend inline fun Scope.processGuard(crossinline block: suspend () -> Obj): Obj { private suspend inline fun ScopeFacade.processGuard(crossinline block: suspend () -> Obj): Obj {
return try { return try {
block() block()
} catch (e: ProcessAccessDeniedException) { } catch (e: ProcessAccessDeniedException) {
raiseError(ObjIllegalOperationException(this, e.reasonDetail ?: "process access denied")) raiseError(ObjIllegalOperationException(requireScope(), e.reasonDetail ?: "process access denied"))
} catch (e: Exception) { } catch (e: Exception) {
raiseError(ObjIllegalOperationException(this, e.message ?: "process error")) raiseError(ObjIllegalOperationException(requireScope(), e.message ?: "process error"))
} }
} }
private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow { private fun Flow<String>.toLyngFlow(flowScope: ScopeFacade): ObjFlow {
val producer = ObjNativeCallable { val producer = net.sergeych.lyng.obj.ObjExternCallable.fromBridge {
val builder = (this as? net.sergeych.lyng.BytecodeClosureScope)?.callScope?.thisObj as? ObjFlowBuilder val scope = requireScope()
?: this.thisObj as? ObjFlowBuilder val builder = (scope as? net.sergeych.lyng.BytecodeClosureScope)?.callScope?.thisObj as? ObjFlowBuilder
?: scope.thisObj as? ObjFlowBuilder
this@toLyngFlow.collect { this@toLyngFlow.collect {
try { try {
@ -229,5 +232,5 @@ private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow {
} }
ObjVoid ObjVoid
} }
return ObjFlow(producer, flowScope) return ObjFlow(producer, flowScope.requireScope())
} }

View File

@ -28,7 +28,7 @@ class BlockStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "block statement") return bytecodeOnly(scope, "block statement")
} }
fun statements(): List<Statement> = block.debugStatements() fun statements(): List<Statement> = block.debugStatements()

View File

@ -24,7 +24,7 @@ class ClassInstanceInitDeclStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "class instance init declaration") return bytecodeOnly(scope, "class instance init declaration")
} }
} }
@ -42,7 +42,7 @@ class ClassInstanceFieldDeclStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "class instance field declaration") return bytecodeOnly(scope, "class instance field declaration")
} }
} }
@ -61,7 +61,7 @@ class ClassInstancePropertyDeclStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "class instance property declaration") return bytecodeOnly(scope, "class instance property declaration")
} }
} }
@ -79,6 +79,6 @@ class ClassInstanceDelegatedDeclStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "class instance delegated declaration") return bytecodeOnly(scope, "class instance delegated declaration")
} }
} }

View File

@ -23,7 +23,7 @@ import net.sergeych.lyng.obj.ObjRecord
/** /**
* Bytecode-oriented closure scope that keeps the call scope parent chain for stack traces * Bytecode-oriented closure scope that keeps the call scope parent chain for stack traces
* while carrying the lexical closure for `this` variants and module resolution. * while carrying the lexical closure for `this` variants and module resolution.
* Unlike interpreter closure scopes, it does not override name lookup. * Unlike legacy closure scopes, it does not override name lookup.
*/ */
class BytecodeClosureScope( class BytecodeClosureScope(
val callScope: Scope, val callScope: Scope,

View File

@ -32,6 +32,6 @@ class DelegatedVarDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return interpreterDisabled(context, "delegated var declaration") return bytecodeOnly(context, "delegated var declaration")
} }
} }

View File

@ -29,6 +29,6 @@ class DestructuringVarDeclStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return interpreterDisabled(context, "destructuring declaration") return bytecodeOnly(context, "destructuring declaration")
} }
} }

View File

@ -28,7 +28,7 @@ class EnumDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "enum declaration") return bytecodeOnly(scope, "enum declaration")
} }
override suspend fun callOn(scope: Scope): Obj { override suspend fun callOn(scope: Scope): Obj {

View File

@ -29,6 +29,6 @@ class ExtensionPropertyDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return interpreterDisabled(context, "extension property declaration") return bytecodeOnly(context, "extension property declaration")
} }
} }

View File

@ -20,7 +20,7 @@ package net.sergeych.lyng
/** /**
* Tiny, size-bounded cache for compiled Regex patterns. Activated only when [PerfFlags.REGEX_CACHE] is true. * Tiny, size-bounded cache for compiled Regex patterns. Activated only when [PerfFlags.REGEX_CACHE] is true.
* This is a very simple FIFO-ish cache sufficient for micro-benchmarks and common repeated patterns. * This is a very simple FIFO-ish cache sufficient for micro-benchmarks and common repeated patterns.
* Not thread-safe by design; the interpreter typically runs scripts on confined executors. * Not thread-safe by design; the runtime typically runs scripts on confined executors.
*/ */
object RegexCache { object RegexCache {
private const val MAX = 64 private const val MAX = 64
@ -48,4 +48,4 @@ object RegexCache {
} }
fun clear() = map.clear() fun clear() = map.clear()
} }

View File

@ -725,7 +725,7 @@ open class Scope(
return ns.objClass return ns.objClass
} }
inline fun addVoidFn(vararg names: String, crossinline fn: suspend Scope.() -> Unit) { inline fun addVoidFn(vararg names: String, crossinline fn: suspend ScopeFacade.() -> Unit) {
addFn(*names) { addFn(*names) {
fn(this) fn(this)
ObjVoid ObjVoid
@ -741,8 +741,8 @@ open class Scope(
return CmdDisassembler.disassemble(bytecode) return CmdDisassembler.disassemble(bytecode)
} }
fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend Scope.() -> Obj) { fun addFn(vararg names: String, callSignature: CallSignature? = null, fn: suspend ScopeFacade.() -> Obj) {
val newFn = net.sergeych.lyng.obj.ObjNativeCallable { fn() } val newFn = net.sergeych.lyng.obj.ObjExternCallable.fromBridge { fn() }
for (name in names) { for (name in names) {
addItem( addItem(
name, name,

View File

@ -18,6 +18,7 @@ package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjRecord import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
/** /**
* Limited facade for Kotlin bridge callables. * Limited facade for Kotlin bridge callables.
@ -31,11 +32,20 @@ interface ScopeFacade {
suspend fun resolve(rec: ObjRecord, name: String): Obj suspend fun resolve(rec: ObjRecord, name: String): Obj
suspend fun assign(rec: ObjRecord, name: String, newValue: Obj) suspend fun assign(rec: ObjRecord, name: String, newValue: Obj)
fun raiseError(message: String): Nothing fun raiseError(message: String): Nothing
fun raiseError(obj: net.sergeych.lyng.obj.ObjException): Nothing
fun raiseClassCastError(message: String): Nothing
fun raiseIllegalArgument(message: String): Nothing
fun raiseNoSuchElement(message: String = "No such element"): Nothing
fun raiseSymbolNotFound(name: String): Nothing fun raiseSymbolNotFound(name: String): Nothing
fun raiseIllegalState(message: String = "Illegal argument error"): Nothing fun raiseIllegalState(message: String = "Illegal argument error"): Nothing
fun raiseNotImplemented(what: String = "operation"): Nothing
suspend fun call(callee: Obj, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Obj
suspend fun toStringOf(obj: Obj, forInspect: Boolean = false): ObjString
suspend fun inspect(obj: Obj): String
fun trace(text: String = "")
} }
internal class ScopeBridge(private val scope: Scope) : ScopeFacade { internal class ScopeBridge(internal val scope: Scope) : ScopeFacade {
override val args: Arguments override val args: Arguments
get() = scope.args get() = scope.args
override var pos: Pos override var pos: Pos
@ -48,6 +58,50 @@ internal class ScopeBridge(private val scope: Scope) : ScopeFacade {
override suspend fun resolve(rec: ObjRecord, name: String): Obj = scope.resolve(rec, name) override suspend fun resolve(rec: ObjRecord, name: String): Obj = scope.resolve(rec, name)
override suspend fun assign(rec: ObjRecord, name: String, newValue: Obj) = scope.assign(rec, name, newValue) override suspend fun assign(rec: ObjRecord, name: String, newValue: Obj) = scope.assign(rec, name, newValue)
override fun raiseError(message: String): Nothing = scope.raiseError(message) override fun raiseError(message: String): Nothing = scope.raiseError(message)
override fun raiseError(obj: net.sergeych.lyng.obj.ObjException): Nothing = scope.raiseError(obj)
override fun raiseClassCastError(message: String): Nothing = scope.raiseClassCastError(message)
override fun raiseIllegalArgument(message: String): Nothing = scope.raiseIllegalArgument(message)
override fun raiseNoSuchElement(message: String): Nothing = scope.raiseNoSuchElement(message)
override fun raiseSymbolNotFound(name: String): Nothing = scope.raiseSymbolNotFound(name) override fun raiseSymbolNotFound(name: String): Nothing = scope.raiseSymbolNotFound(name)
override fun raiseIllegalState(message: String): Nothing = scope.raiseIllegalState(message) override fun raiseIllegalState(message: String): Nothing = scope.raiseIllegalState(message)
override fun raiseNotImplemented(what: String): Nothing = scope.raiseNotImplemented(what)
override suspend fun call(callee: Obj, args: Arguments, newThisObj: Obj?): Obj {
return callee.callOn(scope.createChildScope(scope.pos, args = args, newThisObj = newThisObj))
}
override suspend fun toStringOf(obj: Obj, forInspect: Boolean): ObjString = obj.toString(scope, forInspect)
override suspend fun inspect(obj: Obj): String = obj.inspect(scope)
override fun trace(text: String) = scope.trace(text)
} }
inline fun <reified T : Obj> ScopeFacade.requiredArg(index: Int): T {
if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}")
return (args.list[index].byValueCopy() as? T)
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index]::class.simpleName}")
}
inline fun <reified T : Obj> ScopeFacade.requireOnlyArg(): T {
if (args.list.size != 1) raiseError("Expected exactly 1 argument, got ${args.list.size}")
return requiredArg(0)
}
fun ScopeFacade.requireExactCount(count: Int) {
if (args.list.size != count) {
raiseError("Expected exactly $count arguments, got ${args.list.size}")
}
}
fun ScopeFacade.requireNoArgs() {
if (args.list.isNotEmpty()) {
raiseError("This function does not accept any arguments")
}
}
inline fun <reified T : Obj> ScopeFacade.thisAs(): T {
val obj = thisObj
return (obj as? T) ?: raiseClassCastError(
"Cannot cast ${obj.objClass.className} to ${T::class.simpleName}"
)
}
fun ScopeFacade.requireScope(): Scope =
(this as? ScopeBridge)?.scope ?: raiseIllegalState("ScopeFacade requires ScopeBridge")

View File

@ -90,7 +90,7 @@ class Script(
} }
} }
if (statements.isNotEmpty()) { if (statements.isNotEmpty()) {
scope.raiseIllegalState("interpreter execution is not supported; missing module bytecode") scope.raiseIllegalState("bytecode-only execution is required; missing module bytecode")
} }
return ObjVoid return ObjVoid
} }
@ -212,15 +212,15 @@ class Script(
addConst("Unset", ObjUnset) addConst("Unset", ObjUnset)
addFn("print") { addFn("print") {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + a.toString(this).value) if (i > 0) print(' ' + toStringOf(a).value)
else print(a.toString(this).value) else print(toStringOf(a).value)
} }
ObjVoid ObjVoid
} }
addFn("println") { addFn("println") {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + a.toString(this).value) if (i > 0) print(' ' + toStringOf(a).value)
else print(a.toString(this).value) else print(toStringOf(a).value)
} }
println() println()
ObjVoid ObjVoid
@ -233,7 +233,7 @@ class Script(
} else { } else {
Arguments.EMPTY Arguments.EMPTY
} }
callee.callOn(createChildScope(pos, args = rest)) call(callee, rest)
} }
addFn("floor") { addFn("floor") {
val x = args.firstAndOnly() val x = args.firstAndOnly()
@ -331,12 +331,12 @@ class Script(
var result = value var result = value
if (range.start != null && !range.start.isNull) { if (range.start != null && !range.start.isNull) {
if (result.compareTo(this, range.start) < 0) { if (result.compareTo(requireScope(), range.start) < 0) {
result = range.start result = range.start
} }
} }
if (range.end != null && !range.end.isNull) { if (range.end != null && !range.end.isNull) {
val cmp = range.end.compareTo(this, result) val cmp = range.end.compareTo(requireScope(), result)
if (range.isEndInclusive) { if (range.isEndInclusive) {
if (cmp < 0) result = range.end if (cmp < 0) result = range.end
} else { } else {
@ -359,20 +359,20 @@ class Script(
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 Obj).callOn(this).toString(this).value ": " + toStringOf(call(args[1] as Obj)).value
else "" else ""
if (!cond.value == true) if (!cond.value == true)
raiseError(ObjAssertionFailedException(this, "Assertion failed$message")) raiseError(ObjAssertionFailedException(requireScope(), "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(requireScope(), b) != 0)
raiseError( raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
this, requireScope(),
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}" "Assertion failed: ${inspect(a)} == ${inspect(b)}"
) )
) )
} }
@ -380,22 +380,22 @@ class Script(
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(requireScope(), b) != 0)
raiseError( raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
this, requireScope(),
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}" "Assertion failed: ${inspect(a)} == ${inspect(b)}"
) )
) )
} }
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(requireScope(), b) == 0)
raiseError( raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
this, requireScope(),
"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}" "Assertion failed: ${inspect(a)} != ${inspect(b)}"
) )
) )
} }
@ -428,7 +428,7 @@ class Script(
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}") else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
} }
val result = try { val result = try {
code.callOn(this) call(code)
null null
} catch (e: ExecutionError) { } catch (e: ExecutionError) {
e.errorObject e.errorObject
@ -437,7 +437,7 @@ class Script(
} }
if (result == null) raiseError( if (result == null) raiseError(
ObjAssertionFailedException( ObjAssertionFailedException(
this, requireScope(),
"Expected exception but nothing was thrown" "Expected exception but nothing was thrown"
) )
) )
@ -451,7 +451,7 @@ class Script(
} }
addFn("dynamic", callSignature = CallSignature(tailBlockReceiverType = "DelegateContext")) { addFn("dynamic", callSignature = CallSignature(tailBlockReceiverType = "DelegateContext")) {
ObjDynamic.create(this, requireOnlyArg()) ObjDynamic.create(requireScope(), requireOnlyArg())
} }
val root = this val root = this
@ -468,7 +468,7 @@ class Script(
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if (!condition.value) { if (!condition.value) {
var message = args.list.getOrNull(1) var message = args.list.getOrNull(1)
if (message is Obj && message.objClass == Statement.type) message = message.callOn(this) if (message is Obj && message.objClass == Statement.type) message = call(message)
raiseIllegalArgument(message?.toString() ?: "requirement not met") raiseIllegalArgument(message?.toString() ?: "requirement not met")
} }
ObjVoid ObjVoid
@ -477,26 +477,26 @@ class Script(
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if (!condition.value) { if (!condition.value) {
var message = args.list.getOrNull(1) var message = args.list.getOrNull(1)
if (message is Obj && message.objClass == Statement.type) message = message.callOn(this) if (message is Obj && message.objClass == Statement.type) message = call(message)
raiseIllegalState(message?.toString() ?: "check failed") raiseIllegalState(message?.toString() ?: "check failed")
} }
ObjVoid ObjVoid
} }
addFn("traceScope") { addFn("traceScope") {
this.trace(args.getOrNull(0)?.toString() ?: "") trace(args.getOrNull(0)?.toString() ?: "")
ObjVoid ObjVoid
} }
addFn("run") { addFn("run") {
requireOnlyArg<Obj>().callOn(this) call(requireOnlyArg())
} }
addFn("cached") { addFn("cached") {
val builder = requireOnlyArg<Obj>() val builder = requireOnlyArg<Obj>()
val capturedScope = this val capturedScope = this
var calculated = false var calculated = false
var cachedValue: Obj = ObjVoid var cachedValue: Obj = ObjVoid
ObjNativeCallable { net.sergeych.lyng.obj.ObjExternCallable.fromBridge {
if (!calculated) { if (!calculated) {
cachedValue = builder.callOn(capturedScope) cachedValue = capturedScope.call(builder)
calculated = true calculated = true
} }
cachedValue cachedValue
@ -504,7 +504,7 @@ class Script(
} }
addFn("lazy") { addFn("lazy") {
val builder = requireOnlyArg<Obj>() val builder = requireOnlyArg<Obj>()
ObjLazyDelegate(builder, this) ObjLazyDelegate(builder, requireScope())
} }
addVoidFn("delay") { addVoidFn("delay") {
val a = args.firstAndOnly() val a = args.firstAndOnly()
@ -512,7 +512,7 @@ class Script(
is ObjInt -> delay(a.value) is ObjInt -> delay(a.value)
is ObjReal -> delay((a.value * 1000).roundToLong()) is ObjReal -> delay((a.value * 1000).roundToLong())
is ObjDuration -> delay(a.duration) is ObjDuration -> delay(a.duration)
else -> raiseIllegalArgument("Expected Int, Real or Duration, got ${a.inspect(this)}") else -> raiseIllegalArgument("Expected Int, Real or Duration, got ${inspect(a)}")
} }
} }
@ -551,8 +551,9 @@ class Script(
addFn("launch") { addFn("launch") {
val callable = requireOnlyArg<Obj>() val callable = requireOnlyArg<Obj>()
val captured = this
ObjDeferred(globalDefer { ObjDeferred(globalDefer {
callable.callOn(this@addFn) captured.call(callable)
}) })
} }
@ -564,7 +565,7 @@ class Script(
addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) { addFn("flow", callSignature = CallSignature(tailBlockReceiverType = "FlowBuilder")) {
// important is: current context contains closure often used in call; // important is: current context contains closure often used in call;
// we'll need it for the producer // we'll need it for the producer
ObjFlow(requireOnlyArg<Obj>(), this) ObjFlow(requireOnlyArg<Obj>(), requireScope())
} }
val pi = ObjReal(PI) val pi = ObjReal(PI)
@ -639,7 +640,7 @@ class Script(
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)
else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect(this)}") else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${inspect(a)}")
} }
} }
} }

View File

@ -36,7 +36,7 @@ class TryStatement(
) )
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "try statement") return bytecodeOnly(scope, "try statement")
} }
private fun resolveExceptionClass(scope: Scope, name: String): ObjClass { private fun resolveExceptionClass(scope: Scope, name: String): ObjClass {

View File

@ -33,6 +33,6 @@ class VarDeclStatement(
override val pos: Pos = startPos override val pos: Pos = startPos
override suspend fun execute(context: Scope): Obj { override suspend fun execute(context: Scope): Obj {
return interpreterDisabled(context, "var declaration") return bytecodeOnly(context, "var declaration")
} }
} }

View File

@ -19,8 +19,8 @@ package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
sealed class WhenCondition(open val expr: Statement, open val pos: Pos) { sealed class WhenCondition(open val expr: Statement, open val pos: Pos) {
protected fun interpreterDisabled(scope: Scope): Nothing { protected fun bytecodeOnly(scope: Scope): Nothing {
return scope.raiseIllegalState("interpreter execution is not supported; when condition requires bytecode") return scope.raiseIllegalState("bytecode-only execution is required; when condition needs compiled bytecode")
} }
abstract suspend fun matches(scope: Scope, value: Obj): Boolean abstract suspend fun matches(scope: Scope, value: Obj): Boolean
@ -31,7 +31,7 @@ class WhenEqualsCondition(
override val pos: Pos, override val pos: Pos,
) : WhenCondition(expr, pos) { ) : WhenCondition(expr, pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean { override suspend fun matches(scope: Scope, value: Obj): Boolean {
return interpreterDisabled(scope) return bytecodeOnly(scope)
} }
} }
@ -41,7 +41,7 @@ class WhenInCondition(
override val pos: Pos, override val pos: Pos,
) : WhenCondition(expr, pos) { ) : WhenCondition(expr, pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean { override suspend fun matches(scope: Scope, value: Obj): Boolean {
return interpreterDisabled(scope) return bytecodeOnly(scope)
} }
} }
@ -51,7 +51,7 @@ class WhenIsCondition(
override val pos: Pos, override val pos: Pos,
) : WhenCondition(expr, pos) { ) : WhenCondition(expr, pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean { override suspend fun matches(scope: Scope, value: Obj): Boolean {
return interpreterDisabled(scope) return bytecodeOnly(scope)
} }
} }
@ -64,6 +64,6 @@ class WhenStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "when statement") return bytecodeOnly(scope, "when statement")
} }
} }

View File

@ -1880,13 +1880,23 @@ private suspend fun assignDestructurePattern(frame: CmdFrame, pattern: ListLiter
private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value: Obj, pos: Pos) { private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value: Obj, pos: Pos) {
when (ref) { when (ref) {
is ListLiteralRef -> assignDestructurePattern(frame, ref, value, pos) is ListLiteralRef -> {
assignDestructurePattern(frame, ref, value, pos)
return
}
is LocalSlotRef -> { is LocalSlotRef -> {
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = ref.captureOwnerScopeId != null) val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = ref.captureOwnerScopeId != null)
if (index != null) { if (index != null) {
frame.frame.setObj(index, value) frame.frame.setObj(index, value)
return return
} }
val scopeSlot = frame.fn.scopeSlotNames.indexOfFirst { it == ref.name }
if (scopeSlot >= 0) {
val target = frame.scopeTarget(scopeSlot)
val slotIndex = frame.ensureScopeSlot(target, scopeSlot)
target.setSlotValue(slotIndex, value)
return
}
} }
is LocalVarRef -> { is LocalVarRef -> {
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false) val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
@ -1894,6 +1904,13 @@ private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value:
frame.frame.setObj(index, value) frame.frame.setObj(index, value)
return return
} }
val scopeSlot = frame.fn.scopeSlotNames.indexOfFirst { it == ref.name }
if (scopeSlot >= 0) {
val target = frame.scopeTarget(scopeSlot)
val slotIndex = frame.ensureScopeSlot(target, scopeSlot)
target.setSlotValue(slotIndex, value)
return
}
} }
is FastLocalVarRef -> { is FastLocalVarRef -> {
val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false) val index = resolveLocalSlotIndex(frame.fn, ref.name, preferCapture = false)
@ -1901,6 +1918,13 @@ private suspend fun assignDestructureTarget(frame: CmdFrame, ref: ObjRef, value:
frame.frame.setObj(index, value) frame.frame.setObj(index, value)
return return
} }
val scopeSlot = frame.fn.scopeSlotNames.indexOfFirst { it == ref.name }
if (scopeSlot >= 0) {
val target = frame.scopeTarget(scopeSlot)
val slotIndex = frame.ensureScopeSlot(target, scopeSlot)
target.setSlotValue(slotIndex, value)
return
}
} }
else -> {} else -> {}
} }

View File

@ -19,6 +19,7 @@ package net.sergeych.lyng.miniast
import net.sergeych.lyng.ModuleScope import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.Visibility import net.sergeych.lyng.Visibility
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass import net.sergeych.lyng.obj.ObjClass
@ -40,7 +41,7 @@ inline fun <reified T : Obj> Scope.addFnDoc(
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
callSignature: net.sergeych.lyng.CallSignature? = null, callSignature: net.sergeych.lyng.CallSignature? = null,
crossinline fn: suspend Scope.() -> T crossinline fn: suspend ScopeFacade.() -> T
) { ) {
// Register runtime function(s) // Register runtime function(s)
addFn(*names, callSignature = callSignature) { fn() } addFn(*names, callSignature = callSignature) { fn() }
@ -57,7 +58,7 @@ inline fun Scope.addVoidFnDoc(
doc: String, doc: String,
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
crossinline fn: suspend Scope.() -> Unit crossinline fn: suspend ScopeFacade.() -> Unit
) { ) {
addFnDoc<ObjVoid>( addFnDoc<ObjVoid>(
*names, *names,
@ -98,7 +99,7 @@ fun ObjClass.addFnDoc(
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
code: suspend Scope.() -> Obj code: suspend ScopeFacade.() -> Obj
) { ) {
// Register runtime method // Register runtime method
addFn(name, isOpen, visibility, code = code) addFn(name, isOpen, visibility, code = code)
@ -136,7 +137,7 @@ fun ObjClass.addClassFnDoc(
isOpen: Boolean = false, isOpen: Boolean = false,
tags: Map<String, List<String>> = emptyMap(), tags: Map<String, List<String>> = emptyMap(),
moduleName: String? = null, moduleName: String? = null,
code: suspend Scope.() -> Obj code: suspend ScopeFacade.() -> Obj
) { ) {
addClassFn(name, isOpen, code) addClassFn(name, isOpen, code)
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) { BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
@ -152,8 +153,8 @@ fun ObjClass.addPropertyDoc(
type: TypeDoc? = null, type: TypeDoc? = null,
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
moduleName: String? = null, moduleName: String? = null,
getter: (suspend Scope.() -> Obj)? = null, getter: (suspend ScopeFacade.() -> Obj)? = null,
setter: (suspend Scope.(Obj) -> Unit)? = null setter: (suspend ScopeFacade.(Obj) -> Unit)? = null
) { ) {
addProperty(name, getter, setter, visibility) addProperty(name, getter, setter, visibility)
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) { BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {

View File

@ -20,7 +20,6 @@ package net.sergeych.lyng.obj
import net.sergeych.lyng.Compiler import net.sergeych.lyng.Compiler
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScriptError
// avoid KDOC bug: keep it // avoid KDOC bug: keep it
@Suppress("unused") @Suppress("unused")
@ -37,10 +36,11 @@ private class LambdaRef(
private val getterFn: suspend (Scope) -> ObjRecord, private val getterFn: suspend (Scope) -> ObjRecord,
private val setterFn: (suspend (Pos, Scope, Obj) -> Unit)? = null private val setterFn: (suspend (Pos, Scope, Obj) -> Unit)? = null
) : ObjRef { ) : ObjRef {
override suspend fun get(scope: Scope): ObjRecord = getterFn(scope) override suspend fun get(scope: Scope): ObjRecord {
return scope.raiseIllegalState("bytecode-only execution is required; Accessor evaluation is disabled")
}
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) { override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
val s = setterFn ?: throw ScriptError(pos, "can't assign value") scope.raiseIllegalState("bytecode-only execution is required; Accessor assignment is disabled")
s(pos, scope, newValue)
} }
} }
@ -57,4 +57,4 @@ val Accessor.getter: suspend (Scope) -> ObjRecord
fun Accessor.setter(pos: Pos): suspend (Scope, Obj) -> Unit = { scope, newValue -> fun Accessor.setter(pos: Pos): suspend (Scope, Obj) -> Unit = { scope, newValue ->
this.setAt(pos, scope, newValue) this.setAt(pos, scope, newValue)
} }

View File

@ -562,10 +562,10 @@ open class Obj {
else -> del.objClass.getInstanceMemberOrNull("getValue") else -> del.objClass.getInstanceMemberOrNull("getValue")
} }
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") { if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
val wrapper = ObjNativeCallable { val wrapper = ObjExternCallable.fromBridge {
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj val th2 = if (thisObj === ObjVoid) ObjNull else thisObj
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray() val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
del.invokeInstanceMethod(this, "invoke", Arguments(*allArgs)) del.invokeInstanceMethod(requireScope(), "invoke", Arguments(*allArgs))
} }
return obj.copy( return obj.copy(
value = wrapper, value = wrapper,
@ -740,7 +740,7 @@ open class Obj {
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisObj.toString(this, true) toStringOf(thisObj, true)
} }
addFnDoc( addFnDoc(
name = "inspect", name = "inspect",
@ -748,7 +748,7 @@ open class Obj {
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisObj.inspect(this).toObj() inspect(thisObj).toObj()
} }
addFnDoc( addFnDoc(
name = "contains", name = "contains",
@ -757,7 +757,7 @@ open class Obj {
returns = type("lyng.Bool"), returns = type("lyng.Bool"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
ObjBool(thisObj.contains(this, args.firstAndOnly())) ObjBool(thisObj.contains(requireScope(), args.firstAndOnly()))
} }
// utilities // utilities
addFnDoc( addFnDoc(
@ -766,7 +766,7 @@ open class Obj {
params = listOf(ParamDoc("block")), params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
args.firstAndOnly().callOn(createChildScope(Arguments(thisObj))) call(args.firstAndOnly(), Arguments(thisObj))
} }
addFnDoc( addFnDoc(
name = "apply", name = "apply",
@ -775,11 +775,12 @@ open class Obj {
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val body = args.firstAndOnly() val body = args.firstAndOnly()
val scope = requireScope()
(thisObj as? ObjInstance)?.let { (thisObj as? ObjInstance)?.let {
body.callOn(ApplyScope(this, it.instanceScope)) body.callOn(ApplyScope(scope, it.instanceScope))
} ?: run { } ?: run {
val appliedScope = createChildScope(newThisObj = thisObj) val appliedScope = scope.createChildScope(newThisObj = thisObj)
body.callOn(ApplyScope(this, appliedScope)) body.callOn(ApplyScope(scope, appliedScope))
} }
thisObj thisObj
} }
@ -789,7 +790,7 @@ open class Obj {
params = listOf(ParamDoc("block")), params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
args.firstAndOnly().callOn(createChildScope(Arguments(thisObj))) call(args.firstAndOnly(), Arguments(thisObj))
thisObj thisObj
} }
addFnDoc( addFnDoc(
@ -798,16 +799,16 @@ open class Obj {
params = listOf(ParamDoc("block")), params = listOf(ParamDoc("block")),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
args.firstAndOnly().callOn(this) call(args.firstAndOnly())
} }
addFn("getAt") { addFn("getAt") {
requireExactCount(1) requireExactCount(1)
thisObj.getAt(this, requiredArg<Obj>(0)) thisObj.getAt(requireScope(), requiredArg<Obj>(0))
} }
addFn("putAt") { addFn("putAt") {
requireExactCount(2) requireExactCount(2)
val newValue = args[1] val newValue = args[1]
thisObj.putAt(this, requiredArg<Obj>(0), newValue) thisObj.putAt(requireScope(), requiredArg<Obj>(0), newValue)
newValue newValue
} }
addFnDoc( addFnDoc(
@ -816,7 +817,7 @@ open class Obj {
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisObj.toJson(this).toString().toObj() thisObj.toJson(requireScope()).toString().toObj()
} }
addFnDoc( addFnDoc(
name = "toJsonString", name = "toJsonString",
@ -824,7 +825,7 @@ open class Obj {
returns = type("lyng.String"), returns = type("lyng.String"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisObj.toJson(this).toString().toObj() thisObj.toJson(requireScope()).toString().toObj()
} }
addFnDoc( addFnDoc(
name = "clamp", name = "clamp",
@ -836,12 +837,12 @@ open class Obj {
var result = thisObj var result = thisObj
if (range.start != null && !range.start.isNull) { if (range.start != null && !range.start.isNull) {
if (result.compareTo(this, range.start) < 0) { if (result.compareTo(requireScope(), range.start) < 0) {
result = range.start result = range.start
} }
} }
if (range.end != null && !range.end.isNull) { if (range.end != null && !range.end.isNull) {
val cmp = range.end.compareTo(this, result) val cmp = range.end.compareTo(requireScope(), result)
if (range.isEndInclusive) { if (range.isEndInclusive) {
if (cmp < 0) result = range.end if (cmp < 0) result = range.end
} else { } else {

View File

@ -32,7 +32,7 @@ val ObjArray by lazy {
doc = "Iterator over elements of this array using its indexer.", doc = "Iterator over elements of this array using its indexer.",
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))), returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ObjArrayIterator(thisObj).also { it.init(this) } } ) { ObjArrayIterator(thisObj).also { it.init(requireScope()) } }
addFnDoc( addFnDoc(
name = "contains", name = "contains",
@ -42,9 +42,10 @@ val ObjArray by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val obj = args.firstAndOnly() val obj = args.firstAndOnly()
for (i in 0..<thisObj.invokeInstanceMethod(this, "size").toInt()) { for (i in 0..<thisObj.invokeInstanceMethod(scope, "size").toInt()) {
if (thisObj.getAt(this, ObjInt(i.toLong())).compareTo(this, obj) == 0) return@addFnDoc ObjTrue if (thisObj.getAt(scope, ObjInt(i.toLong())).compareTo(scope, obj) == 0) return@addFnDoc ObjTrue
} }
ObjFalse ObjFalse
} }
@ -55,10 +56,11 @@ val ObjArray by lazy {
type = type("lyng.Any"), type = type("lyng.Any"),
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = {
val scope = requireScope()
this.thisObj.invokeInstanceMethod( this.thisObj.invokeInstanceMethod(
this, scope,
"getAt", "getAt",
(this.thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj() (this.thisObj.invokeInstanceMethod(scope, "size").toInt() - 1).toObj()
) )
} }
) )
@ -68,7 +70,10 @@ val ObjArray by lazy {
doc = "Index of the last element (size - 1).", doc = "Index of the last element (size - 1).",
type = type("lyng.Int"), type = type("lyng.Int"),
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { (this.thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj() } getter = {
val scope = requireScope()
(this.thisObj.invokeInstanceMethod(scope, "size").toInt() - 1).toObj()
}
) )
addPropertyDoc( addPropertyDoc(
@ -76,7 +81,10 @@ val ObjArray by lazy {
doc = "Range of valid indices for this array.", doc = "Range of valid indices for this array.",
type = type("lyng.Range"), type = type("lyng.Range"),
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { ObjRange(0.toObj(), this.thisObj.invokeInstanceMethod(this, "size"), false) } getter = {
val scope = requireScope()
ObjRange(0.toObj(), this.thisObj.invokeInstanceMethod(scope, "size"), false)
}
) )
addFnDoc( addFnDoc(
@ -86,15 +94,16 @@ val ObjArray by lazy {
returns = type("lyng.Int"), returns = type("lyng.Int"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val target = args.firstAndOnly() val target = args.firstAndOnly()
var low = 0 var low = 0
var high = thisObj.invokeInstanceMethod(this, "size").toInt() - 1 var high = thisObj.invokeInstanceMethod(scope, "size").toInt() - 1
while (low <= high) { while (low <= high) {
val mid = (low + high) / 2 val mid = (low + high) / 2
val midVal = thisObj.getAt(this, ObjInt(mid.toLong())) val midVal = thisObj.getAt(scope, ObjInt(mid.toLong()))
val cmp = midVal.compareTo(this, target) val cmp = midVal.compareTo(scope, target)
when { when {
cmp == 0 -> return@addFnDoc (mid).toObj() cmp == 0 -> return@addFnDoc (mid).toObj()
cmp > 0 -> high = mid - 1 cmp > 0 -> high = mid - 1
@ -105,4 +114,4 @@ val ObjArray by lazy {
(-low - 1).toObj() (-low - 1).toObj()
} }
} }
} }

View File

@ -38,8 +38,8 @@ class ObjArrayIterator(val array: Obj) : Obj() {
addFn("next") { addFn("next") {
val self = thisAs<ObjArrayIterator>() val self = thisAs<ObjArrayIterator>()
if (self.nextIndex < self.lastIndex) { if (self.nextIndex < self.lastIndex) {
self.array.invokeInstanceMethod(this, "getAt", (self.nextIndex++).toObj()) self.array.invokeInstanceMethod(requireScope(), "getAt", (self.nextIndex++).toObj())
} else raiseError(ObjIterationFinishedException(this)) } else raiseError(ObjIterationFinishedException(requireScope()))
} }
addFn("hasNext") { addFn("hasNext") {
val self = thisAs<ObjArrayIterator>() val self = thisAs<ObjArrayIterator>()
@ -48,4 +48,4 @@ class ObjArrayIterator(val array: Obj) : Obj() {
} }
} }
} }
} }

View File

@ -930,9 +930,9 @@ open class ObjClass(
isOverride: Boolean = false, isOverride: Boolean = false,
pos: Pos = Pos.builtIn, pos: Pos = Pos.builtIn,
methodId: Int? = null, methodId: Int? = null,
code: (suspend Scope.() -> Obj)? = null code: (suspend net.sergeych.lyng.ScopeFacade.() -> Obj)? = null
) { ) {
val stmt = code?.let { ObjNativeCallable { it() } } ?: ObjNull val stmt = code?.let { ObjExternCallable.fromBridge { it() } } ?: ObjNull
createField( createField(
name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass, name, stmt, isMutable, visibility, writeVisibility, pos, declaringClass,
isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride, isAbstract = isAbstract, isClosed = isClosed, isOverride = isOverride,
@ -945,8 +945,8 @@ open class ObjClass(
fun addProperty( fun addProperty(
name: String, name: String,
getter: (suspend Scope.() -> Obj)? = null, getter: (suspend net.sergeych.lyng.ScopeFacade.() -> Obj)? = null,
setter: (suspend Scope.(Obj) -> Unit)? = null, setter: (suspend net.sergeych.lyng.ScopeFacade.(Obj) -> Unit)? = null,
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
writeVisibility: Visibility? = null, writeVisibility: Visibility? = null,
declaringClass: ObjClass? = this, declaringClass: ObjClass? = this,
@ -957,8 +957,8 @@ open class ObjClass(
prop: ObjProperty? = null, prop: ObjProperty? = null,
methodId: Int? = null methodId: Int? = null
) { ) {
val g = getter?.let { ObjNativeCallable { it() } } val g = getter?.let { ObjExternCallable.fromBridge { it() } }
val s = setter?.let { ObjNativeCallable { it(requiredArg(0)); ObjVoid } } val s = setter?.let { ObjExternCallable.fromBridge { it(requiredArg(0)); ObjVoid } }
val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s) val finalProp = prop ?: if (isAbstract) ObjNull else ObjProperty(name, g, s)
createField( createField(
name, finalProp, false, visibility, writeVisibility, pos, declaringClass, name, finalProp, false, visibility, writeVisibility, pos, declaringClass,
@ -969,8 +969,8 @@ open class ObjClass(
} }
fun addClassConst(name: String, value: Obj) = createClassField(name, value) fun addClassConst(name: String, value: Obj) = createClassField(name, value)
fun addClassFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) { fun addClassFn(name: String, isOpen: Boolean = false, code: suspend net.sergeych.lyng.ScopeFacade.() -> Obj) {
createClassField(name, ObjNativeCallable { code() }, isOpen, type = ObjRecord.Type.Fun) createClassField(name, ObjExternCallable.fromBridge { code() }, isOpen, type = ObjRecord.Type.Fun)
} }

View File

@ -53,9 +53,8 @@ class ObjDateTime(val instant: Instant, val timeZone: TimeZone) : Obj() {
if (rec.type == ObjRecord.Type.Fun) { if (rec.type == ObjRecord.Type.Fun) {
val target = rec.value val target = rec.value
return ObjRecord( return ObjRecord(
ObjNativeCallable { ObjExternCallable.fromBridge {
val callScope = createChildScope(args = args, newThisObj = this@ObjDateTime) call(target, args, newThisObj = this@ObjDateTime)
target.callOn(callScope)
}, },
rec.isMutable rec.isMutable
) )

View File

@ -139,7 +139,7 @@ open class ObjException(
class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) { class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
init { init {
constructorMeta = ArgsDeclaration( constructorMeta = ArgsDeclaration(
listOf(ArgsDeclaration.Item("message", defaultValue = ObjNativeCallable { ObjString(name) })), listOf(ArgsDeclaration.Item("message", defaultValue = ObjExternCallable.fromBridge { ObjString(name) })),
Token.Type.RPAREN Token.Type.RPAREN
) )
} }
@ -177,17 +177,17 @@ open class ObjException(
} }
val Root = ExceptionClass("Exception").apply { val Root = ExceptionClass("Exception").apply {
instanceInitializers.add(ObjNativeCallable { instanceInitializers.add(ObjExternCallable.fromBridge {
if (thisObj is ObjInstance) { if (thisObj is ObjInstance) {
val msg = get("message")?.value ?: ObjString("Exception") val msg = get("message")?.value ?: ObjString("Exception")
(thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg) (thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
val stack = captureStackTrace(this) val stack = captureStackTrace(requireScope())
(thisObj as ObjInstance).instanceScope.addItem("Exception::stackTrace", false, stack) (thisObj as ObjInstance).instanceScope.addItem("Exception::stackTrace", false, stack)
} }
ObjVoid ObjVoid
}) })
instanceConstructor = ObjNativeCallable { ObjVoid } instanceConstructor = ObjExternCallable.fromBridge { ObjVoid }
addPropertyDoc( addPropertyDoc(
name = "message", name = "message",
doc = "Human‑readable error message.", doc = "Human‑readable error message.",
@ -244,7 +244,7 @@ open class ObjException(
is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList() is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList()
else -> ObjList() else -> ObjList()
} }
val at = stack.list.firstOrNull()?.toString(this) ?: ObjString("(unknown)") val at = stack.list.firstOrNull()?.let { toStringOf(it) } ?: ObjString("(unknown)")
ObjString("${thisObj.objClass.className}: $msg at $at") ObjString("${thisObj.objClass.className}: $msg at $at")
} }
} }

View File

@ -106,8 +106,8 @@ class ObjFlow(val producer: Obj, val scope: Scope) : Obj() {
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val objFlow = thisAs<ObjFlow>() val objFlow = thisAs<ObjFlow>()
ObjFlowIterator(ObjNativeCallable { ObjFlowIterator(ObjExternCallable.fromBridge {
objFlow.producer.callOn(this) call(objFlow.producer)
}) })
} }
} }
@ -164,7 +164,7 @@ class ObjFlowIterator(val producer: Obj) : Obj() {
doc = "Whether another element is available from the flow.", doc = "Whether another element is available from the flow.",
returns = type("lyng.Bool"), returns = type("lyng.Bool"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { thisAs<ObjFlowIterator>().hasNext(this).toObj() } ) { thisAs<ObjFlowIterator>().hasNext(requireScope()).toObj() }
addFnDoc( addFnDoc(
name = "next", name = "next",
doc = "Receive the next element from the flow or throw if completed.", doc = "Receive the next element from the flow or throw if completed.",
@ -172,7 +172,7 @@ class ObjFlowIterator(val producer: Obj) : Obj() {
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val x = thisAs<ObjFlowIterator>() val x = thisAs<ObjFlowIterator>()
x.next(this) x.next(requireScope())
} }
addFnDoc( addFnDoc(
name = "cancelIteration", name = "cancelIteration",

View File

@ -177,10 +177,10 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
else -> del.objClass.getInstanceMemberOrNull("getValue") else -> del.objClass.getInstanceMemberOrNull("getValue")
} }
if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") { if (getValueRec == null || getValueRec.declaringClass?.className == "Delegate") {
val wrapper = ObjNativeCallable { val wrapper = ObjExternCallable.fromBridge {
val th2 = if (thisObj === ObjVoid) ObjNull else thisObj val th2 = if (thisObj === ObjVoid) ObjNull else thisObj
val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray() val allArgs = (listOf(th2, ObjString(name)) + args.list).toTypedArray()
del.invokeInstanceMethod(this, "invoke", Arguments(*allArgs)) del.invokeInstanceMethod(requireScope(), "invoke", Arguments(*allArgs))
} }
return obj.copy(value = wrapper, type = ObjRecord.Type.Other) return obj.copy(value = wrapper, type = ObjRecord.Type.Other)
} }

View File

@ -41,10 +41,11 @@ val ObjIterable by lazy {
returns = type("lyng.List"), returns = type("lyng.List"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
val it = thisObj.invokeInstanceMethod(this, "iterator") val it = thisObj.invokeInstanceMethod(scope, "iterator")
while (it.invokeInstanceMethod(this, "hasNext").toBool()) { while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
result.add(it.invokeInstanceMethod(this, "next")) result.add(it.invokeInstanceMethod(scope, "next"))
} }
ObjList(result) ObjList(result)
} }
@ -58,10 +59,11 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val obj = args.firstAndOnly() val obj = args.firstAndOnly()
val it = thisObj.invokeInstanceMethod(this, "iterator") val it = thisObj.invokeInstanceMethod(scope, "iterator")
while (it.invokeInstanceMethod(this, "hasNext").toBool()) { while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
if (obj.equals(this, it.invokeInstanceMethod(this, "next"))) if (obj.equals(scope, it.invokeInstanceMethod(scope, "next")))
return@addFnDoc ObjTrue return@addFnDoc ObjTrue
} }
ObjFalse ObjFalse
@ -75,11 +77,12 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val scope = requireScope()
val obj = args.firstAndOnly() val obj = args.firstAndOnly()
var index = 0 var index = 0
val it = thisObj.invokeInstanceMethod(this, "iterator") val it = thisObj.invokeInstanceMethod(scope, "iterator")
while (it.invokeInstanceMethod(this, "hasNext").toBool()) { while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
if (obj.equals(this, it.invokeInstanceMethod(this, "next"))) if (obj.equals(scope, it.invokeInstanceMethod(scope, "next")))
return@addFnDoc ObjInt(index.toLong()) return@addFnDoc ObjInt(index.toLong())
index++ index++
} }
@ -96,9 +99,10 @@ val ObjIterable by lazy {
this.thisObj this.thisObj
else { else {
val result = mutableSetOf<Obj>() val result = mutableSetOf<Obj>()
val it = this.thisObj.invokeInstanceMethod(this, "iterator") val scope = requireScope()
while (it.invokeInstanceMethod(this, "hasNext").toBool()) { val it = this.thisObj.invokeInstanceMethod(scope, "iterator")
result.add(it.invokeInstanceMethod(this, "next")) while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
result.add(it.invokeInstanceMethod(scope, "next"))
} }
ObjSet(result) ObjSet(result)
} }
@ -112,10 +116,11 @@ val ObjIterable by lazy {
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = {
val result = mutableMapOf<Obj, Obj>() val result = mutableMapOf<Obj, Obj>()
this.thisObj.enumerate(this) { pair -> val scope = requireScope()
this.thisObj.enumerate(scope) { pair ->
when (pair) { when (pair) {
is ObjMapEntry -> result[pair.key] = pair.value is ObjMapEntry -> result[pair.key] = pair.value
else -> result[pair.getAt(this, 0)] = pair.getAt(this, 1) else -> result[pair.getAt(scope, 0)] = pair.getAt(scope, 1)
} }
true true
} }
@ -132,9 +137,8 @@ val ObjIterable by lazy {
) { ) {
val association = requireOnlyArg<Obj>() val association = requireOnlyArg<Obj>()
val result = ObjMap() val result = ObjMap()
thisObj.toFlow(this).collect { thisObj.toFlow(requireScope()).collect {
val callScope = createChildScope(args = Arguments(it)) result.map[call(association, Arguments(it))] = it
result.map[association.callOn(callScope)] = it
} }
result result
} }
@ -146,11 +150,12 @@ val ObjIterable by lazy {
isOpen = true, isOpen = true,
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val it = thisObj.invokeInstanceMethod(this, "iterator") val scope = requireScope()
val it = thisObj.invokeInstanceMethod(scope, "iterator")
val fn = requiredArg<Obj>(0) val fn = requiredArg<Obj>(0)
while (it.invokeInstanceMethod(this, "hasNext").toBool()) { while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
val x = it.invokeInstanceMethod(this, "next") val x = it.invokeInstanceMethod(scope, "next")
fn.callOn(this.createChildScope(Arguments(listOf(x)))) call(fn, Arguments(listOf(x)))
} }
ObjVoid ObjVoid
} }
@ -165,9 +170,8 @@ val ObjIterable by lazy {
) { ) {
val fn = requiredArg<Obj>(0) val fn = requiredArg<Obj>(0)
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
thisObj.toFlow(this).collect { thisObj.toFlow(requireScope()).collect {
val callScope = createChildScope(args = Arguments(it)) result.add(call(fn, Arguments(it)))
result.add(fn.callOn(callScope))
} }
ObjList(result) ObjList(result)
} }
@ -182,9 +186,8 @@ val ObjIterable by lazy {
) { ) {
val fn = requiredArg<Obj>(0) val fn = requiredArg<Obj>(0)
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
thisObj.toFlow(this).collect { thisObj.toFlow(requireScope()).collect {
val callScope = createChildScope(args = Arguments(it)) val transformed = call(fn, Arguments(it))
val transformed = fn.callOn(callScope)
if( transformed != ObjNull) result.add(transformed) if( transformed != ObjNull) result.add(transformed)
} }
ObjList(result) ObjList(result)
@ -200,7 +203,7 @@ val ObjIterable by lazy {
var n = requireOnlyArg<ObjInt>().value.toInt() var n = requireOnlyArg<ObjInt>().value.toInt()
val result = mutableListOf<Obj>() val result = mutableListOf<Obj>()
if (n > 0) { if (n > 0) {
thisObj.enumerate(this) { thisObj.enumerate(requireScope()) {
result.add(it) result.add(it)
--n > 0 --n > 0
} }
@ -215,8 +218,8 @@ val ObjIterable by lazy {
moduleName = "lyng.stdlib", moduleName = "lyng.stdlib",
getter = { getter = {
ObjBool( ObjBool(
this.thisObj.invokeInstanceMethod(this, "iterator") this.thisObj.invokeInstanceMethod(requireScope(), "iterator")
.invokeInstanceMethod(this, "hasNext").toBool() .invokeInstanceMethod(requireScope(), "hasNext").toBool()
.not() .not()
) )
} }
@ -229,11 +232,10 @@ val ObjIterable by lazy {
returns = type("lyng.List"), returns = type("lyng.List"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val list = thisObj.callMethod<ObjList>(this, "toList") val list = thisObj.callMethod<ObjList>(requireScope(), "toList")
val comparator = requireOnlyArg<Obj>() val comparator = requireOnlyArg<Obj>()
list.quicksort { a, b -> list.quicksort { a, b ->
val callScope = createChildScope(args = Arguments(a, b)) call(comparator, Arguments(a, b)).toInt()
comparator.callOn(callScope).toInt()
} }
list list
} }
@ -244,7 +246,7 @@ val ObjIterable by lazy {
returns = type("lyng.List"), returns = type("lyng.List"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val list = thisObj.callMethod<ObjList>(this, "toList") val list = thisObj.callMethod<ObjList>(requireScope(), "toList")
list.list.reverse() list.list.reverse()
list list
} }

View File

@ -69,13 +69,12 @@ val ObjIterator by lazy {
) { ) {
val out = mutableListOf<Obj>() val out = mutableListOf<Obj>()
while (true) { while (true) {
val has = thisObj.invokeInstanceMethod(this, "hasNext").toBool() val has = thisObj.invokeInstanceMethod(requireScope(), "hasNext").toBool()
if (!has) break if (!has) break
val v = thisObj.invokeInstanceMethod(this, "next") val v = thisObj.invokeInstanceMethod(requireScope(), "next")
out += v out += v
} }
ObjList(out.toMutableList()) ObjList(out.toMutableList())
} }
} }
} }

View File

@ -373,8 +373,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val comparator = requireOnlyArg<Obj>() val comparator = requireOnlyArg<Obj>()
thisAs<ObjList>().quicksort { a, b -> thisAs<ObjList>().quicksort { a, b ->
val callScope = createChildScope(args = Arguments(a, b)) call(comparator, Arguments(a, b)).toInt()
comparator.callOn(callScope).toInt()
} }
ObjVoid ObjVoid
} }
@ -394,6 +393,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
val self = thisAs<ObjList>() val self = thisAs<ObjList>()
val l = self.list val l = self.list
if (l.isEmpty()) return@addFnDoc ObjNull if (l.isEmpty()) return@addFnDoc ObjNull
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
// Fast path: all ints → accumulate as long // Fast path: all ints → accumulate as long
var i = 0 var i = 0
@ -407,7 +407,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
// Fallback to generic dynamic '+' accumulation starting from current acc // Fallback to generic dynamic '+' accumulation starting from current acc
var res: Obj = ObjInt(acc) var res: Obj = ObjInt(acc)
while (i < l.size) { while (i < l.size) {
res = res.plus(this, l[i]) res = res.plus(scope, l[i])
i++ i++
} }
return@addFnDoc res return@addFnDoc res
@ -419,7 +419,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
var res: Obj = l[0] var res: Obj = l[0]
var k = 1 var k = 1
while (k < l.size) { while (k < l.size) {
res = res.plus(this, l[k]) res = res.plus(scope, l[k])
k++ k++
} }
res res
@ -431,6 +431,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val l = thisAs<ObjList>().list val l = thisAs<ObjList>().list
if (l.isEmpty()) return@addFnDoc ObjNull if (l.isEmpty()) return@addFnDoc ObjNull
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
var i = 0 var i = 0
var hasOnlyInts = true var hasOnlyInts = true
@ -451,7 +452,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
var i = 1 var i = 1
while (i < l.size) { while (i < l.size) {
val v = l[i] val v = l[i]
if (v.compareTo(this, res) < 0) res = v if (v.compareTo(scope, res) < 0) res = v
i++ i++
} }
res res
@ -463,6 +464,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val l = thisAs<ObjList>().list val l = thisAs<ObjList>().list
if (l.isEmpty()) return@addFnDoc ObjNull if (l.isEmpty()) return@addFnDoc ObjNull
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
var i = 0 var i = 0
var hasOnlyInts = true var hasOnlyInts = true
@ -483,7 +485,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
var i = 1 var i = 1
while (i < l.size) { while (i < l.size) {
val v = l[i] val v = l[i]
if (v.compareTo(this, res) > 0) res = v if (v.compareTo(scope, res) > 0) res = v
i++ i++
} }
res res
@ -497,6 +499,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
) { ) {
val l = thisAs<ObjList>().list val l = thisAs<ObjList>().list
val needle = args.firstAndOnly() val needle = args.firstAndOnly()
val scope = requireScope()
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) { if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) {
var i = 0 var i = 0
while (i < l.size) { while (i < l.size) {
@ -508,7 +511,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
} }
var i = 0 var i = 0
while (i < l.size) { while (i < l.size) {
if (l[i].compareTo(this, needle) == 0) return@addFnDoc ObjInt(i.toLong()) if (l[i].compareTo(scope, needle) == 0) return@addFnDoc ObjInt(i.toLong())
i++ i++
} }
ObjInt((-1).toLong()) ObjInt((-1).toLong())

View File

@ -261,7 +261,7 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
val key = requiredArg<Obj>(0) val key = requiredArg<Obj>(0)
thisAs<ObjMap>().map.getOrPut(key) { thisAs<ObjMap>().map.getOrPut(key) {
val lambda = requiredArg<Obj>(1) val lambda = requiredArg<Obj>(1)
lambda.callOn(this) call(lambda)
} }
} }
addPropertyDoc( addPropertyDoc(

View File

@ -44,7 +44,7 @@ class ObjMutex(val mutex: Mutex): Obj() {
// Execute user lambda directly in the current scope to preserve the active scope // Execute user lambda directly in the current scope to preserve the active scope
// ancestry across suspension points. The lambda still constructs a closure scope // ancestry across suspension points. The lambda still constructs a closure scope
// on top of this frame, and parseLambdaExpression sets skipScopeCreation for its body. // on top of this frame, and parseLambdaExpression sets skipScopeCreation for its body.
thisAs<ObjMutex>().mutex.withLock { f.callOn(this) } thisAs<ObjMutex>().mutex.withLock { call(f) }
} }
} }
} }

View File

@ -1,33 +0,0 @@
/*
* Copyright 2026 Sergey S. Chernov
*
* 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.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
class ObjNativeCallable(
private val fn: suspend Scope.() -> Obj
) : Obj() {
override val objClass: ObjClass
get() = Statement.type
override suspend fun callOn(scope: Scope): Obj = scope.fn()
override fun toString(): String = "NativeCallable@${hashCode()}"
}

View File

@ -270,7 +270,7 @@ class ObjRange(
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
val self = thisAs<ObjRange>() val self = thisAs<ObjRange>()
self.buildIterator(this) self.buildIterator(requireScope())
} }
} }
} }

View File

@ -65,10 +65,10 @@ class ObjRangeIterator(
companion object { companion object {
val type = ObjClass("RangeIterator", ObjIterator).apply { val type = ObjClass("RangeIterator", ObjIterator).apply {
addFn("hasNext") { addFn("hasNext") {
thisAs<ObjRangeIterator>().hasNext(this).toObj() thisAs<ObjRangeIterator>().hasNext(requireScope()).toObj()
} }
addFn("next") { addFn("next") {
thisAs<ObjRangeIterator>().next(this) thisAs<ObjRangeIterator>().next(requireScope())
} }
} }
} }
@ -98,7 +98,7 @@ class ObjFastIntRangeIterator(private val start: Int, private val endExclusive:
companion object { companion object {
val type = ObjClass("FastIntRangeIterator", ObjIterator).apply { val type = ObjClass("FastIntRangeIterator", ObjIterator).apply {
addFn("hasNext") { thisAs<ObjFastIntRangeIterator>().hasNext().toObj() } addFn("hasNext") { thisAs<ObjFastIntRangeIterator>().hasNext().toObj() }
addFn("next") { thisAs<ObjFastIntRangeIterator>().next(this) } addFn("next") { thisAs<ObjFastIntRangeIterator>().next(requireScope()) }
} }
} }
} }

View File

@ -125,7 +125,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
// roundToInt: number rounded to the nearest integer // roundToInt: number rounded to the nearest integer
addConstDoc( addConstDoc(
name = "roundToInt", name = "roundToInt",
value = ObjNativeCallable { value = ObjExternCallable.fromBridge {
(thisObj as ObjReal).value.roundToLong().toObj() (thisObj as ObjReal).value.roundToLong().toObj()
}, },
doc = "This real number rounded to the nearest integer.", doc = "This real number rounded to the nearest integer.",

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@ import net.sergeych.lyng.PerfFlags
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.RegexCache import net.sergeych.lyng.RegexCache
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
class ObjRegex(val regex: Regex) : Obj() { class ObjRegex(val regex: Regex) : Obj() {
@ -75,9 +76,10 @@ class ObjRegex(val regex: Regex) : Obj() {
} }
createField( createField(
name = "operatorMatch", name = "operatorMatch",
initialValue = ObjNativeCallable { initialValue = ObjExternCallable.fromBridge {
val other = args.firstAndOnly(Pos.builtIn) val other = args.firstAndOnly(Pos.builtIn)
val targetScope = parent ?: this val scope = requireScope()
val targetScope = scope.parent ?: scope
(thisObj as ObjRegex).operatorMatch(targetScope, other) (thisObj as ObjRegex).operatorMatch(targetScope, other)
}, },
type = ObjRecord.Type.Fun type = ObjRecord.Type.Fun

View File

@ -175,7 +175,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
returns = type("lyng.Set"), returns = type("lyng.Set"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisAs<ObjSet>().mul(this, args.firstAndOnly()) thisAs<ObjSet>().mul(requireScope(), args.firstAndOnly())
} }
addFnDoc( addFnDoc(
name = "iterator", name = "iterator",
@ -192,7 +192,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
returns = type("lyng.Set"), returns = type("lyng.Set"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisAs<ObjSet>().plus(this, args.firstAndOnly()) thisAs<ObjSet>().plus(requireScope(), args.firstAndOnly())
} }
addFnDoc( addFnDoc(
name = "subtract", name = "subtract",
@ -201,7 +201,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
returns = type("lyng.Set"), returns = type("lyng.Set"),
moduleName = "lyng.stdlib" moduleName = "lyng.stdlib"
) { ) {
thisAs<ObjSet>().minus(this, args.firstAndOnly()) thisAs<ObjSet>().minus(requireScope(), args.firstAndOnly())
} }
addFnDoc( addFnDoc(
name = "remove", name = "remove",
@ -216,4 +216,4 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
} }
} }
} }
} }

View File

@ -25,6 +25,7 @@ import net.sergeych.lyng.PerfFlags
import net.sergeych.lyng.Pos import net.sergeych.lyng.Pos
import net.sergeych.lyng.RegexCache import net.sergeych.lyng.RegexCache
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonEncoder
@ -343,7 +344,7 @@ data class ObjString(val value: String) : Obj() {
name = "re", name = "re",
initialValue = ObjProperty( initialValue = ObjProperty(
name = "re", name = "re",
getter = ObjNativeCallable { getter = ObjExternCallable.fromBridge {
val pattern = (thisObj as ObjString).value val pattern = (thisObj as ObjString).value
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex() val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(pattern) else pattern.toRegex()
ObjRegex(re) ObjRegex(re)
@ -354,9 +355,10 @@ data class ObjString(val value: String) : Obj() {
) )
createField( createField(
name = "operatorMatch", name = "operatorMatch",
initialValue = ObjNativeCallable { initialValue = ObjExternCallable.fromBridge {
val other = args.firstAndOnly(Pos.builtIn) val other = args.firstAndOnly(Pos.builtIn)
val targetScope = parent ?: this val scope = requireScope()
val targetScope = scope.parent ?: scope
(thisObj as ObjString).operatorMatch(targetScope, other) (thisObj as ObjString).operatorMatch(targetScope, other)
}, },
type = ObjRecord.Type.Fun type = ObjRecord.Type.Fun

View File

@ -0,0 +1,38 @@
/*
* Copyright 2026 Sergey S. Chernov
*
* 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.
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireExactCount as coreRequireExactCount
import net.sergeych.lyng.requireNoArgs as coreRequireNoArgs
import net.sergeych.lyng.requireOnlyArg as coreRequireOnlyArg
import net.sergeych.lyng.requiredArg as coreRequiredArg
import net.sergeych.lyng.requireScope as coreRequireScope
import net.sergeych.lyng.thisAs as coreThisAs
inline fun <reified T : Obj> ScopeFacade.requiredArg(index: Int): T = coreRequiredArg(index)
inline fun <reified T : Obj> ScopeFacade.requireOnlyArg(): T = coreRequireOnlyArg()
fun ScopeFacade.requireExactCount(count: Int) = coreRequireExactCount(count)
fun ScopeFacade.requireNoArgs() = coreRequireNoArgs()
inline fun <reified T : Obj> ScopeFacade.thisAs(): T = coreThisAs()
internal fun ScopeFacade.requireScope(): Scope = coreRequireScope()

View File

@ -60,8 +60,8 @@ abstract class Statement(
suspend fun call(scope: Scope, vararg args: Obj) = execute(scope.createChildScope(args = Arguments(*args))) suspend fun call(scope: Scope, vararg args: Obj) = execute(scope.createChildScope(args = Arguments(*args)))
protected fun interpreterDisabled(scope: Scope, label: String): Nothing { protected fun bytecodeOnly(scope: Scope, label: String): Nothing {
return scope.raiseIllegalState("interpreter execution is not supported; $label requires bytecode") return scope.raiseIllegalState("bytecode-only execution is required; $label needs compiled bytecode")
} }
} }
@ -73,7 +73,7 @@ class IfStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "if statement") return bytecodeOnly(scope, "if statement")
} }
} }
@ -92,7 +92,7 @@ class ForInStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "for-in statement") return bytecodeOnly(scope, "for-in statement")
} }
} }
@ -106,7 +106,7 @@ class WhileStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "while statement") return bytecodeOnly(scope, "while statement")
} }
} }
@ -119,7 +119,7 @@ class DoWhileStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "do-while statement") return bytecodeOnly(scope, "do-while statement")
} }
} }
@ -129,7 +129,7 @@ class BreakStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "break statement") return bytecodeOnly(scope, "break statement")
} }
} }
@ -138,7 +138,7 @@ class ContinueStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "continue statement") return bytecodeOnly(scope, "continue statement")
} }
} }
@ -148,7 +148,7 @@ class ReturnStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "return statement") return bytecodeOnly(scope, "return statement")
} }
} }
@ -157,7 +157,7 @@ class ThrowStatement(
override val pos: Pos, override val pos: Pos,
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
return interpreterDisabled(scope, "throw statement") return bytecodeOnly(scope, "throw statement")
} }
} }
@ -165,7 +165,7 @@ class ExpressionStatement(
val ref: net.sergeych.lyng.obj.ObjRef, val ref: net.sergeych.lyng.obj.ObjRef,
override val pos: Pos override val pos: Pos
) : Statement() { ) : Statement() {
override suspend fun execute(scope: Scope): Obj = interpreterDisabled(scope, "expression statement") override suspend fun execute(scope: Scope): Obj = bytecodeOnly(scope, "expression statement")
} }
fun Statement.raise(text: String): Nothing { fun Statement.raise(text: String): Nothing {
@ -180,5 +180,5 @@ fun Statement.require(cond: Boolean, message: () -> String) {
object NopStatement: Statement(true, true, ObjType.Void) { object NopStatement: Statement(true, true, ObjType.Void) {
override val pos: Pos = Pos.builtIn override val pos: Pos = Pos.builtIn
override suspend fun execute(scope: Scope): Obj = interpreterDisabled(scope, "nop statement") override suspend fun execute(scope: Scope): Obj = bytecodeOnly(scope, "nop statement")
} }

View File

@ -18,6 +18,8 @@
package net.sergeych.lynon package net.sergeych.lynon
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.requireOnlyArg
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
// Most often used types: // Most often used types:
@ -42,10 +44,10 @@ object ObjLynonClass : ObjClass("Lynon") {
init { init {
addClassConst("test", ObjString("test_const")) addClassConst("test", ObjString("test_const"))
addClassFn("encode") { addClassFn("encode") {
encodeAny(this, requireOnlyArg<Obj>()) encodeAny(requireScope(), requireOnlyArg<Obj>())
} }
addClassFn("decode") { addClassFn("decode") {
decodeAny(this, requireOnlyArg<Obj>()) decodeAny(requireScope(), requireOnlyArg<Obj>())
} }
} }
} }

View File

@ -30,6 +30,7 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import net.sergeych.lyng.* import net.sergeych.lyng.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.thisAs
import net.sergeych.lyng.pacman.InlineSourcesImportProvider import net.sergeych.lyng.pacman.InlineSourcesImportProvider
import net.sergeych.mp_tools.globalDefer import net.sergeych.mp_tools.globalDefer
import net.sergeych.tools.bm import net.sergeych.tools.bm

View File

@ -178,11 +178,11 @@ suspend fun DocTest.test(_scope: Scope? = null) {
scope.apply { scope.apply {
addFn("println") { addFn("println") {
if( bookMode ) { if( bookMode ) {
println("${currentTest.fileNamePart}:${currentTest.line}> ${args.map{it.toString(this).value}.joinToString(" ")}") println("${currentTest.fileNamePart}:${currentTest.line}> ${args.map{toStringOf(it).value}.joinToString(" ")}")
} }
else { else {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {
if (i > 0) collectedOutput.append(' '); collectedOutput.append(a.toString(this).value) if (i > 0) collectedOutput.append(' '); collectedOutput.append(toStringOf(a).value)
collectedOutput.append('\n') collectedOutput.append('\n')
} }
} }