From 66d9a40bb11b2efc198b7bb24fe56b56b579f816 Mon Sep 17 00:00:00 2001 From: sergeych Date: Thu, 19 Feb 2026 11:41:39 +0300 Subject: [PATCH] better scopeFacade --- docs/embedding.md | 11 +++++ .../net/sergeych/lyng/io/fs/LyngFsModule.kt | 6 +-- .../lyng/io/process/LyngProcessModule.kt | 6 +-- .../kotlin/net/sergeych/lyng/ScopeFacade.kt | 33 +++++++++++++-- .../kotlin/net/sergeych/lyng/Script.kt | 31 +++----------- .../net/sergeych/lyng/obj/ObjArrayIterator.kt | 4 +- .../lyng/obj/ScopeFacadeObjExtensions.kt | 41 ++++++++++++++++++- 7 files changed, 92 insertions(+), 40 deletions(-) diff --git a/docs/embedding.md b/docs/embedding.md index 7eac8ee..18ae85a 100644 --- a/docs/embedding.md +++ b/docs/embedding.md @@ -114,6 +114,17 @@ scope.eval("val y = inc(41); log('Answer:', y)") You can register multiple names (aliases) at once: `addFn("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()`, `requireOnlyArg()`, `requireExactCount(...)`, `requireNoArgs()`, `thisAs()` +- 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. diff --git a/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/fs/LyngFsModule.kt b/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/fs/LyngFsModule.kt index 1ae8b86..9ef3d4f 100644 --- a/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/fs/LyngFsModule.kt +++ b/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/fs/LyngFsModule.kt @@ -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") } } diff --git a/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/process/LyngProcessModule.kt b/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/process/LyngProcessModule.kt index 3c41a16..c559536 100644 --- a/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/process/LyngProcessModule.kt +++ b/lyngio/src/commonMain/kotlin/net/sergeych/lyng/io/process/LyngProcessModule.kt @@ -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") } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ScopeFacade.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ScopeFacade.kt index cd2f973..b33f60f 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ScopeFacade.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ScopeFacade.kt @@ -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 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())) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt index 4034ea8..917ec42 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt @@ -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 diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArrayIterator.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArrayIterator.kt index 89af06b..7b58d2d 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArrayIterator.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjArrayIterator.kt @@ -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() if (self.nextIndex < self.lastIndex) { self.array.invokeInstanceMethod(requireScope(), "getAt", (self.nextIndex++).toObj()) - } else raiseError(ObjIterationFinishedException(requireScope())) + } else raiseIterationFinished() } addFn("hasNext") { val self = thisAs() diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ScopeFacadeObjExtensions.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ScopeFacadeObjExtensions.kt index f20e060..bb2e849 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ScopeFacadeObjExtensions.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ScopeFacadeObjExtensions.kt @@ -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 ScopeFacade.requiredArg(index: Int): T = coreRequiredArg(index) @@ -36,3 +47,29 @@ fun ScopeFacade.requireNoArgs() = coreRequireNoArgs() inline fun 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()