better scopeFacade

This commit is contained in:
Sergey Chernov 2026-02-19 11:41:39 +03:00
parent d82302dd01
commit 66d9a40bb1
7 changed files with 92 additions and 40 deletions

View File

@ -114,6 +114,17 @@ scope.eval("val y = inc(41); log('Answer:', y)")
You can register multiple names (aliases) at once: `addFn<ObjInt>("inc", "increment") { ... }`.
Scope-backed Kotlin lambdas receive a `ScopeFacade` (not a full `Scope`). For migration and convenience, these utilities are available on the facade:
- Access: `args`, `pos`, `thisObj`, `get(name)`
- Invocation: `call(...)`, `resolve(...)`, `assign(...)`, `toStringOf(...)`, `inspect(...)`, `trace(...)`
- Args helpers: `requiredArg<T>()`, `requireOnlyArg<T>()`, `requireExactCount(...)`, `requireNoArgs()`, `thisAs<T>()`
- Errors: `raiseError(...)`, `raiseClassCastError(...)`, `raiseIllegalArgument(...)`, `raiseIllegalState(...)`, `raiseNoSuchElement(...)`,
`raiseSymbolNotFound(...)`, `raiseNotImplemented(...)`, `raiseNPE()`, `raiseIndexOutOfBounds(...)`, `raiseIllegalAssignment(...)`,
`raiseUnset(...)`, `raiseNotFound(...)`, `raiseAssertionFailed(...)`, `raiseIllegalOperation(...)`, `raiseIterationFinished()`
If you truly need the full `Scope` (e.g., for low-level interop), use `requireScope()` explicitly.
### 5) Add Kotlin‑backed fields
If you need a simple field (with a value) instead of a computed property, use `createField`. This adds a field to the class that will be present in all its instances.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,10 +24,10 @@ package net.sergeych.lyng.io.fs
import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.requireScope
import net.sergeych.lyngio.fs.LyngFS
import net.sergeych.lyngio.fs.LyngFs
import net.sergeych.lyngio.fs.LyngPath
@ -478,7 +478,7 @@ private suspend inline fun ScopeFacade.fsGuard(crossinline block: suspend () ->
return try {
block()
} catch (e: AccessDeniedException) {
raiseError(ObjIllegalOperationException(requireScope(), e.reasonDetail ?: "access denied"))
raiseIllegalOperation(e.reasonDetail ?: "access denied")
}
}

View File

@ -21,10 +21,10 @@ import kotlinx.coroutines.flow.Flow
import net.sergeych.lyng.ModuleScope
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.requireScope
import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.requireScope
import net.sergeych.lyngio.process.*
import net.sergeych.lyngio.process.security.ProcessAccessDeniedException
import net.sergeych.lyngio.process.security.ProcessAccessPolicy
@ -210,9 +210,9 @@ private suspend inline fun ScopeFacade.processGuard(crossinline block: suspend (
return try {
block()
} catch (e: ProcessAccessDeniedException) {
raiseError(ObjIllegalOperationException(requireScope(), e.reasonDetail ?: "process access denied"))
raiseIllegalOperation(e.reasonDetail ?: "process access denied")
} catch (e: Exception) {
raiseError(ObjIllegalOperationException(requireScope(), e.message ?: "process error"))
raiseIllegalOperation(e.message ?: "process error")
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2026 Sergey S. Chernov
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -12,13 +12,12 @@
* 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
import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjRecord
import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.obj.*
/**
* Limited facade for Kotlin bridge callables.
@ -108,3 +107,29 @@ inline fun <reified T : Obj> ScopeFacade.thisAs(): T {
fun ScopeFacade.requireScope(): Scope =
(this as? ScopeBridge)?.scope ?: raiseIllegalState("ScopeFacade requires ScopeBridge")
fun ScopeFacade.raiseNPE(): Nothing = requireScope().raiseNPE()
fun ScopeFacade.raiseIndexOutOfBounds(message: String = "Index out of bounds"): Nothing =
requireScope().raiseIndexOutOfBounds(message)
fun ScopeFacade.raiseIllegalAssignment(message: String): Nothing =
requireScope().raiseIllegalAssignment(message)
fun ScopeFacade.raiseUnset(message: String = "property is unset (not initialized)"): Nothing =
requireScope().raiseUnset(message)
fun ScopeFacade.raiseNotFound(message: String = "not found"): Nothing =
requireScope().raiseNotFound(message)
fun ScopeFacade.raiseError(obj: Obj, pos: Pos = this.pos, message: String): Nothing =
requireScope().raiseError(obj, pos, message)
fun ScopeFacade.raiseAssertionFailed(message: String): Nothing =
raiseError(ObjAssertionFailedException(requireScope(), message))
fun ScopeFacade.raiseIllegalOperation(message: String = "Operation is illegal"): Nothing =
raiseError(ObjIllegalOperationException(requireScope(), message))
fun ScopeFacade.raiseIterationFinished(): Nothing =
raiseError(ObjIterationFinishedException(requireScope()))

View File

@ -26,7 +26,6 @@ import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.stdlib_included.rootLyng
import net.sergeych.lyng.bridge.LyngClassBridge
import net.sergeych.lynon.ObjLynonClass
import net.sergeych.mp_tools.globalDefer
import kotlin.math.*
@ -326,7 +325,7 @@ class Script(
": " + toStringOf(call(args[1])).value
else ""
if (!cond.value == true)
raiseError(ObjAssertionFailedException(requireScope(), "Assertion failed$message"))
raiseAssertionFailed("Assertion failed$message")
}
fun unwrapCompareArg(value: Obj): Obj {
@ -342,12 +341,7 @@ class Script(
val a = unwrapCompareArg(requiredArg(0))
val b = unwrapCompareArg(requiredArg(1))
if (a.compareTo(requireScope(), b) != 0) {
raiseError(
ObjAssertionFailedException(
requireScope(),
"Assertion failed: ${inspect(a)} == ${inspect(b)}"
)
)
raiseAssertionFailed("Assertion failed: ${inspect(a)} == ${inspect(b)}")
}
}
// alias used in tests
@ -355,23 +349,13 @@ class Script(
val a = unwrapCompareArg(requiredArg(0))
val b = unwrapCompareArg(requiredArg(1))
if (a.compareTo(requireScope(), b) != 0)
raiseError(
ObjAssertionFailedException(
requireScope(),
"Assertion failed: ${inspect(a)} == ${inspect(b)}"
)
)
raiseAssertionFailed("Assertion failed: ${inspect(a)} == ${inspect(b)}")
}
addVoidFn("assertNotEquals") {
val a = unwrapCompareArg(requiredArg(0))
val b = unwrapCompareArg(requiredArg(1))
if (a.compareTo(requireScope(), b) == 0)
raiseError(
ObjAssertionFailedException(
requireScope(),
"Assertion failed: ${inspect(a)} != ${inspect(b)}"
)
)
raiseAssertionFailed("Assertion failed: ${inspect(a)} != ${inspect(b)}")
}
addFnDoc(
"assertThrows",
@ -409,12 +393,7 @@ class Script(
} catch (_: ScriptError) {
ObjNull
}
if (result == null) raiseError(
ObjAssertionFailedException(
requireScope(),
"Expected exception but nothing was thrown"
)
)
if (result == null) raiseAssertionFailed("Expected exception but nothing was thrown")
expectedClass?.let {
if (!result.isInstanceOf(it)) {
val actual = if (result is ObjException) result.exceptionClass else result.objClass

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -39,7 +39,7 @@ class ObjArrayIterator(val array: Obj) : Obj() {
val self = thisAs<ObjArrayIterator>()
if (self.nextIndex < self.lastIndex) {
self.array.invokeInstanceMethod(requireScope(), "getAt", (self.nextIndex++).toObj())
} else raiseError(ObjIterationFinishedException(requireScope()))
} else raiseIterationFinished()
}
addFn("hasNext") {
val self = thisAs<ObjArrayIterator>()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2026 Sergey S. Chernov
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -12,17 +12,28 @@
* 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.Pos
import net.sergeych.lyng.Scope
import net.sergeych.lyng.ScopeFacade
import net.sergeych.lyng.raiseAssertionFailed as coreRaiseAssertionFailed
import net.sergeych.lyng.raiseError as coreRaiseError
import net.sergeych.lyng.raiseIllegalAssignment as coreRaiseIllegalAssignment
import net.sergeych.lyng.raiseIllegalOperation as coreRaiseIllegalOperation
import net.sergeych.lyng.raiseIndexOutOfBounds as coreRaiseIndexOutOfBounds
import net.sergeych.lyng.raiseIterationFinished as coreRaiseIterationFinished
import net.sergeych.lyng.raiseNPE as coreRaiseNPE
import net.sergeych.lyng.raiseNotFound as coreRaiseNotFound
import net.sergeych.lyng.raiseUnset as coreRaiseUnset
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.requiredArg as coreRequiredArg
import net.sergeych.lyng.thisAs as coreThisAs
inline fun <reified T : Obj> ScopeFacade.requiredArg(index: Int): T = coreRequiredArg(index)
@ -36,3 +47,29 @@ fun ScopeFacade.requireNoArgs() = coreRequireNoArgs()
inline fun <reified T : Obj> ScopeFacade.thisAs(): T = coreThisAs()
internal fun ScopeFacade.requireScope(): Scope = coreRequireScope()
fun ScopeFacade.raiseNPE(): Nothing = coreRaiseNPE()
fun ScopeFacade.raiseIndexOutOfBounds(message: String = "Index out of bounds"): Nothing =
coreRaiseIndexOutOfBounds(message)
fun ScopeFacade.raiseIllegalAssignment(message: String): Nothing =
coreRaiseIllegalAssignment(message)
fun ScopeFacade.raiseUnset(message: String = "property is unset (not initialized)"): Nothing =
coreRaiseUnset(message)
fun ScopeFacade.raiseNotFound(message: String = "not found"): Nothing =
coreRaiseNotFound(message)
fun ScopeFacade.raiseError(obj: Obj, pos: Pos = this.pos, message: String): Nothing =
coreRaiseError(obj, pos, message)
fun ScopeFacade.raiseAssertionFailed(message: String): Nothing =
coreRaiseAssertionFailed(message)
fun ScopeFacade.raiseIllegalOperation(message: String = "Operation is illegal"): Nothing =
coreRaiseIllegalOperation(message)
fun ScopeFacade.raiseIterationFinished(): Nothing =
coreRaiseIterationFinished()