heroic attempt to fix kotlin 3.2.0 wasmJS compiler
(incomplete)
This commit is contained in:
parent
7b1ba71ef0
commit
f9ae00b7f4
@ -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");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
#Gradle
|
#Gradle
|
||||||
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
|
org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.configuration-cache=true
|
org.gradle.configuration-cache=true
|
||||||
#Kotlin
|
#Kotlin
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -23,9 +23,11 @@ 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.ScopeCallable
|
||||||
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
|
||||||
|
import net.sergeych.lyng.pacman.ModuleBuilder
|
||||||
import net.sergeych.lyngio.fs.LyngFS
|
import net.sergeych.lyngio.fs.LyngFS
|
||||||
import net.sergeych.lyngio.fs.LyngFs
|
import net.sergeych.lyngio.fs.LyngFs
|
||||||
import net.sergeych.lyngio.fs.LyngPath
|
import net.sergeych.lyngio.fs.LyngPath
|
||||||
@ -50,9 +52,11 @@ fun createFsModule(policy: FsAccessPolicy, manager: ImportManager): Boolean {
|
|||||||
// Avoid re-registering in this ImportManager
|
// Avoid re-registering in this ImportManager
|
||||||
if (manager.packageNames.contains(name)) return false
|
if (manager.packageNames.contains(name)) return false
|
||||||
|
|
||||||
manager.addPackage(name) { module ->
|
manager.addPackage(name, object : ModuleBuilder {
|
||||||
buildFsModule(module, policy)
|
override suspend fun build(module: ModuleScope) {
|
||||||
}
|
buildFsModule(module, policy)
|
||||||
|
}
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,322 +82,394 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
|||||||
name = "name",
|
name = "name",
|
||||||
doc = "Base name of the path (last segment).",
|
doc = "Base name of the path (last segment).",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjPath>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.path.name.toObj()
|
val self = scp.thisAs<ObjPath>()
|
||||||
}
|
return self.path.name.toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "parent",
|
name = "parent",
|
||||||
doc = "Parent directory as a Path or null if none.",
|
doc = "Parent directory as a Path or null if none.",
|
||||||
returns = type("Path", nullable = true),
|
returns = type("Path", nullable = true),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjPath>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.path.parent?.let {
|
val self = scp.thisAs<ObjPath>()
|
||||||
ObjPath( this@apply, self.secured, it)
|
return self.path.parent?.let {
|
||||||
} ?: ObjNull
|
ObjPath(this@apply, self.secured, it)
|
||||||
}
|
} ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "segments",
|
name = "segments",
|
||||||
doc = "List of path segments.",
|
doc = "List of path segments.",
|
||||||
// returns: List<String>
|
// returns: List<String>
|
||||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjPath>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjList(self.path.segments.map { ObjString(it) }.toMutableList())
|
val self = scp.thisAs<ObjPath>()
|
||||||
}
|
return ObjList(self.path.segments.map { ObjString(it) }.toMutableList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
// exists(): Bool
|
// exists(): Bool
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "exists",
|
name = "exists",
|
||||||
doc = "Check whether this path exists.",
|
doc = "Check whether this path exists.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
(self.secured.exists(self.path)).toObj()
|
val self = scp.thisObj as ObjPath
|
||||||
|
(self.secured.exists(self.path)).toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// isFile(): Bool — cached metadata
|
// isFile(): Bool — cached metadata
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "isFile",
|
name = "isFile",
|
||||||
doc = "True if this path is a regular file (based on cached metadata).",
|
doc = "True if this path is a regular file (based on cached metadata).",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
self.ensureMetadata().let { ObjBool(it.isRegularFile) }
|
val self = scp.thisObj as ObjPath
|
||||||
|
self.ensureMetadata().let { ObjBool(it.isRegularFile) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// isDirectory(): Bool — cached metadata
|
// isDirectory(): Bool — cached metadata
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "isDirectory",
|
name = "isDirectory",
|
||||||
doc = "True if this path is a directory (based on cached metadata).",
|
doc = "True if this path is a directory (based on cached metadata).",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
self.ensureMetadata().let { ObjBool(it.isDirectory) }
|
val self = scp.thisObj as ObjPath
|
||||||
|
self.ensureMetadata().let { ObjBool(it.isDirectory) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// size(): Int? — null when unavailable
|
// size(): Int? — null when unavailable
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "File size in bytes, or null when unavailable.",
|
doc = "File size in bytes, or null when unavailable.",
|
||||||
returns = type("lyng.Int", nullable = true),
|
returns = type("lyng.Int", nullable = true),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val m = self.ensureMetadata()
|
val self = scp.thisObj as ObjPath
|
||||||
m.size?.let { ObjInt(it) } ?: ObjNull
|
val m = self.ensureMetadata()
|
||||||
|
m.size?.let { ObjInt(it) } ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// createdAt(): Instant? — Lyng Instant, null when unavailable
|
// createdAt(): Instant? — Lyng Instant, null when unavailable
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "createdAt",
|
name = "createdAt",
|
||||||
doc = "Creation time as `Instant`, or null when unavailable.",
|
doc = "Creation time as `Instant`, or null when unavailable.",
|
||||||
returns = type("lyng.Instant", nullable = true),
|
returns = type("lyng.Instant", nullable = true),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val m = self.ensureMetadata()
|
val self = scp.thisObj as ObjPath
|
||||||
m.createdAtMillis?.let { ObjInstant(kotlin.time.Instant.fromEpochMilliseconds(it)) } ?: ObjNull
|
val m = self.ensureMetadata()
|
||||||
|
m.createdAtMillis?.let { ObjInstant(kotlin.time.Instant.fromEpochMilliseconds(it)) } ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// createdAtMillis(): Int? — milliseconds since epoch or null
|
// createdAtMillis(): Int? — milliseconds since epoch or null
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "createdAtMillis",
|
name = "createdAtMillis",
|
||||||
doc = "Creation time in milliseconds since epoch, or null when unavailable.",
|
doc = "Creation time in milliseconds since epoch, or null when unavailable.",
|
||||||
returns = type("lyng.Int", nullable = true),
|
returns = type("lyng.Int", nullable = true),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val m = self.ensureMetadata()
|
val self = scp.thisObj as ObjPath
|
||||||
m.createdAtMillis?.let { ObjInt(it) } ?: ObjNull
|
val m = self.ensureMetadata()
|
||||||
|
m.createdAtMillis?.let { ObjInt(it) } ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// modifiedAt(): Instant? — Lyng Instant, null when unavailable
|
// modifiedAt(): Instant? — Lyng Instant, null when unavailable
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "modifiedAt",
|
name = "modifiedAt",
|
||||||
doc = "Last modification time as `Instant`, or null when unavailable.",
|
doc = "Last modification time as `Instant`, or null when unavailable.",
|
||||||
returns = type("lyng.Instant", nullable = true),
|
returns = type("lyng.Instant", nullable = true),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val m = self.ensureMetadata()
|
val self = scp.thisObj as ObjPath
|
||||||
m.modifiedAtMillis?.let { ObjInstant(kotlinx.datetime.Instant.fromEpochMilliseconds(it)) } ?: ObjNull
|
val m = self.ensureMetadata()
|
||||||
|
m.modifiedAtMillis?.let { ObjInstant(kotlinx.datetime.Instant.fromEpochMilliseconds(it)) } ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// modifiedAtMillis(): Int? — milliseconds since epoch or null
|
// modifiedAtMillis(): Int? — milliseconds since epoch or null
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "modifiedAtMillis",
|
name = "modifiedAtMillis",
|
||||||
doc = "Last modification time in milliseconds since epoch, or null when unavailable.",
|
doc = "Last modification time in milliseconds since epoch, or null when unavailable.",
|
||||||
returns = type("lyng.Int", nullable = true),
|
returns = type("lyng.Int", nullable = true),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val m = self.ensureMetadata()
|
val self = scp.thisObj as ObjPath
|
||||||
m.modifiedAtMillis?.let { ObjInt(it) } ?: ObjNull
|
val m = self.ensureMetadata()
|
||||||
|
m.modifiedAtMillis?.let { ObjInt(it) } ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// list(): List<Path>
|
// list(): List<Path>
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "list",
|
name = "list",
|
||||||
doc = "List directory entries as `Path` objects.",
|
doc = "List directory entries as `Path` objects.",
|
||||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("Path"))),
|
returns = TypeGenericDoc(type("lyng.List"), listOf(type("Path"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val items = self.secured.list(self.path).map { ObjPath(self.objClass, self.secured, it) }
|
val self = scp.thisObj as ObjPath
|
||||||
ObjList(items.toMutableList())
|
val items = self.secured.list(self.path).map { ObjPath(self.objClass, self.secured, it) }
|
||||||
|
ObjList(items.toMutableList())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// readBytes(): Buffer
|
// readBytes(): Buffer
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "readBytes",
|
name = "readBytes",
|
||||||
doc = "Read the file into a binary buffer.",
|
doc = "Read the file into a binary buffer.",
|
||||||
returns = type("lyng.Buffer"),
|
returns = type("lyng.Buffer"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val bytes = self.secured.readBytes(self.path)
|
val self = scp.thisObj as ObjPath
|
||||||
ObjBuffer(bytes.asUByteArray())
|
val bytes = self.secured.readBytes(self.path)
|
||||||
|
ObjBuffer(bytes.asUByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// writeBytes(bytes: Buffer)
|
// writeBytes(bytes: Buffer)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "writeBytes",
|
name = "writeBytes",
|
||||||
doc = "Write a binary buffer to the file, replacing content.",
|
doc = "Write a binary buffer to the file, replacing content.",
|
||||||
params = listOf(ParamDoc("bytes", type("lyng.Buffer"))),
|
params = listOf(ParamDoc("bytes", type("lyng.Buffer"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val buf = requiredArg<ObjBuffer>(0)
|
val self = scp.thisObj as ObjPath
|
||||||
self.secured.writeBytes(self.path, buf.byteArray.asByteArray(), append = false)
|
val buf = scp.requiredArg<ObjBuffer>(0)
|
||||||
ObjVoid
|
self.secured.writeBytes(self.path, buf.byteArray.asByteArray(), append = false)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// appendBytes(bytes: Buffer)
|
// appendBytes(bytes: Buffer)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "appendBytes",
|
name = "appendBytes",
|
||||||
doc = "Append a binary buffer to the end of the file.",
|
doc = "Append a binary buffer to the end of the file.",
|
||||||
params = listOf(ParamDoc("bytes", type("lyng.Buffer"))),
|
params = listOf(ParamDoc("bytes", type("lyng.Buffer"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val buf = requiredArg<ObjBuffer>(0)
|
val self = scp.thisObj as ObjPath
|
||||||
self.secured.writeBytes(self.path, buf.byteArray.asByteArray(), append = true)
|
val buf = scp.requiredArg<ObjBuffer>(0)
|
||||||
ObjVoid
|
self.secured.writeBytes(self.path, buf.byteArray.asByteArray(), append = true)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// readUtf8(): String
|
// readUtf8(): String
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "readUtf8",
|
name = "readUtf8",
|
||||||
doc = "Read the file as a UTF-8 string.",
|
doc = "Read the file as a UTF-8 string.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
self.secured.readUtf8(self.path).toObj()
|
val self = scp.thisObj as ObjPath
|
||||||
|
self.secured.readUtf8(self.path).toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// writeUtf8(text: String)
|
// writeUtf8(text: String)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "writeUtf8",
|
name = "writeUtf8",
|
||||||
doc = "Write a UTF-8 string to the file, replacing content.",
|
doc = "Write a UTF-8 string to the file, replacing content.",
|
||||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val text = requireOnlyArg<ObjString>().value
|
val self = scp.thisObj as ObjPath
|
||||||
self.secured.writeUtf8(self.path, text, append = false)
|
val text = scp.requireOnlyArg<ObjString>().value
|
||||||
ObjVoid
|
self.secured.writeUtf8(self.path, text, append = false)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// appendUtf8(text: String)
|
// appendUtf8(text: String)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "appendUtf8",
|
name = "appendUtf8",
|
||||||
doc = "Append UTF-8 text to the end of the file.",
|
doc = "Append UTF-8 text to the end of the file.",
|
||||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val text = requireOnlyArg<ObjString>().value
|
val self = scp.thisObj as ObjPath
|
||||||
self.secured.writeUtf8(self.path, text, append = true)
|
val text = scp.requireOnlyArg<ObjString>().value
|
||||||
ObjVoid
|
self.secured.writeUtf8(self.path, text, append = true)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// metadata(): Map
|
// metadata(): Map
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "metadata",
|
name = "metadata",
|
||||||
doc = "Fetch cached metadata as a map of fields: `isFile`, `isDirectory`, `size`, `createdAtMillis`, `modifiedAtMillis`, `isSymlink`.",
|
doc = "Fetch cached metadata as a map of fields: `isFile`, `isDirectory`, `size`, `createdAtMillis`, `modifiedAtMillis`, `isSymlink`.",
|
||||||
returns = TypeGenericDoc(type("lyng.Map"), listOf(type("lyng.String"), type("lyng.Any"))),
|
returns = TypeGenericDoc(type("lyng.Map"), listOf(type("lyng.String"), type("lyng.Any"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val m = self.secured.metadata(self.path)
|
val self = scp.thisObj as ObjPath
|
||||||
ObjMap(mutableMapOf(
|
val m = self.secured.metadata(self.path)
|
||||||
ObjString("isFile") to ObjBool(m.isRegularFile),
|
ObjMap(mutableMapOf(
|
||||||
ObjString("isDirectory") to ObjBool(m.isDirectory),
|
ObjString("isFile") to ObjBool(m.isRegularFile),
|
||||||
ObjString("size") to (m.size?.toLong() ?: 0L).toObj(),
|
ObjString("isDirectory") to ObjBool(m.isDirectory),
|
||||||
ObjString("createdAtMillis") to ((m.createdAtMillis ?: 0L)).toObj(),
|
ObjString("size") to (m.size?.toLong() ?: 0L).toObj(),
|
||||||
ObjString("modifiedAtMillis") to ((m.modifiedAtMillis ?: 0L)).toObj(),
|
ObjString("createdAtMillis") to ((m.createdAtMillis ?: 0L)).toObj(),
|
||||||
ObjString("isSymlink") to ObjBool(m.isSymlink),
|
ObjString("modifiedAtMillis") to ((m.modifiedAtMillis ?: 0L)).toObj(),
|
||||||
))
|
ObjString("isSymlink") to ObjBool(m.isSymlink),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// mkdirs(mustCreate: Bool=false)
|
// mkdirs(mustCreate: Bool=false)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "mkdirs",
|
name = "mkdirs",
|
||||||
doc = "Create directories (like `mkdir -p`). If `mustCreate` is true and the path already exists, the call fails. Otherwise it is a no‑op when the directory exists.",
|
doc = "Create directories (like `mkdir -p`). If `mustCreate` is true and the path already exists, the call fails. Otherwise it is a no‑op when the directory exists.",
|
||||||
params = listOf(ParamDoc("mustCreate", type("lyng.Bool"))),
|
params = listOf(ParamDoc("mustCreate", type("lyng.Bool"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val mustCreate = args.list.getOrNull(0)?.toBool() ?: false
|
val self = scp.thisObj as ObjPath
|
||||||
self.secured.createDirectories(self.path, mustCreate)
|
val mustCreate = scp.args.list.getOrNull(0)?.toBool() ?: false
|
||||||
ObjVoid
|
self.secured.createDirectories(self.path, mustCreate)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// move(to: Path|String, overwrite: Bool=false)
|
// move(to: Path|String, overwrite: Bool=false)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "move",
|
name = "move",
|
||||||
doc = "Move this path to a new location. `to` may be a `Path` or `String`. When `overwrite` is false and the target exists, the operation fails (provider may throw `AccessDeniedException`).",
|
doc = "Move this path to a new location. `to` may be a `Path` or `String`. When `overwrite` is false and the target exists, the operation fails (provider may throw `AccessDeniedException`).",
|
||||||
// types vary; keep generic description in doc
|
// types vary; keep generic description in doc
|
||||||
params = listOf(ParamDoc("to"), ParamDoc("overwrite", type("lyng.Bool"))),
|
params = listOf(ParamDoc("to"), ParamDoc("overwrite", type("lyng.Bool"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val toPath = parsePathArg(this, self, requiredArg<Obj>(0))
|
val self = scp.thisObj as ObjPath
|
||||||
val overwrite = args.list.getOrNull(1)?.toBool() ?: false
|
val toPath = parsePathArg(scp, self, scp.requiredArg<Obj>(0))
|
||||||
self.secured.move(self.path, toPath, overwrite)
|
val overwrite = scp.args.list.getOrNull(1)?.toBool() ?: false
|
||||||
ObjVoid
|
self.secured.move(self.path, toPath, overwrite)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// delete(mustExist: Bool=false, recursively: Bool=false)
|
// delete(mustExist: Bool=false, recursively: Bool=false)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "delete",
|
name = "delete",
|
||||||
doc = "Delete this path. `mustExist=true` causes failure if the path does not exist. `recursively=true` removes directories with their contents. Providers can throw `AccessDeniedException` on policy violations.",
|
doc = "Delete this path. `mustExist=true` causes failure if the path does not exist. `recursively=true` removes directories with their contents. Providers can throw `AccessDeniedException` on policy violations.",
|
||||||
params = listOf(ParamDoc("mustExist", type("lyng.Bool")), ParamDoc("recursively", type("lyng.Bool"))),
|
params = listOf(ParamDoc("mustExist", type("lyng.Bool")), ParamDoc("recursively", type("lyng.Bool"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val mustExist = args.list.getOrNull(0)?.toBool() ?: false
|
val self = scp.thisObj as ObjPath
|
||||||
val recursively = args.list.getOrNull(1)?.toBool() ?: false
|
val mustExist = scp.args.list.getOrNull(0)?.toBool() ?: false
|
||||||
self.secured.delete(self.path, mustExist, recursively)
|
val recursively = scp.args.list.getOrNull(1)?.toBool() ?: false
|
||||||
ObjVoid
|
self.secured.delete(self.path, mustExist, recursively)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// copy(to: Path|String, overwrite: Bool=false)
|
// copy(to: Path|String, overwrite: Bool=false)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "copy",
|
name = "copy",
|
||||||
doc = "Copy this path to a new location. `to` may be a `Path` or `String`. When `overwrite` is false and the target exists, the operation fails (provider may throw `AccessDeniedException`).",
|
doc = "Copy this path to a new location. `to` may be a `Path` or `String`. When `overwrite` is false and the target exists, the operation fails (provider may throw `AccessDeniedException`).",
|
||||||
params = listOf(ParamDoc("to"), ParamDoc("overwrite", type("lyng.Bool"))),
|
params = listOf(ParamDoc("to"), ParamDoc("overwrite", type("lyng.Bool"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val toPath = parsePathArg(this, self, requiredArg<Obj>(0))
|
val self = scp.thisObj as ObjPath
|
||||||
val overwrite = args.list.getOrNull(1)?.toBool() ?: false
|
val toPath = parsePathArg(scp, self, scp.requiredArg<Obj>(0))
|
||||||
self.secured.copy(self.path, toPath, overwrite)
|
val overwrite = scp.args.list.getOrNull(1)?.toBool() ?: false
|
||||||
ObjVoid
|
self.secured.copy(self.path, toPath, overwrite)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// glob(pattern: String): List<Path>
|
// glob(pattern: String): List<Path>
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "glob",
|
name = "glob",
|
||||||
doc = "List entries matching a glob pattern (no recursion).",
|
doc = "List entries matching a glob pattern (no recursion).",
|
||||||
params = listOf(ParamDoc("pattern", type("lyng.String"))),
|
params = listOf(ParamDoc("pattern", type("lyng.String"))),
|
||||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("Path"))),
|
returns = TypeGenericDoc(type("lyng.List"), listOf(type("Path"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val pattern = requireOnlyArg<ObjString>().value
|
val self = scp.thisObj as ObjPath
|
||||||
val matches = self.secured.glob(self.path, pattern)
|
val pattern = scp.requireOnlyArg<ObjString>().value
|
||||||
ObjList(matches.map { ObjPath(self.objClass, self.secured, it) }.toMutableList())
|
val matches = self.secured.glob(self.path, pattern)
|
||||||
|
ObjList(matches.map { ObjPath(self.objClass, self.secured, it) }.toMutableList())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
// --- streaming readers (initial version: chunk from whole content, API stable) ---
|
// --- streaming readers (initial version: chunk from whole content, API stable) ---
|
||||||
|
|
||||||
@ -403,15 +479,18 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
|||||||
doc = "Read file in fixed-size chunks as an iterator of `Buffer`.",
|
doc = "Read file in fixed-size chunks as an iterator of `Buffer`.",
|
||||||
params = listOf(ParamDoc("size", type("lyng.Int"))),
|
params = listOf(ParamDoc("size", type("lyng.Int"))),
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Buffer"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Buffer"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val size = args.list.getOrNull(0)?.toInt() ?: 65536
|
val self = scp.thisObj as ObjPath
|
||||||
val bytes = self.secured.readBytes(self.path)
|
val size = scp.args.list.getOrNull(0)?.toInt() ?: 65536
|
||||||
ObjFsBytesIterator(bytes, size)
|
val bytes = self.secured.readBytes(self.path)
|
||||||
|
ObjFsBytesIterator(bytes, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
// readUtf8Chunks(size: Int = 65536) -> Iterator<String>
|
// readUtf8Chunks(size: Int = 65536) -> Iterator<String>
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
@ -419,28 +498,34 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
|||||||
doc = "Read UTF-8 text in fixed-size chunks as an iterator of `String`.",
|
doc = "Read UTF-8 text in fixed-size chunks as an iterator of `String`.",
|
||||||
params = listOf(ParamDoc("size", type("lyng.Int"))),
|
params = listOf(ParamDoc("size", type("lyng.Int"))),
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.String"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.String"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = this.thisObj as ObjPath
|
return scp.fsGuard {
|
||||||
val size = args.list.getOrNull(0)?.toInt() ?: 65536
|
val self = scp.thisObj as ObjPath
|
||||||
val text = self.secured.readUtf8(self.path)
|
val size = scp.args.list.getOrNull(0)?.toInt() ?: 65536
|
||||||
ObjFsStringChunksIterator(text, size)
|
val text = self.secured.readUtf8(self.path)
|
||||||
|
ObjFsStringChunksIterator(text, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
// lines() -> Iterator<String>, implemented via readUtf8Chunks
|
// lines() -> Iterator<String>, implemented via readUtf8Chunks
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "lines",
|
name = "lines",
|
||||||
doc = "Iterate lines of the file as `String` values.",
|
doc = "Iterate lines of the file as `String` values.",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.String"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.String"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
fsGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val chunkIt = thisObj.invokeInstanceMethod(this, "readUtf8Chunks")
|
return scp.fsGuard {
|
||||||
ObjFsLinesIterator(chunkIt)
|
val chunkIt = scp.thisObj.invokeInstanceMethod(scp, "readUtf8Chunks")
|
||||||
|
ObjFsLinesIterator(chunkIt)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export into the module scope with docs
|
// Export into the module scope with docs
|
||||||
@ -518,39 +603,51 @@ class ObjFsBytesIterator(
|
|||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Return this iterator instance (enables `for` loops).",
|
doc = "Return this iterator instance (enables `for` loops).",
|
||||||
returns = type("BytesIterator"),
|
returns = type("BytesIterator"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) { thisObj }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisObj
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "hasNext",
|
name = "hasNext",
|
||||||
doc = "Whether there is another chunk available.",
|
doc = "Whether there is another chunk available.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsBytesIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
(self.pos < self.data.size).toObj()
|
val self = scp.thisAs<ObjFsBytesIterator>()
|
||||||
}
|
return (self.pos < self.data.size).toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "next",
|
name = "next",
|
||||||
doc = "Return the next chunk as a `Buffer`.",
|
doc = "Return the next chunk as a `Buffer`.",
|
||||||
returns = type("lyng.Buffer"),
|
returns = type("lyng.Buffer"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsBytesIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (self.pos >= self.data.size) raiseIllegalState("iterator exhausted")
|
val self = scp.thisAs<ObjFsBytesIterator>()
|
||||||
val end = minOf(self.pos + self.chunkSize, self.data.size)
|
if (self.pos >= self.data.size) scp.raiseIllegalState("iterator exhausted")
|
||||||
val chunk = self.data.copyOfRange(self.pos, end)
|
val end = minOf(self.pos + self.chunkSize, self.data.size)
|
||||||
self.pos = end
|
val chunk = self.data.copyOfRange(self.pos, end)
|
||||||
ObjBuffer(chunk.asUByteArray())
|
self.pos = end
|
||||||
}
|
return ObjBuffer(chunk.asUByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "cancelIteration",
|
name = "cancelIteration",
|
||||||
doc = "Stop the iteration early; subsequent `hasNext` returns false.",
|
doc = "Stop the iteration early; subsequent `hasNext` returns false.",
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsBytesIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.pos = self.data.size
|
val self = scp.thisAs<ObjFsBytesIterator>()
|
||||||
ObjVoid
|
self.pos = self.data.size
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -573,35 +670,47 @@ class ObjFsStringChunksIterator(
|
|||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Return this iterator instance (enables `for` loops).",
|
doc = "Return this iterator instance (enables `for` loops).",
|
||||||
returns = type("StringChunksIterator"),
|
returns = type("StringChunksIterator"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) { thisObj }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisObj
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "hasNext",
|
name = "hasNext",
|
||||||
doc = "Whether there is another chunk available.",
|
doc = "Whether there is another chunk available.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsStringChunksIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
(self.pos < self.text.length).toObj()
|
val self = scp.thisAs<ObjFsStringChunksIterator>()
|
||||||
}
|
return (self.pos < self.text.length).toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "next",
|
name = "next",
|
||||||
doc = "Return the next UTF-8 chunk as a `String`.",
|
doc = "Return the next UTF-8 chunk as a `String`.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsStringChunksIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (self.pos >= self.text.length) raiseIllegalState("iterator exhausted")
|
val self = scp.thisAs<ObjFsStringChunksIterator>()
|
||||||
val end = minOf(self.pos + self.chunkChars, self.text.length)
|
if (self.pos >= self.text.length) scp.raiseIllegalState("iterator exhausted")
|
||||||
val chunk = self.text.substring(self.pos, end)
|
val end = minOf(self.pos + self.chunkChars, self.text.length)
|
||||||
self.pos = end
|
val chunk = self.text.substring(self.pos, end)
|
||||||
ObjString(chunk)
|
self.pos = end
|
||||||
}
|
return ObjString(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "cancelIteration",
|
name = "cancelIteration",
|
||||||
doc = "Stop the iteration early; subsequent `hasNext` returns false.",
|
doc = "Stop the iteration early; subsequent `hasNext` returns false.",
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) { ObjVoid }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjVoid
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -624,46 +733,58 @@ class ObjFsLinesIterator(
|
|||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Return this iterator instance (enables `for` loops).",
|
doc = "Return this iterator instance (enables `for` loops).",
|
||||||
returns = type("LinesIterator"),
|
returns = type("LinesIterator"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) { thisObj }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisObj
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "hasNext",
|
name = "hasNext",
|
||||||
doc = "Whether another line is available.",
|
doc = "Whether another line is available.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsLinesIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.ensureBufferFilled(this)
|
val self = scp.thisAs<ObjFsLinesIterator>()
|
||||||
(self.buffer.isNotEmpty() || !self.exhausted).toObj()
|
self.ensureBufferFilled(scp)
|
||||||
}
|
return (self.buffer.isNotEmpty() || !self.exhausted).toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "next",
|
name = "next",
|
||||||
doc = "Return the next line as `String`.",
|
doc = "Return the next line as `String`.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjFsLinesIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.ensureBufferFilled(this)
|
val self = scp.thisAs<ObjFsLinesIterator>()
|
||||||
if (self.buffer.isEmpty() && self.exhausted) raiseIllegalState("iterator exhausted")
|
self.ensureBufferFilled(scp)
|
||||||
val idx = self.buffer.indexOf('\n')
|
if (self.buffer.isEmpty() && self.exhausted) scp.raiseIllegalState("iterator exhausted")
|
||||||
val line = if (idx >= 0) {
|
val idx = self.buffer.indexOf('\n')
|
||||||
val l = self.buffer.substring(0, idx)
|
val line = if (idx >= 0) {
|
||||||
self.buffer = self.buffer.substring(idx + 1)
|
val l = self.buffer.substring(0, idx)
|
||||||
l
|
self.buffer = self.buffer.substring(idx + 1)
|
||||||
} else {
|
l
|
||||||
// last line without trailing newline
|
} else {
|
||||||
val l = self.buffer
|
// last line without trailing newline
|
||||||
self.buffer = ""
|
val l = self.buffer
|
||||||
self.exhausted = true
|
self.buffer = ""
|
||||||
l
|
self.exhausted = true
|
||||||
|
l
|
||||||
|
}
|
||||||
|
return ObjString(line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjString(line)
|
)
|
||||||
}
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "cancelIteration",
|
name = "cancelIteration",
|
||||||
doc = "Stop the iteration early; subsequent `hasNext` returns false.",
|
doc = "Stop the iteration early; subsequent `hasNext` returns false.",
|
||||||
moduleName = "lyng.io.fs"
|
moduleName = "lyng.io.fs",
|
||||||
) { ObjVoid }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjVoid
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,9 +20,11 @@ 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.ScopeCallable
|
||||||
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
|
||||||
|
import net.sergeych.lyng.pacman.ModuleBuilder
|
||||||
import net.sergeych.lyng.statement
|
import net.sergeych.lyng.statement
|
||||||
import net.sergeych.lyngio.process.*
|
import net.sergeych.lyngio.process.*
|
||||||
import net.sergeych.lyngio.process.security.ProcessAccessDeniedException
|
import net.sergeych.lyngio.process.security.ProcessAccessDeniedException
|
||||||
@ -39,9 +41,11 @@ fun createProcessModule(policy: ProcessAccessPolicy, manager: ImportManager): Bo
|
|||||||
val name = "lyng.io.process"
|
val name = "lyng.io.process"
|
||||||
if (manager.packageNames.contains(name)) return false
|
if (manager.packageNames.contains(name)) return false
|
||||||
|
|
||||||
manager.addPackage(name) { module ->
|
manager.addPackage(name, object : ModuleBuilder {
|
||||||
buildProcessModule(module, policy)
|
override suspend fun build(module: ModuleScope) {
|
||||||
}
|
buildProcessModule(module, policy)
|
||||||
|
}
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,59 +63,74 @@ private suspend fun buildProcessModule(module: ModuleScope, policy: ProcessAcces
|
|||||||
name = "stdout",
|
name = "stdout",
|
||||||
doc = "Get standard output stream as a Flow of lines.",
|
doc = "Get standard output stream as a Flow of lines.",
|
||||||
returns = type("lyng.Flow"),
|
returns = type("lyng.Flow"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjRunningProcess>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.process.stdout.toLyngFlow(this)
|
val self = scp.thisAs<ObjRunningProcess>()
|
||||||
}
|
return self.process.stdout.toLyngFlow(scp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "stderr",
|
name = "stderr",
|
||||||
doc = "Get standard error stream as a Flow of lines.",
|
doc = "Get standard error stream as a Flow of lines.",
|
||||||
returns = type("lyng.Flow"),
|
returns = type("lyng.Flow"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjRunningProcess>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.process.stderr.toLyngFlow(this)
|
val self = scp.thisAs<ObjRunningProcess>()
|
||||||
}
|
return self.process.stderr.toLyngFlow(scp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "signal",
|
name = "signal",
|
||||||
doc = "Send a signal to the process (e.g. 'SIGINT', 'SIGTERM', 'SIGKILL').",
|
doc = "Send a signal to the process (e.g. 'SIGINT', 'SIGTERM', 'SIGKILL').",
|
||||||
params = listOf(ParamDoc("signal", type("lyng.String"))),
|
params = listOf(ParamDoc("signal", type("lyng.String"))),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
processGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val sigStr = requireOnlyArg<ObjString>().value.uppercase()
|
return scp.processGuard {
|
||||||
val sig = try {
|
val sigStr = scp.requireOnlyArg<ObjString>().value.uppercase()
|
||||||
ProcessSignal.valueOf(sigStr)
|
val sig = try {
|
||||||
} catch (e: Exception) {
|
ProcessSignal.valueOf(sigStr)
|
||||||
try {
|
} catch (e: Exception) {
|
||||||
ProcessSignal.valueOf("SIG$sigStr")
|
try {
|
||||||
} catch (e2: Exception) {
|
ProcessSignal.valueOf("SIG$sigStr")
|
||||||
raiseIllegalArgument("Unknown signal: $sigStr")
|
} catch (e2: Exception) {
|
||||||
|
scp.raiseIllegalArgument("Unknown signal: $sigStr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scp.thisAs<ObjRunningProcess>().process.sendSignal(sig)
|
||||||
|
ObjVoid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thisAs<ObjRunningProcess>().process.sendSignal(sig)
|
|
||||||
ObjVoid
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "waitFor",
|
name = "waitFor",
|
||||||
doc = "Wait for the process to exit and return its exit code.",
|
doc = "Wait for the process to exit and return its exit code.",
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
processGuard {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisAs<ObjRunningProcess>().process.waitFor().toObj()
|
return scp.processGuard {
|
||||||
|
scp.thisAs<ObjRunningProcess>().process.waitFor().toObj()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "destroy",
|
name = "destroy",
|
||||||
doc = "Forcefully terminate the process.",
|
doc = "Forcefully terminate the process.",
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjRunningProcess>().process.destroy()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjVoid
|
scp.thisAs<ObjRunningProcess>().process.destroy()
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val processType = object : ObjClass("Process") {}
|
val processType = object : ObjClass("Process") {}
|
||||||
@ -122,30 +141,36 @@ private suspend fun buildProcessModule(module: ModuleScope, policy: ProcessAcces
|
|||||||
doc = "Execute a process with arguments.",
|
doc = "Execute a process with arguments.",
|
||||||
params = listOf(ParamDoc("executable", type("lyng.String")), ParamDoc("args", type("lyng.List"))),
|
params = listOf(ParamDoc("executable", type("lyng.String")), ParamDoc("args", type("lyng.List"))),
|
||||||
returns = type("RunningProcess"),
|
returns = type("RunningProcess"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
if (runner == null) raiseError("Processes are not supported on this platform")
|
override suspend fun call(scp: Scope): Obj {
|
||||||
processGuard {
|
if (runner == null) scp.raiseError("Processes are not supported on this platform")
|
||||||
val executable = requiredArg<ObjString>(0).value
|
return scp.processGuard {
|
||||||
val args = requiredArg<ObjList>(1).list.map { it.toString() }
|
val executable = scp.requiredArg<ObjString>(0).value
|
||||||
val lp = runner.execute(executable, args)
|
val args = scp.requiredArg<ObjList>(1).list.map { it.toString() }
|
||||||
ObjRunningProcess(runningProcessType, lp)
|
val lp = runner.execute(executable, args)
|
||||||
|
ObjRunningProcess(runningProcessType, lp)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
addClassFnDoc(
|
addClassFnDoc(
|
||||||
name = "shell",
|
name = "shell",
|
||||||
doc = "Execute a command via system shell.",
|
doc = "Execute a command via system shell.",
|
||||||
params = listOf(ParamDoc("command", type("lyng.String"))),
|
params = listOf(ParamDoc("command", type("lyng.String"))),
|
||||||
returns = type("RunningProcess"),
|
returns = type("RunningProcess"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
if (runner == null) raiseError("Processes are not supported on this platform")
|
override suspend fun call(scp: Scope): Obj {
|
||||||
processGuard {
|
if (runner == null) scp.raiseError("Processes are not supported on this platform")
|
||||||
val command = requireOnlyArg<ObjString>().value
|
return scp.processGuard {
|
||||||
val lp = runner.shell(command)
|
val command = scp.requireOnlyArg<ObjString>().value
|
||||||
ObjRunningProcess(runningProcessType, lp)
|
val lp = runner.shell(command)
|
||||||
|
ObjRunningProcess(runningProcessType, lp)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val platformType = object : ObjClass("Platform") {}
|
val platformType = object : ObjClass("Platform") {}
|
||||||
@ -155,24 +180,28 @@ private suspend fun buildProcessModule(module: ModuleScope, policy: ProcessAcces
|
|||||||
name = "details",
|
name = "details",
|
||||||
doc = "Get platform core details.",
|
doc = "Get platform core details.",
|
||||||
returns = type("lyng.Map"),
|
returns = type("lyng.Map"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val d = getPlatformDetails()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjMap(mutableMapOf(
|
val d = getPlatformDetails()
|
||||||
ObjString("name") to ObjString(d.name),
|
return ObjMap(mutableMapOf(
|
||||||
ObjString("version") to ObjString(d.version),
|
ObjString("name") to ObjString(d.name),
|
||||||
ObjString("arch") to ObjString(d.arch),
|
ObjString("version") to ObjString(d.version),
|
||||||
ObjString("kernelVersion") to (d.kernelVersion?.toObj() ?: ObjNull)
|
ObjString("arch") to ObjString(d.arch),
|
||||||
))
|
ObjString("kernelVersion") to (d.kernelVersion?.toObj() ?: ObjNull)
|
||||||
}
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addClassFnDoc(
|
addClassFnDoc(
|
||||||
name = "isSupported",
|
name = "isSupported",
|
||||||
doc = "Check if processes are supported on this platform.",
|
doc = "Check if processes are supported on this platform.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = module.packageName
|
moduleName = module.packageName,
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
isProcessSupported().toObj()
|
override suspend fun call(scp: Scope): Obj = isProcessSupported().toObj()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.addConstDoc(
|
module.addConstDoc(
|
||||||
@ -216,19 +245,21 @@ private suspend inline fun Scope.processGuard(crossinline block: suspend () -> O
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow {
|
private fun Flow<String>.toLyngFlow(flowScope: Scope): ObjFlow {
|
||||||
val producer = statement {
|
val producer = statement(f = object : ScopeCallable {
|
||||||
val builder = (this as? net.sergeych.lyng.ClosureScope)?.callScope?.thisObj as? ObjFlowBuilder
|
override suspend fun call(scp: Scope): Obj {
|
||||||
?: this.thisObj as? ObjFlowBuilder
|
val builder = (scp as? net.sergeych.lyng.ClosureScope)?.callScope?.thisObj as? ObjFlowBuilder
|
||||||
|
?: scp.thisObj as? ObjFlowBuilder
|
||||||
|
|
||||||
this@toLyngFlow.collect {
|
this@toLyngFlow.collect {
|
||||||
try {
|
try {
|
||||||
builder?.output?.send(ObjString(it))
|
builder?.output?.send(ObjString(it))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Channel closed or other error, stop collecting
|
// Channel closed or other error, stop collecting
|
||||||
return@collect
|
return@collect
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
ObjVoid
|
})
|
||||||
}
|
|
||||||
return ObjFlow(producer, flowScope)
|
return ObjFlow(producer, flowScope)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,69 +154,73 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: assign head part, consuming from headPos; stop at ellipsis
|
// Locate ellipsis index within considered parameters
|
||||||
suspend fun processHead(index: Int, headPos: Int): Pair<Int, Int> {
|
val ellipsisIndex = params.subList(0, paramsSize).indexOfFirst { it.isEllipsis }
|
||||||
var i = index
|
|
||||||
var hp = headPos
|
var headPos = 0
|
||||||
|
var tailPos = callArgs.size - 1
|
||||||
|
|
||||||
|
if (ellipsisIndex >= 0) {
|
||||||
|
// Assign head first to know how many positionals are consumed from the start
|
||||||
|
var i = 0
|
||||||
while (i < paramsSize) {
|
while (i < paramsSize) {
|
||||||
val a = params[i]
|
val a = params[i]
|
||||||
if (a.isEllipsis) break
|
if (a.isEllipsis) break
|
||||||
if (assignedByName[i]) {
|
if (assignedByName[i]) {
|
||||||
assign(a, namedValues[i]!!)
|
assign(a, namedValues[i]!!)
|
||||||
} else {
|
} else {
|
||||||
val value = if (hp < callArgs.size) callArgs[hp++]
|
val value = if (headPos < callArgs.size) callArgs[headPos++]
|
||||||
else a.defaultValue?.execute(scope)
|
else a.defaultValue?.execute(scope)
|
||||||
?: scope.raiseIllegalArgument("too few arguments for the call (missing ${a.name})")
|
?: scope.raiseIllegalArgument("too few arguments for the call (missing ${a.name})")
|
||||||
assign(a, value)
|
assign(a, value)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return i to hp
|
val afterHead = i
|
||||||
}
|
val headConsumedTo = headPos
|
||||||
|
|
||||||
// Helper: assign tail part from the end, consuming from tailPos; stop before ellipsis index
|
// Then assign tail consuming from the end down to headConsumedTo boundary
|
||||||
// Do not consume elements below headPosBound to avoid overlap with head consumption
|
i = paramsSize - 1
|
||||||
suspend fun processTail(startExclusive: Int, tailStart: Int, headPosBound: Int): Int {
|
var tp = tailPos
|
||||||
var i = paramsSize - 1
|
while (i > ellipsisIndex) {
|
||||||
var tp = tailStart
|
|
||||||
while (i > startExclusive) {
|
|
||||||
val a = params[i]
|
val a = params[i]
|
||||||
if (a.isEllipsis) break
|
if (a.isEllipsis) break
|
||||||
if (i < assignedByName.size && assignedByName[i]) {
|
if (i < assignedByName.size && assignedByName[i]) {
|
||||||
assign(a, namedValues[i]!!)
|
assign(a, namedValues[i]!!)
|
||||||
} else {
|
} else {
|
||||||
val value = if (tp >= headPosBound) callArgs[tp--]
|
val value = if (tp >= headConsumedTo) callArgs[tp--]
|
||||||
else a.defaultValue?.execute(scope)
|
else a.defaultValue?.execute(scope)
|
||||||
?: scope.raiseIllegalArgument("too few arguments for the call")
|
?: scope.raiseIllegalArgument("too few arguments for the call")
|
||||||
assign(a, value)
|
assign(a, value)
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
return tp
|
val tailConsumedFrom = tp
|
||||||
}
|
|
||||||
|
|
||||||
fun processEllipsis(index: Int, headPos: Int, tailPos: Int) {
|
// Assign ellipsis list from remaining positionals between headConsumedTo..tailConsumedFrom
|
||||||
val a = params[index]
|
val a = params[ellipsisIndex]
|
||||||
val from = headPos
|
val from = headConsumedTo
|
||||||
val to = tailPos
|
val to = tailConsumedFrom
|
||||||
val l = if (from > to) ObjList()
|
val l = if (from > to) ObjList()
|
||||||
else ObjList(callArgs.subList(from, to + 1).toMutableList())
|
else ObjList(callArgs.subList(from, to + 1).toMutableList())
|
||||||
assign(a, l)
|
assign(a, l)
|
||||||
}
|
|
||||||
|
|
||||||
// Locate ellipsis index within considered parameters
|
|
||||||
val ellipsisIndex = params.subList(0, paramsSize).indexOfFirst { it.isEllipsis }
|
|
||||||
|
|
||||||
if (ellipsisIndex >= 0) {
|
|
||||||
// Assign head first to know how many positionals are consumed from the start
|
|
||||||
val (afterHead, headConsumedTo) = processHead(0, 0)
|
|
||||||
// Then assign tail consuming from the end down to headConsumedTo boundary
|
|
||||||
val tailConsumedFrom = processTail(ellipsisIndex, callArgs.size - 1, headConsumedTo)
|
|
||||||
// Assign ellipsis list from remaining positionals between headConsumedTo..tailConsumedFrom
|
|
||||||
processEllipsis(ellipsisIndex, headConsumedTo, tailConsumedFrom)
|
|
||||||
} else {
|
} else {
|
||||||
// No ellipsis: assign head only; any leftover positionals → error
|
// No ellipsis: assign head only; any leftover positionals → error
|
||||||
val (_, headConsumedTo) = processHead(0, 0)
|
var i = 0
|
||||||
|
while (i < paramsSize) {
|
||||||
|
val a = params[i]
|
||||||
|
if (a.isEllipsis) break
|
||||||
|
if (assignedByName[i]) {
|
||||||
|
assign(a, namedValues[i]!!)
|
||||||
|
} else {
|
||||||
|
val value = if (headPos < callArgs.size) callArgs[headPos++]
|
||||||
|
else a.defaultValue?.execute(scope)
|
||||||
|
?: scope.raiseIllegalArgument("too few arguments for the call (missing ${a.name})")
|
||||||
|
assign(a, value)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
val headConsumedTo = headPos
|
||||||
if (headConsumedTo != callArgs.size)
|
if (headConsumedTo != callArgs.size)
|
||||||
scope.raiseIllegalArgument("too many arguments for the call")
|
scope.raiseIllegalArgument("too many arguments for the call")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -244,10 +244,16 @@ data class ParsedArgument(
|
|||||||
* Convert to list of kotlin objects, see [Obj.toKotlin].
|
* Convert to list of kotlin objects, see [Obj.toKotlin].
|
||||||
*/
|
*/
|
||||||
suspend fun toKotlinList(scope: Scope): List<Any?> {
|
suspend fun toKotlinList(scope: Scope): List<Any?> {
|
||||||
return list.map { it.toKotlin(scope) }
|
val res = ArrayList<Any?>(list.size)
|
||||||
|
for (i in list) res.add(i.toKotlin(scope))
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun inspect(scope: Scope): String {
|
||||||
|
val res = ArrayList<String>(list.size)
|
||||||
|
for (i in list) res.add(i.inspect(scope))
|
||||||
|
return res.joinToString(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun inspect(scope: Scope): String = list.map{ it.inspect(scope)}.joinToString(",")
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = Arguments(emptyList())
|
val EMPTY = Arguments(emptyList())
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,23 @@ import net.sergeych.lyng.obj.*
|
|||||||
import net.sergeych.lyng.pacman.ImportManager
|
import net.sergeych.lyng.pacman.ImportManager
|
||||||
import net.sergeych.lyng.pacman.ImportProvider
|
import net.sergeych.lyng.pacman.ImportProvider
|
||||||
|
|
||||||
|
interface ScopeCallable {
|
||||||
|
suspend fun call(scope: Scope): Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VoidScopeCallable {
|
||||||
|
suspend fun call(scope: Scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScopeBlock<R> {
|
||||||
|
suspend fun call(scope: Scope): R
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FnStatement(val fn: ScopeCallable) : Statement() {
|
||||||
|
override val pos: Pos = Pos.builtIn
|
||||||
|
override suspend fun execute(scope: Scope): Obj = fn.call(scope)
|
||||||
|
}
|
||||||
|
|
||||||
// Simple per-frame id generator for perf caches (not thread-safe, fine for scripts)
|
// Simple per-frame id generator for perf caches (not thread-safe, fine for scripts)
|
||||||
object FrameIdGen { var c: Long = 1L; fun nextId(): Long = c++ }
|
object FrameIdGen { var c: Long = 1L; fun nextId(): Long = c++ }
|
||||||
fun nextFrameId(): Long = FrameIdGen.nextId()
|
fun nextFrameId(): Long = FrameIdGen.nextId()
|
||||||
@ -447,17 +464,17 @@ open class Scope(
|
|||||||
* Execute a block inside a child frame. Guarded for future pooling via [PerfFlags.SCOPE_POOL].
|
* Execute a block inside a child frame. Guarded for future pooling via [PerfFlags.SCOPE_POOL].
|
||||||
* Currently always creates a fresh child scope to preserve unique frameId semantics.
|
* Currently always creates a fresh child scope to preserve unique frameId semantics.
|
||||||
*/
|
*/
|
||||||
inline suspend fun <R> withChildFrame(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null, crossinline block: suspend (Scope) -> R): R {
|
suspend fun <R> withChildFrame(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null, block: ScopeBlock<R>): R {
|
||||||
if (PerfFlags.SCOPE_POOL) {
|
if (PerfFlags.SCOPE_POOL) {
|
||||||
val child = ScopePool.borrow(this, args, pos, newThisObj ?: thisObj)
|
val child = ScopePool.borrow(this, args, pos, newThisObj ?: thisObj)
|
||||||
try {
|
try {
|
||||||
return block(child)
|
return block.call(child)
|
||||||
} finally {
|
} finally {
|
||||||
ScopePool.release(child)
|
ScopePool.release(child)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val child = createChildScope(args, newThisObj)
|
val child = createChildScope(args, newThisObj)
|
||||||
return block(child)
|
return block.call(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,20 +580,17 @@ open class Scope(
|
|||||||
return ns.objClass
|
return ns.objClass
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun addVoidFn(vararg names: String, crossinline fn: suspend Scope.() -> Unit) {
|
fun addVoidFn(vararg names: String, fn: VoidScopeCallable) {
|
||||||
addFn(*names) {
|
addFn(*names, fn = object : ScopeCallable {
|
||||||
fn(this)
|
override suspend fun call(scope: Scope): Obj {
|
||||||
ObjVoid
|
fn.call(scope)
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFn(vararg names: String, fn: suspend Scope.() -> Obj) {
|
fun addFn(vararg names: String, fn: ScopeCallable) {
|
||||||
val newFn = object : Statement() {
|
val newFn = FnStatement(fn)
|
||||||
override val pos: Pos = Pos.builtIn
|
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj = scope.fn()
|
|
||||||
|
|
||||||
}
|
|
||||||
for (name in names) {
|
for (name in names) {
|
||||||
addItem(
|
addItem(
|
||||||
name,
|
name,
|
||||||
|
|||||||
@ -18,14 +18,13 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.yield
|
|
||||||
import net.sergeych.lyng.Script.Companion.defaultImportManager
|
import net.sergeych.lyng.Script.Companion.defaultImportManager
|
||||||
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
|
||||||
|
import net.sergeych.lyng.pacman.ModuleBuilder
|
||||||
import net.sergeych.lyng.stdlib_included.rootLyng
|
import net.sergeych.lyng.stdlib_included.rootLyng
|
||||||
import net.sergeych.lynon.ObjLynonClass
|
import net.sergeych.lynon.ObjLynonClass
|
||||||
import net.sergeych.mp_tools.globalDefer
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
@Suppress("TYPE_INTERSECTION_AS_REIFIED_WARNING")
|
@Suppress("TYPE_INTERSECTION_AS_REIFIED_WARNING")
|
||||||
@ -59,186 +58,173 @@ class Script(
|
|||||||
internal val rootScope: Scope = Scope(null).apply {
|
internal val rootScope: Scope = Scope(null).apply {
|
||||||
ObjException.addExceptionsToContext(this)
|
ObjException.addExceptionsToContext(this)
|
||||||
addConst("Unset", ObjUnset)
|
addConst("Unset", ObjUnset)
|
||||||
addFn("print") {
|
addFn("print", fn = object : ScopeCallable {
|
||||||
for ((i, a) in args.withIndex()) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (i > 0) print(' ' + a.toString(this).value)
|
for ((i, a) in scp.args.withIndex()) {
|
||||||
else print(a.toString(this).value)
|
if (i > 0) print(' ' + a.toString(scp).value)
|
||||||
|
else print(a.toString(scp).value)
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
ObjVoid
|
})
|
||||||
}
|
addFn("println", fn = object : ScopeCallable {
|
||||||
addFn("println") {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
for ((i, a) in args.withIndex()) {
|
for ((i, a) in scp.args.withIndex()) {
|
||||||
if (i > 0) print(' ' + a.toString(this).value)
|
if (i > 0) print(' ' + a.toString(scp).value)
|
||||||
else print(a.toString(this).value)
|
else print(a.toString(scp).value)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
println()
|
})
|
||||||
ObjVoid
|
addFn("floor", fn = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj {
|
||||||
addFn("floor") {
|
val x = scp.args.firstAndOnly()
|
||||||
val x = args.firstAndOnly()
|
return (if (x is ObjInt) x
|
||||||
(if (x is ObjInt) x
|
else ObjReal(floor(x.toDouble())))
|
||||||
else ObjReal(floor(x.toDouble())))
|
}
|
||||||
}
|
})
|
||||||
addFn("ceil") {
|
addFn("ceil", fn = object : ScopeCallable {
|
||||||
val x = args.firstAndOnly()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
(if (x is ObjInt) x
|
val x = scp.args.firstAndOnly()
|
||||||
else ObjReal(ceil(x.toDouble())))
|
return (if (x is ObjInt) x
|
||||||
}
|
else ObjReal(ceil(x.toDouble())))
|
||||||
addFn("round") {
|
}
|
||||||
val x = args.firstAndOnly()
|
})
|
||||||
(if (x is ObjInt) x
|
addFn("round", fn = object : ScopeCallable {
|
||||||
else ObjReal(round(x.toDouble())))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
val x = scp.args.firstAndOnly()
|
||||||
|
return (if (x is ObjInt) x
|
||||||
|
else ObjReal(round(x.toDouble())))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
addFn("sin") {
|
addFn("sin", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(sin(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(sin(args.firstAndOnly().toDouble()))
|
addFn("cos", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(cos(scp.args.firstAndOnly().toDouble())) })
|
||||||
}
|
addFn("tan", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(tan(scp.args.firstAndOnly().toDouble())) })
|
||||||
addFn("cos") {
|
addFn("asin", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(asin(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(cos(args.firstAndOnly().toDouble()))
|
addFn("acos", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(acos(scp.args.firstAndOnly().toDouble())) })
|
||||||
}
|
addFn("atan", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(atan(scp.args.firstAndOnly().toDouble())) })
|
||||||
addFn("tan") {
|
|
||||||
ObjReal(tan(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
addFn("asin") {
|
|
||||||
ObjReal(asin(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
addFn("acos") {
|
|
||||||
ObjReal(acos(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
addFn("atan") {
|
|
||||||
ObjReal(atan(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
|
|
||||||
addFn("sinh") {
|
addFn("sinh", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(sinh(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(sinh(args.firstAndOnly().toDouble()))
|
addFn("cosh", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(cosh(scp.args.firstAndOnly().toDouble())) })
|
||||||
}
|
addFn("tanh", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(tanh(scp.args.firstAndOnly().toDouble())) })
|
||||||
addFn("cosh") {
|
addFn("asinh", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(asinh(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(cosh(args.firstAndOnly().toDouble()))
|
addFn("acosh", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(acosh(scp.args.firstAndOnly().toDouble())) })
|
||||||
}
|
addFn("atanh", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(atanh(scp.args.firstAndOnly().toDouble())) })
|
||||||
addFn("tanh") {
|
|
||||||
ObjReal(tanh(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
addFn("asinh") {
|
|
||||||
ObjReal(asinh(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
addFn("acosh") {
|
|
||||||
ObjReal(acosh(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
addFn("atanh") {
|
|
||||||
ObjReal(atanh(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
|
|
||||||
addFn("exp") {
|
addFn("exp", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(exp(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(exp(args.firstAndOnly().toDouble()))
|
addFn("ln", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(ln(scp.args.firstAndOnly().toDouble())) })
|
||||||
}
|
|
||||||
addFn("ln") {
|
|
||||||
ObjReal(ln(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
|
|
||||||
addFn("log10") {
|
addFn("log10", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(log10(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(log10(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
|
|
||||||
addFn("log2") {
|
addFn("log2", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(log2(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(log2(args.firstAndOnly().toDouble()))
|
|
||||||
}
|
|
||||||
|
|
||||||
addFn("pow") {
|
addFn("pow", fn = object : ScopeCallable {
|
||||||
requireExactCount(2)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjReal(
|
scp.requireExactCount(2)
|
||||||
(args[0].toDouble()).pow(args[1].toDouble())
|
return ObjReal((scp.args[0].toDouble()).pow(scp.args[1].toDouble()))
|
||||||
)
|
}
|
||||||
}
|
})
|
||||||
addFn("sqrt") {
|
addFn("sqrt", fn = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjReal(sqrt(scp.args.firstAndOnly().toDouble())) })
|
||||||
ObjReal(
|
addFn("abs", fn = object : ScopeCallable {
|
||||||
sqrt(args.firstAndOnly().toDouble())
|
override suspend fun call(scp: Scope): Obj {
|
||||||
)
|
val x = scp.args.firstAndOnly()
|
||||||
}
|
return if (x is ObjInt) ObjInt(x.value.absoluteValue) else ObjReal(x.toDouble().absoluteValue)
|
||||||
addFn("abs") {
|
}
|
||||||
val x = args.firstAndOnly()
|
})
|
||||||
if (x is ObjInt) ObjInt(x.value.absoluteValue) else ObjReal(x.toDouble().absoluteValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc<Obj>(
|
||||||
"clamp",
|
"clamp",
|
||||||
doc = "Clamps the value within the specified range. If the value is outside the range, it is set to the nearest boundary. Respects inclusive/exclusive range ends.",
|
doc = "Clamps the value within the specified range. If the value is outside the range, it is set to the nearest boundary. Respects inclusive/exclusive range ends.",
|
||||||
params = listOf(ParamDoc("value"), ParamDoc("range")),
|
params = listOf(ParamDoc("value"), ParamDoc("range")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
fn = object : ScopeCallable {
|
||||||
val value = requiredArg<Obj>(0)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val range = requiredArg<ObjRange>(1)
|
val value = scp.requiredArg<Obj>(0)
|
||||||
|
val range = scp.requiredArg<ObjRange>(1)
|
||||||
var result = value
|
|
||||||
if (range.start != null && !range.start.isNull) {
|
var result = value
|
||||||
if (result.compareTo(this, range.start) < 0) {
|
if (range.start != null && !range.start.isNull) {
|
||||||
result = range.start
|
if (result.compareTo(scp, range.start) < 0) {
|
||||||
}
|
result = range.start
|
||||||
}
|
|
||||||
if (range.end != null && !range.end.isNull) {
|
|
||||||
val cmp = range.end.compareTo(this, result)
|
|
||||||
if (range.isEndInclusive) {
|
|
||||||
if (cmp < 0) result = range.end
|
|
||||||
} else {
|
|
||||||
if (cmp <= 0) {
|
|
||||||
if (range.end is ObjInt) {
|
|
||||||
result = ObjInt.of(range.end.value - 1)
|
|
||||||
} else if (range.end is ObjChar) {
|
|
||||||
result = ObjChar((range.end.value.code - 1).toChar())
|
|
||||||
} else {
|
|
||||||
// For types where we can't easily find "previous" value (like Real),
|
|
||||||
// we just return the exclusive boundary as a fallback.
|
|
||||||
result = range.end
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (range.end != null && !range.end.isNull) {
|
||||||
|
val cmp = range.end.compareTo(scp, result)
|
||||||
|
if (range.isEndInclusive) {
|
||||||
|
if (cmp < 0) result = range.end
|
||||||
|
} else {
|
||||||
|
if (cmp <= 0) {
|
||||||
|
if (range.end is ObjInt) {
|
||||||
|
result = ObjInt.of(range.end.value - 1)
|
||||||
|
} else if (range.end is ObjChar) {
|
||||||
|
result = ObjChar((range.end.value.code - 1).toChar())
|
||||||
|
} else {
|
||||||
|
// For types where we can't easily find "previous" value (like Real),
|
||||||
|
// we just return the exclusive boundary as a fallback.
|
||||||
|
result = range.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addVoidFn("assert") {
|
addVoidFn("assert", fn = object : VoidScopeCallable {
|
||||||
val cond = requiredArg<ObjBool>(0)
|
override suspend fun call(scp: Scope) {
|
||||||
val message = if (args.size > 1)
|
val cond = scp.requiredArg<ObjBool>(0)
|
||||||
": " + (args[1] as Statement).execute(this).toString(this).value
|
val message = if (scp.args.size > 1)
|
||||||
else ""
|
": " + (scp.args[1] as Statement).execute(scp).toString(scp).value
|
||||||
if (!cond.value == true)
|
else ""
|
||||||
raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
|
if (!cond.value == true)
|
||||||
}
|
scp.raiseError(ObjAssertionFailedException(scp, "Assertion failed$message"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
addVoidFn("assertEquals") {
|
addVoidFn("assertEquals", fn = object : VoidScopeCallable {
|
||||||
val a = requiredArg<Obj>(0)
|
override suspend fun call(scp: Scope) {
|
||||||
val b = requiredArg<Obj>(1)
|
val a = scp.requiredArg<Obj>(0)
|
||||||
if (a.compareTo(this, b) != 0)
|
val b = scp.requiredArg<Obj>(1)
|
||||||
raiseError(
|
if (a.compareTo(scp, b) != 0)
|
||||||
ObjAssertionFailedException(
|
scp.raiseError(
|
||||||
this,
|
ObjAssertionFailedException(
|
||||||
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
|
scp,
|
||||||
|
"Assertion failed: ${a.inspect(scp)} == ${b.inspect(scp)}"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
})
|
||||||
// alias used in tests
|
// alias used in tests
|
||||||
addVoidFn("assertEqual") {
|
addVoidFn("assertEqual", fn = object : VoidScopeCallable {
|
||||||
val a = requiredArg<Obj>(0)
|
override suspend fun call(scp: Scope) {
|
||||||
val b = requiredArg<Obj>(1)
|
val a = scp.requiredArg<Obj>(0)
|
||||||
if (a.compareTo(this, b) != 0)
|
val b = scp.requiredArg<Obj>(1)
|
||||||
raiseError(
|
if (a.compareTo(scp, b) != 0)
|
||||||
ObjAssertionFailedException(
|
scp.raiseError(
|
||||||
this,
|
ObjAssertionFailedException(
|
||||||
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
|
scp,
|
||||||
|
"Assertion failed: ${a.inspect(scp)} == ${b.inspect(scp)}"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
})
|
||||||
addVoidFn("assertNotEquals") {
|
addVoidFn("assertNotEquals", fn = object : VoidScopeCallable {
|
||||||
val a = requiredArg<Obj>(0)
|
override suspend fun call(scp: Scope) {
|
||||||
val b = requiredArg<Obj>(1)
|
val a = scp.requiredArg<Obj>(0)
|
||||||
if (a.compareTo(this, b) == 0)
|
val b = scp.requiredArg<Obj>(1)
|
||||||
raiseError(
|
if (a.compareTo(scp, b) == 0)
|
||||||
ObjAssertionFailedException(
|
scp.raiseError(
|
||||||
this,
|
ObjAssertionFailedException(
|
||||||
"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"
|
scp,
|
||||||
|
"Assertion failed: ${a.inspect(scp)} != ${b.inspect(scp)}"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
})
|
||||||
addFnDoc(
|
addFnDoc<Obj>(
|
||||||
"assertThrows",
|
"assertThrows",
|
||||||
doc = """
|
doc = """
|
||||||
Asserts that the provided code block throws an exception, with or without exception:
|
Asserts that the provided code block throws an exception, with or without exception:
|
||||||
@ -249,83 +235,97 @@ class Script(
|
|||||||
If an expected exception class is provided,
|
If an expected exception class is provided,
|
||||||
it checks that the thrown exception is of that class. If no expected class is provided, any exception
|
it checks that the thrown exception is of that class. If no expected class is provided, any exception
|
||||||
will be accepted.
|
will be accepted.
|
||||||
""".trimIndent()
|
""".trimIndent(),
|
||||||
) {
|
fn = object : ScopeCallable {
|
||||||
val code: Statement
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val expectedClass: ObjClass?
|
val code: Statement
|
||||||
when (args.size) {
|
val expectedClass: ObjClass?
|
||||||
1 -> {
|
when (scp.args.size) {
|
||||||
code = requiredArg<Statement>(0)
|
1 -> {
|
||||||
expectedClass = null
|
code = scp.requiredArg<Statement>(0)
|
||||||
}
|
expectedClass = null
|
||||||
|
}
|
||||||
|
|
||||||
2 -> {
|
2 -> {
|
||||||
code = requiredArg<Statement>(1)
|
code = scp.requiredArg<Statement>(1)
|
||||||
expectedClass = requiredArg<ObjClass>(0)
|
expectedClass = scp.requiredArg<ObjClass>(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
|
else -> scp.raiseIllegalArgument("Expected 1 or 2 arguments, got ${scp.args.size}")
|
||||||
}
|
}
|
||||||
val result = try {
|
val result = try {
|
||||||
code.execute(this)
|
code.execute(scp)
|
||||||
null
|
null
|
||||||
} catch (e: ExecutionError) {
|
} catch (e: ExecutionError) {
|
||||||
e.errorObject
|
e.errorObject
|
||||||
} catch (_: ScriptError) {
|
} catch (_: ScriptError) {
|
||||||
ObjNull
|
ObjNull
|
||||||
}
|
}
|
||||||
if (result == null) raiseError(
|
if (result == null) scp.raiseError(
|
||||||
ObjAssertionFailedException(
|
ObjAssertionFailedException(
|
||||||
this,
|
scp,
|
||||||
"Expected exception but nothing was thrown"
|
"Expected exception but nothing was thrown"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
expectedClass?.let {
|
expectedClass?.let {
|
||||||
if (!result.isInstanceOf(it)) {
|
if (!result.isInstanceOf(it)) {
|
||||||
val actual = if (result is ObjException) result.exceptionClass else result.objClass
|
val actual = if (result is ObjException) result.exceptionClass else result.objClass
|
||||||
raiseError("Expected $it, got $actual")
|
scp.raiseError("Expected $it, got $actual")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result ?: ObjNull
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFn("dynamic") {
|
addFn("dynamic", fn = object : ScopeCallable {
|
||||||
ObjDynamic.create(this, requireOnlyArg())
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
return ObjDynamic.create(scp, scp.requireOnlyArg())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
val root = this
|
val root = this
|
||||||
val mathClass = ObjClass("Math").apply {
|
val mathClass = ObjClass("Math").apply {
|
||||||
addFn("sqrt") {
|
addFn("sqrt", fn = object : ScopeCallable {
|
||||||
ObjReal(sqrt(args.firstAndOnly().toDouble()))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
return ObjReal(sqrt(scp.args.firstAndOnly().toDouble()))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
addItem("Math", false, ObjInstance(mathClass).apply {
|
addItem("Math", false, ObjInstance(mathClass).apply {
|
||||||
instanceScope = Scope(root, thisObj = this)
|
instanceScope = Scope(root, thisObj = this)
|
||||||
})
|
})
|
||||||
|
|
||||||
addFn("require") {
|
addFn("require", fn = object : ScopeCallable {
|
||||||
val condition = requiredArg<ObjBool>(0)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (!condition.value) {
|
val condition = scp.requiredArg<ObjBool>(0)
|
||||||
var message = args.list.getOrNull(1)
|
if (!condition.value) {
|
||||||
if (message is Statement) message = message.execute(this)
|
var message = scp.args.list.getOrNull(1)
|
||||||
raiseIllegalArgument(message?.toString() ?: "requirement not met")
|
if (message is Statement) message = message.execute(scp)
|
||||||
|
scp.raiseIllegalArgument(message?.toString() ?: "requirement not met")
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
ObjVoid
|
})
|
||||||
}
|
addFn("check", fn = object : ScopeCallable {
|
||||||
addFn("check") {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val condition = requiredArg<ObjBool>(0)
|
val condition = scp.requiredArg<ObjBool>(0)
|
||||||
if (!condition.value) {
|
if (!condition.value) {
|
||||||
var message = args.list.getOrNull(1)
|
var message = scp.args.list.getOrNull(1)
|
||||||
if (message is Statement) message = message.execute(this)
|
if (message is Statement) message = message.execute(scp)
|
||||||
raiseIllegalState(message?.toString() ?: "check failed")
|
scp.raiseIllegalState(message?.toString() ?: "check failed")
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
ObjVoid
|
})
|
||||||
}
|
addFn("traceScope", fn = object : ScopeCallable {
|
||||||
addFn("traceScope") {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
this.trace(args.getOrNull(0)?.toString() ?: "")
|
scp.trace(scp.args.getOrNull(0)?.toString() ?: "")
|
||||||
ObjVoid
|
return ObjVoid
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
addVoidFn("delay") {
|
addVoidFn("delay") {
|
||||||
val a = args.firstAndOnly()
|
val a = args.firstAndOnly()
|
||||||
when (a) {
|
when (a) {
|
||||||
@ -335,7 +335,7 @@ class Script(
|
|||||||
else -> raiseIllegalArgument("Expected Int, Real or Duration, got ${a.inspect(this)}")
|
else -> raiseIllegalArgument("Expected Int, Real or Duration, got ${a.inspect(this)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
addConst("Object", rootObjectType)
|
addConst("Object", rootObjectType)
|
||||||
addConst("Real", ObjReal.type)
|
addConst("Real", ObjReal.type)
|
||||||
addConst("String", ObjString.type)
|
addConst("String", ObjString.type)
|
||||||
@ -362,7 +362,7 @@ class Script(
|
|||||||
addConst("Flow", ObjFlow.type)
|
addConst("Flow", ObjFlow.type)
|
||||||
|
|
||||||
addConst("Regex", ObjRegex.type)
|
addConst("Regex", ObjRegex.type)
|
||||||
|
/*
|
||||||
addFn("launch") {
|
addFn("launch") {
|
||||||
val callable = requireOnlyArg<Statement>()
|
val callable = requireOnlyArg<Statement>()
|
||||||
ObjDeferred(globalDefer {
|
ObjDeferred(globalDefer {
|
||||||
@ -380,7 +380,7 @@ class Script(
|
|||||||
// we'll need it for the producer
|
// we'll need it for the producer
|
||||||
ObjFlow(requireOnlyArg<Statement>(), this)
|
ObjFlow(requireOnlyArg<Statement>(), this)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
val pi = ObjReal(PI)
|
val pi = ObjReal(PI)
|
||||||
addConstDoc(
|
addConstDoc(
|
||||||
name = "π",
|
name = "π",
|
||||||
@ -403,60 +403,69 @@ class Script(
|
|||||||
addTextPackages(
|
addTextPackages(
|
||||||
rootLyng
|
rootLyng
|
||||||
)
|
)
|
||||||
addPackage("lyng.buffer") {
|
addPackage("lyng.buffer", object : ModuleBuilder {
|
||||||
it.addConstDoc(
|
override suspend fun build(ms: ModuleScope) {
|
||||||
name = "Buffer",
|
ms.addConstDoc(
|
||||||
value = ObjBuffer.type,
|
name = "Buffer",
|
||||||
doc = "Immutable sequence of bytes. Use for binary data and IO.",
|
value = ObjBuffer.type,
|
||||||
type = type("lyng.Class")
|
doc = "Immutable sequence of bytes. Use for binary data and IO.",
|
||||||
)
|
type = type("lyng.Class")
|
||||||
it.addConstDoc(
|
)
|
||||||
name = "MutableBuffer",
|
ms.addConstDoc(
|
||||||
value = ObjMutableBuffer.type,
|
name = "MutableBuffer",
|
||||||
doc = "Mutable byte buffer. Supports in-place modifications.",
|
value = ObjMutableBuffer.type,
|
||||||
type = type("lyng.Class")
|
doc = "Mutable byte buffer. Supports in-place modifications.",
|
||||||
)
|
type = type("lyng.Class")
|
||||||
}
|
)
|
||||||
addPackage("lyng.serialization") {
|
|
||||||
it.addConstDoc(
|
|
||||||
name = "Lynon",
|
|
||||||
value = ObjLynonClass,
|
|
||||||
doc = "Lynon serialization utilities: encode/decode data structures to a portable binary/text form.",
|
|
||||||
type = type("lyng.Class")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
addPackage("lyng.time") {
|
|
||||||
it.addConstDoc(
|
|
||||||
name = "Instant",
|
|
||||||
value = ObjInstant.type,
|
|
||||||
doc = "Point in time (epoch-based).",
|
|
||||||
type = type("lyng.Class")
|
|
||||||
)
|
|
||||||
it.addConstDoc(
|
|
||||||
name = "DateTime",
|
|
||||||
value = ObjDateTime.type,
|
|
||||||
doc = "Point in time in a specific time zone.",
|
|
||||||
type = type("lyng.Class")
|
|
||||||
)
|
|
||||||
it.addConstDoc(
|
|
||||||
name = "Duration",
|
|
||||||
value = ObjDuration.type,
|
|
||||||
doc = "Time duration with millisecond precision.",
|
|
||||||
type = type("lyng.Class")
|
|
||||||
)
|
|
||||||
it.addVoidFnDoc(
|
|
||||||
"delay",
|
|
||||||
doc = "Suspend for the given time. Accepts Duration, Int seconds, or Real seconds."
|
|
||||||
) {
|
|
||||||
val a = args.firstAndOnly()
|
|
||||||
when (a) {
|
|
||||||
is ObjInt -> delay(a.value * 1000)
|
|
||||||
is ObjReal -> delay((a.value * 1000).roundToLong())
|
|
||||||
is ObjDuration -> delay(a.duration)
|
|
||||||
else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect(this)}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
addPackage("lyng.serialization", object : ModuleBuilder {
|
||||||
|
override suspend fun build(ms: ModuleScope) {
|
||||||
|
ms.addConstDoc(
|
||||||
|
name = "Lynon",
|
||||||
|
value = ObjLynonClass,
|
||||||
|
doc = "Lynon serialization utilities: encode/decode data structures to a portable binary/text form.",
|
||||||
|
type = type("lyng.Class")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
addPackage("lyng.time", object : ModuleBuilder {
|
||||||
|
override suspend fun build(ms: ModuleScope) {
|
||||||
|
ms.addConstDoc(
|
||||||
|
name = "Instant",
|
||||||
|
value = ObjInstant.type,
|
||||||
|
doc = "Point in time (epoch-based).",
|
||||||
|
type = type("lyng.Class")
|
||||||
|
)
|
||||||
|
ms.addConstDoc(
|
||||||
|
name = "DateTime",
|
||||||
|
value = ObjDateTime.type,
|
||||||
|
doc = "Point in time in a specific time zone.",
|
||||||
|
type = type("lyng.Class")
|
||||||
|
)
|
||||||
|
ms.addConstDoc(
|
||||||
|
name = "Duration",
|
||||||
|
value = ObjDuration.type,
|
||||||
|
doc = "Time duration with millisecond precision.",
|
||||||
|
type = type("lyng.Class")
|
||||||
|
)
|
||||||
|
ms.addVoidFnDoc(
|
||||||
|
"delay",
|
||||||
|
doc = "Suspend for the given time. Accepts Duration, Int seconds, or Real seconds.",
|
||||||
|
fn = object : VoidScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope) {
|
||||||
|
val a = scp.args.firstAndOnly()
|
||||||
|
when (a) {
|
||||||
|
is ObjInt -> delay(a.value * 1000)
|
||||||
|
is ObjReal -> delay((a.value * 1000).roundToLong())
|
||||||
|
is ObjDuration -> delay(a.duration)
|
||||||
|
else -> scp.raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect(scp)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,7 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.miniast
|
package net.sergeych.lyng.miniast
|
||||||
|
|
||||||
import net.sergeych.lyng.ModuleScope
|
import net.sergeych.lyng.*
|
||||||
import net.sergeych.lyng.Scope
|
|
||||||
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
|
||||||
import net.sergeych.lyng.obj.ObjVoid
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
@ -39,10 +37,10 @@ inline fun <reified T : Obj> Scope.addFnDoc(
|
|||||||
returns: TypeDoc? = null,
|
returns: TypeDoc? = null,
|
||||||
tags: Map<String, List<String>> = emptyMap(),
|
tags: Map<String, List<String>> = emptyMap(),
|
||||||
moduleName: String? = null,
|
moduleName: String? = null,
|
||||||
crossinline fn: suspend Scope.() -> T
|
fn: ScopeCallable
|
||||||
) {
|
) {
|
||||||
// Register runtime function(s)
|
// Register runtime function(s)
|
||||||
addFn(*names) { fn() }
|
addFn(*names, fn = fn)
|
||||||
// Determine module
|
// Determine module
|
||||||
val mod = moduleName ?: findModuleNameOrUnknown()
|
val mod = moduleName ?: findModuleNameOrUnknown()
|
||||||
// Register docs once per name
|
// Register docs once per name
|
||||||
@ -56,7 +54,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
|
fn: VoidScopeCallable
|
||||||
) {
|
) {
|
||||||
addFnDoc<ObjVoid>(
|
addFnDoc<ObjVoid>(
|
||||||
*names,
|
*names,
|
||||||
@ -64,11 +62,14 @@ inline fun Scope.addVoidFnDoc(
|
|||||||
params = emptyList(),
|
params = emptyList(),
|
||||||
returns = null,
|
returns = null,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
moduleName = moduleName
|
moduleName = moduleName,
|
||||||
) {
|
fn = object : ScopeCallable {
|
||||||
fn(this)
|
override suspend fun call(sc: Scope): Obj {
|
||||||
ObjVoid
|
fn.call(sc)
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Scope.addConstDoc(
|
fun Scope.addConstDoc(
|
||||||
@ -97,7 +98,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: ScopeCallable
|
||||||
) {
|
) {
|
||||||
// Register runtime method
|
// Register runtime method
|
||||||
addFn(name, isOpen, visibility, code = code)
|
addFn(name, isOpen, visibility, code = code)
|
||||||
@ -135,7 +136,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: ScopeCallable
|
||||||
) {
|
) {
|
||||||
addClassFn(name, isOpen, code)
|
addClassFn(name, isOpen, code)
|
||||||
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
|
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
|
||||||
@ -151,8 +152,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: ScopeCallable? = null,
|
||||||
setter: (suspend Scope.(Obj) -> Unit)? = null
|
setter: ScopeCallable? = null
|
||||||
) {
|
) {
|
||||||
addProperty(name, getter, setter, visibility)
|
addProperty(name, getter, setter, visibility)
|
||||||
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
|
BuiltinDocRegistry.module(moduleName ?: ownerModuleNameFromClassOrUnknown()) {
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -32,29 +32,41 @@ typealias DocCompiler = Compiler
|
|||||||
*/
|
*/
|
||||||
typealias Accessor = ObjRef
|
typealias Accessor = ObjRef
|
||||||
|
|
||||||
|
interface AccessorGetter {
|
||||||
|
suspend fun call(scope: Scope): ObjRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccessorSetter {
|
||||||
|
suspend fun call(scope: Scope, value: Obj)
|
||||||
|
}
|
||||||
|
|
||||||
/** Lambda-based reference for edge cases that still construct access via lambdas. */
|
/** Lambda-based reference for edge cases that still construct access via lambdas. */
|
||||||
private class LambdaRef(
|
private class LambdaRef(
|
||||||
private val getterFn: suspend (Scope) -> ObjRecord,
|
private val getterFn: AccessorGetter,
|
||||||
private val setterFn: (suspend (Pos, Scope, Obj) -> Unit)? = null
|
private val setterFn: AccessorSetter? = null
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
override suspend fun get(scope: Scope): ObjRecord = getterFn(scope)
|
override suspend fun get(scope: Scope): ObjRecord = getterFn.call(scope)
|
||||||
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")
|
val s = setterFn ?: throw ScriptError(pos, "can't assign value")
|
||||||
s(pos, scope, newValue)
|
s.call(scope, newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory functions to preserve current call sites like `Accessor { ... }`
|
// Factory functions to preserve current call sites like `Accessor { ... }`
|
||||||
fun Accessor(getter: suspend (Scope) -> ObjRecord): Accessor = LambdaRef(getter)
|
fun Accessor(getter: AccessorGetter): Accessor = LambdaRef(getter)
|
||||||
fun Accessor(
|
fun Accessor(
|
||||||
getter: suspend (Scope) -> ObjRecord,
|
getter: AccessorGetter,
|
||||||
setter: suspend (Scope, Obj) -> Unit
|
setter: AccessorSetter
|
||||||
): Accessor = LambdaRef(getter) { _, scope, value -> setter(scope, value) }
|
): Accessor = LambdaRef(getter, setter)
|
||||||
|
|
||||||
// Compatibility shims used throughout Compiler: `.getter(...)` and `.setter(pos)`
|
// Compatibility shims used throughout Compiler: `.getter(...)` and `.setter(pos)`
|
||||||
val Accessor.getter: suspend (Scope) -> ObjRecord
|
val Accessor.getter: AccessorGetter
|
||||||
get() = { scope -> this.get(scope) }
|
get() = object : AccessorGetter {
|
||||||
|
override suspend fun call(scope: Scope): ObjRecord = this@getter.get(scope)
|
||||||
|
}
|
||||||
|
|
||||||
fun Accessor.setter(pos: Pos): suspend (Scope, Obj) -> Unit = { scope, newValue ->
|
fun Accessor.setter(pos: Pos): AccessorSetter = object : AccessorSetter {
|
||||||
this.setAt(pos, scope, newValue)
|
override suspend fun call(scope: Scope, value: Obj) {
|
||||||
|
this@setter.setAt(pos, scope, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -32,6 +32,14 @@ import net.sergeych.lynon.LynonDecoder
|
|||||||
import net.sergeych.lynon.LynonEncoder
|
import net.sergeych.lynon.LynonEncoder
|
||||||
import net.sergeych.lynon.LynonType
|
import net.sergeych.lynon.LynonType
|
||||||
|
|
||||||
|
fun interface OnNotFound {
|
||||||
|
suspend fun call(): Obj?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface EnumerateCallback {
|
||||||
|
suspend fun call(element: Obj): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
open class Obj {
|
open class Obj {
|
||||||
|
|
||||||
open val isConst: Boolean = false
|
open val isConst: Boolean = false
|
||||||
@ -91,7 +99,7 @@ open class Obj {
|
|||||||
scope: Scope,
|
scope: Scope,
|
||||||
name: String,
|
name: String,
|
||||||
args: Arguments = Arguments.EMPTY,
|
args: Arguments = Arguments.EMPTY,
|
||||||
onNotFoundResult: (suspend () -> Obj?)? = null
|
onNotFoundResult: OnNotFound? = null
|
||||||
): Obj {
|
): Obj {
|
||||||
// 0. Prefer private member of current class context
|
// 0. Prefer private member of current class context
|
||||||
scope.currentClassCtx?.let { caller ->
|
scope.currentClassCtx?.let { caller ->
|
||||||
@ -100,7 +108,7 @@ open class Obj {
|
|||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, caller)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, caller)
|
||||||
} else if (rec.type != ObjRecord.Type.Delegated) {
|
} else if (rec.type != ObjRecord.Type.Delegated) {
|
||||||
return rec.value.invoke(scope, this, args, caller)
|
return rec.value.invokeCallable(scope, this, args, caller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,12 +122,12 @@ open class Obj {
|
|||||||
val decl = rec.declaringClass ?: cls
|
val decl = rec.declaringClass ?: cls
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(rec.visibility, decl, caller, name))
|
if (!canAccessMember(rec.visibility, decl, caller, name))
|
||||||
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
scope.raiseError(ObjIllegalAccessException(scope, "can't invokeCallable ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
||||||
|
|
||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
||||||
} else if (rec.type != ObjRecord.Type.Delegated) {
|
} else if (rec.type != ObjRecord.Type.Delegated) {
|
||||||
return rec.value.invoke(scope, this, args, decl)
|
return rec.value.invokeCallable(scope, this, args, decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +138,7 @@ open class Obj {
|
|||||||
if (extension.type == ObjRecord.Type.Property) {
|
if (extension.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (extension.value as ObjProperty).callGetter(scope, this, extension.declaringClass)
|
if (args.isEmpty()) return (extension.value as ObjProperty).callGetter(scope, this, extension.declaringClass)
|
||||||
} else if (extension.type != ObjRecord.Type.Delegated) {
|
} else if (extension.type != ObjRecord.Type.Delegated) {
|
||||||
return extension.value.invoke(scope, this, args)
|
return extension.value.invokeCallable(scope, this, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,18 +149,18 @@ open class Obj {
|
|||||||
val decl = rec.declaringClass ?: cls
|
val decl = rec.declaringClass ?: cls
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(rec.visibility, decl, caller, name))
|
if (!canAccessMember(rec.visibility, decl, caller, name))
|
||||||
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
scope.raiseError(ObjIllegalAccessException(scope, "can't invokeCallable ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
||||||
|
|
||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
||||||
} else if (rec.type != ObjRecord.Type.Delegated) {
|
} else if (rec.type != ObjRecord.Type.Delegated) {
|
||||||
return rec.value.invoke(scope, this, args, decl)
|
return rec.value.invokeCallable(scope, this, args, decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return onNotFoundResult?.invoke()
|
return onNotFoundResult?.call()
|
||||||
?: scope.raiseError(
|
?: scope.raiseError(
|
||||||
"no such member: $name on ${objClass.className}. Considered order: ${objClass.renderLinearization(true)}. " +
|
"no such member: $name on ${objClass.className}. Considered order: ${objClass.renderLinearization(true)}. " +
|
||||||
"Tip: try this@Base.$name(...) or (obj as Base).$name(...) if ambiguous"
|
"Tip: try this@Base.$name(...) or (obj as Base).$name(...) if ambiguous"
|
||||||
@ -174,9 +182,11 @@ open class Obj {
|
|||||||
open suspend fun compareTo(scope: Scope, other: Obj): Int {
|
open suspend fun compareTo(scope: Scope, other: Obj): Int {
|
||||||
if (other === this) return 0
|
if (other === this) return 0
|
||||||
if (other === ObjNull || other === ObjUnset || other === ObjVoid) return 2
|
if (other === ObjNull || other === ObjUnset || other === ObjVoid) return 2
|
||||||
return invokeInstanceMethod(scope, "compareTo", Arguments(other)) {
|
return invokeInstanceMethod(scope, "compareTo", Arguments(other), onNotFoundResult = object : OnNotFound {
|
||||||
scope.raiseNotImplemented("compareTo for ${objClass.className}")
|
override suspend fun call(): Obj? {
|
||||||
}.cast<ObjInt>(scope).toInt()
|
scope.raiseNotImplemented("compareTo for ${objClass.className}")
|
||||||
|
}
|
||||||
|
}).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun equals(scope: Scope, other: Obj): Boolean {
|
open suspend fun equals(scope: Scope, other: Obj): Boolean {
|
||||||
@ -202,16 +212,16 @@ open class Obj {
|
|||||||
*
|
*
|
||||||
* IF callback returns false, iteration is stopped.
|
* IF callback returns false, iteration is stopped.
|
||||||
*/
|
*/
|
||||||
open suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
|
open suspend fun enumerate(scope: Scope, callback: EnumerateCallback) {
|
||||||
val iterator = invokeInstanceMethod(scope, "iterator")
|
val iterator = invokeInstanceMethod(scope, "iterator")
|
||||||
val hasNext = iterator.getInstanceMethod(scope, "hasNext")
|
val hasNext = iterator.getInstanceMethod(scope, "hasNext")
|
||||||
val next = iterator.getInstanceMethod(scope, "next")
|
val next = iterator.getInstanceMethod(scope, "next")
|
||||||
var closeIt = false
|
var closeIt = false
|
||||||
try {
|
try {
|
||||||
while (hasNext.invoke(scope, iterator).toBool()) {
|
while (hasNext.invokeCallable(scope, iterator).toBool()) {
|
||||||
val nextValue = next.invoke(scope, iterator)
|
val nextValue = next.invokeCallable(scope, iterator)
|
||||||
val shouldContinue = try {
|
val shouldContinue = try {
|
||||||
callback(nextValue)
|
callback.call(nextValue)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// iteration aborted due to exception in callback
|
// iteration aborted due to exception in callback
|
||||||
closeIt = true
|
closeIt = true
|
||||||
@ -448,7 +458,7 @@ open class Obj {
|
|||||||
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
if (rec.visibility == Visibility.Private && !rec.isAbstract) {
|
||||||
val resolved = resolveRecord(scope, rec, name, caller)
|
val resolved = resolveRecord(scope, rec, name, caller)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, caller))
|
return resolved.copy(value = resolved.value.invokeCallable(scope, this, Arguments.EMPTY, caller))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,7 +472,7 @@ open class Obj {
|
|||||||
val decl = rec.declaringClass ?: cls
|
val decl = rec.declaringClass ?: cls
|
||||||
val resolved = resolveRecord(scope, rec, name, decl)
|
val resolved = resolveRecord(scope, rec, name, decl)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
|
return resolved.copy(value = resolved.value.invokeCallable(scope, this, Arguments.EMPTY, decl))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,7 +482,7 @@ open class Obj {
|
|||||||
if (extension != null) {
|
if (extension != null) {
|
||||||
val resolved = resolveRecord(scope, extension, name, extension.declaringClass)
|
val resolved = resolveRecord(scope, extension, name, extension.declaringClass)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, extension.declaringClass))
|
return resolved.copy(value = resolved.value.invokeCallable(scope, this, Arguments.EMPTY, extension.declaringClass))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,7 +496,7 @@ open class Obj {
|
|||||||
scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
|
||||||
val resolved = resolveRecord(scope, rec, name, decl)
|
val resolved = resolveRecord(scope, rec, name, decl)
|
||||||
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
if (resolved.type == ObjRecord.Type.Fun && resolved.value is Statement)
|
||||||
return resolved.copy(value = resolved.value.invoke(scope, this, Arguments.EMPTY, decl))
|
return resolved.copy(value = resolved.value.invokeCallable(scope, this, Arguments.EMPTY, decl))
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,13 +512,13 @@ open class Obj {
|
|||||||
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
val del = obj.delegate ?: scope.raiseError("Internal error: delegated property $name has no delegate")
|
||||||
val th = if (this === ObjVoid) ObjNull else this
|
val th = if (this === ObjVoid) ObjNull else this
|
||||||
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name)), onNotFoundResult = {
|
val res = del.invokeInstanceMethod(scope, "getValue", Arguments(th, ObjString(name)), onNotFoundResult = {
|
||||||
// If getValue not found, return a wrapper that calls invoke
|
// If getValue not found, return a wrapper that calls invokeCallable
|
||||||
object : Statement() {
|
object : Statement() {
|
||||||
override val pos: Pos = Pos.builtIn
|
override val pos: Pos = Pos.builtIn
|
||||||
override suspend fun execute(s: Scope): Obj {
|
override suspend fun execute(s: Scope): Obj {
|
||||||
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
|
val th2 = if (s.thisObj === ObjVoid) ObjNull else s.thisObj
|
||||||
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
|
val allArgs = (listOf(th2, ObjString(name)) + s.args.list).toTypedArray()
|
||||||
return del.invokeInstanceMethod(s, "invoke", Arguments(*allArgs))
|
return del.invokeInstanceMethod(s, "invokeCallable", Arguments(*allArgs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -605,18 +615,20 @@ open class Obj {
|
|||||||
scope.raiseNotImplemented()
|
scope.raiseNotImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun invoke(scope: Scope, thisObj: Obj, args: Arguments, declaringClass: ObjClass? = null): Obj =
|
suspend fun invokeCallable(scope: Scope, thisObj: Obj, args: Arguments, declaringClass: ObjClass? = null): Obj =
|
||||||
if (PerfFlags.SCOPE_POOL)
|
if (PerfFlags.SCOPE_POOL)
|
||||||
scope.withChildFrame(args, newThisObj = thisObj) { child ->
|
scope.withChildFrame(args, newThisObj = thisObj, block = object : ScopeBlock<Obj> {
|
||||||
if (declaringClass != null) child.currentClassCtx = declaringClass
|
override suspend fun call(child: Scope): Obj {
|
||||||
callOn(child)
|
if (declaringClass != null) child.currentClassCtx = declaringClass
|
||||||
}
|
return callOn(child)
|
||||||
|
}
|
||||||
|
})
|
||||||
else
|
else
|
||||||
callOn(scope.createChildScope(scope.pos, args = args, newThisObj = thisObj).also {
|
callOn(scope.createChildScope(scope.pos, args = args, newThisObj = thisObj).also {
|
||||||
if (declaringClass != null) it.currentClassCtx = declaringClass
|
if (declaringClass != null) it.currentClassCtx = declaringClass
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun invoke(scope: Scope, thisObj: Obj, vararg args: Obj): Obj =
|
suspend fun invokeCallable(scope: Scope, thisObj: Obj, vararg args: Obj): Obj =
|
||||||
callOn(
|
callOn(
|
||||||
scope.createChildScope(
|
scope.createChildScope(
|
||||||
scope.pos,
|
scope.pos,
|
||||||
@ -625,7 +637,7 @@ open class Obj {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun invoke(scope: Scope, thisObj: Obj): Obj =
|
suspend fun invokeCallable(scope: Scope, thisObj: Obj): Obj =
|
||||||
callOn(
|
callOn(
|
||||||
scope.createChildScope(
|
scope.createChildScope(
|
||||||
scope.pos,
|
scope.pos,
|
||||||
@ -634,7 +646,7 @@ open class Obj {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun invoke(scope: Scope, atPos: Pos, thisObj: Obj, args: Arguments): Obj =
|
suspend fun invokeCallable(scope: Scope, atPos: Pos, thisObj: Obj, args: Arguments): Obj =
|
||||||
callOn(scope.createChildScope(atPos, args = args, newThisObj = thisObj))
|
callOn(scope.createChildScope(atPos, args = args, newThisObj = thisObj))
|
||||||
|
|
||||||
|
|
||||||
@ -681,117 +693,138 @@ open class Obj {
|
|||||||
name = "toString",
|
name = "toString",
|
||||||
doc = "Returns a string representation of the object.",
|
doc = "Returns a string representation of the object.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisObj.toString(this, true)
|
override suspend fun call(scp: Scope): Obj = scp.thisObj.toString(scp, true)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "inspect",
|
name = "inspect",
|
||||||
doc = "Returns a detailed string representation for debugging.",
|
doc = "Returns a detailed string representation for debugging.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisObj.inspect(this).toObj()
|
override suspend fun call(scp: Scope): Obj = scp.thisObj.inspect(scp).toObj()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "contains",
|
name = "contains",
|
||||||
doc = "Returns true if the object contains the given element.",
|
doc = "Returns true if the object contains the given element.",
|
||||||
params = listOf(ParamDoc("element")),
|
params = listOf(ParamDoc("element")),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjBool(thisObj.contains(this, args.firstAndOnly()))
|
override suspend fun call(scp: Scope): Obj = ObjBool(scp.thisObj.contains(scp, scp.args.firstAndOnly()))
|
||||||
}
|
}
|
||||||
|
)
|
||||||
// utilities
|
// utilities
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "let",
|
name = "let",
|
||||||
doc = "Calls the specified function block with `this` value as its argument and returns its result.",
|
doc = "Calls the specified function block with `this` value as its argument and returns its result.",
|
||||||
params = listOf(ParamDoc("block")),
|
params = listOf(ParamDoc("block")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
args.firstAndOnly().callOn(createChildScope(Arguments(thisObj)))
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
scp.args.firstAndOnly().callOn(scp.createChildScope(Arguments(scp.thisObj)))
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "apply",
|
name = "apply",
|
||||||
doc = "Calls the specified function block with `this` value as its receiver and returns `this` value.",
|
doc = "Calls the specified function block with `this` value as its receiver and returns `this` value.",
|
||||||
params = listOf(ParamDoc("block")),
|
params = listOf(ParamDoc("block")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val body = args.firstAndOnly()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
(thisObj as? ObjInstance)?.let {
|
val body = scp.args.firstAndOnly()
|
||||||
body.callOn(ApplyScope(this, it.instanceScope))
|
(scp.thisObj as? ObjInstance)?.let {
|
||||||
} ?: run {
|
body.callOn(ApplyScope(scp, it.instanceScope))
|
||||||
body.callOn(this)
|
} ?: run {
|
||||||
|
body.callOn(scp)
|
||||||
|
}
|
||||||
|
return scp.thisObj
|
||||||
|
}
|
||||||
}
|
}
|
||||||
thisObj
|
)
|
||||||
}
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "also",
|
name = "also",
|
||||||
doc = "Calls the specified function block with `this` value as its argument and returns `this` value.",
|
doc = "Calls the specified function block with `this` value as its argument and returns `this` value.",
|
||||||
params = listOf(ParamDoc("block")),
|
params = listOf(ParamDoc("block")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
args.firstAndOnly().callOn(createChildScope(Arguments(thisObj)))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisObj
|
scp.args.firstAndOnly().callOn(scp.createChildScope(Arguments(scp.thisObj)))
|
||||||
}
|
return scp.thisObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "run",
|
name = "run",
|
||||||
doc = "Calls the specified function block with `this` value as its receiver and returns its result.",
|
doc = "Calls the specified function block with `this` value as its receiver and returns its result.",
|
||||||
params = listOf(ParamDoc("block")),
|
params = listOf(ParamDoc("block")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
args.firstAndOnly().callOn(this)
|
override suspend fun call(scp: Scope): Obj = scp.args.firstAndOnly().callOn(scp)
|
||||||
}
|
}
|
||||||
addFn("getAt") {
|
)
|
||||||
requireExactCount(1)
|
addFn("getAt", code = object : ScopeCallable {
|
||||||
thisObj.getAt(this, requiredArg<Obj>(0))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
scp.requireExactCount(1)
|
||||||
addFn("putAt") {
|
return scp.thisObj.getAt(scp, scp.requiredArg<Obj>(0))
|
||||||
requireExactCount(2)
|
}
|
||||||
val newValue = args[1]
|
})
|
||||||
thisObj.putAt(this, requiredArg<Obj>(0), newValue)
|
addFn("putAt", code = object : ScopeCallable {
|
||||||
newValue
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
scp.requireExactCount(2)
|
||||||
|
val newValue = scp.args[1]
|
||||||
|
scp.thisObj.putAt(scp, scp.requiredArg<Obj>(0), newValue)
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
})
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toJsonString",
|
name = "toJsonString",
|
||||||
doc = "Encodes this object to a JSON string.",
|
doc = "Encodes this object to a JSON string.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisObj.toJson(this).toString().toObj()
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
scp.thisObj.toJson(scp).toString().toObj()
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "clamp",
|
name = "clamp",
|
||||||
doc = "Clamps this value within the specified range. If the value is outside the range, it is set to the nearest boundary. Respects inclusive/exclusive range ends.",
|
doc = "Clamps this value within the specified range. If the value is outside the range, it is set to the nearest boundary. Respects inclusive/exclusive range ends.",
|
||||||
params = listOf(ParamDoc("range")),
|
params = listOf(ParamDoc("range")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val range = requiredArg<ObjRange>(0)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
|
val range = scp.requiredArg<ObjRange>(0)
|
||||||
|
|
||||||
var result = thisObj
|
var result = scp.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(scp, range.start) < 0) {
|
||||||
result = range.start
|
result = range.start
|
||||||
}
|
|
||||||
}
|
|
||||||
if (range.end != null && !range.end.isNull) {
|
|
||||||
val cmp = range.end.compareTo(this, result)
|
|
||||||
if (range.isEndInclusive) {
|
|
||||||
if (cmp < 0) result = range.end
|
|
||||||
} else {
|
|
||||||
if (cmp <= 0) {
|
|
||||||
if (range.end is ObjInt) {
|
|
||||||
result = ObjInt.of(range.end.value - 1)
|
|
||||||
} else if (range.end is ObjChar) {
|
|
||||||
result = ObjChar((range.end.value.code - 1).toChar())
|
|
||||||
} else {
|
|
||||||
result = range.end
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (range.end != null && !range.end.isNull) {
|
||||||
|
val cmp = range.end.compareTo(scp, result)
|
||||||
|
if (range.isEndInclusive) {
|
||||||
|
if (cmp < 0) result = range.end
|
||||||
|
} else {
|
||||||
|
if (cmp <= 0) {
|
||||||
|
if (range.end is ObjInt) {
|
||||||
|
result = ObjInt.of(range.end.value - 1)
|
||||||
|
} else if (range.end is ObjChar) {
|
||||||
|
result = ObjChar((range.end.value.code - 1).toChar())
|
||||||
|
} else {
|
||||||
|
result = range.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -919,7 +952,7 @@ object ObjUnset : Obj() {
|
|||||||
scope: Scope,
|
scope: Scope,
|
||||||
name: String,
|
name: String,
|
||||||
args: Arguments,
|
args: Arguments,
|
||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: OnNotFound?
|
||||||
): Obj = scope.raiseUnset()
|
): Obj = scope.raiseUnset()
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj = scope.raiseUnset()
|
override suspend fun getAt(scope: Scope, index: Obj): Obj = scope.raiseUnset()
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
|
|
||||||
val ObjArray by lazy {
|
val ObjArray by lazy {
|
||||||
@ -31,8 +33,11 @@ val ObjArray by lazy {
|
|||||||
name = "iterator",
|
name = "iterator",
|
||||||
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) } }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjArrayIterator(scp.thisObj).also { it.init(scp) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "contains",
|
name = "contains",
|
||||||
@ -40,26 +45,31 @@ val ObjArray by lazy {
|
|||||||
params = listOf(ParamDoc("element")),
|
params = listOf(ParamDoc("element")),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val obj = args.firstAndOnly()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
for (i in 0..<thisObj.invokeInstanceMethod(this, "size").toInt()) {
|
val obj = scp.args.firstAndOnly()
|
||||||
if (thisObj.getAt(this, ObjInt(i.toLong())).compareTo(this, obj) == 0) return@addFnDoc ObjTrue
|
for (i in 0..<scp.thisObj.invokeInstanceMethod(scp, "size").toInt()) {
|
||||||
|
if (scp.thisObj.getAt(scp, ObjInt(i.toLong())).compareTo(scp, obj) == 0) return ObjTrue
|
||||||
|
}
|
||||||
|
return ObjFalse
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjFalse
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "last",
|
name = "last",
|
||||||
doc = "The last element of this array.",
|
doc = "The last element of this array.",
|
||||||
type = type("lyng.Any"),
|
type = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
this.thisObj.invokeInstanceMethod(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
this,
|
return scp.thisObj.invokeInstanceMethod(
|
||||||
"getAt",
|
scp,
|
||||||
(this.thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj()
|
"getAt",
|
||||||
)
|
(scp.thisObj.invokeInstanceMethod(scp, "size").toInt() - 1).toObj()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,7 +78,9 @@ 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 = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = (scp.thisObj.invokeInstanceMethod(scp, "size").toInt() - 1).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
@ -76,7 +88,9 @@ 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 = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjRange(0.toObj(), scp.thisObj.invokeInstanceMethod(scp, "size"), false)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
@ -84,25 +98,28 @@ val ObjArray by lazy {
|
|||||||
doc = "Binary search for a target in a sorted array. Returns index or negative insertion point - 1.",
|
doc = "Binary search for a target in a sorted array. Returns index or negative insertion point - 1.",
|
||||||
params = listOf(ParamDoc("target")),
|
params = listOf(ParamDoc("target")),
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val target = args.firstAndOnly()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
var low = 0
|
val target = scp.args.firstAndOnly()
|
||||||
var high = thisObj.invokeInstanceMethod(this, "size").toInt() - 1
|
var low = 0
|
||||||
|
var high = scp.thisObj.invokeInstanceMethod(scp, "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 = scp.thisObj.getAt(scp, ObjInt(mid.toLong()))
|
||||||
|
|
||||||
val cmp = midVal.compareTo(this, target)
|
val cmp = midVal.compareTo(scp, target)
|
||||||
when {
|
when {
|
||||||
cmp == 0 -> return@addFnDoc (mid).toObj()
|
cmp == 0 -> return (mid).toObj()
|
||||||
cmp > 0 -> high = mid - 1
|
cmp > 0 -> high = mid - 1
|
||||||
else -> low = mid + 1
|
else -> low = mid + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (-low - 1).toObj()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
(-low - 1).toObj()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
|
|
||||||
class ObjArrayIterator(val array: Obj) : Obj() {
|
class ObjArrayIterator(val array: Obj) : Obj() {
|
||||||
|
|
||||||
@ -35,16 +36,20 @@ class ObjArrayIterator(val array: Obj) : Obj() {
|
|||||||
companion object {
|
companion object {
|
||||||
val type by lazy {
|
val type by lazy {
|
||||||
ObjClass("ArrayIterator", ObjIterator).apply {
|
ObjClass("ArrayIterator", ObjIterator).apply {
|
||||||
addFn("next") {
|
addFn("next", code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjArrayIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (self.nextIndex < self.lastIndex) {
|
val self = scp.thisAs<ObjArrayIterator>()
|
||||||
self.array.invokeInstanceMethod(this, "getAt", (self.nextIndex++).toObj())
|
return if (self.nextIndex < self.lastIndex) {
|
||||||
} else raiseError(ObjIterationFinishedException(this))
|
self.array.invokeInstanceMethod(scp, "getAt", (self.nextIndex++).toObj())
|
||||||
}
|
} else scp.raiseError(ObjIterationFinishedException(scp))
|
||||||
addFn("hasNext") {
|
}
|
||||||
val self = thisAs<ObjArrayIterator>()
|
})
|
||||||
if (self.nextIndex < self.lastIndex) ObjTrue else ObjFalse
|
addFn("hasNext", code = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj {
|
||||||
|
val self = scp.thisAs<ObjArrayIterator>()
|
||||||
|
return if (self.nextIndex < self.lastIndex) ObjTrue else ObjFalse
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import net.sergeych.bintools.toDump
|
import net.sergeych.bintools.toDump
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
import net.sergeych.lynon.BitArray
|
import net.sergeych.lynon.BitArray
|
||||||
@ -35,29 +36,37 @@ class ObjBitBuffer(val bitArray: BitArray) : Obj() {
|
|||||||
val type = object: ObjClass("BitBuffer", ObjArray) {
|
val type = object: ObjClass("BitBuffer", ObjArray) {
|
||||||
|
|
||||||
}.apply {
|
}.apply {
|
||||||
addFn("toBuffer") {
|
addFn("toBuffer", code = object : ScopeCallable {
|
||||||
requireNoArgs()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjBuffer(thisAs<ObjBitBuffer>().bitArray.asUByteArray())
|
scp.requireNoArgs()
|
||||||
}
|
return ObjBuffer(scp.thisAs<ObjBitBuffer>().bitArray.asUByteArray())
|
||||||
addFn("toDump") {
|
}
|
||||||
requireNoArgs()
|
})
|
||||||
ObjString(
|
addFn("toDump", code = object : ScopeCallable {
|
||||||
thisAs<ObjBitBuffer>().bitArray.asUByteArray().toDump()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
)
|
scp.requireNoArgs()
|
||||||
}
|
return ObjString(
|
||||||
|
scp.thisAs<ObjBitBuffer>().bitArray.asUByteArray().toByteArray().toDump()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "Size of the bit buffer in bits.",
|
doc = "Size of the bit buffer in bits.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjBitBuffer>().bitArray.size.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjBitBuffer>().bitArray.size.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "sizeInBytes",
|
name = "sizeInBytes",
|
||||||
doc = "Size of the bit buffer in full bytes (rounded up).",
|
doc = "Size of the bit buffer in full bytes (rounded up).",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { ObjInt((thisAs<ObjBitBuffer>().bitArray.size + 7) shr 3) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjInt(((scp.thisAs<ObjBitBuffer>().bitArray.size + 7) shr 3).toLong())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,8 @@ import net.sergeych.bintools.decodeHex
|
|||||||
import net.sergeych.bintools.encodeToHex
|
import net.sergeych.bintools.encodeToHex
|
||||||
import net.sergeych.bintools.toDump
|
import net.sergeych.bintools.toDump
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
import net.sergeych.lynon.BitArray
|
import net.sergeych.lynon.BitArray
|
||||||
@ -169,51 +171,85 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
}.apply {
|
}.apply {
|
||||||
addClassFn("decodeBase64") {
|
addClassFn("decodeBase64", code = object : ScopeCallable {
|
||||||
ObjBuffer(requireOnlyArg<Obj>().toString().decodeBase64Url().asUByteArray())
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
ObjBuffer(scp.requireOnlyArg<Obj>().toString().decodeBase64Url().asUByteArray())
|
||||||
addClassFn("decodeHex") {
|
})
|
||||||
ObjBuffer(requireOnlyArg<Obj>().toString().decodeHex().asUByteArray())
|
addClassFn("decodeHex", code = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj =
|
||||||
|
ObjBuffer(scp.requireOnlyArg<Obj>().toString().decodeHex().asUByteArray())
|
||||||
|
})
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "Number of bytes in this buffer.",
|
doc = "Number of bytes in this buffer.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { (this.thisObj as ObjBuffer).byteArray.size.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = (scp.thisObj as ObjBuffer).byteArray.size.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "hex",
|
name = "hex",
|
||||||
doc = "Hexadecimal string representation of the buffer.",
|
doc = "Hexadecimal string representation of the buffer.",
|
||||||
type = type("lyng.String"),
|
type = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjBuffer>().hex.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjBuffer>().hex.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "base64",
|
name = "base64",
|
||||||
doc = "Base64 (URL-safe) string representation of the buffer.",
|
doc = "Base64 (URL-safe) string representation of the buffer.",
|
||||||
type = type("lyng.String"),
|
type = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjBuffer>().base64.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjBuffer>().base64.toObj()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addFnDoc(
|
||||||
|
name = "decodeUtf8",
|
||||||
|
doc = "Decode the buffer content as a UTF-8 string.",
|
||||||
|
returns = type("lyng.String"),
|
||||||
|
moduleName = "lyng.stdlib",
|
||||||
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj =
|
||||||
|
ObjString(scp.thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addFnDoc(
|
||||||
|
name = "toMutable",
|
||||||
|
doc = "Return a mutable copy of this buffer.",
|
||||||
|
returns = type("lyng.MutableBuffer"),
|
||||||
|
moduleName = "lyng.stdlib",
|
||||||
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj {
|
||||||
|
scp.requireNoArgs()
|
||||||
|
return ObjMutableBuffer(scp.thisAs<ObjBuffer>().byteArray.copyOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addFnDoc(
|
||||||
|
name = "toDump",
|
||||||
|
doc = "Return a hexadecimal dump string of the buffer.",
|
||||||
|
returns = type("lyng.String"),
|
||||||
|
moduleName = "lyng.stdlib",
|
||||||
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj {
|
||||||
|
scp.requireNoArgs()
|
||||||
|
return ObjString(scp.thisAs<ObjBuffer>().byteArray.toByteArray().toDump())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addFnDoc(
|
||||||
|
name = "toBitInput",
|
||||||
|
doc = "Return a bit buffer for reading bits from this buffer.",
|
||||||
|
returns = type("lyng.BitBuffer"),
|
||||||
|
moduleName = "lyng.stdlib",
|
||||||
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj =
|
||||||
|
ObjBitBuffer(BitArray(scp.thisAs<ObjBuffer>().byteArray, 8))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFn("decodeUtf8") {
|
|
||||||
ObjString(
|
|
||||||
thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
addFn("toMutable") {
|
|
||||||
requireNoArgs()
|
|
||||||
ObjMutableBuffer(thisAs<ObjBuffer>().byteArray.copyOf())
|
|
||||||
}
|
|
||||||
addFn("toDump") {
|
|
||||||
requireNoArgs()
|
|
||||||
ObjString(
|
|
||||||
thisAs<ObjBuffer>().byteArray.toByteArray().toDump()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
addFn("toBitInput") {
|
|
||||||
ObjBitBuffer(BitArray(thisAs<ObjBuffer>().byteArray, 8))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
|
|
||||||
@ -52,14 +53,16 @@ class ObjChar(val value: Char): Obj() {
|
|||||||
doc = "Unicode code point (UTF-16 code unit) of this character.",
|
doc = "Unicode code point (UTF-16 code unit) of this character.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { ObjInt((this.thisObj as ObjChar).value.code.toLong()) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjInt((scp.thisObj as ObjChar).value.code.toLong())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFn("isDigit") {
|
addFn("isDigit", code = object : ScopeCallable {
|
||||||
thisAs<ObjChar>().value.isDigit().toObj()
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjChar>().value.isDigit().toObj()
|
||||||
}
|
})
|
||||||
addFn("isSpace") {
|
addFn("isSpace", code = object : ScopeCallable {
|
||||||
thisAs<ObjChar>().value.isWhitespace().toObj()
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjChar>().value.isWhitespace().toObj()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,14 +40,18 @@ val ObjClassType by lazy {
|
|||||||
doc = "Full name of this class including package if available.",
|
doc = "Full name of this class including package if available.",
|
||||||
type = type("lyng.String"),
|
type = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { (this.thisObj as ObjClass).classNameObj }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = (scp.thisObj as ObjClass).classNameObj
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "name",
|
name = "name",
|
||||||
doc = "Simple name of this class (without package).",
|
doc = "Simple name of this class (without package).",
|
||||||
type = type("lyng.String"),
|
type = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { (this.thisObj as ObjClass).classNameObj }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = (scp.thisObj as ObjClass).classNameObj
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
@ -55,16 +59,18 @@ val ObjClassType by lazy {
|
|||||||
doc = "Declared instance fields of this class and its ancestors (C3 order), without duplicates.",
|
doc = "Declared instance fields of this class and its ancestors (C3 order), without duplicates.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val cls = this.thisObj as ObjClass
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val seen = hashSetOf<String>()
|
val cls = scp.thisObj as ObjClass
|
||||||
val names = mutableListOf<Obj>()
|
val seen = hashSetOf<String>()
|
||||||
for (c in cls.mro) {
|
val names = mutableListOf<Obj>()
|
||||||
for ((n, rec) in c.members) {
|
for (c in cls.mro) {
|
||||||
if (rec.value !is Statement && seen.add(n)) names += ObjString(n)
|
for ((n, rec) in c.members) {
|
||||||
|
if (rec.value !is Statement && seen.add(n)) names += ObjString(n)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ObjList(names.toMutableList())
|
||||||
}
|
}
|
||||||
ObjList(names.toMutableList())
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,16 +79,18 @@ val ObjClassType by lazy {
|
|||||||
doc = "Declared instance methods of this class and its ancestors (C3 order), without duplicates.",
|
doc = "Declared instance methods of this class and its ancestors (C3 order), without duplicates.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val cls = this.thisObj as ObjClass
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val seen = hashSetOf<String>()
|
val cls = scp.thisObj as ObjClass
|
||||||
val names = mutableListOf<Obj>()
|
val seen = hashSetOf<String>()
|
||||||
for (c in cls.mro) {
|
val names = mutableListOf<Obj>()
|
||||||
for ((n, rec) in c.members) {
|
for (c in cls.mro) {
|
||||||
if (rec.value is Statement && seen.add(n)) names += ObjString(n)
|
for ((n, rec) in c.members) {
|
||||||
|
if (rec.value is Statement && seen.add(n)) names += ObjString(n)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ObjList(names.toMutableList())
|
||||||
}
|
}
|
||||||
ObjList(names.toMutableList())
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,13 +99,16 @@ val ObjClassType by lazy {
|
|||||||
doc = "Lookup a member by name in this class (including ancestors) and return it, or null if absent.",
|
doc = "Lookup a member by name in this class (including ancestors) and return it, or null if absent.",
|
||||||
params = listOf(ParamDoc("name", type("lyng.String"))),
|
params = listOf(ParamDoc("name", type("lyng.String"))),
|
||||||
returns = type("lyng.Any", nullable = true),
|
returns = type("lyng.Any", nullable = true),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val cls = thisAs<ObjClass>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val name = requiredArg<ObjString>(0).value
|
val cls = scp.thisAs<ObjClass>()
|
||||||
val rec = cls.getInstanceMemberOrNull(name)
|
val name = scp.requiredArg<ObjString>(0).value
|
||||||
rec?.value ?: ObjNull
|
val rec = cls.getInstanceMemberOrNull(name)
|
||||||
}
|
return rec?.value ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,9 +558,9 @@ open class ObjClass(
|
|||||||
isClosed: Boolean = false,
|
isClosed: Boolean = false,
|
||||||
isOverride: Boolean = false,
|
isOverride: Boolean = false,
|
||||||
pos: Pos = Pos.builtIn,
|
pos: Pos = Pos.builtIn,
|
||||||
code: (suspend Scope.() -> Obj)? = null
|
code: ScopeCallable? = null
|
||||||
) {
|
) {
|
||||||
val stmt = code?.let { statement { it() } } ?: ObjNull
|
val stmt = code?.let { statement(pos, f = 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,
|
||||||
@ -561,8 +572,8 @@ open class ObjClass(
|
|||||||
|
|
||||||
fun addProperty(
|
fun addProperty(
|
||||||
name: String,
|
name: String,
|
||||||
getter: (suspend Scope.() -> Obj)? = null,
|
getter: ScopeCallable? = null,
|
||||||
setter: (suspend Scope.(Obj) -> Unit)? = null,
|
setter: ScopeCallable? = null,
|
||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
writeVisibility: Visibility? = null,
|
writeVisibility: Visibility? = null,
|
||||||
declaringClass: ObjClass? = this,
|
declaringClass: ObjClass? = this,
|
||||||
@ -572,8 +583,8 @@ open class ObjClass(
|
|||||||
pos: Pos = Pos.builtIn,
|
pos: Pos = Pos.builtIn,
|
||||||
prop: ObjProperty? = null
|
prop: ObjProperty? = null
|
||||||
) {
|
) {
|
||||||
val g = getter?.let { statement { it() } }
|
val g = getter?.let { statement(pos, f = it) }
|
||||||
val s = setter?.let { statement { it(requiredArg(0)); ObjVoid } }
|
val s = setter?.let { statement(pos, f = it) }
|
||||||
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,
|
||||||
@ -583,8 +594,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: ScopeCallable) {
|
||||||
createClassField(name, statement { code() }, isOpen, type = ObjRecord.Type.Fun)
|
createClassField(name, statement(f = code), isOpen, type = ObjRecord.Type.Fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -694,25 +705,25 @@ open class ObjClass(
|
|||||||
|
|
||||||
override suspend fun invokeInstanceMethod(
|
override suspend fun invokeInstanceMethod(
|
||||||
scope: Scope, name: String, args: Arguments,
|
scope: Scope, name: String, args: Arguments,
|
||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: OnNotFound?
|
||||||
): Obj {
|
): Obj {
|
||||||
getInstanceMemberOrNull(name)?.let { rec ->
|
getInstanceMemberOrNull(name)?.let { rec ->
|
||||||
val decl = rec.declaringClass ?: findDeclaringClassOf(name) ?: this
|
val decl = rec.declaringClass ?: findDeclaringClassOf(name) ?: this
|
||||||
if (rec.type == ObjRecord.Type.Delegated) {
|
if (rec.type == ObjRecord.Type.Delegated) {
|
||||||
val del = rec.delegate ?: scope.raiseError("Internal error: delegated member $name has no delegate")
|
val del = rec.delegate ?: scope.raiseError("Internal error: delegated member $name has no delegate")
|
||||||
val allArgs = (listOf(this, ObjString(name)) + args.list).toTypedArray()
|
val allArgs = (listOf(this, ObjString(name)) + args.list).toTypedArray()
|
||||||
return del.invokeInstanceMethod(scope, "invoke", Arguments(*allArgs), onNotFoundResult = {
|
return del.invokeInstanceMethod(scope, "invokeCallable", Arguments(*allArgs), onNotFoundResult = {
|
||||||
// Fallback: property delegation
|
// Fallback: property delegation
|
||||||
val propVal = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
val propVal = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
||||||
propVal.invoke(scope, this, args, decl)
|
propVal.invokeCallable(scope, this, args, decl)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (rec.type == ObjRecord.Type.Fun) {
|
if (rec.type == ObjRecord.Type.Fun) {
|
||||||
return rec.value.invoke(scope, this, args, decl)
|
return rec.value.invokeCallable(scope, this, args, decl)
|
||||||
} else {
|
} else {
|
||||||
// Resolved field or property value
|
// Resolved field or property value
|
||||||
val resolved = readField(scope, name)
|
val resolved = readField(scope, name)
|
||||||
return resolved.value.invoke(scope, this, args, decl)
|
return resolved.value.invokeCallable(scope, this, args, decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
return super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -19,6 +19,7 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
@ -38,11 +39,14 @@ class ObjCompletableDeferred(val completableDeferred: CompletableDeferred<Obj>):
|
|||||||
doc = "Complete this deferred with the given value. Subsequent calls have no effect.",
|
doc = "Complete this deferred with the given value. Subsequent calls have no effect.",
|
||||||
params = listOf(ParamDoc("value")),
|
params = listOf(ParamDoc("value")),
|
||||||
returns = type("lyng.Void"),
|
returns = type("lyng.Void"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjCompletableDeferred>().completableDeferred.complete(args.firstAndOnly())
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjVoid
|
scp.thisAs<ObjCompletableDeferred>().completableDeferred.complete(scp.args.firstAndOnly())
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,6 +21,7 @@ import kotlinx.datetime.*
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
import net.sergeych.lyng.miniast.addClassFnDoc
|
import net.sergeych.lyng.miniast.addClassFnDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
@ -54,7 +55,9 @@ class ObjDateTime(val instant: Instant, val timeZone: TimeZone) : Obj() {
|
|||||||
}
|
}
|
||||||
if (rec.type == ObjRecord.Type.Fun || rec.value is Statement) {
|
if (rec.type == ObjRecord.Type.Fun || rec.value is Statement) {
|
||||||
val s = rec.value as Statement
|
val s = rec.value as Statement
|
||||||
return ObjRecord(net.sergeych.lyng.statement { s.execute(this.createChildScope(newThisObj = this@ObjDateTime)) }, rec.isMutable)
|
return ObjRecord(net.sergeych.lyng.statement(f = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = s.execute(scp.createChildScope(newThisObj = this@ObjDateTime))
|
||||||
|
}), rec.isMutable)
|
||||||
}
|
}
|
||||||
return resolveRecord(scope, rec, name, rec.declaringClass ?: cls)
|
return resolveRecord(scope, rec, name, rec.declaringClass ?: cls)
|
||||||
}
|
}
|
||||||
@ -172,122 +175,129 @@ class ObjDateTime(val instant: Instant, val timeZone: TimeZone) : Obj() {
|
|||||||
}
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
addPropertyDoc("year", "The year component.", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("year", "The year component.", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.year.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.year.toObj() })
|
||||||
addPropertyDoc("month", "The month component (1..12).", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("month", "The month component (1..12).", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.monthNumber.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.monthNumber.toObj() })
|
||||||
addPropertyDoc("dayOfMonth", "The day of month component.", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("dayOfMonth", "The day of month component.", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.dayOfMonth.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.dayOfMonth.toObj() })
|
||||||
addPropertyDoc("day", "Alias to dayOfMonth.", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("day", "Alias to dayOfMonth.", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.dayOfMonth.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.dayOfMonth.toObj() })
|
||||||
addPropertyDoc("hour", "The hour component (0..23).", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("hour", "The hour component (0..23).", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.hour.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.hour.toObj() })
|
||||||
addPropertyDoc("minute", "The minute component (0..59).", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("minute", "The minute component (0..59).", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.minute.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.minute.toObj() })
|
||||||
addPropertyDoc("second", "The second component (0..59).", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("second", "The second component (0..59).", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.second.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.second.toObj() })
|
||||||
addPropertyDoc("dayOfWeek", "The day of week (1=Monday, 7=Sunday).", type("lyng.Int"), moduleName = "lyng.time",
|
addPropertyDoc("dayOfWeek", "The day of week (1=Monday, 7=Sunday).", type("lyng.Int"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().localDateTime.dayOfWeek.isoDayNumber.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().localDateTime.dayOfWeek.isoDayNumber.toObj() })
|
||||||
addPropertyDoc("timeZone", "The time zone ID (e.g. 'Z', '+02:00', 'Europe/Prague').", type("lyng.String"), moduleName = "lyng.time",
|
addPropertyDoc("timeZone", "The time zone ID (e.g. 'Z', '+02:00', 'Europe/Prague').", type("lyng.String"), moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDateTime>().timeZone.id.toObj() })
|
getter = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().timeZone.id.toObj() })
|
||||||
|
|
||||||
addFnDoc("toInstant", "Convert this localized date time back to an absolute Instant.", returns = type("lyng.Instant"), moduleName = "lyng.time") {
|
addFnDoc("toInstant", "Convert this localized date time back to an absolute Instant.", returns = type("lyng.Instant"), moduleName = "lyng.time",
|
||||||
ObjInstant(thisAs<ObjDateTime>().instant)
|
code = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjInstant(scp.thisAs<ObjDateTime>().instant) })
|
||||||
}
|
addFnDoc("toEpochSeconds", "Return the number of full seconds since the Unix epoch (UTC).", returns = type("lyng.Int"), moduleName = "lyng.time",
|
||||||
addFnDoc("toEpochSeconds", "Return the number of full seconds since the Unix epoch (UTC).", returns = type("lyng.Int"), moduleName = "lyng.time") {
|
code = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().instant.epochSeconds.toObj() })
|
||||||
thisAs<ObjDateTime>().instant.epochSeconds.toObj()
|
addFnDoc("toRFC3339", "Return the RFC3339 string representation of this date time, including its timezone offset.", returns = type("lyng.String"), moduleName = "lyng.time",
|
||||||
}
|
code = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().toRFC3339().toObj() })
|
||||||
addFnDoc("toRFC3339", "Return the RFC3339 string representation of this date time, including its timezone offset.", returns = type("lyng.String"), moduleName = "lyng.time") {
|
addFnDoc("toSortableString", "Alias to toRFC3339.", returns = type("lyng.String"), moduleName = "lyng.time",
|
||||||
thisAs<ObjDateTime>().toRFC3339().toObj()
|
code = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().toRFC3339().toObj() })
|
||||||
}
|
|
||||||
addFnDoc("toSortableString", "Alias to toRFC3339.", returns = type("lyng.String"), moduleName = "lyng.time") {
|
addFnDoc("toEpochMilliseconds", "Return the number of milliseconds since the Unix epoch (UTC).", returns = type("lyng.Int"), moduleName = "lyng.time",
|
||||||
thisAs<ObjDateTime>().toRFC3339().toObj()
|
code = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDateTime>().instant.toEpochMilliseconds().toObj() })
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc("toEpochMilliseconds", "Return the number of milliseconds since the Unix epoch (UTC).", returns = type("lyng.Int"), moduleName = "lyng.time") {
|
|
||||||
thisAs<ObjDateTime>().instant.toEpochMilliseconds().toObj()
|
|
||||||
}
|
|
||||||
addFnDoc("toTimeZone", "Return a new DateTime representing the same instant but in a different time zone. " +
|
addFnDoc("toTimeZone", "Return a new DateTime representing the same instant but in a different time zone. " +
|
||||||
"Accepts a timezone ID string (e.g., 'UTC', '+02:00') or an integer offset in seconds.",
|
"Accepts a timezone ID string (e.g., 'UTC', '+02:00') or an integer offset in seconds.",
|
||||||
params = listOf(net.sergeych.lyng.miniast.ParamDoc("tz", type = type("lyng.Any"))),
|
params = listOf(net.sergeych.lyng.miniast.ParamDoc("tz", type = type("lyng.Any"))),
|
||||||
returns = type("lyng.DateTime"), moduleName = "lyng.time") {
|
returns = type("lyng.DateTime"), moduleName = "lyng.time",
|
||||||
val tz = when (val a = args.list.getOrNull(0)) {
|
code = object : ScopeCallable {
|
||||||
is ObjString -> TimeZone.of(a.value)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
is ObjInt -> UtcOffset(seconds = a.value.toInt()).asTimeZone()
|
val tz = when (val a = scp.args.list.getOrNull(0)) {
|
||||||
else -> raiseIllegalArgument("invalid timezone: $a")
|
is ObjString -> TimeZone.of(a.value)
|
||||||
}
|
is ObjInt -> UtcOffset(seconds = a.value.toInt()).asTimeZone()
|
||||||
ObjDateTime(thisAs<ObjDateTime>().instant, tz)
|
else -> scp.raiseIllegalArgument("invalid timezone: $a")
|
||||||
}
|
}
|
||||||
addFnDoc("toUTC", "Shortcut to convert this date time to the UTC time zone.", returns = type("lyng.DateTime"), moduleName = "lyng.time") {
|
return ObjDateTime(scp.thisAs<ObjDateTime>().instant, tz)
|
||||||
ObjDateTime(thisAs<ObjDateTime>().instant, TimeZone.UTC)
|
}
|
||||||
}
|
})
|
||||||
|
addFnDoc("toUTC", "Shortcut to convert this date time to the UTC time zone.", returns = type("lyng.DateTime"), moduleName = "lyng.time",
|
||||||
|
code = object : ScopeCallable { override suspend fun call(scp: Scope): Obj = ObjDateTime(scp.thisAs<ObjDateTime>().instant, TimeZone.UTC) })
|
||||||
|
|
||||||
addFnDoc("addMonths", "Return a new DateTime with the specified number of months added (or subtracted if negative). " +
|
addFnDoc("addMonths", "Return a new DateTime with the specified number of months added (or subtracted if negative). " +
|
||||||
"Normalizes the day of month if necessary (e.g., Jan 31 + 1 month = Feb 28/29).",
|
"Normalizes the day of month if necessary (e.g., Jan 31 + 1 month = Feb 28/29).",
|
||||||
params = listOf(net.sergeych.lyng.miniast.ParamDoc("months", type = type("lyng.Int"))),
|
params = listOf(net.sergeych.lyng.miniast.ParamDoc("months", type = type("lyng.Int"))),
|
||||||
returns = type("lyng.DateTime"), moduleName = "lyng.time") {
|
returns = type("lyng.DateTime"), moduleName = "lyng.time",
|
||||||
val n = args.list.getOrNull(0)?.toInt() ?: 0
|
code = object : ScopeCallable {
|
||||||
val res = thisAs<ObjDateTime>().instant.plus(n, DateTimeUnit.MONTH, thisAs<ObjDateTime>().timeZone)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjDateTime(res, thisAs<ObjDateTime>().timeZone)
|
val n = scp.args.list.getOrNull(0)?.toInt() ?: 0
|
||||||
}
|
val res = scp.thisAs<ObjDateTime>().instant.plus(n, DateTimeUnit.MONTH, scp.thisAs<ObjDateTime>().timeZone)
|
||||||
|
return ObjDateTime(res, scp.thisAs<ObjDateTime>().timeZone)
|
||||||
|
}
|
||||||
|
})
|
||||||
addFnDoc("addYears", "Return a new DateTime with the specified number of years added (or subtracted if negative).",
|
addFnDoc("addYears", "Return a new DateTime with the specified number of years added (or subtracted if negative).",
|
||||||
params = listOf(net.sergeych.lyng.miniast.ParamDoc("years", type = type("lyng.Int"))),
|
params = listOf(net.sergeych.lyng.miniast.ParamDoc("years", type = type("lyng.Int"))),
|
||||||
returns = type("lyng.DateTime"), moduleName = "lyng.time") {
|
returns = type("lyng.DateTime"), moduleName = "lyng.time",
|
||||||
val n = args.list.getOrNull(0)?.toInt() ?: 0
|
code = object : ScopeCallable {
|
||||||
val res = thisAs<ObjDateTime>().instant.plus(n, DateTimeUnit.YEAR, thisAs<ObjDateTime>().timeZone)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjDateTime(res, thisAs<ObjDateTime>().timeZone)
|
val n = scp.args.list.getOrNull(0)?.toInt() ?: 0
|
||||||
}
|
val res = scp.thisAs<ObjDateTime>().instant.plus(n, DateTimeUnit.YEAR, scp.thisAs<ObjDateTime>().timeZone)
|
||||||
|
return ObjDateTime(res, scp.thisAs<ObjDateTime>().timeZone)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
addClassFn("now") {
|
addClassFn("now", code = object : ScopeCallable {
|
||||||
val tz = when (val a = args.list.getOrNull(0)) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
null -> TimeZone.currentSystemDefault()
|
val tz = when (val a = scp.args.list.getOrNull(0)) {
|
||||||
is ObjString -> TimeZone.of(a.value)
|
null -> TimeZone.currentSystemDefault()
|
||||||
is ObjInt -> UtcOffset(seconds = a.value.toInt()).asTimeZone()
|
is ObjString -> TimeZone.of(a.value)
|
||||||
else -> raiseIllegalArgument("invalid timezone: $a")
|
is ObjInt -> UtcOffset(seconds = a.value.toInt()).asTimeZone()
|
||||||
|
else -> scp.raiseIllegalArgument("invalid timezone: $a")
|
||||||
|
}
|
||||||
|
return ObjDateTime(kotlin.time.Clock.System.now(), tz)
|
||||||
}
|
}
|
||||||
ObjDateTime(kotlin.time.Clock.System.now(), tz)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
addClassFnDoc("parseRFC3339",
|
addClassFnDoc("parseRFC3339",
|
||||||
"Parse an RFC3339 string into a DateTime object. " +
|
"Parse an RFC3339 string into a DateTime object. " +
|
||||||
"Note: if the string does not specify a timezone, UTC is assumed.",
|
"Note: if the string does not specify a timezone, UTC is assumed.",
|
||||||
params = listOf(net.sergeych.lyng.miniast.ParamDoc("string", type = type("lyng.String"))),
|
params = listOf(net.sergeych.lyng.miniast.ParamDoc("string", type = type("lyng.String"))),
|
||||||
returns = type("lyng.DateTime"),
|
returns = type("lyng.DateTime"),
|
||||||
moduleName = "lyng.time") {
|
moduleName = "lyng.time",
|
||||||
val s = (args.firstAndOnly() as ObjString).value
|
code = object : ScopeCallable {
|
||||||
// kotlinx-datetime's Instant.parse handles RFC3339
|
override suspend fun call(scp: Scope): Obj {
|
||||||
// But we want to preserve the offset if present for DateTime.
|
val s = (scp.args.firstAndOnly() as ObjString).value
|
||||||
// However, Instant.parse("...") always gives an Instant.
|
// kotlinx-datetime's Instant.parse handles RFC3339
|
||||||
// If we want the specific offset from the string, we might need a more complex parse.
|
// But we want to preserve the offset if present for DateTime.
|
||||||
// For now, let's stick to parsing it as Instant and converting to UTC or specified TZ.
|
// However, Instant.parse("...") always gives an Instant.
|
||||||
// Actually, if the string has an offset, Instant.parse handles it but returns UTC instant.
|
// If we want the specific offset from the string, we might need a more complex parse.
|
||||||
|
// For now, let's stick to parsing it as Instant and converting to UTC or specified TZ.
|
||||||
// Let's try to detect if there is an offset in the string.
|
// Actually, if the string has an offset, Instant.parse handles it but returns UTC instant.
|
||||||
// If not, use UTC.
|
|
||||||
val instant = Instant.parse(s)
|
// Let's try to detect if there is an offset in the string.
|
||||||
|
// If not, use UTC.
|
||||||
// RFC3339 can have Z or +/-HH:mm or +/-HHmm or +/-HH
|
val instant = Instant.parse(s)
|
||||||
val tz = try {
|
|
||||||
if (s.endsWith("Z", ignoreCase = true)) {
|
// RFC3339 can have Z or +/-HH:mm or +/-HHmm or +/-HH
|
||||||
TimeZone.of("Z")
|
val tz = try {
|
||||||
} else {
|
if (s.endsWith("Z", ignoreCase = true)) {
|
||||||
// Look for the last + or - which is likely the start of the offset
|
TimeZone.of("Z")
|
||||||
val lastPlus = s.lastIndexOf('+')
|
} else {
|
||||||
val lastMinus = s.lastIndexOf('-')
|
// Look for the last + or - which is likely the start of the offset
|
||||||
val offsetStart = if (lastPlus > lastMinus) lastPlus else lastMinus
|
val lastPlus = s.lastIndexOf('+')
|
||||||
if (offsetStart > s.lastIndexOf('T')) {
|
val lastMinus = s.lastIndexOf('-')
|
||||||
// Likely an offset
|
val offsetStart = if (lastPlus > lastMinus) lastPlus else lastMinus
|
||||||
val offsetStr = s.substring(offsetStart)
|
if (offsetStart > s.lastIndexOf('T')) {
|
||||||
TimeZone.of(offsetStr)
|
// Likely an offset
|
||||||
} else {
|
val offsetStr = s.substring(offsetStart)
|
||||||
|
TimeZone.of(offsetStr)
|
||||||
|
} else {
|
||||||
|
TimeZone.UTC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
TimeZone.UTC
|
TimeZone.UTC
|
||||||
}
|
}
|
||||||
|
return ObjDateTime(instant, tz)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
})
|
||||||
TimeZone.UTC
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjDateTime(instant, tz)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
@ -37,23 +38,30 @@ open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
|
|||||||
name = "await",
|
name = "await",
|
||||||
doc = "Suspend until completion and return the result value (or throw if failed).",
|
doc = "Suspend until completion and return the result value (or throw if failed).",
|
||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) { thisAs<ObjDeferred>().deferred.await() }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDeferred>().deferred.await()
|
||||||
|
}
|
||||||
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isCompleted",
|
name = "isCompleted",
|
||||||
doc = "Whether this deferred has completed (successfully or with an error).",
|
doc = "Whether this deferred has completed (successfully or with an error).",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjDeferred>().deferred.isCompleted.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDeferred>().deferred.isCompleted.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isActive",
|
name = "isActive",
|
||||||
doc = "Whether this deferred is currently active (not completed and not cancelled).",
|
doc = "Whether this deferred is currently active (not completed and not cancelled).",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val d = thisAs<ObjDeferred>().deferred
|
override suspend fun call(scp: Scope): Obj {
|
||||||
(d.isActive || (!d.isCompleted && !d.isCancelled)).toObj()
|
val d = scp.thisAs<ObjDeferred>().deferred
|
||||||
|
return (d.isActive || (!d.isCompleted && !d.isCancelled)).toObj()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
@ -61,7 +69,9 @@ open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
|
|||||||
doc = "Whether this deferred was cancelled.",
|
doc = "Whether this deferred was cancelled.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjDeferred>().deferred.isCancelled.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDeferred>().deferred.isCancelled.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
@ -79,42 +80,54 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Return this duration as a real number of days.",
|
doc = "Return this duration as a real number of days.",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDuration>().duration.toDouble(DurationUnit.DAYS).toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDuration>().duration.toDouble(DurationUnit.DAYS).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "hours",
|
name = "hours",
|
||||||
doc = "Return this duration as a real number of hours.",
|
doc = "Return this duration as a real number of hours.",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDuration>().duration.toDouble(DurationUnit.HOURS).toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDuration>().duration.toDouble(DurationUnit.HOURS).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "minutes",
|
name = "minutes",
|
||||||
doc = "Return this duration as a real number of minutes.",
|
doc = "Return this duration as a real number of minutes.",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDuration>().duration.toDouble(DurationUnit.MINUTES).toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDuration>().duration.toDouble(DurationUnit.MINUTES).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "seconds",
|
name = "seconds",
|
||||||
doc = "Return this duration as a real number of seconds.",
|
doc = "Return this duration as a real number of seconds.",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDuration>().duration.toDouble(DurationUnit.SECONDS).toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDuration>().duration.toDouble(DurationUnit.SECONDS).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "milliseconds",
|
name = "milliseconds",
|
||||||
doc = "Return this duration as a real number of milliseconds.",
|
doc = "Return this duration as a real number of milliseconds.",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDuration>().duration.toDouble(DurationUnit.MILLISECONDS).toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDuration>().duration.toDouble(DurationUnit.MILLISECONDS).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "microseconds",
|
name = "microseconds",
|
||||||
doc = "Return this duration as a real number of microseconds.",
|
doc = "Return this duration as a real number of microseconds.",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjDuration>().duration.toDouble(DurationUnit.MICROSECONDS).toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjDuration>().duration.toDouble(DurationUnit.MICROSECONDS).toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
// extensions
|
// extensions
|
||||||
|
|
||||||
@ -123,7 +136,9 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Construct a `Duration` equal to this integer number of seconds.",
|
doc = "Construct a `Duration` equal to this integer number of seconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.seconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.seconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
@ -131,14 +146,18 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Construct a `Duration` equal to this integer number of seconds.",
|
doc = "Construct a `Duration` equal to this integer number of seconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.seconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.seconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
name = "milliseconds",
|
name = "milliseconds",
|
||||||
doc = "Construct a `Duration` equal to this integer number of milliseconds.",
|
doc = "Construct a `Duration` equal to this integer number of milliseconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.milliseconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.milliseconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
@ -146,14 +165,18 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Construct a `Duration` equal to this integer number of milliseconds.",
|
doc = "Construct a `Duration` equal to this integer number of milliseconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.milliseconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.milliseconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "seconds",
|
name = "seconds",
|
||||||
doc = "Construct a `Duration` equal to this real number of seconds.",
|
doc = "Construct a `Duration` equal to this real number of seconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.seconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.seconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
@ -161,7 +184,9 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Construct a `Duration` equal to this real number of seconds.",
|
doc = "Construct a `Duration` equal to this real number of seconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.seconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.seconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
@ -169,14 +194,18 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Construct a `Duration` equal to this real number of milliseconds.",
|
doc = "Construct a `Duration` equal to this real number of milliseconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.milliseconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.milliseconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "millisecond",
|
name = "millisecond",
|
||||||
doc = "Construct a `Duration` equal to this real number of milliseconds.",
|
doc = "Construct a `Duration` equal to this real number of milliseconds.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.milliseconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.milliseconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
@ -184,84 +213,108 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
doc = "Construct a `Duration` equal to this integer number of minutes.",
|
doc = "Construct a `Duration` equal to this integer number of minutes.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.minutes) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.minutes)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "minutes",
|
name = "minutes",
|
||||||
doc = "Construct a `Duration` equal to this real number of minutes.",
|
doc = "Construct a `Duration` equal to this real number of minutes.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.minutes) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.minutes)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
name = "minute",
|
name = "minute",
|
||||||
doc = "Construct a `Duration` equal to this integer number of minutes.",
|
doc = "Construct a `Duration` equal to this integer number of minutes.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.minutes) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.minutes)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "minute",
|
name = "minute",
|
||||||
doc = "Construct a `Duration` equal to this real number of minutes.",
|
doc = "Construct a `Duration` equal to this real number of minutes.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.minutes) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.minutes)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
name = "hours",
|
name = "hours",
|
||||||
doc = "Construct a `Duration` equal to this integer number of hours.",
|
doc = "Construct a `Duration` equal to this integer number of hours.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.hours) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.hours)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "hours",
|
name = "hours",
|
||||||
doc = "Construct a `Duration` equal to this real number of hours.",
|
doc = "Construct a `Duration` equal to this real number of hours.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.hours) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.hours)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
name = "hour",
|
name = "hour",
|
||||||
doc = "Construct a `Duration` equal to this integer number of hours.",
|
doc = "Construct a `Duration` equal to this integer number of hours.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.hours) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.hours)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "hour",
|
name = "hour",
|
||||||
doc = "Construct a `Duration` equal to this real number of hours.",
|
doc = "Construct a `Duration` equal to this real number of hours.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.hours) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.hours)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
name = "days",
|
name = "days",
|
||||||
doc = "Construct a `Duration` equal to this integer number of days.",
|
doc = "Construct a `Duration` equal to this integer number of days.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.days) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.days)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "days",
|
name = "days",
|
||||||
doc = "Construct a `Duration` equal to this real number of days.",
|
doc = "Construct a `Duration` equal to this real number of days.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.days) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.days)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjInt.type.addPropertyDoc(
|
ObjInt.type.addPropertyDoc(
|
||||||
name = "day",
|
name = "day",
|
||||||
doc = "Construct a `Duration` equal to this integer number of days.",
|
doc = "Construct a `Duration` equal to this integer number of days.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjInt>().value.days) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjInt>().value.days)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
ObjReal.type.addPropertyDoc(
|
ObjReal.type.addPropertyDoc(
|
||||||
name = "day",
|
name = "day",
|
||||||
doc = "Construct a `Duration` equal to this real number of days.",
|
doc = "Construct a `Duration` equal to this real number of days.",
|
||||||
type = type("lyng.Duration"),
|
type = type("lyng.Duration"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjDuration(thisAs<ObjReal>().value.days) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjDuration(scp.thisAs<ObjReal>().value.days)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,31 +17,32 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.*
|
||||||
import net.sergeych.lyng.ClosureScope
|
|
||||||
import net.sergeych.lyng.Scope
|
|
||||||
import net.sergeych.lyng.Statement
|
|
||||||
|
|
||||||
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
|
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
|
||||||
override val objClass: ObjClass get() = type
|
override val objClass: ObjClass get() = type
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("DelegateContext").apply {
|
val type = ObjClass("DelegateContext").apply {
|
||||||
addFn("get") {
|
addFn("get", code = object : ScopeCallable {
|
||||||
val d = thisAs<ObjDynamicContext>().delegate
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (d.readCallback != null)
|
val d = scp.thisAs<ObjDynamicContext>().delegate
|
||||||
raiseIllegalState("get already defined")
|
if (d.readCallback != null)
|
||||||
d.readCallback = requireOnlyArg()
|
scp.raiseIllegalState("get already defined")
|
||||||
ObjVoid
|
d.readCallback = scp.requireOnlyArg()
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
addFn("set") {
|
addFn("set", code = object : ScopeCallable {
|
||||||
val d = thisAs<ObjDynamicContext>().delegate
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (d.writeCallback != null)
|
val d = scp.thisAs<ObjDynamicContext>().delegate
|
||||||
raiseIllegalState("set already defined")
|
if (d.writeCallback != null)
|
||||||
d.writeCallback = requireOnlyArg()
|
scp.raiseIllegalState("set already defined")
|
||||||
ObjVoid
|
d.writeCallback = scp.requireOnlyArg()
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,11 +82,11 @@ open class ObjDynamic(var readCallback: Statement? = null, var writeCallback: St
|
|||||||
scope: Scope,
|
scope: Scope,
|
||||||
name: String,
|
name: String,
|
||||||
args: Arguments,
|
args: Arguments,
|
||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: OnNotFound?
|
||||||
): Obj {
|
): Obj {
|
||||||
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
val execBase = builderScope?.let { ClosureScope(scope, it) } ?: scope
|
||||||
val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))
|
val over = readCallback?.execute(execBase.createChildScope(Arguments(ObjString(name))))
|
||||||
return over?.invoke(scope, scope.thisObj, args)
|
return over?.invokeCallable(scope, scope.thisObj, args)
|
||||||
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ package net.sergeych.lyng.obj/*
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
import net.sergeych.lynon.LynonDecoder
|
import net.sergeych.lynon.LynonDecoder
|
||||||
@ -74,12 +75,18 @@ class ObjEnumClass(val name: String) : ObjClass(name, EnumBase) {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
addClassConst("entries", objEntries )
|
addClassConst("entries", objEntries )
|
||||||
addClassFn("valueOf") {
|
addClassFn("valueOf", code = object : ScopeCallable {
|
||||||
val name = requireOnlyArg<ObjString>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
byName[name] ?: raiseSymbolNotFound("does not exists: enum ${className}.$name")
|
val name = scp.requireOnlyArg<ObjString>()
|
||||||
}
|
return byName[name] ?: scp.raiseSymbolNotFound("does not exists: enum ${className}.$name")
|
||||||
addPropertyDoc("name", doc = "Entry name as string", type = type("lyng.String"), getter = { thisAs<ObjEnumEntry>().name })
|
}
|
||||||
addPropertyDoc("ordinal", doc = "Entry ordinal position", type = type("lyng.Int"), getter = { thisAs<ObjEnumEntry>().ordinal })
|
})
|
||||||
|
addPropertyDoc("name", doc = "Entry name as string", type = type("lyng.String"), getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjEnumEntry>().name
|
||||||
|
})
|
||||||
|
addPropertyDoc("ordinal", doc = "Entry ordinal position", type = type("lyng.Int"), getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjEnumEntry>().ordinal
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -125,7 +125,9 @@ 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 = statement { ObjString(name) })),
|
listOf(ArgsDeclaration.Item("message", defaultValue = statement(f = object : ScopeCallable {
|
||||||
|
override suspend fun call(scope: Scope): Obj = ObjString(name)
|
||||||
|
}))),
|
||||||
Token.Type.RPAREN
|
Token.Type.RPAREN
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -163,27 +165,33 @@ open class ObjException(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val Root = ExceptionClass("Exception").apply {
|
val Root = ExceptionClass("Exception").apply {
|
||||||
instanceInitializers.add(statement {
|
instanceInitializers.add(statement(f = object : ScopeCallable {
|
||||||
if (thisObj is ObjInstance) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val msg = get("message")?.value ?: ObjString("Exception")
|
if (scp.thisObj is ObjInstance) {
|
||||||
(thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
|
val msg = scp.get("message")?.value ?: ObjString("Exception")
|
||||||
|
(scp.thisObj as ObjInstance).instanceScope.addItem("Exception::message", false, msg)
|
||||||
|
|
||||||
val stack = captureStackTrace(this)
|
val stack = captureStackTrace(scp)
|
||||||
(thisObj as ObjInstance).instanceScope.addItem("Exception::stackTrace", false, stack)
|
(scp.thisObj as ObjInstance).instanceScope.addItem("Exception::stackTrace", false, stack)
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
ObjVoid
|
}))
|
||||||
|
instanceConstructor = statement(f = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjVoid
|
||||||
})
|
})
|
||||||
instanceConstructor = statement { ObjVoid }
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "message",
|
name = "message",
|
||||||
doc = "Human‑readable error message.",
|
doc = "Human‑readable error message.",
|
||||||
type = type("lyng.String"),
|
type = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
when (val t = this.thisObj) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
is ObjException -> t.message
|
return when (val t = scp.thisObj) {
|
||||||
is ObjInstance -> t.instanceScope.get("Exception::message")?.value ?: ObjNull
|
is ObjException -> t.message
|
||||||
else -> ObjNull
|
is ObjInstance -> t.instanceScope.get("Exception::message")?.value ?: ObjNull
|
||||||
|
else -> ObjNull
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -192,10 +200,12 @@ open class ObjException(
|
|||||||
doc = "Extra data associated with the exception.",
|
doc = "Extra data associated with the exception.",
|
||||||
type = type("lyng.Any", nullable = true),
|
type = type("lyng.Any", nullable = true),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
when (val t = this.thisObj) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
is ObjException -> t.extraData
|
return when (val t = scp.thisObj) {
|
||||||
else -> ObjNull
|
is ObjException -> t.extraData
|
||||||
|
else -> ObjNull
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -204,11 +214,13 @@ open class ObjException(
|
|||||||
doc = "Stack trace captured at throw site as a list of `StackTraceEntry`.",
|
doc = "Stack trace captured at throw site as a list of `StackTraceEntry`.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.StackTraceEntry"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.StackTraceEntry"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
when (val t = this.thisObj) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
is ObjException -> t.getStackTrace()
|
return when (val t = scp.thisObj) {
|
||||||
is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList()
|
is ObjException -> t.getStackTrace()
|
||||||
else -> ObjList()
|
is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList()
|
||||||
|
else -> ObjList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -216,23 +228,26 @@ open class ObjException(
|
|||||||
name = "toString",
|
name = "toString",
|
||||||
doc = "Human‑readable string representation of the error.",
|
doc = "Human‑readable string representation of the error.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val msg = when (val t = thisObj) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
is ObjException -> t.message.value
|
val msg = when (val t = scp.thisObj) {
|
||||||
is ObjInstance -> (t.instanceScope.get("Exception::message")?.value as? ObjString)?.value
|
is ObjException -> t.message.value
|
||||||
?: t.objClass.className
|
is ObjInstance -> (t.instanceScope.get("Exception::message")?.value as? ObjString)?.value
|
||||||
|
?: t.objClass.className
|
||||||
|
|
||||||
else -> t.objClass.className
|
else -> t.objClass.className
|
||||||
|
}
|
||||||
|
val stack = when (val t = scp.thisObj) {
|
||||||
|
is ObjException -> t.getStackTrace()
|
||||||
|
is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList()
|
||||||
|
else -> ObjList()
|
||||||
|
}
|
||||||
|
val at = stack.list.firstOrNull()?.toString(scp) ?: ObjString("(unknown)")
|
||||||
|
return ObjString("${scp.thisObj.objClass.className}: $msg at $at")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val stack = when (val t = thisObj) {
|
)
|
||||||
is ObjException -> t.getStackTrace()
|
|
||||||
is ObjInstance -> t.instanceScope.get("Exception::stackTrace")?.value as? ObjList ?: ObjList()
|
|
||||||
else -> ObjList()
|
|
||||||
}
|
|
||||||
val at = stack.list.firstOrNull()?.toString(this) ?: ObjString("(unknown)")
|
|
||||||
ObjString("${thisObj.objClass.className}: $msg at $at")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val op = ProtectedOp()
|
private val op = ProtectedOp()
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -45,28 +45,31 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
|
|||||||
doc = "Send a value to the flow consumer. Suspends if back‑pressured; no‑ops after consumer stops.",
|
doc = "Send a value to the flow consumer. Suspends if back‑pressured; no‑ops after consumer stops.",
|
||||||
params = listOf(ParamDoc("value", type("lyng.Any"))),
|
params = listOf(ParamDoc("value", type("lyng.Any"))),
|
||||||
returns = type("lyng.Void"),
|
returns = type("lyng.Void"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val data = requireOnlyArg<Obj>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
try {
|
val data = scp.requireOnlyArg<Obj>()
|
||||||
val channel = thisAs<ObjFlowBuilder>().output
|
try {
|
||||||
if (!channel.isClosedForSend)
|
val channel = scp.thisAs<ObjFlowBuilder>().output
|
||||||
channel.send(data)
|
if (!channel.isClosedForSend)
|
||||||
else
|
channel.send(data)
|
||||||
// Flow consumer is no longer collecting; signal producer to stop
|
else
|
||||||
throw ScriptFlowIsNoMoreCollected()
|
// Flow consumer is no longer collecting; signal producer to stop
|
||||||
} catch (x: Exception) {
|
throw ScriptFlowIsNoMoreCollected()
|
||||||
// Any failure to send (including closed channel) should gracefully stop the producer.
|
} catch (x: Exception) {
|
||||||
if (x is CancellationException) {
|
// Any failure to send (including closed channel) should gracefully stop the producer.
|
||||||
// Cancellation is a normal control-flow event
|
if (x is CancellationException) {
|
||||||
throw ScriptFlowIsNoMoreCollected()
|
// Cancellation is a normal control-flow event
|
||||||
} else {
|
throw ScriptFlowIsNoMoreCollected()
|
||||||
// Treat other send failures as normal flow termination as well
|
} else {
|
||||||
throw ScriptFlowIsNoMoreCollected()
|
// Treat other send failures as normal flow termination as well
|
||||||
|
throw ScriptFlowIsNoMoreCollected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjVoid
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,15 +106,21 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
|||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Create a pull‑based iterator over this flow. Each step resumes the producer as needed.",
|
doc = "Create a pull‑based iterator over this flow. Each step resumes the producer as needed.",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val objFlow = thisAs<ObjFlow>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjFlowIterator(statement {
|
val objFlow = scp.thisAs<ObjFlow>()
|
||||||
objFlow.producer.execute(
|
return ObjFlowIterator(statement(f = object : ScopeCallable {
|
||||||
ClosureScope(this, objFlow.scope)
|
override suspend fun call(scp2: Scope): Obj {
|
||||||
)
|
objFlow.producer.execute(
|
||||||
})
|
ClosureScope(scp2, objFlow.scope)
|
||||||
}
|
)
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,27 +173,36 @@ class ObjFlowIterator(val producer: Statement) : Obj() {
|
|||||||
name = "hasNext",
|
name = "hasNext",
|
||||||
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() }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjFlowIterator>().hasNext(scp).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.",
|
||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val x = thisAs<ObjFlowIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
x.next(this)
|
val x = scp.thisAs<ObjFlowIterator>()
|
||||||
}
|
return x.next(scp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "cancelIteration",
|
name = "cancelIteration",
|
||||||
doc = "Stop iteration and cancel the underlying flow producer.",
|
doc = "Stop iteration and cancel the underlying flow producer.",
|
||||||
returns = type("lyng.Void"),
|
returns = type("lyng.Void"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val x = thisAs<ObjFlowIterator>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
x.cancel()
|
val x = scp.thisAs<ObjFlowIterator>()
|
||||||
ObjVoid
|
x.cancel()
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -218,7 +218,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
|
|
||||||
override suspend fun invokeInstanceMethod(
|
override suspend fun invokeInstanceMethod(
|
||||||
scope: Scope, name: String, args: Arguments,
|
scope: Scope, name: String, args: Arguments,
|
||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: OnNotFound?
|
||||||
): Obj {
|
): Obj {
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
||||||
} else if (rec.type == ObjRecord.Type.Fun) {
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
return rec.value.invoke(instanceScope, this, args, decl)
|
return rec.value.invokeCallable(instanceScope, this, args, decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,7 +246,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, c)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, c)
|
||||||
} else if (rec.type == ObjRecord.Type.Fun) {
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
return rec.value.invoke(instanceScope, this, args, c)
|
return rec.value.invokeCallable(instanceScope, this, args, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,7 +255,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, c)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, c)
|
||||||
} else if (rec.type == ObjRecord.Type.Fun) {
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
return rec.value.invoke(instanceScope, this, args, c)
|
return rec.value.invokeCallable(instanceScope, this, args, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,12 +271,12 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val del = instanceScope[storageName]?.delegate ?: rec.delegate
|
val del = instanceScope[storageName]?.delegate ?: rec.delegate
|
||||||
?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
|
?: scope.raiseError("Internal error: delegated member $name has no delegate (tried $storageName)")
|
||||||
|
|
||||||
// For delegated member, try 'invoke' first if it's a function-like call
|
// For delegated member, try 'invokeCallable' first if it's a function-like call
|
||||||
val allArgs = (listOf(this, ObjString(name)) + args.list).toTypedArray()
|
val allArgs = (listOf(this, ObjString(name)) + args.list).toTypedArray()
|
||||||
return del.invokeInstanceMethod(scope, "invoke", Arguments(*allArgs), onNotFoundResult = {
|
return del.invokeInstanceMethod(scope, "invokeCallable", Arguments(*allArgs), onNotFoundResult = {
|
||||||
// Fallback: property delegation (getValue then call result)
|
// Fallback: property delegation (getValue then call result)
|
||||||
val propVal = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
val propVal = del.invokeInstanceMethod(scope, "getValue", Arguments(this, ObjString(name)))
|
||||||
propVal.invoke(scope, this, args, rec.declaringClass ?: cls)
|
propVal.invokeCallable(scope, this, args, rec.declaringClass ?: cls)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
val decl = rec.declaringClass ?: cls
|
val decl = rec.declaringClass ?: cls
|
||||||
@ -285,14 +285,14 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
scope.raiseError(
|
scope.raiseError(
|
||||||
ObjIllegalAccessException(
|
ObjIllegalAccessException(
|
||||||
scope,
|
scope,
|
||||||
"can't invoke method $name (declared in ${decl.className})"
|
"can't invokeCallable method $name (declared in ${decl.className})"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (rec.type == ObjRecord.Type.Property) {
|
if (rec.type == ObjRecord.Type.Property) {
|
||||||
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
if (args.isEmpty()) return (rec.value as ObjProperty).callGetter(scope, this, decl)
|
||||||
} else if (rec.type == ObjRecord.Type.Fun) {
|
} else if (rec.type == ObjRecord.Type.Fun) {
|
||||||
return rec.value.invoke(
|
return rec.value.invokeCallable(
|
||||||
instanceScope,
|
instanceScope,
|
||||||
this,
|
this,
|
||||||
args,
|
args,
|
||||||
@ -300,7 +300,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
)
|
)
|
||||||
} else if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField || rec.type == ObjRecord.Type.Argument) {
|
} else if (rec.type == ObjRecord.Type.Field || rec.type == ObjRecord.Type.ConstructorField || rec.type == ObjRecord.Type.Argument) {
|
||||||
val resolved = readField(scope, name)
|
val resolved = readField(scope, name)
|
||||||
return resolved.value.invoke(scope, this, args, resolved.declaringClass)
|
return resolved.value.invokeCallable(scope, this, args, resolved.declaringClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,18 +507,18 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
scope: Scope,
|
scope: Scope,
|
||||||
name: String,
|
name: String,
|
||||||
args: Arguments,
|
args: Arguments,
|
||||||
onNotFoundResult: (suspend () -> Obj?)?
|
onNotFoundResult: OnNotFound?
|
||||||
): Obj {
|
): Obj {
|
||||||
// Qualified method dispatch must start from the specified ancestor, not from the instance scope.
|
// Qualified method dispatch must start from the specified ancestor, not from the instance scope.
|
||||||
memberFromAncestor(name)?.let { rec ->
|
memberFromAncestor(name)?.let { rec ->
|
||||||
val decl = rec.declaringClass ?: startClass
|
val decl = rec.declaringClass ?: startClass
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(rec.visibility, decl, caller, name))
|
if (!canAccessMember(rec.visibility, decl, caller, name))
|
||||||
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke method $name (declared in ${decl.className})"))
|
scope.raiseError(ObjIllegalAccessException(scope, "can't invokeCallable method $name (declared in ${decl.className})"))
|
||||||
val saved = instance.instanceScope.currentClassCtx
|
val saved = instance.instanceScope.currentClassCtx
|
||||||
instance.instanceScope.currentClassCtx = decl
|
instance.instanceScope.currentClassCtx = decl
|
||||||
try {
|
try {
|
||||||
return rec.value.invoke(instance.instanceScope, instance, args)
|
return rec.value.invokeCallable(instance.instanceScope, instance, args)
|
||||||
} finally {
|
} finally {
|
||||||
instance.instanceScope.currentClassCtx = saved
|
instance.instanceScope.currentClassCtx = saved
|
||||||
}
|
}
|
||||||
@ -532,19 +532,19 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
scope.raiseError(
|
scope.raiseError(
|
||||||
ObjIllegalAccessException(
|
ObjIllegalAccessException(
|
||||||
scope,
|
scope,
|
||||||
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
"can't invokeCallable method $name (declared in ${decl?.className ?: "?"})"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val saved = instance.instanceScope.currentClassCtx
|
val saved = instance.instanceScope.currentClassCtx
|
||||||
instance.instanceScope.currentClassCtx = decl
|
instance.instanceScope.currentClassCtx = decl
|
||||||
try {
|
try {
|
||||||
return rec.value.invoke(instance.instanceScope, instance, args)
|
return rec.value.invokeCallable(instance.instanceScope, instance, args)
|
||||||
} finally {
|
} finally {
|
||||||
instance.instanceScope.currentClassCtx = saved
|
instance.instanceScope.currentClassCtx = saved
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return onNotFoundResult?.invoke() ?: scope.raiseSymbolNotFound(name)
|
return onNotFoundResult?.call() ?: scope.raiseSymbolNotFound(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = instance.toString()
|
override fun toString(): String = instance.toString()
|
||||||
|
|||||||
@ -18,17 +18,16 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import kotlinx.datetime.*
|
import kotlinx.datetime.*
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.*
|
||||||
import net.sergeych.lyng.miniast.type
|
|
||||||
import net.sergeych.lynon.LynonDecoder
|
import net.sergeych.lynon.LynonDecoder
|
||||||
import net.sergeych.lynon.LynonEncoder
|
import net.sergeych.lynon.LynonEncoder
|
||||||
import net.sergeych.lynon.LynonSettings
|
import net.sergeych.lynon.LynonSettings
|
||||||
import net.sergeych.lynon.LynonType
|
import net.sergeych.lynon.LynonType
|
||||||
import kotlin.time.Clock
|
|
||||||
import kotlin.time.isDistantFuture
|
import kotlin.time.isDistantFuture
|
||||||
import kotlin.time.isDistantPast
|
import kotlin.time.isDistantPast
|
||||||
|
|
||||||
@ -113,7 +112,7 @@ class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTru
|
|||||||
return ObjInstant(
|
return ObjInstant(
|
||||||
when (a0) {
|
when (a0) {
|
||||||
null -> {
|
null -> {
|
||||||
val t = Clock.System.now()
|
val t = kotlin.time.Clock.System.now()
|
||||||
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond)
|
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond)
|
||||||
}
|
}
|
||||||
is ObjInt -> Instant.fromEpochSeconds(a0.value)
|
is ObjInt -> Instant.fromEpochSeconds(a0.value)
|
||||||
@ -157,9 +156,11 @@ class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTru
|
|||||||
doc = "Return the number of seconds since the Unix epoch as a real number (including fractions).",
|
doc = "Return the number of seconds since the Unix epoch as a real number (including fractions).",
|
||||||
type = type("lyng.Real"),
|
type = type("lyng.Real"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val instant = thisAs<ObjInstant>().instant
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjReal(instant.epochSeconds + instant.nanosecondsOfSecond * 1e-9)
|
val instant = scp.thisAs<ObjInstant>().instant
|
||||||
|
return ObjReal(instant.epochSeconds + instant.nanosecondsOfSecond * 1e-9)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
@ -167,92 +168,114 @@ class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTru
|
|||||||
doc = "Whether this instant represents the distant future.",
|
doc = "Whether this instant represents the distant future.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjInstant>().instant.isDistantFuture.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjInstant>().instant.isDistantFuture.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isDistantPast",
|
name = "isDistantPast",
|
||||||
doc = "Whether this instant represents the distant past.",
|
doc = "Whether this instant represents the distant past.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { thisAs<ObjInstant>().instant.isDistantPast.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjInstant>().instant.isDistantPast.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "epochWholeSeconds",
|
name = "epochWholeSeconds",
|
||||||
doc = "Return the number of full seconds since the Unix epoch.",
|
doc = "Return the number of full seconds since the Unix epoch.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjInt(thisAs<ObjInstant>().instant.epochSeconds) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjInt(scp.thisAs<ObjInstant>().instant.epochSeconds)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "nanosecondsOfSecond",
|
name = "nanosecondsOfSecond",
|
||||||
doc = "The number of nanoseconds within the current second.",
|
doc = "The number of nanoseconds within the current second.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.time",
|
moduleName = "lyng.time",
|
||||||
getter = { ObjInt(thisAs<ObjInstant>().instant.nanosecondsOfSecond.toLong()) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjInt(scp.thisAs<ObjInstant>().instant.nanosecondsOfSecond.toLong())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "truncateToMinute",
|
name = "truncateToMinute",
|
||||||
doc = "Truncate this instant to the nearest minute.",
|
doc = "Truncate this instant to the nearest minute.",
|
||||||
returns = type("lyng.Instant"),
|
returns = type("lyng.Instant"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val t = thisAs<ObjInstant>().instant
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val tz = TimeZone.UTC
|
val t = scp.thisAs<ObjInstant>().instant
|
||||||
val dt = t.toLocalDateTime(tz)
|
val tz = TimeZone.UTC
|
||||||
val truncated = LocalDateTime(dt.year, dt.month, dt.dayOfMonth, dt.hour, dt.minute, 0, 0)
|
val dt = t.toLocalDateTime(tz)
|
||||||
ObjInstant(truncated.toInstant(tz), LynonSettings.InstantTruncateMode.Second)
|
val truncated = LocalDateTime(dt.year, dt.month, dt.dayOfMonth, dt.hour, dt.minute, 0, 0)
|
||||||
}
|
return ObjInstant(truncated.toInstant(tz), LynonSettings.InstantTruncateMode.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "truncateToSecond",
|
name = "truncateToSecond",
|
||||||
doc = "Truncate this instant to the nearest second.",
|
doc = "Truncate this instant to the nearest second.",
|
||||||
returns = type("lyng.Instant"),
|
returns = type("lyng.Instant"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val t = thisAs<ObjInstant>().instant
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjInstant(Instant.fromEpochSeconds(t.epochSeconds), LynonSettings.InstantTruncateMode.Second)
|
val t = scp.thisAs<ObjInstant>().instant
|
||||||
}
|
return ObjInstant(Instant.fromEpochSeconds(t.epochSeconds), LynonSettings.InstantTruncateMode.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "truncateToMillisecond",
|
name = "truncateToMillisecond",
|
||||||
doc = "Truncate this instant to the nearest millisecond.",
|
doc = "Truncate this instant to the nearest millisecond.",
|
||||||
returns = type("lyng.Instant"),
|
returns = type("lyng.Instant"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val t = thisAs<ObjInstant>().instant
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjInstant(
|
val t = scp.thisAs<ObjInstant>().instant
|
||||||
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond / 1_000_000 * 1_000_000),
|
return ObjInstant(
|
||||||
LynonSettings.InstantTruncateMode.Millisecond
|
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond / 1_000_000 * 1_000_000),
|
||||||
)
|
LynonSettings.InstantTruncateMode.Millisecond
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "truncateToMicrosecond",
|
name = "truncateToMicrosecond",
|
||||||
doc = "Truncate this instant to the nearest microsecond.",
|
doc = "Truncate this instant to the nearest microsecond.",
|
||||||
returns = type("lyng.Instant"),
|
returns = type("lyng.Instant"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val t = thisAs<ObjInstant>().instant
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjInstant(
|
val t = scp.thisAs<ObjInstant>().instant
|
||||||
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond / 1_000 * 1_000),
|
return ObjInstant(
|
||||||
LynonSettings.InstantTruncateMode.Microsecond
|
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond / 1_000 * 1_000),
|
||||||
)
|
LynonSettings.InstantTruncateMode.Microsecond
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toRFC3339",
|
name = "toRFC3339",
|
||||||
doc = "Return the RFC3339 string representation of this instant in UTC (e.g., '1970-01-01T00:00:00Z').",
|
doc = "Return the RFC3339 string representation of this instant in UTC (e.g., '1970-01-01T00:00:00Z').",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjInstant>().instant.toString().toObj()
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjInstant>().instant.toString().toObj()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toSortableString",
|
name = "toSortableString",
|
||||||
doc = "Alias to toRFC3339.",
|
doc = "Alias to toRFC3339.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjInstant>().instant.toString().toObj()
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjInstant>().instant.toString().toObj()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toDateTime",
|
name = "toDateTime",
|
||||||
@ -261,24 +284,27 @@ class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTru
|
|||||||
"If no argument is provided, the system's current default time zone is used.",
|
"If no argument is provided, the system's current default time zone is used.",
|
||||||
params = listOf(net.sergeych.lyng.miniast.ParamDoc("tz", type = type("lyng.Any", true))),
|
params = listOf(net.sergeych.lyng.miniast.ParamDoc("tz", type = type("lyng.Any", true))),
|
||||||
returns = type("lyng.DateTime"),
|
returns = type("lyng.DateTime"),
|
||||||
moduleName = "lyng.time"
|
moduleName = "lyng.time",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val tz = when (val a = args.list.getOrNull(0)) {
|
override suspend fun call(scp: Scope): Obj {
|
||||||
null -> TimeZone.currentSystemDefault()
|
val tz = when (val a = scp.args.list.getOrNull(0)) {
|
||||||
is ObjString -> TimeZone.of(a.value)
|
null -> TimeZone.currentSystemDefault()
|
||||||
is ObjInt -> UtcOffset(seconds = a.value.toInt()).asTimeZone()
|
is ObjString -> TimeZone.of(a.value)
|
||||||
else -> raiseIllegalArgument("invalid timezone: $a")
|
is ObjInt -> UtcOffset(seconds = a.value.toInt()).asTimeZone()
|
||||||
|
else -> scp.raiseIllegalArgument("invalid timezone: $a")
|
||||||
|
}
|
||||||
|
return ObjDateTime(scp.thisAs<ObjInstant>().instant, tz)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjDateTime(thisAs<ObjInstant>().instant, tz)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// class members
|
// class members
|
||||||
|
|
||||||
addClassConst("distantFuture", distantFuture)
|
addClassConst("distantFuture", distantFuture)
|
||||||
addClassConst("distantPast", distantPast)
|
addClassConst("distantPast", distantPast)
|
||||||
addClassFn("now") {
|
addClassFn("now", code = object : ScopeCallable {
|
||||||
ObjInstant(Clock.System.now())
|
override suspend fun call(scp: Scope): Obj = ObjInstant(kotlin.time.Clock.System.now())
|
||||||
}
|
})
|
||||||
// addFn("epochMilliseconds") {
|
// addFn("epochMilliseconds") {
|
||||||
// ObjInt(instant.toEpochMilliseconds())
|
// ObjInt(instant.toEpochMilliseconds())
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lynon.LynonDecoder
|
import net.sergeych.lynon.LynonDecoder
|
||||||
import net.sergeych.lynon.LynonEncoder
|
import net.sergeych.lynon.LynonEncoder
|
||||||
@ -183,10 +184,11 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
|
|||||||
name = "toInt",
|
name = "toInt",
|
||||||
doc = "Returns this integer (identity operation).",
|
doc = "Returns this integer (identity operation).",
|
||||||
returns = net.sergeych.lyng.miniast.type("lyng.Int"),
|
returns = net.sergeych.lyng.miniast.type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisObj
|
override suspend fun call(scp: Scope): Obj = scp.thisObj
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,8 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
@ -35,13 +37,15 @@ val ObjIterable by lazy {
|
|||||||
doc = "Collect elements of this iterable into a new list.",
|
doc = "Collect elements of this iterable into a new list.",
|
||||||
type = type("lyng.List"),
|
type = type("lyng.List"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val result = mutableListOf<Obj>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val it = this.thisObj.invokeInstanceMethod(this, "iterator")
|
val result = mutableListOf<Obj>()
|
||||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
val it = scp.thisObj.invokeInstanceMethod(scp, "iterator")
|
||||||
result.add(it.invokeInstanceMethod(this, "next"))
|
while (it.invokeInstanceMethod(scp, "hasNext").toBool()) {
|
||||||
|
result.add(it.invokeInstanceMethod(scp, "next"))
|
||||||
|
}
|
||||||
|
return ObjList(result)
|
||||||
}
|
}
|
||||||
ObjList(result)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,16 +56,19 @@ val ObjIterable by lazy {
|
|||||||
params = listOf(ParamDoc("element")),
|
params = listOf(ParamDoc("element")),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val obj = args.firstAndOnly()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
val obj = scp.args.firstAndOnly()
|
||||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
val it = scp.thisObj.invokeInstanceMethod(scp, "iterator")
|
||||||
if (obj.equals(this, it.invokeInstanceMethod(this, "next")))
|
while (it.invokeInstanceMethod(scp, "hasNext").toBool()) {
|
||||||
return@addFnDoc ObjTrue
|
if (obj.equals(scp, it.invokeInstanceMethod(scp, "next")))
|
||||||
|
return ObjTrue
|
||||||
|
}
|
||||||
|
return ObjFalse
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjFalse
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "indexOf",
|
name = "indexOf",
|
||||||
@ -69,34 +76,39 @@ val ObjIterable by lazy {
|
|||||||
params = listOf(ParamDoc("element")),
|
params = listOf(ParamDoc("element")),
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val obj = args.firstAndOnly()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
var index = 0
|
val obj = scp.args.firstAndOnly()
|
||||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
var index = 0
|
||||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
val it = scp.thisObj.invokeInstanceMethod(scp, "iterator")
|
||||||
if (obj.equals(this, it.invokeInstanceMethod(this, "next")))
|
while (it.invokeInstanceMethod(scp, "hasNext").toBool()) {
|
||||||
return@addFnDoc ObjInt(index.toLong())
|
if (obj.equals(scp, it.invokeInstanceMethod(scp, "next")))
|
||||||
index++
|
return ObjInt(index.toLong())
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
return ObjInt(-1L)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjInt(-1L)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "toSet",
|
name = "toSet",
|
||||||
doc = "Collect elements of this iterable into a new set.",
|
doc = "Collect elements of this iterable into a new set.",
|
||||||
type = type("lyng.Set"),
|
type = type("lyng.Set"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
if( this.thisObj.isInstanceOf(ObjSet.type) )
|
override suspend fun call(scp: Scope): Obj {
|
||||||
this.thisObj
|
return if( scp.thisObj.isInstanceOf(ObjSet.type) )
|
||||||
else {
|
scp.thisObj
|
||||||
val result = mutableSetOf<Obj>()
|
else {
|
||||||
val it = this.thisObj.invokeInstanceMethod(this, "iterator")
|
val result = mutableSetOf<Obj>()
|
||||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
val it = scp.thisObj.invokeInstanceMethod(scp, "iterator")
|
||||||
result.add(it.invokeInstanceMethod(this, "next"))
|
while (it.invokeInstanceMethod(scp, "hasNext").toBool()) {
|
||||||
|
result.add(it.invokeInstanceMethod(scp, "next"))
|
||||||
|
}
|
||||||
|
ObjSet(result)
|
||||||
}
|
}
|
||||||
ObjSet(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -106,16 +118,20 @@ val ObjIterable by lazy {
|
|||||||
doc = "Collect pairs into a map using [0] as key and [1] as value for each element.",
|
doc = "Collect pairs into a map using [0] as key and [1] as value for each element.",
|
||||||
type = type("lyng.Map"),
|
type = type("lyng.Map"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val result = mutableMapOf<Obj, Obj>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
this.thisObj.enumerate(this) { pair ->
|
val result = mutableMapOf<Obj, Obj>()
|
||||||
when (pair) {
|
scp.thisObj.enumerate(scp, object : EnumerateCallback {
|
||||||
is ObjMapEntry -> result[pair.key] = pair.value
|
override suspend fun call(pair: Obj): Boolean {
|
||||||
else -> result[pair.getAt(this, 0)] = pair.getAt(this, 1)
|
when (pair) {
|
||||||
}
|
is ObjMapEntry -> result[pair.key] = pair.value
|
||||||
true
|
else -> result[pair.getAt(scp, ObjInt(0))] = pair.getAt(scp, ObjInt(1))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ObjMap(result)
|
||||||
}
|
}
|
||||||
ObjMap(result)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,31 +140,37 @@ val ObjIterable by lazy {
|
|||||||
doc = "Build a map from elements using the lambda result as key.",
|
doc = "Build a map from elements using the lambda result as key.",
|
||||||
params = listOf(ParamDoc("keySelector")),
|
params = listOf(ParamDoc("keySelector")),
|
||||||
returns = type("lyng.Map"),
|
returns = type("lyng.Map"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val association = requireOnlyArg<Statement>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val result = ObjMap()
|
val association = scp.requireOnlyArg<Statement>()
|
||||||
thisObj.toFlow(this).collect {
|
val result = ObjMap()
|
||||||
result.map[association.call(this, it)] = it
|
scp.thisObj.toFlow(scp).collect {
|
||||||
|
result.map[association.invokeCallable(scp, scp.thisObj, it)] = it
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "forEach",
|
name = "forEach",
|
||||||
doc = "Apply the lambda to each element in iteration order.",
|
doc = "Apply the lambda to each element in iteration order.",
|
||||||
params = listOf(ParamDoc("action")),
|
params = listOf(ParamDoc("action")),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val fn = requiredArg<Statement>(0)
|
val it = scp.thisObj.invokeInstanceMethod(scp, "iterator")
|
||||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
val fn = scp.requiredArg<Statement>(0)
|
||||||
val x = it.invokeInstanceMethod(this, "next")
|
while (it.invokeInstanceMethod(scp, "hasNext").toBool()) {
|
||||||
fn.execute(this.createChildScope(Arguments(listOf(x))))
|
val x = it.invokeInstanceMethod(scp, "next")
|
||||||
|
fn.execute(scp.createChildScope(Arguments(listOf(x))))
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjVoid
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "map",
|
name = "map",
|
||||||
@ -156,15 +178,18 @@ val ObjIterable by lazy {
|
|||||||
params = listOf(ParamDoc("transform")),
|
params = listOf(ParamDoc("transform")),
|
||||||
returns = type("lyng.List"),
|
returns = type("lyng.List"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val fn = requiredArg<Statement>(0)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val result = mutableListOf<Obj>()
|
val fn = scp.requiredArg<Statement>(0)
|
||||||
thisObj.toFlow(this).collect {
|
val result = mutableListOf<Obj>()
|
||||||
result.add(fn.call(this, it))
|
scp.thisObj.toFlow(scp).collect {
|
||||||
|
result.add(fn.invokeCallable(scp, scp.thisObj, it))
|
||||||
|
}
|
||||||
|
return ObjList(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjList(result)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "mapNotNull",
|
name = "mapNotNull",
|
||||||
@ -172,46 +197,56 @@ val ObjIterable by lazy {
|
|||||||
params = listOf(ParamDoc("transform")),
|
params = listOf(ParamDoc("transform")),
|
||||||
returns = type("lyng.List"),
|
returns = type("lyng.List"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val fn = requiredArg<Statement>(0)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val result = mutableListOf<Obj>()
|
val fn = scp.requiredArg<Statement>(0)
|
||||||
thisObj.toFlow(this).collect {
|
val result = mutableListOf<Obj>()
|
||||||
val transformed = fn.call(this, it)
|
scp.thisObj.toFlow(scp).collect {
|
||||||
if( transformed != ObjNull) result.add(transformed)
|
val transformed = fn.invokeCallable(scp, scp.thisObj, it)
|
||||||
|
if( transformed != ObjNull) result.add(transformed)
|
||||||
|
}
|
||||||
|
return ObjList(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjList(result)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "take",
|
name = "take",
|
||||||
doc = "Take the first N elements and return them as a list.",
|
doc = "Take the first N elements and return them as a list.",
|
||||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||||
returns = type("lyng.List"),
|
returns = type("lyng.List"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
var n = requireOnlyArg<ObjInt>().value.toInt()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val result = mutableListOf<Obj>()
|
var n = scp.requireOnlyArg<ObjInt>().value.toInt()
|
||||||
if (n > 0) {
|
val result = mutableListOf<Obj>()
|
||||||
thisObj.enumerate(this) {
|
if (n > 0) {
|
||||||
result.add(it)
|
scp.thisObj.enumerate(scp, object : EnumerateCallback {
|
||||||
--n > 0
|
override suspend fun call(element: Obj): Boolean {
|
||||||
|
result.add(element)
|
||||||
|
return --n > 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ObjList(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjList(result)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isEmpty",
|
name = "isEmpty",
|
||||||
doc = "Whether the iterable has no elements.",
|
doc = "Whether the iterable has no elements.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
ObjBool(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
this.thisObj.invokeInstanceMethod(this, "iterator")
|
return ObjBool(
|
||||||
.invokeInstanceMethod(this, "hasNext").toBool()
|
scp.thisObj.invokeInstanceMethod(scp, "iterator")
|
||||||
.not()
|
.invokeInstanceMethod(scp, "hasNext").toBool()
|
||||||
)
|
.not()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -220,25 +255,31 @@ val ObjIterable by lazy {
|
|||||||
doc = "Return a new list sorted using the provided comparator `(a, b) -> Int`.",
|
doc = "Return a new list sorted using the provided comparator `(a, b) -> Int`.",
|
||||||
params = listOf(ParamDoc("comparator")),
|
params = listOf(ParamDoc("comparator")),
|
||||||
returns = type("lyng.List"),
|
returns = type("lyng.List"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val list = thisObj.callMethod<ObjList>(this, "toList")
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val comparator = requireOnlyArg<Statement>()
|
val list = scp.thisObj.callMethod<ObjList>(scp, "toList")
|
||||||
list.quicksort { a, b ->
|
val comparator = scp.requireOnlyArg<Statement>()
|
||||||
comparator.call(this, a, b).toInt()
|
list.quicksort { a, b ->
|
||||||
|
comparator.invokeCallable(scp, scp.thisObj, a, b).toInt()
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
list
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "reversed",
|
name = "reversed",
|
||||||
doc = "Return a new list with elements in reverse order.",
|
doc = "Return a new list with elements in reverse order.",
|
||||||
returns = type("lyng.List"),
|
returns = type("lyng.List"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val list = thisObj.callMethod<ObjList>(this, "toList")
|
override suspend fun call(scp: Scope): Obj {
|
||||||
list.list.reverse()
|
val list = scp.thisObj.callMethod<ObjList>(scp, "toList")
|
||||||
list
|
list.list.reverse()
|
||||||
}
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
@ -38,44 +40,50 @@ val ObjIterator by lazy {
|
|||||||
doc = "Optional hint to stop iteration early and free resources.",
|
doc = "Optional hint to stop iteration early and free resources.",
|
||||||
returns = type("lyng.Void"),
|
returns = type("lyng.Void"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjVoid
|
override suspend fun call(scp: Scope): Obj = ObjVoid
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "hasNext",
|
name = "hasNext",
|
||||||
doc = "Whether another element is available.",
|
doc = "Whether another element is available.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
raiseNotImplemented("hasNext() is not implemented")
|
override suspend fun call(scp: Scope): Obj = scp.raiseNotImplemented("hasNext() is not implemented")
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "next",
|
name = "next",
|
||||||
doc = "Return the next element.",
|
doc = "Return the next element.",
|
||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
isOpen = true,
|
isOpen = true,
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
raiseNotImplemented("next() is not implemented")
|
override suspend fun call(scp: Scope): Obj = scp.raiseNotImplemented("next() is not implemented")
|
||||||
}
|
}
|
||||||
|
)
|
||||||
// Helper to consume iterator into a list
|
// Helper to consume iterator into a list
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toList",
|
name = "toList",
|
||||||
doc = "Consume this iterator and collect elements into a list.",
|
doc = "Consume this iterator and collect elements into a list.",
|
||||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val out = mutableListOf<Obj>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
while (true) {
|
val out = mutableListOf<Obj>()
|
||||||
val has = thisObj.invokeInstanceMethod(this, "hasNext").toBool()
|
while (true) {
|
||||||
if (!has) break
|
val has = scp.thisObj.invokeInstanceMethod(scp, "hasNext").toBool()
|
||||||
val v = thisObj.invokeInstanceMethod(this, "next")
|
if (!has) break
|
||||||
out += v
|
val v = scp.thisObj.invokeInstanceMethod(scp, "next")
|
||||||
|
out += v
|
||||||
|
}
|
||||||
|
return ObjList(out.toMutableList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ObjList(out.toMutableList())
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -22,6 +22,7 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterator wrapper to allow Kotlin collections to be returned from Lyng objects;
|
* Iterator wrapper to allow Kotlin collections to be returned from Lyng objects;
|
||||||
@ -33,8 +34,12 @@ class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("KotlinIterator", ObjIterator).apply {
|
val type = ObjClass("KotlinIterator", ObjIterator).apply {
|
||||||
addFn("next") { thisAs<ObjKotlinIterator>().iterator.next().toObj() }
|
addFn("next", code = object : ScopeCallable {
|
||||||
addFn("hasNext") { thisAs<ObjKotlinIterator>().iterator.hasNext().toObj() }
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjKotlinIterator>().iterator.next().toObj()
|
||||||
|
})
|
||||||
|
addFn("hasNext", code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjKotlinIterator>().iterator.hasNext().toObj()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -50,12 +55,14 @@ class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("KotlinIterator", ObjIterator).apply {
|
val type = ObjClass("KotlinIterator", ObjIterator).apply {
|
||||||
addFn("next") {
|
addFn("next", code = object : ScopeCallable {
|
||||||
thisAs<ObjKotlinObjIterator>().iterator.next()
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
scp.thisAs<ObjKotlinObjIterator>().iterator.next()
|
||||||
addFn("hasNext") {
|
})
|
||||||
thisAs<ObjKotlinObjIterator>().iterator.hasNext().toObj()
|
addFn("hasNext", code = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj =
|
||||||
|
scp.thisAs<ObjKotlinObjIterator>().iterator.hasNext().toObj()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -71,8 +78,8 @@ fun Obj.toFlow(scope: Scope): Flow<Obj> = flow {
|
|||||||
val iterator = invokeInstanceMethod(scope, "iterator")
|
val iterator = invokeInstanceMethod(scope, "iterator")
|
||||||
val hasNext = iterator.getInstanceMethod(scope, "hasNext")
|
val hasNext = iterator.getInstanceMethod(scope, "hasNext")
|
||||||
val next = iterator.getInstanceMethod(scope, "next")
|
val next = iterator.getInstanceMethod(scope, "next")
|
||||||
while (hasNext.invoke(scope, iterator).toBool()) {
|
while (hasNext.invokeCallable(scope, iterator).toBool()) {
|
||||||
emit(next.invoke(scope, iterator))
|
emit(next.invokeCallable(scope, iterator))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
@ -111,13 +112,13 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
val next2 = it2.getInstanceMethod(scope, "next")
|
val next2 = it2.getInstanceMethod(scope, "next")
|
||||||
|
|
||||||
while (it1.hasNext()) {
|
while (it1.hasNext()) {
|
||||||
if (!hasNext2.invoke(scope, it2).toBool()) return 1 // I'm longer
|
if (!hasNext2.invokeCallable(scope, it2).toBool()) return 1 // I'm longer
|
||||||
val v1 = it1.next()
|
val v1 = it1.next()
|
||||||
val v2 = next2.invoke(scope, it2)
|
val v2 = next2.invokeCallable(scope, it2)
|
||||||
val d = v1.compareTo(scope, v2)
|
val d = v1.compareTo(scope, v2)
|
||||||
if (d != 0) return d
|
if (d != 0) return d
|
||||||
}
|
}
|
||||||
return if (hasNext2.invoke(scope, it2).toBool()) -1 else 0
|
return if (hasNext2.invokeCallable(scope, it2).toBool()) -1 else 0
|
||||||
}
|
}
|
||||||
return -2
|
return -2
|
||||||
}
|
}
|
||||||
@ -169,9 +170,9 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
return list.contains(other)
|
return list.contains(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
|
override suspend fun enumerate(scope: Scope, callback: EnumerateCallback) {
|
||||||
for (item in list) {
|
for (item in list) {
|
||||||
if (!callback(item)) break
|
if (!callback.call(item)) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +180,9 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
get() = type
|
get() = type
|
||||||
|
|
||||||
override suspend fun toKotlin(scope: Scope): Any {
|
override suspend fun toKotlin(scope: Scope): Any {
|
||||||
return list.map { it.toKotlin(scope) }
|
val res = ArrayList<Any?>(list.size)
|
||||||
|
for (i in list) res.add(i.toKotlin(scope))
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun quicksort(compare: suspend (Obj, Obj) -> Int) = quicksort(compare, 0, list.size - 1)
|
suspend fun quicksort(compare: suspend (Obj, Obj) -> Int) = quicksort(compare, 0, list.size - 1)
|
||||||
@ -230,7 +233,9 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
override suspend fun lynonType(): LynonType = LynonType.List
|
override suspend fun lynonType(): LynonType = LynonType.List
|
||||||
|
|
||||||
override suspend fun toJson(scope: Scope): JsonElement {
|
override suspend fun toJson(scope: Scope): JsonElement {
|
||||||
return JsonArray(list.map { it.toJson(scope) })
|
val res = ArrayList<JsonElement>(list.size)
|
||||||
|
for (i in list) res.add(i.toJson(scope))
|
||||||
|
return JsonArray(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun defaultToString(scope: Scope): ObjString {
|
override suspend fun defaultToString(scope: Scope): ObjString {
|
||||||
@ -256,256 +261,290 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
doc = "Number of elements in this list.",
|
doc = "Number of elements in this list.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val s = (this.thisObj as ObjList).list.size
|
override suspend fun call(scp: Scope): Obj {
|
||||||
s.toObj()
|
return (scp.thisObj as ObjList).list.size.toObj()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "add",
|
name = "add",
|
||||||
doc = "Append one or more elements to the end of this list.",
|
doc = "Append one or more elements to the end of this list.",
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val l = thisAs<ObjList>().list
|
override suspend fun call(scp: Scope): Obj {
|
||||||
for (a in args) l.add(a)
|
val l = scp.thisAs<ObjList>().list
|
||||||
ObjVoid
|
for (a in scp.args) l.add(a)
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "insertAt",
|
name = "insertAt",
|
||||||
doc = "Insert elements starting at the given index.",
|
doc = "Insert elements starting at the given index.",
|
||||||
params = listOf(ParamDoc("index", type("lyng.Int"))),
|
params = listOf(ParamDoc("index", type("lyng.Int"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
if (args.size < 2) raiseError("addAt takes 2+ arguments")
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val l = thisAs<ObjList>()
|
if (scp.args.size < 2) scp.raiseError("addAt takes 2+ arguments")
|
||||||
var index = requiredArg<ObjInt>(0).value.toInt()
|
val l = scp.thisAs<ObjList>()
|
||||||
for (i in 1..<args.size) l.list.add(index++, args[i])
|
var index = scp.requiredArg<ObjInt>(0).value.toInt()
|
||||||
ObjVoid
|
for (i in 1..<scp.args.size) l.list.add(index++, scp.args[i])
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "removeAt",
|
name = "removeAt",
|
||||||
doc = "Remove element at index, or a range [start,end) if two indices are provided. Returns the list.",
|
doc = "Remove element at index, or a range [start,end) if two indices are provided. Returns the list.",
|
||||||
params = listOf(ParamDoc("start", type("lyng.Int")), ParamDoc("end", type("lyng.Int"))),
|
params = listOf(ParamDoc("start", type("lyng.Int")), ParamDoc("end", type("lyng.Int"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjList>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val start = requiredArg<ObjInt>(0).value.toInt()
|
val self = scp.thisAs<ObjList>()
|
||||||
if (args.size == 2) {
|
val start = scp.requiredArg<ObjInt>(0).value.toInt()
|
||||||
val end = requireOnlyArg<ObjInt>().value.toInt()
|
if (scp.args.size == 2) {
|
||||||
self.list.subList(start, end).clear()
|
val end = scp.requireOnlyArg<ObjInt>().value.toInt()
|
||||||
} else
|
self.list.subList(start, end).clear()
|
||||||
self.list.removeAt(start)
|
} else
|
||||||
self
|
self.list.removeAt(start)
|
||||||
}
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "removeLast",
|
name = "removeLast",
|
||||||
doc = "Remove the last element or the last N elements if a count is provided. Returns the list.",
|
doc = "Remove the last element or the last N elements if a count is provided. Returns the list.",
|
||||||
params = listOf(ParamDoc("count", type("lyng.Int"))),
|
params = listOf(ParamDoc("count", type("lyng.Int"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjList>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (args.isNotEmpty()) {
|
val self = scp.thisAs<ObjList>()
|
||||||
val count = requireOnlyArg<ObjInt>().value.toInt()
|
if (scp.args.isNotEmpty()) {
|
||||||
val size = self.list.size
|
val count = scp.requireOnlyArg<ObjInt>().value.toInt()
|
||||||
if (count >= size) self.list.clear()
|
val size = self.list.size
|
||||||
else self.list.subList(size - count, size).clear()
|
if (count >= size) self.list.clear()
|
||||||
} else self.list.removeLast()
|
else self.list.subList(size - count, size).clear()
|
||||||
self
|
} else self.list.removeLast()
|
||||||
}
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "removeRange",
|
name = "removeRange",
|
||||||
doc = "Remove a range of elements. Accepts a Range or (start, endInclusive). Returns the list.",
|
doc = "Remove a range of elements. Accepts a Range or (start, endInclusive). Returns the list.",
|
||||||
params = listOf(ParamDoc("range")),
|
params = listOf(ParamDoc("range")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjList>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val list = self.list
|
val self = scp.thisAs<ObjList>()
|
||||||
val range = requiredArg<Obj>(0)
|
val list = self.list
|
||||||
if (range is ObjRange) {
|
val range = scp.requiredArg<Obj>(0)
|
||||||
val index = range
|
if (range is ObjRange) {
|
||||||
when {
|
val index = range
|
||||||
index.start is ObjInt && index.end is ObjInt -> {
|
when {
|
||||||
if (index.isEndInclusive)
|
index.start is ObjInt && index.end is ObjInt -> {
|
||||||
list.subList(index.start.toInt(), index.end.toInt() + 1)
|
if (index.isEndInclusive)
|
||||||
else
|
list.subList(index.start.toInt(), index.end.toInt() + 1)
|
||||||
list.subList(index.start.toInt(), index.end.toInt())
|
else
|
||||||
}
|
list.subList(index.start.toInt(), index.end.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
index.isOpenStart && !index.isOpenEnd -> {
|
index.isOpenStart && !index.isOpenEnd -> {
|
||||||
if (index.isEndInclusive)
|
if (index.isEndInclusive)
|
||||||
list.subList(0, index.end!!.toInt() + 1)
|
list.subList(0, index.end!!.toInt() + 1)
|
||||||
else
|
else
|
||||||
list.subList(0, index.end!!.toInt())
|
list.subList(0, index.end!!.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
index.isOpenEnd && !index.isOpenStart -> {
|
index.isOpenEnd && !index.isOpenStart -> {
|
||||||
list.subList(index.start!!.toInt(), list.size)
|
list.subList(index.start!!.toInt(), list.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
index.isOpenStart && index.isOpenEnd -> {
|
index.isOpenStart && index.isOpenEnd -> {
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
throw RuntimeException("Can't apply range for index: $index")
|
throw RuntimeException("Can't apply range for index: $index")
|
||||||
|
}
|
||||||
|
}.clear()
|
||||||
|
} else {
|
||||||
|
val start = range.toInt()
|
||||||
|
val end = scp.requiredArg<ObjInt>(1).value.toInt() + 1
|
||||||
|
self.list.subList(start, end).clear()
|
||||||
}
|
}
|
||||||
}.clear()
|
return self
|
||||||
} else {
|
}
|
||||||
val start = range.toInt()
|
|
||||||
val end = requiredArg<ObjInt>(1).value.toInt() + 1
|
|
||||||
self.list.subList(start, end).clear()
|
|
||||||
}
|
}
|
||||||
self
|
)
|
||||||
}
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "sortWith",
|
name = "sortWith",
|
||||||
doc = "Sort this list in-place using a comparator function (a, b) -> Int.",
|
doc = "Sort this list in-place using a comparator function (a, b) -> Int.",
|
||||||
params = listOf(ParamDoc("comparator")),
|
params = listOf(ParamDoc("comparator")),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val comparator = requireOnlyArg<Statement>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisAs<ObjList>().quicksort { a, b -> comparator.call(this, a, b).toInt() }
|
val comparator = scp.requireOnlyArg<Statement>()
|
||||||
ObjVoid
|
scp.thisAs<ObjList>().quicksort { a, b -> comparator.invokeCallable(scp, scp.thisObj, a, b).toInt() }
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "shuffle",
|
name = "shuffle",
|
||||||
doc = "Shuffle elements of this list in-place.",
|
doc = "Shuffle elements of this list in-place.",
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjList>().list.shuffle()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjVoid
|
scp.thisAs<ObjList>().list.shuffle()
|
||||||
}
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "sum",
|
name = "sum",
|
||||||
doc = "Sum elements using dynamic '+' or optimized integer path. Returns null for empty lists.",
|
doc = "Sum elements using dynamic '+' or optimized integer path. Returns null for empty lists.",
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjList>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val l = self.list
|
val self = scp.thisAs<ObjList>()
|
||||||
if (l.isEmpty()) return@addFnDoc ObjNull
|
val l = self.list
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
if (l.isEmpty()) return ObjNull
|
||||||
// Fast path: all ints → accumulate as long
|
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
var i = 0
|
// Fast path: all ints → accumulate as long
|
||||||
var acc: Long = 0
|
var i = 0
|
||||||
while (i < l.size) {
|
var acc: Long = 0
|
||||||
val v = l[i]
|
|
||||||
if (v is ObjInt) {
|
|
||||||
acc += v.value
|
|
||||||
i++
|
|
||||||
} else {
|
|
||||||
// Fallback to generic dynamic '+' accumulation starting from current acc
|
|
||||||
var res: Obj = ObjInt(acc)
|
|
||||||
while (i < l.size) {
|
while (i < l.size) {
|
||||||
res = res.plus(this, l[i])
|
val v = l[i]
|
||||||
i++
|
if (v is ObjInt) {
|
||||||
|
acc += v.value
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
// Fallback to generic dynamic '+' accumulation starting from current acc
|
||||||
|
var res: Obj = ObjInt(acc)
|
||||||
|
while (i < l.size) {
|
||||||
|
res = res.plus(scp, l[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return@addFnDoc res
|
return ObjInt(acc)
|
||||||
}
|
}
|
||||||
|
// Generic path: dynamic '+' starting from first element
|
||||||
|
var res: Obj = l[0]
|
||||||
|
var k = 1
|
||||||
|
while (k < l.size) {
|
||||||
|
res = res.plus(scp, l[k])
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
return@addFnDoc ObjInt(acc)
|
|
||||||
}
|
}
|
||||||
// Generic path: dynamic '+' starting from first element
|
)
|
||||||
var res: Obj = l[0]
|
|
||||||
var k = 1
|
|
||||||
while (k < l.size) {
|
|
||||||
res = res.plus(this, l[k])
|
|
||||||
k++
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "min",
|
name = "min",
|
||||||
doc = "Minimum element by natural order. Returns null for empty lists.",
|
doc = "Minimum element by natural order. Returns null for empty lists.",
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val l = thisAs<ObjList>().list
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (l.isEmpty()) return@addFnDoc ObjNull
|
val l = scp.thisAs<ObjList>().list
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
if (l.isEmpty()) return ObjNull
|
||||||
var i = 0
|
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
var hasOnlyInts = true
|
var i = 0
|
||||||
var minVal: Long = Long.MAX_VALUE
|
var hasOnlyInts = true
|
||||||
while (i < l.size) {
|
var minVal: Long = Long.MAX_VALUE
|
||||||
val v = l[i]
|
while (i < l.size) {
|
||||||
if (v is ObjInt) {
|
val v = l[i]
|
||||||
if (v.value < minVal) minVal = v.value
|
if (v is ObjInt) {
|
||||||
} else {
|
if (v.value < minVal) minVal = v.value
|
||||||
hasOnlyInts = false
|
} else {
|
||||||
break
|
hasOnlyInts = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if (hasOnlyInts) return ObjInt(minVal)
|
||||||
}
|
}
|
||||||
i++
|
var res: Obj = l[0]
|
||||||
|
var i = 1
|
||||||
|
while (i < l.size) {
|
||||||
|
val v = l[i]
|
||||||
|
if (v.compareTo(scp, res) < 0) res = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
if (hasOnlyInts) return@addFnDoc ObjInt(minVal)
|
|
||||||
}
|
}
|
||||||
var res: Obj = l[0]
|
)
|
||||||
var i = 1
|
|
||||||
while (i < l.size) {
|
|
||||||
val v = l[i]
|
|
||||||
if (v.compareTo(this, res) < 0) res = v
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "max",
|
name = "max",
|
||||||
doc = "Maximum element by natural order. Returns null for empty lists.",
|
doc = "Maximum element by natural order. Returns null for empty lists.",
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val l = thisAs<ObjList>().list
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (l.isEmpty()) return@addFnDoc ObjNull
|
val l = scp.thisAs<ObjList>().list
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
if (l.isEmpty()) return ObjNull
|
||||||
var i = 0
|
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
var hasOnlyInts = true
|
var i = 0
|
||||||
var maxVal: Long = Long.MIN_VALUE
|
var hasOnlyInts = true
|
||||||
while (i < l.size) {
|
var maxVal: Long = Long.MIN_VALUE
|
||||||
val v = l[i]
|
while (i < l.size) {
|
||||||
if (v is ObjInt) {
|
val v = l[i]
|
||||||
if (v.value > maxVal) maxVal = v.value
|
if (v is ObjInt) {
|
||||||
} else {
|
if (v.value > maxVal) maxVal = v.value
|
||||||
hasOnlyInts = false
|
} else {
|
||||||
break
|
hasOnlyInts = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if (hasOnlyInts) return ObjInt(maxVal)
|
||||||
}
|
}
|
||||||
i++
|
var res: Obj = l[0]
|
||||||
|
var i = 1
|
||||||
|
while (i < l.size) {
|
||||||
|
val v = l[i]
|
||||||
|
if (v.compareTo(scp, res) > 0) res = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
if (hasOnlyInts) return@addFnDoc ObjInt(maxVal)
|
|
||||||
}
|
}
|
||||||
var res: Obj = l[0]
|
)
|
||||||
var i = 1
|
|
||||||
while (i < l.size) {
|
|
||||||
val v = l[i]
|
|
||||||
if (v.compareTo(this, res) > 0) res = v
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "indexOf",
|
name = "indexOf",
|
||||||
doc = "Index of the first occurrence of the given element, or -1 if not found.",
|
doc = "Index of the first occurrence of the given element, or -1 if not found.",
|
||||||
params = listOf(ParamDoc("element")),
|
params = listOf(ParamDoc("element")),
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val l = thisAs<ObjList>().list
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val needle = args.firstAndOnly()
|
val l = scp.thisAs<ObjList>().list
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) {
|
val needle = scp.args.firstAndOnly()
|
||||||
var i = 0
|
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) {
|
||||||
while (i < l.size) {
|
var i = 0
|
||||||
val v = l[i]
|
while (i < l.size) {
|
||||||
if (v is ObjInt && v.value == needle.value) return@addFnDoc ObjInt(i.toLong())
|
val v = l[i]
|
||||||
i++
|
if (v is ObjInt && v.value == needle.value) return ObjInt(i.toLong())
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return ObjInt((-1).toLong())
|
||||||
|
}
|
||||||
|
var i = 0
|
||||||
|
while (i < l.size) {
|
||||||
|
if (l[i].compareTo(scp, needle) == 0) return ObjInt(i.toLong())
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return ObjInt((-1).toLong())
|
||||||
}
|
}
|
||||||
return@addFnDoc ObjInt((-1).toLong())
|
|
||||||
}
|
}
|
||||||
var i = 0
|
)
|
||||||
while (i < l.size) {
|
|
||||||
if (l[i].compareTo(this, needle) == 0) return@addFnDoc ObjInt(i.toLong())
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
ObjInt((-1).toLong())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
|
|||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
import net.sergeych.lynon.LynonDecoder
|
import net.sergeych.lynon.LynonDecoder
|
||||||
@ -78,21 +79,27 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
|||||||
doc = "Key component of this map entry.",
|
doc = "Key component of this map entry.",
|
||||||
type = type("lyng.Any"),
|
type = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjMapEntry>().key }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjMapEntry>().key
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "value",
|
name = "value",
|
||||||
doc = "Value component of this map entry.",
|
doc = "Value component of this map entry.",
|
||||||
type = type("lyng.Any"),
|
type = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjMapEntry>().value }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjMapEntry>().value
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "Number of components in this entry (always 2).",
|
doc = "Number of components in this entry (always 2).",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { 2.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = 2.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,9 +188,11 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun toJson(scope: Scope): JsonElement {
|
override suspend fun toJson(scope: Scope): JsonElement {
|
||||||
return JsonObject(
|
val res = mutableMapOf<String, JsonElement>()
|
||||||
map.map { it.key.toString(scope).value to it.value.toJson(scope) }.toMap()
|
for ((k, v) in map) {
|
||||||
)
|
res[k.toString(scope).value] = v.toJson(scope)
|
||||||
|
}
|
||||||
|
return JsonObject(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -224,94 +233,119 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
implementingNames.add("Delegate")
|
implementingNames.add("Delegate")
|
||||||
addFn("getValue") {
|
addFn("getValue", code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjMap>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val key = requiredArg<Obj>(1)
|
val self = scp.thisAs<ObjMap>()
|
||||||
self.map[key] ?: ObjNull
|
val key = scp.requiredArg<Obj>(1)
|
||||||
}
|
return self.map[key] ?: ObjNull
|
||||||
addFn("setValue") {
|
}
|
||||||
val self = thisAs<ObjMap>()
|
})
|
||||||
val key = requiredArg<Obj>(1)
|
addFn("setValue", code = object : ScopeCallable {
|
||||||
val value = requiredArg<Obj>(2)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
self.map[key] = value
|
val self = scp.thisAs<ObjMap>()
|
||||||
self
|
val key = scp.requiredArg<Obj>(1)
|
||||||
}
|
val value = scp.requiredArg<Obj>(2)
|
||||||
addFn("bind") {
|
self.map[key] = value
|
||||||
val mode = requiredArg<ObjEnumEntry>(1)
|
return self
|
||||||
if( mode.ordinal.value > 1)
|
}
|
||||||
raiseIllegalArgument("Map can be delegated only to val or var, got ${mode.name.value}")
|
})
|
||||||
thisObj
|
addFn("bind", code = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj {
|
||||||
|
val mode = scp.requiredArg<ObjEnumEntry>(1)
|
||||||
|
if (mode.ordinal.value > 1)
|
||||||
|
scp.raiseIllegalArgument("Map can be delegated only to val or var, got ${mode.name.value}")
|
||||||
|
return scp.thisObj
|
||||||
|
}
|
||||||
|
})
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "getOrNull",
|
name = "getOrNull",
|
||||||
doc = "Get value by key or return null if the key is absent.",
|
doc = "Get value by key or return null if the key is absent.",
|
||||||
params = listOf(ParamDoc("key")),
|
params = listOf(ParamDoc("key")),
|
||||||
returns = type("lyng.Any", nullable = true),
|
returns = type("lyng.Any", nullable = true),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val key = args.firstAndOnly(pos)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisAs<ObjMap>().map.getOrElse(key) { ObjNull }
|
val key = scp.args.firstAndOnly(scp.pos)
|
||||||
}
|
return scp.thisAs<ObjMap>().map.getOrElse(key) { ObjNull }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "getOrPut",
|
name = "getOrPut",
|
||||||
doc = "Get value by key or compute, store, and return the default from a lambda.",
|
doc = "Get value by key or compute, store, and return the default from a lambda.",
|
||||||
params = listOf(ParamDoc("key"), ParamDoc("default")),
|
params = listOf(ParamDoc("key"), ParamDoc("default")),
|
||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val key = requiredArg<Obj>(0)
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisAs<ObjMap>().map.getOrPut(key) {
|
val key = scp.requiredArg<Obj>(0)
|
||||||
val lambda = requiredArg<Statement>(1)
|
return scp.thisAs<ObjMap>().map.getOrPut(key) {
|
||||||
lambda.execute(this)
|
val lambda = scp.requiredArg<Statement>(1)
|
||||||
|
lambda.execute(scp)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "Number of entries in the map.",
|
doc = "Number of entries in the map.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { (this.thisObj as ObjMap).map.size.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = (scp.thisObj as ObjMap).map.size.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "remove",
|
name = "remove",
|
||||||
doc = "Remove the entry by key and return the previous value or null if absent.",
|
doc = "Remove the entry by key and return the previous value or null if absent.",
|
||||||
params = listOf(ParamDoc("key")),
|
params = listOf(ParamDoc("key")),
|
||||||
returns = type("lyng.Any", nullable = true),
|
returns = type("lyng.Any", nullable = true),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjMap>().map.remove(requiredArg<Obj>(0))?.toObj() ?: ObjNull
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
return scp.thisAs<ObjMap>().map.remove(scp.requiredArg<Obj>(0))?.toObj() ?: ObjNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "clear",
|
name = "clear",
|
||||||
doc = "Remove all entries from this map. Returns the map.",
|
doc = "Remove all entries from this map. Returns the map.",
|
||||||
returns = type("lyng.Map"),
|
returns = type("lyng.Map"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjMap>().map.clear()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisObj
|
scp.thisAs<ObjMap>().map.clear()
|
||||||
}
|
return scp.thisObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "keys",
|
name = "keys",
|
||||||
doc = "List of keys in this map.",
|
doc = "List of keys in this map.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjMap>().map.keys.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjMap>().map.keys.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "values",
|
name = "values",
|
||||||
doc = "List of values in this map.",
|
doc = "List of values in this map.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { ObjList(thisAs<ObjMap>().map.values.toMutableList()) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjList(scp.thisAs<ObjMap>().map.values.toMutableList())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Iterator over map entries as MapEntry objects.",
|
doc = "Iterator over map entries as MapEntry objects.",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.MapEntry"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.MapEntry"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjKotlinIterator(thisAs<ObjMap>().map.entries.iterator())
|
override suspend fun call(scp: Scope): Obj = ObjKotlinIterator(scp.thisAs<ObjMap>().map.entries.iterator())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -40,12 +40,12 @@ class ObjMutex(val mutex: Mutex): Obj() {
|
|||||||
params = listOf(ParamDoc("action")),
|
params = listOf(ParamDoc("action")),
|
||||||
returns = type("lyng.Any"),
|
returns = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
) {
|
) { scp ->
|
||||||
val f = requiredArg<Statement>(0)
|
val f = scp.requiredArg<Statement>(0)
|
||||||
// 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 ClosureScope
|
// ancestry across suspension points. The lambda still constructs a ClosureScope
|
||||||
// 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.execute(this) }
|
scp.thisAs<ObjMutex>().mutex.withLock { f.execute(scp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
import net.sergeych.lyng.miniast.addPropertyDoc
|
||||||
@ -115,17 +116,17 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
|
|||||||
start is ObjChar && end is ObjChar
|
start is ObjChar && end is ObjChar
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
|
override suspend fun enumerate(scope: Scope, callback: EnumerateCallback) {
|
||||||
if (start is ObjInt && end is ObjInt) {
|
if (start is ObjInt && end is ObjInt) {
|
||||||
val s = start.value
|
val s = start.value
|
||||||
val e = end.value
|
val e = end.value
|
||||||
if (isEndInclusive) {
|
if (isEndInclusive) {
|
||||||
for (i in s..e) {
|
for (i in s..e) {
|
||||||
if (!callback(ObjInt.of(i))) break
|
if (!callback.call(ObjInt.of(i))) break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i in s..<e) {
|
for (i in s..<e) {
|
||||||
if (!callback(ObjInt.of(i))) break
|
if (!callback.call(ObjInt.of(i))) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (start is ObjChar && end is ObjChar) {
|
} else if (start is ObjChar && end is ObjChar) {
|
||||||
@ -133,11 +134,11 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
|
|||||||
val e = end.value
|
val e = end.value
|
||||||
if (isEndInclusive) {
|
if (isEndInclusive) {
|
||||||
for (c in s..e) {
|
for (c in s..e) {
|
||||||
if (!callback(ObjChar(c))) break
|
if (!callback.call(ObjChar(c))) break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (c in s..<e) {
|
for (c in s..<e) {
|
||||||
if (!callback(ObjChar(c))) break
|
if (!callback.call(ObjChar(c))) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -180,64 +181,79 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
|
|||||||
doc = "Start bound of the range or null if open.",
|
doc = "Start bound of the range or null if open.",
|
||||||
type = type("lyng.Any", nullable = true),
|
type = type("lyng.Any", nullable = true),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRange>().start ?: ObjNull }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRange>().start ?: ObjNull
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "end",
|
name = "end",
|
||||||
doc = "End bound of the range or null if open.",
|
doc = "End bound of the range or null if open.",
|
||||||
type = type("lyng.Any", nullable = true),
|
type = type("lyng.Any", nullable = true),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRange>().end ?: ObjNull }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRange>().end ?: ObjNull
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isOpen",
|
name = "isOpen",
|
||||||
doc = "Whether the range is open on either side (no start or no end).",
|
doc = "Whether the range is open on either side (no start or no end).",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRange>().let { it.start == null || it.end == null }.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRange>().let { it.start == null || it.end == null }.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isIntRange",
|
name = "isIntRange",
|
||||||
doc = "True if both bounds are Int values.",
|
doc = "True if both bounds are Int values.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRange>().isIntRange.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRange>().isIntRange.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isCharRange",
|
name = "isCharRange",
|
||||||
doc = "True if both bounds are Char values.",
|
doc = "True if both bounds are Char values.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRange>().isCharRange.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRange>().isCharRange.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "isEndInclusive",
|
name = "isEndInclusive",
|
||||||
doc = "Whether the end bound is inclusive.",
|
doc = "Whether the end bound is inclusive.",
|
||||||
type = type("lyng.Bool"),
|
type = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRange>().isEndInclusive.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRange>().isEndInclusive.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Iterator over elements in this range (optimized for Int ranges).",
|
doc = "Iterator over elements in this range (optimized for Int ranges).",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val self = thisAs<ObjRange>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (net.sergeych.lyng.PerfFlags.RANGE_FAST_ITER) {
|
val self = scp.thisAs<ObjRange>()
|
||||||
val s = self.start
|
if (net.sergeych.lyng.PerfFlags.RANGE_FAST_ITER) {
|
||||||
val e = self.end
|
val s = self.start
|
||||||
if (s is ObjInt && e is ObjInt) {
|
val e = self.end
|
||||||
val start = s.value.toInt()
|
if (s is ObjInt && e is ObjInt) {
|
||||||
val endExclusive = (if (self.isEndInclusive) e.value.toInt() + 1 else e.value.toInt())
|
val start = s.value.toInt()
|
||||||
// Only for ascending simple ranges; fall back otherwise
|
val endExclusive = (if (self.isEndInclusive) e.value.toInt() + 1 else e.value.toInt())
|
||||||
if (start <= endExclusive) {
|
// Only for ascending simple ranges; fall back otherwise
|
||||||
return@addFnDoc ObjFastIntRangeIterator(start, endExclusive)
|
if (start <= endExclusive) {
|
||||||
|
return ObjFastIntRangeIterator(start, endExclusive)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ObjRangeIterator(self).apply { init(scp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjRangeIterator(self).apply { init() }
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -19,6 +19,7 @@ package net.sergeych.lyng.obj
|
|||||||
|
|
||||||
import net.sergeych.lyng.PerfFlags
|
import net.sergeych.lyng.PerfFlags
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
|
|
||||||
class ObjRangeIterator(val self: ObjRange) : Obj() {
|
class ObjRangeIterator(val self: ObjRange) : Obj() {
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
|
|||||||
|
|
||||||
override val objClass: ObjClass get() = type
|
override val objClass: ObjClass get() = type
|
||||||
|
|
||||||
fun Scope.init() {
|
fun init(scope: Scope) {
|
||||||
val s = self.start
|
val s = self.start
|
||||||
val e = self.end
|
val e = self.end
|
||||||
if (s is ObjInt && e is ObjInt) {
|
if (s is ObjInt && e is ObjInt) {
|
||||||
@ -43,7 +44,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
|
|||||||
else
|
else
|
||||||
(e.value.code - s.value.code)
|
(e.value.code - s.value.code)
|
||||||
} else {
|
} else {
|
||||||
raiseError("not implemented iterator for range of $this")
|
scope.raiseError("not implemented iterator for range of $this")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +67,14 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("RangeIterator", ObjIterator).apply {
|
val type = ObjClass("RangeIterator", ObjIterator).apply {
|
||||||
addFn("hasNext") {
|
addFn("hasNext", code = object : ScopeCallable {
|
||||||
thisAs<ObjRangeIterator>().hasNext().toObj()
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
scp.thisAs<ObjRangeIterator>().hasNext().toObj()
|
||||||
addFn("next") {
|
})
|
||||||
thisAs<ObjRangeIterator>().next(this)
|
addFn("next", code = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj =
|
||||||
|
scp.thisAs<ObjRangeIterator>().next(scp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,8 +97,12 @@ 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", code = object : ScopeCallable {
|
||||||
addFn("next") { thisAs<ObjFastIntRangeIterator>().next(this) }
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjFastIntRangeIterator>().hasNext().toObj()
|
||||||
|
})
|
||||||
|
addFn("next", code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjFastIntRangeIterator>().next(scp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -21,6 +21,7 @@ import kotlinx.serialization.json.JsonElement
|
|||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import net.sergeych.lyng.Pos
|
import net.sergeych.lyng.Pos
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.addConstDoc
|
import net.sergeych.lyng.miniast.addConstDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
import net.sergeych.lyng.miniast.type
|
import net.sergeych.lyng.miniast.type
|
||||||
@ -126,9 +127,9 @@ 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 = statement(Pos.builtIn) {
|
value = statement(Pos.builtIn, f = object : ScopeCallable {
|
||||||
(it.thisObj as ObjReal).value.roundToLong().toObj()
|
override suspend fun call(scp: Scope): Obj = (scp.thisObj as ObjReal).value.roundToLong().toObj()
|
||||||
},
|
}),
|
||||||
doc = "This real number rounded to the nearest integer.",
|
doc = "This real number rounded to the nearest integer.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib"
|
||||||
@ -137,10 +138,11 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
|||||||
name = "toInt",
|
name = "toInt",
|
||||||
doc = "Truncate this real number toward zero to an integer.",
|
doc = "Truncate this real number toward zero to an integer.",
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjInt.of(thisAs<ObjReal>().value.toLong())
|
override suspend fun call(scp: Scope): Obj = ObjInt.of(scp.thisAs<ObjReal>().value.toLong())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,9 +64,40 @@ sealed interface ObjRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RecordProvider {
|
||||||
|
suspend fun getRecord(scope: Scope): ObjRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FieldGetter {
|
||||||
|
suspend fun call(obj: Obj, scope: Scope): ObjRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FieldSetter {
|
||||||
|
suspend fun call(obj: Obj, scope: Scope, value: Obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface IndexGetter {
|
||||||
|
suspend fun call(obj: Obj, scope: Scope, index: Obj): Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface IndexSetter {
|
||||||
|
suspend fun call(obj: Obj, scope: Scope, index: Obj, value: Obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface MethodInvoker {
|
||||||
|
suspend fun call(obj: Obj, scope: Scope, args: Arguments): Obj
|
||||||
|
}
|
||||||
|
|
||||||
/** Runtime-computed read-only reference backed by a lambda. */
|
/** Runtime-computed read-only reference backed by a lambda. */
|
||||||
class ValueFnRef(private val fn: suspend (Scope) -> ObjRecord) : ObjRef {
|
class ValueFnRef(private val provider: RecordProvider) : ObjRef {
|
||||||
override suspend fun get(scope: Scope): ObjRecord = fn(scope)
|
|
||||||
|
constructor(fn: suspend (Scope) -> ObjRecord) : this(object : RecordProvider {
|
||||||
|
override suspend fun getRecord(scope: Scope): ObjRecord = fn(scope)
|
||||||
|
})
|
||||||
|
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
return provider.getRecord(scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unary operations supported by ObjRef. */
|
/** Unary operations supported by ObjRef. */
|
||||||
@ -523,16 +554,16 @@ class FieldRef(
|
|||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
// 4-entry PIC for reads/writes (guarded by PerfFlags.FIELD_PIC)
|
// 4-entry PIC for reads/writes (guarded by PerfFlags.FIELD_PIC)
|
||||||
// Reads
|
// Reads
|
||||||
private var rKey1: Long = 0L; private var rVer1: Int = -1; private var rGetter1: (suspend (Obj, Scope) -> ObjRecord)? = null
|
private var rKey1: Long = 0L; private var rVer1: Int = -1; private var rGetter1: FieldGetter? = null
|
||||||
private var rKey2: Long = 0L; private var rVer2: Int = -1; private var rGetter2: (suspend (Obj, Scope) -> ObjRecord)? = null
|
private var rKey2: Long = 0L; private var rVer2: Int = -1; private var rGetter2: FieldGetter? = null
|
||||||
private var rKey3: Long = 0L; private var rVer3: Int = -1; private var rGetter3: (suspend (Obj, Scope) -> ObjRecord)? = null
|
private var rKey3: Long = 0L; private var rVer3: Int = -1; private var rGetter3: FieldGetter? = null
|
||||||
private var rKey4: Long = 0L; private var rVer4: Int = -1; private var rGetter4: (suspend (Obj, Scope) -> ObjRecord)? = null
|
private var rKey4: Long = 0L; private var rVer4: Int = -1; private var rGetter4: FieldGetter? = null
|
||||||
|
|
||||||
// Writes
|
// Writes
|
||||||
private var wKey1: Long = 0L; private var wVer1: Int = -1; private var wSetter1: (suspend (Obj, Scope, Obj) -> Unit)? = null
|
private var wKey1: Long = 0L; private var wVer1: Int = -1; private var wSetter1: FieldSetter? = null
|
||||||
private var wKey2: Long = 0L; private var wVer2: Int = -1; private var wSetter2: (suspend (Obj, Scope, Obj) -> Unit)? = null
|
private var wKey2: Long = 0L; private var wVer2: Int = -1; private var wSetter2: FieldSetter? = null
|
||||||
private var wKey3: Long = 0L; private var wVer3: Int = -1; private var wSetter3: (suspend (Obj, Scope, Obj) -> Unit)? = null
|
private var wKey3: Long = 0L; private var wVer3: Int = -1; private var wSetter3: FieldSetter? = null
|
||||||
private var wKey4: Long = 0L; private var wVer4: Int = -1; private var wSetter4: (suspend (Obj, Scope, Obj) -> Unit)? = null
|
private var wKey4: Long = 0L; private var wVer4: Int = -1; private var wSetter4: FieldSetter? = null
|
||||||
|
|
||||||
// Transient per-step cache to optimize read-then-write sequences within the same frame
|
// Transient per-step cache to optimize read-then-write sequences within the same frame
|
||||||
private var tKey: Long = 0L; private var tVer: Int = -1; private var tFrameId: Long = -1L; private var tRecord: ObjRecord? = null
|
private var tKey: Long = 0L; private var tVer: Int = -1; private var tFrameId: Long = -1L; private var tRecord: ObjRecord? = null
|
||||||
@ -598,7 +629,7 @@ class FieldRef(
|
|||||||
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
||||||
if (picCounters) PerfStats.fieldPicHit++
|
if (picCounters) PerfStats.fieldPicHit++
|
||||||
noteReadHit()
|
noteReadHit()
|
||||||
val rec0 = g(base, scope)
|
val rec0 = g.call(base, scope)
|
||||||
if (base is ObjClass) {
|
if (base is ObjClass) {
|
||||||
val idx0 = base.classScope?.getSlotIndexOf(name)
|
val idx0 = base.classScope?.getSlotIndexOf(name)
|
||||||
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
||||||
@ -612,7 +643,7 @@ class FieldRef(
|
|||||||
val tK = rKey2; val tV = rVer2; val tG = rGetter2
|
val tK = rKey2; val tV = rVer2; val tG = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
||||||
val rec0 = g(base, scope)
|
val rec0 = g.call(base, scope)
|
||||||
if (base is ObjClass) {
|
if (base is ObjClass) {
|
||||||
val idx0 = base.classScope?.getSlotIndexOf(name)
|
val idx0 = base.classScope?.getSlotIndexOf(name)
|
||||||
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
||||||
@ -627,7 +658,7 @@ class FieldRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
||||||
val rec0 = g(base, scope)
|
val rec0 = g.call(base, scope)
|
||||||
if (base is ObjClass) {
|
if (base is ObjClass) {
|
||||||
val idx0 = base.classScope?.getSlotIndexOf(name)
|
val idx0 = base.classScope?.getSlotIndexOf(name)
|
||||||
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
||||||
@ -643,7 +674,7 @@ class FieldRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
||||||
val rec0 = g(base, scope)
|
val rec0 = g.call(base, scope)
|
||||||
if (base is ObjClass) {
|
if (base is ObjClass) {
|
||||||
val idx0 = base.classScope?.getSlotIndexOf(name)
|
val idx0 = base.classScope?.getSlotIndexOf(name)
|
||||||
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
if (idx0 != null) { tKey = key; tVer = ver; tFrameId = scope.frameId; tRecord = rec0 } else { tRecord = null }
|
||||||
@ -660,7 +691,7 @@ class FieldRef(
|
|||||||
rKey4 = rKey3; rVer4 = rVer3; rGetter4 = rGetter3
|
rKey4 = rKey3; rVer4 = rVer3; rGetter4 = rGetter3
|
||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { _, sc -> sc.raiseError(e.message ?: "no such field: $name") }
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { _, sc -> sc.raiseError(e.message ?: "no such field: $name") }
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
// Install move-to-front with a handle-aware getter; honor PIC size flag
|
// Install move-to-front with a handle-aware getter; honor PIC size flag
|
||||||
@ -674,7 +705,7 @@ class FieldRef(
|
|||||||
val clsScope = base.classScope
|
val clsScope = base.classScope
|
||||||
val capturedIdx = clsScope?.getSlotIndexOf(name)
|
val capturedIdx = clsScope?.getSlotIndexOf(name)
|
||||||
if (clsScope != null && capturedIdx != null) {
|
if (clsScope != null && capturedIdx != null) {
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc ->
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { obj, sc ->
|
||||||
val scope0 = (obj as ObjClass).classScope!!
|
val scope0 = (obj as ObjClass).classScope!!
|
||||||
val r0 = scope0.getSlotRecord(capturedIdx)
|
val r0 = scope0.getSlotRecord(capturedIdx)
|
||||||
if (!r0.visibility.isPublic)
|
if (!r0.visibility.isPublic)
|
||||||
@ -682,14 +713,14 @@ class FieldRef(
|
|||||||
r0
|
r0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc -> obj.readField(sc, name) }
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { obj, sc -> obj.readField(sc, name) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ObjInstance -> {
|
is ObjInstance -> {
|
||||||
val cls = base.objClass
|
val cls = base.objClass
|
||||||
val effectiveKey = cls.publicMemberResolution[name]
|
val effectiveKey = cls.publicMemberResolution[name]
|
||||||
if (effectiveKey != null) {
|
if (effectiveKey != null) {
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc ->
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { obj, sc ->
|
||||||
if (obj is ObjInstance && obj.objClass === cls) {
|
if (obj is ObjInstance && obj.objClass === cls) {
|
||||||
val rec = obj.instanceScope.objects[effectiveKey]
|
val rec = obj.instanceScope.objects[effectiveKey]
|
||||||
if (rec != null && rec.type != ObjRecord.Type.Delegated) rec
|
if (rec != null && rec.type != ObjRecord.Type.Delegated) rec
|
||||||
@ -697,12 +728,12 @@ class FieldRef(
|
|||||||
} else obj.readField(sc, name)
|
} else obj.readField(sc, name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc -> obj.readField(sc, name) }
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { obj, sc -> obj.readField(sc, name) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// For instances and other types, fall back to name-based lookup per access (slot index may differ per instance)
|
// For instances and other types, fall back to name-based lookup per access (slot index may differ per instance)
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc -> obj.readField(sc, name) }
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { obj, sc -> obj.readField(sc, name) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rec
|
return rec
|
||||||
@ -745,7 +776,7 @@ class FieldRef(
|
|||||||
wSetter1?.let { s -> if (key == wKey1 && ver == wVer1) {
|
wSetter1?.let { s -> if (key == wKey1 && ver == wVer1) {
|
||||||
if (picCounters) PerfStats.fieldPicSetHit++
|
if (picCounters) PerfStats.fieldPicSetHit++
|
||||||
noteWriteHit()
|
noteWriteHit()
|
||||||
return s(base, scope, newValue)
|
return s.call(base, scope, newValue)
|
||||||
} }
|
} }
|
||||||
wSetter2?.let { s -> if (key == wKey2 && ver == wVer2) {
|
wSetter2?.let { s -> if (key == wKey2 && ver == wVer2) {
|
||||||
if (picCounters) PerfStats.fieldPicSetHit++
|
if (picCounters) PerfStats.fieldPicSetHit++
|
||||||
@ -754,7 +785,7 @@ class FieldRef(
|
|||||||
val tK = wKey2; val tV = wVer2; val tS = wSetter2
|
val tK = wKey2; val tV = wVer2; val tS = wSetter2
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = tK; wVer1 = tV; wSetter1 = tS
|
wKey1 = tK; wVer1 = tV; wSetter1 = tS
|
||||||
return s(base, scope, newValue)
|
return s.call(base, scope, newValue)
|
||||||
} }
|
} }
|
||||||
if (size4WritesEnabled()) wSetter3?.let { s -> if (key == wKey3 && ver == wVer3) {
|
if (size4WritesEnabled()) wSetter3?.let { s -> if (key == wKey3 && ver == wVer3) {
|
||||||
if (picCounters) PerfStats.fieldPicSetHit++
|
if (picCounters) PerfStats.fieldPicSetHit++
|
||||||
@ -764,7 +795,7 @@ class FieldRef(
|
|||||||
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = tK; wVer1 = tV; wSetter1 = tS
|
wKey1 = tK; wVer1 = tV; wSetter1 = tS
|
||||||
return s(base, scope, newValue)
|
return s.call(base, scope, newValue)
|
||||||
} }
|
} }
|
||||||
if (size4WritesEnabled()) wSetter4?.let { s -> if (key == wKey4 && ver == wVer4) {
|
if (size4WritesEnabled()) wSetter4?.let { s -> if (key == wKey4 && ver == wVer4) {
|
||||||
if (picCounters) PerfStats.fieldPicSetHit++
|
if (picCounters) PerfStats.fieldPicSetHit++
|
||||||
@ -775,7 +806,7 @@ class FieldRef(
|
|||||||
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = tK; wVer1 = tV; wSetter1 = tS
|
wKey1 = tK; wVer1 = tV; wSetter1 = tS
|
||||||
return s(base, scope, newValue)
|
return s.call(base, scope, newValue)
|
||||||
} }
|
} }
|
||||||
// Slow path
|
// Slow path
|
||||||
if (picCounters) PerfStats.fieldPicSetMiss++
|
if (picCounters) PerfStats.fieldPicSetMiss++
|
||||||
@ -792,7 +823,7 @@ class FieldRef(
|
|||||||
val clsScope = base.classScope
|
val clsScope = base.classScope
|
||||||
val capturedIdx = clsScope?.getSlotIndexOf(name)
|
val capturedIdx = clsScope?.getSlotIndexOf(name)
|
||||||
if (clsScope != null && capturedIdx != null) {
|
if (clsScope != null && capturedIdx != null) {
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, v ->
|
wKey1 = key; wVer1 = ver; wSetter1 = FieldSetter { obj, sc, v ->
|
||||||
val scope0 = (obj as ObjClass).classScope!!
|
val scope0 = (obj as ObjClass).classScope!!
|
||||||
val r0 = scope0.getSlotRecord(capturedIdx)
|
val r0 = scope0.getSlotRecord(capturedIdx)
|
||||||
if (!r0.isMutable)
|
if (!r0.isMutable)
|
||||||
@ -800,14 +831,14 @@ class FieldRef(
|
|||||||
if (r0.value.assign(sc, v) == null) r0.value = v
|
if (r0.value.assign(sc, v) == null) r0.value = v
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, v -> obj.writeField(sc, name, v) }
|
wKey1 = key; wVer1 = ver; wSetter1 = FieldSetter { obj, sc, v -> obj.writeField(sc, name, v) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ObjInstance -> {
|
is ObjInstance -> {
|
||||||
val cls = base.objClass
|
val cls = base.objClass
|
||||||
val effectiveKey = cls.publicMemberResolution[name]
|
val effectiveKey = cls.publicMemberResolution[name]
|
||||||
if (effectiveKey != null) {
|
if (effectiveKey != null) {
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, nv ->
|
wKey1 = key; wVer1 = ver; wSetter1 = FieldSetter { obj, sc, nv ->
|
||||||
if (obj is ObjInstance && obj.objClass === cls) {
|
if (obj is ObjInstance && obj.objClass === cls) {
|
||||||
val rec = obj.instanceScope.objects[effectiveKey]
|
val rec = obj.instanceScope.objects[effectiveKey]
|
||||||
if (rec != null && rec.effectiveWriteVisibility == Visibility.Public && rec.isMutable && rec.type == ObjRecord.Type.Field) {
|
if (rec != null && rec.effectiveWriteVisibility == Visibility.Public && rec.isMutable && rec.type == ObjRecord.Type.Field) {
|
||||||
@ -816,12 +847,12 @@ class FieldRef(
|
|||||||
} else obj.writeField(sc, name, nv)
|
} else obj.writeField(sc, name, nv)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, v -> obj.writeField(sc, name, v) }
|
wKey1 = key; wVer1 = ver; wSetter1 = FieldSetter { obj, sc, v -> obj.writeField(sc, name, v) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// For instances and other types, fall back to generic write (instance slot indices may differ per instance)
|
// For instances and other types, fall back to generic write (instance slot indices may differ per instance)
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, v -> obj.writeField(sc, name, v) }
|
wKey1 = key; wVer1 = ver; wSetter1 = FieldSetter { obj, sc, v -> obj.writeField(sc, name, v) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -853,14 +884,14 @@ class FieldRef(
|
|||||||
if (key != 0L) {
|
if (key != 0L) {
|
||||||
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
||||||
if (picCounters) PerfStats.fieldPicHit++
|
if (picCounters) PerfStats.fieldPicHit++
|
||||||
return g(base, scope).value
|
return g.call(base, scope).value
|
||||||
} }
|
} }
|
||||||
rGetter2?.let { g -> if (key == rKey2 && ver == rVer2) {
|
rGetter2?.let { g -> if (key == rKey2 && ver == rVer2) {
|
||||||
if (picCounters) PerfStats.fieldPicHit++
|
if (picCounters) PerfStats.fieldPicHit++
|
||||||
val tK = rKey2; val tV = rVer2; val tG = rGetter2
|
val tK = rKey2; val tV = rVer2; val tG = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
||||||
return g(base, scope).value
|
return g.call(base, scope).value
|
||||||
} }
|
} }
|
||||||
if (size4ReadsEnabled()) rGetter3?.let { g -> if (key == rKey3 && ver == rVer3) {
|
if (size4ReadsEnabled()) rGetter3?.let { g -> if (key == rKey3 && ver == rVer3) {
|
||||||
if (picCounters) PerfStats.fieldPicHit++
|
if (picCounters) PerfStats.fieldPicHit++
|
||||||
@ -868,7 +899,7 @@ class FieldRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
||||||
return g(base, scope).value
|
return g.call(base, scope).value
|
||||||
} }
|
} }
|
||||||
if (size4ReadsEnabled()) rGetter4?.let { g -> if (key == rKey4 && ver == rVer4) {
|
if (size4ReadsEnabled()) rGetter4?.let { g -> if (key == rKey4 && ver == rVer4) {
|
||||||
if (picCounters) PerfStats.fieldPicHit++
|
if (picCounters) PerfStats.fieldPicHit++
|
||||||
@ -877,12 +908,12 @@ class FieldRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
rKey1 = tK; rVer1 = tV; rGetter1 = tG
|
||||||
return g(base, scope).value
|
return g.call(base, scope).value
|
||||||
} }
|
} }
|
||||||
if (picCounters) PerfStats.fieldPicMiss++
|
if (picCounters) PerfStats.fieldPicMiss++
|
||||||
val rec = base.readField(scope, name)
|
val rec = base.readField(scope, name)
|
||||||
// install primary generic getter for this shape
|
// install primary generic getter for this shape
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc -> obj.readField(sc, name) }
|
rKey1 = key; rVer1 = ver; rGetter1 = FieldGetter { obj, sc -> obj.readField(sc, name) }
|
||||||
return rec.value
|
return rec.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -899,16 +930,16 @@ class IndexRef(
|
|||||||
private val isOptional: Boolean,
|
private val isOptional: Boolean,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
// Tiny 4-entry PIC for index reads (guarded implicitly by RVAL_FASTPATH); move-to-front on hits
|
// Tiny 4-entry PIC for index reads (guarded implicitly by RVAL_FASTPATH); move-to-front on hits
|
||||||
private var rKey1: Long = 0L; private var rVer1: Int = -1; private var rGetter1: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
private var rKey1: Long = 0L; private var rVer1: Int = -1; private var rGetter1: IndexGetter? = null
|
||||||
private var rKey2: Long = 0L; private var rVer2: Int = -1; private var rGetter2: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
private var rKey2: Long = 0L; private var rVer2: Int = -1; private var rGetter2: IndexGetter? = null
|
||||||
private var rKey3: Long = 0L; private var rVer3: Int = -1; private var rGetter3: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
private var rKey3: Long = 0L; private var rVer3: Int = -1; private var rGetter3: IndexGetter? = null
|
||||||
private var rKey4: Long = 0L; private var rVer4: Int = -1; private var rGetter4: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
private var rKey4: Long = 0L; private var rVer4: Int = -1; private var rGetter4: IndexGetter? = null
|
||||||
|
|
||||||
// Tiny 4-entry PIC for index writes
|
// Tiny 4-entry PIC for index writes
|
||||||
private var wKey1: Long = 0L; private var wVer1: Int = -1; private var wSetter1: (suspend (Obj, Scope, Obj, Obj) -> Unit)? = null
|
private var wKey1: Long = 0L; private var wVer1: Int = -1; private var wSetter1: IndexSetter? = null
|
||||||
private var wKey2: Long = 0L; private var wVer2: Int = -1; private var wSetter2: (suspend (Obj, Scope, Obj, Obj) -> Unit)? = null
|
private var wKey2: Long = 0L; private var wVer2: Int = -1; private var wSetter2: IndexSetter? = null
|
||||||
private var wKey3: Long = 0L; private var wVer3: Int = -1; private var wSetter3: (suspend (Obj, Scope, Obj, Obj) -> Unit)? = null
|
private var wKey3: Long = 0L; private var wVer3: Int = -1; private var wSetter3: IndexSetter? = null
|
||||||
private var wKey4: Long = 0L; private var wVer4: Int = -1; private var wSetter4: (suspend (Obj, Scope, Obj, Obj) -> Unit)? = null
|
private var wKey4: Long = 0L; private var wVer4: Int = -1; private var wSetter4: IndexSetter? = null
|
||||||
|
|
||||||
private fun receiverKeyAndVersion(obj: Obj): Pair<Long, Int> = when (obj) {
|
private fun receiverKeyAndVersion(obj: Obj): Pair<Long, Int> = when (obj) {
|
||||||
is ObjInstance -> obj.objClass.classId to obj.objClass.layoutVersion
|
is ObjInstance -> obj.objClass.classId to obj.objClass.layoutVersion
|
||||||
@ -949,14 +980,14 @@ class IndexRef(
|
|||||||
if (key != 0L) {
|
if (key != 0L) {
|
||||||
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
return g(base, scope, idx).asMutable
|
return g.call(base, scope, idx).asMutable
|
||||||
} }
|
} }
|
||||||
rGetter2?.let { g -> if (key == rKey2 && ver == rVer2) {
|
rGetter2?.let { g -> if (key == rKey2 && ver == rVer2) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
val tk = rKey2; val tv = rVer2; val tg = rGetter2
|
val tk = rKey2; val tv = rVer2; val tg = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
||||||
return g(base, scope, idx).asMutable
|
return g.call(base, scope, idx).asMutable
|
||||||
} }
|
} }
|
||||||
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter3?.let { g -> if (key == rKey3 && ver == rVer3) {
|
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter3?.let { g -> if (key == rKey3 && ver == rVer3) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
@ -964,7 +995,7 @@ class IndexRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
||||||
return g(base, scope, idx).asMutable
|
return g.call(base, scope, idx).asMutable
|
||||||
} }
|
} }
|
||||||
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter4?.let { g -> if (key == rKey4 && ver == rVer4) {
|
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter4?.let { g -> if (key == rKey4 && ver == rVer4) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
@ -973,7 +1004,7 @@ class IndexRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
||||||
return g(base, scope, idx).asMutable
|
return g.call(base, scope, idx).asMutable
|
||||||
} }
|
} }
|
||||||
// Miss: resolve and install generic handler
|
// Miss: resolve and install generic handler
|
||||||
if (picCounters) PerfStats.indexPicMiss++
|
if (picCounters) PerfStats.indexPicMiss++
|
||||||
@ -983,7 +1014,7 @@ class IndexRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
}
|
}
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc, ix -> obj.getAt(sc, ix) }
|
rKey1 = key; rVer1 = ver; rGetter1 = IndexGetter { obj, sc, ix -> obj.getAt(sc, ix) }
|
||||||
return v.asMutable
|
return v.asMutable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1023,14 +1054,14 @@ class IndexRef(
|
|||||||
if (key != 0L) {
|
if (key != 0L) {
|
||||||
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
rGetter1?.let { g -> if (key == rKey1 && ver == rVer1) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
return g(base, scope, idx)
|
return g.call(base, scope, idx)
|
||||||
} }
|
} }
|
||||||
rGetter2?.let { g -> if (key == rKey2 && ver == rVer2) {
|
rGetter2?.let { g -> if (key == rKey2 && ver == rVer2) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
val tk = rKey2; val tv = rVer2; val tg = rGetter2
|
val tk = rKey2; val tv = rVer2; val tg = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
||||||
return g(base, scope, idx)
|
return g.call(base, scope, idx)
|
||||||
} }
|
} }
|
||||||
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter3?.let { g -> if (key == rKey3 && ver == rVer3) {
|
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter3?.let { g -> if (key == rKey3 && ver == rVer3) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
@ -1038,7 +1069,7 @@ class IndexRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
||||||
return g(base, scope, idx)
|
return g.call(base, scope, idx)
|
||||||
} }
|
} }
|
||||||
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter4?.let { g -> if (key == rKey4 && ver == rVer4) {
|
if (PerfFlags.INDEX_PIC_SIZE_4) rGetter4?.let { g -> if (key == rKey4 && ver == rVer4) {
|
||||||
if (picCounters) PerfStats.indexPicHit++
|
if (picCounters) PerfStats.indexPicHit++
|
||||||
@ -1047,7 +1078,7 @@ class IndexRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
rKey1 = tk; rVer1 = tv; rGetter1 = tg
|
||||||
return g(base, scope, idx)
|
return g.call(base, scope, idx)
|
||||||
} }
|
} }
|
||||||
if (picCounters) PerfStats.indexPicMiss++
|
if (picCounters) PerfStats.indexPicMiss++
|
||||||
val v = base.getAt(scope, idx)
|
val v = base.getAt(scope, idx)
|
||||||
@ -1056,7 +1087,7 @@ class IndexRef(
|
|||||||
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
rKey3 = rKey2; rVer3 = rVer2; rGetter3 = rGetter2
|
||||||
}
|
}
|
||||||
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
rKey2 = rKey1; rVer2 = rVer1; rGetter2 = rGetter1
|
||||||
rKey1 = key; rVer1 = ver; rGetter1 = { obj, sc, ix -> obj.getAt(sc, ix) }
|
rKey1 = key; rVer1 = ver; rGetter1 = IndexGetter { obj, sc, ix -> obj.getAt(sc, ix) }
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1093,19 +1124,19 @@ class IndexRef(
|
|||||||
else -> { key = 0L; ver = -1 }
|
else -> { key = 0L; ver = -1 }
|
||||||
}
|
}
|
||||||
if (key != 0L) {
|
if (key != 0L) {
|
||||||
wSetter1?.let { s -> if (key == wKey1 && ver == wVer1) { s(base, scope, idx, newValue); return } }
|
wSetter1?.let { s -> if (key == wKey1 && ver == wVer1) { s.call(base, scope, idx, newValue); return } }
|
||||||
wSetter2?.let { s -> if (key == wKey2 && ver == wVer2) {
|
wSetter2?.let { s -> if (key == wKey2 && ver == wVer2) {
|
||||||
val tk = wKey2; val tv = wVer2; val ts = wSetter2
|
val tk = wKey2; val tv = wVer2; val ts = wSetter2
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = tk; wVer1 = tv; wSetter1 = ts
|
wKey1 = tk; wVer1 = tv; wSetter1 = ts
|
||||||
s(base, scope, idx, newValue); return
|
s.call(base, scope, idx, newValue); return
|
||||||
} }
|
} }
|
||||||
if (PerfFlags.INDEX_PIC_SIZE_4) wSetter3?.let { s -> if (key == wKey3 && ver == wVer3) {
|
if (PerfFlags.INDEX_PIC_SIZE_4) wSetter3?.let { s -> if (key == wKey3 && ver == wVer3) {
|
||||||
val tk = wKey3; val tv = wVer3; val ts = wSetter3
|
val tk = wKey3; val tv = wVer3; val ts = wSetter3
|
||||||
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = tk; wVer1 = tv; wSetter1 = ts
|
wKey1 = tk; wVer1 = tv; wSetter1 = ts
|
||||||
s(base, scope, idx, newValue); return
|
s.call(base, scope, idx, newValue); return
|
||||||
} }
|
} }
|
||||||
if (PerfFlags.INDEX_PIC_SIZE_4) wSetter4?.let { s -> if (key == wKey4 && ver == wVer4) {
|
if (PerfFlags.INDEX_PIC_SIZE_4) wSetter4?.let { s -> if (key == wKey4 && ver == wVer4) {
|
||||||
val tk = wKey4; val tv = wVer4; val ts = wSetter4
|
val tk = wKey4; val tv = wVer4; val ts = wSetter4
|
||||||
@ -1113,7 +1144,7 @@ class IndexRef(
|
|||||||
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = tk; wVer1 = tv; wSetter1 = ts
|
wKey1 = tk; wVer1 = tv; wSetter1 = ts
|
||||||
s(base, scope, idx, newValue); return
|
s.call(base, scope, idx, newValue); return
|
||||||
} }
|
} }
|
||||||
// Miss: perform write and install generic handler
|
// Miss: perform write and install generic handler
|
||||||
base.putAt(scope, idx, newValue)
|
base.putAt(scope, idx, newValue)
|
||||||
@ -1122,7 +1153,7 @@ class IndexRef(
|
|||||||
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
wKey3 = wKey2; wVer3 = wVer2; wSetter3 = wSetter2
|
||||||
}
|
}
|
||||||
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
wKey2 = wKey1; wVer2 = wVer1; wSetter2 = wSetter1
|
||||||
wKey1 = key; wVer1 = ver; wSetter1 = { obj, sc, ix, v -> obj.putAt(sc, ix, v) }
|
wKey1 = key; wVer1 = ver; wSetter1 = IndexSetter { obj, sc, ix, v -> obj.putAt(sc, ix, v) }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1173,10 +1204,10 @@ class MethodCallRef(
|
|||||||
private val isOptional: Boolean,
|
private val isOptional: Boolean,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
// 4-entry PIC for method invocations (guarded by PerfFlags.METHOD_PIC)
|
// 4-entry PIC for method invocations (guarded by PerfFlags.METHOD_PIC)
|
||||||
private var mKey1: Long = 0L; private var mVer1: Int = -1; private var mInvoker1: (suspend (Obj, Scope, Arguments) -> Obj)? = null
|
private var mKey1: Long = 0L; private var mVer1: Int = -1; private var mInvoker1: MethodInvoker? = null
|
||||||
private var mKey2: Long = 0L; private var mVer2: Int = -1; private var mInvoker2: (suspend (Obj, Scope, Arguments) -> Obj)? = null
|
private var mKey2: Long = 0L; private var mVer2: Int = -1; private var mInvoker2: MethodInvoker? = null
|
||||||
private var mKey3: Long = 0L; private var mVer3: Int = -1; private var mInvoker3: (suspend (Obj, Scope, Arguments) -> Obj)? = null
|
private var mKey3: Long = 0L; private var mVer3: Int = -1; private var mInvoker3: MethodInvoker? = null
|
||||||
private var mKey4: Long = 0L; private var mVer4: Int = -1; private var mInvoker4: (suspend (Obj, Scope, Arguments) -> Obj)? = null
|
private var mKey4: Long = 0L; private var mVer4: Int = -1; private var mInvoker4: MethodInvoker? = null
|
||||||
|
|
||||||
// Adaptive PIC (2→4) for methods
|
// Adaptive PIC (2→4) for methods
|
||||||
private var mAccesses: Int = 0; private var mMisses: Int = 0; private var mPromotedTo4: Boolean = false
|
private var mAccesses: Int = 0; private var mMisses: Int = 0; private var mPromotedTo4: Boolean = false
|
||||||
@ -1274,7 +1305,7 @@ class MethodCallRef(
|
|||||||
if (key == mKey1 && ver == mVer1) {
|
if (key == mKey1 && ver == mVer1) {
|
||||||
if (picCounters) PerfStats.methodPicHit++
|
if (picCounters) PerfStats.methodPicHit++
|
||||||
noteMethodHit()
|
noteMethodHit()
|
||||||
return inv(base, scope, callArgs)
|
return inv.call(base, scope, callArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mInvoker2?.let { inv ->
|
mInvoker2?.let { inv ->
|
||||||
@ -1285,7 +1316,7 @@ class MethodCallRef(
|
|||||||
val tK = mKey2; val tV = mVer2; val tI = mInvoker2
|
val tK = mKey2; val tV = mVer2; val tI = mInvoker2
|
||||||
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
||||||
mKey1 = tK; mVer1 = tV; mInvoker1 = tI
|
mKey1 = tK; mVer1 = tV; mInvoker1 = tI
|
||||||
return inv(base, scope, callArgs)
|
return inv.call(base, scope, callArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (size4MethodsEnabled()) mInvoker3?.let { inv ->
|
if (size4MethodsEnabled()) mInvoker3?.let { inv ->
|
||||||
@ -1297,7 +1328,7 @@ class MethodCallRef(
|
|||||||
mKey3 = mKey2; mVer3 = mVer2; mInvoker3 = mInvoker2
|
mKey3 = mKey2; mVer3 = mVer2; mInvoker3 = mInvoker2
|
||||||
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
||||||
mKey1 = tK; mVer1 = tV; mInvoker1 = tI
|
mKey1 = tK; mVer1 = tV; mInvoker1 = tI
|
||||||
return inv(base, scope, callArgs)
|
return inv.call(base, scope, callArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (size4MethodsEnabled()) mInvoker4?.let { inv ->
|
if (size4MethodsEnabled()) mInvoker4?.let { inv ->
|
||||||
@ -1310,7 +1341,7 @@ class MethodCallRef(
|
|||||||
mKey3 = mKey2; mVer3 = mVer2; mInvoker3 = mInvoker2
|
mKey3 = mKey2; mVer3 = mVer2; mInvoker3 = mInvoker2
|
||||||
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
||||||
mKey1 = tK; mVer1 = tV; mInvoker1 = tI
|
mKey1 = tK; mVer1 = tV; mInvoker1 = tI
|
||||||
return inv(base, scope, callArgs)
|
return inv.call(base, scope, callArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Slow path
|
// Slow path
|
||||||
@ -1323,7 +1354,7 @@ class MethodCallRef(
|
|||||||
mKey4 = mKey3; mVer4 = mVer3; mInvoker4 = mInvoker3
|
mKey4 = mKey3; mVer4 = mVer3; mInvoker4 = mInvoker3
|
||||||
mKey3 = mKey2; mVer3 = mVer2; mInvoker3 = mInvoker2
|
mKey3 = mKey2; mVer3 = mVer2; mInvoker3 = mInvoker2
|
||||||
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
mKey2 = mKey1; mVer2 = mVer1; mInvoker2 = mInvoker1
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { _, sc, _ -> sc.raiseError(e.message ?: "method not found: $name") }
|
mKey1 = key; mVer1 = ver; mInvoker1 = MethodInvoker { _, sc, _ -> sc.raiseError(e.message ?: "method not found: $name") }
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
// Install move-to-front with a handle-aware invoker; honor PIC size flag
|
// Install move-to-front with a handle-aware invoker; honor PIC size flag
|
||||||
@ -1361,15 +1392,15 @@ class MethodCallRef(
|
|||||||
val visibility = hierarchyMember.visibility
|
val visibility = hierarchyMember.visibility
|
||||||
val callable = hierarchyMember.value
|
val callable = hierarchyMember.value
|
||||||
val decl = hierarchyMember.declaringClass ?: base.objClass
|
val decl = hierarchyMember.declaringClass ?: base.objClass
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a ->
|
mKey1 = key; mVer1 = ver; mInvoker1 = MethodInvoker { obj, sc, a ->
|
||||||
val inst = obj as ObjInstance
|
val inst = obj as ObjInstance
|
||||||
if (!visibility.isPublic && !canAccessMember(visibility, decl, sc.currentClassCtx, name))
|
if (!visibility.isPublic && !canAccessMember(visibility, decl, sc.currentClassCtx, name))
|
||||||
sc.raiseError(ObjIllegalAccessException(sc, "can't invoke non-public method $name"))
|
sc.raiseError(ObjIllegalAccessException(sc, "can't invokeCallable non-public method $name"))
|
||||||
callable.invoke(inst.instanceScope, inst, a)
|
callable.invokeCallable(inst.instanceScope, inst, a)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback to name-based lookup per call (handles extensions and root members)
|
// Fallback to name-based lookup per call (handles extensions and root members)
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
mKey1 = key; mVer1 = ver; mInvoker1 = MethodInvoker { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ObjClass -> {
|
is ObjClass -> {
|
||||||
@ -1377,13 +1408,13 @@ class MethodCallRef(
|
|||||||
val rec = clsScope?.get(name)
|
val rec = clsScope?.get(name)
|
||||||
if (rec != null) {
|
if (rec != null) {
|
||||||
val callable = rec.value
|
val callable = rec.value
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a -> callable.invoke(sc, obj, a) }
|
mKey1 = key; mVer1 = ver; mInvoker1 = MethodInvoker { obj, sc, a -> callable.invokeCallable(sc, obj, a) }
|
||||||
} else {
|
} else {
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
mKey1 = key; mVer1 = ver; mInvoker1 = MethodInvoker { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
mKey1 = key; mVer1 = ver; mInvoker1 = MethodInvoker { obj, sc, a -> obj.invokeInstanceMethod(sc, name, a) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package net.sergeych.lyng.obj
|
|||||||
import net.sergeych.lyng.PerfFlags
|
import net.sergeych.lyng.PerfFlags
|
||||||
import net.sergeych.lyng.RegexCache
|
import net.sergeych.lyng.RegexCache
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
|
|
||||||
class ObjRegex(val regex: Regex) : Obj() {
|
class ObjRegex(val regex: Regex) : Obj() {
|
||||||
@ -49,29 +50,36 @@ class ObjRegex(val regex: Regex) : Obj() {
|
|||||||
doc = "Whether the entire string matches this regular expression.",
|
doc = "Whether the entire string matches this regular expression.",
|
||||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjBool(args.firstAndOnly().toString().matches(thisAs<ObjRegex>().regex))
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
ObjBool(scp.args.firstAndOnly().toString().matches(scp.thisAs<ObjRegex>().regex))
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "find",
|
name = "find",
|
||||||
doc = "Find the first match in the given string.",
|
doc = "Find the first match in the given string.",
|
||||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||||
returns = type("lyng.RegexMatch", nullable = true),
|
returns = type("lyng.RegexMatch", nullable = true),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjRegex>().find(requireOnlyArg<ObjString>())
|
override suspend fun call(scp: Scope): Obj =
|
||||||
}
|
scp.thisAs<ObjRegex>().find(scp.requireOnlyArg<ObjString>())
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "findAll",
|
name = "findAll",
|
||||||
doc = "Find all matches in the given string.",
|
doc = "Find all matches in the given string.",
|
||||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.RegexMatch"))),
|
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.RegexMatch"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val s = requireOnlyArg<ObjString>().value
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjList(thisAs<ObjRegex>().regex.findAll(s).map { ObjRegexMatch(it) }.toMutableList())
|
val s = scp.requireOnlyArg<ObjString>().value
|
||||||
}
|
return ObjList(scp.thisAs<ObjRegex>().regex.findAll(s).map { ObjRegexMatch(it) }.toMutableList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,21 +133,27 @@ class ObjRegexMatch(val match: MatchResult) : Obj() {
|
|||||||
doc = "List of captured groups with index 0 as the whole match.",
|
doc = "List of captured groups with index 0 as the whole match.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRegexMatch>().objGroups }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRegexMatch>().objGroups
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "value",
|
name = "value",
|
||||||
doc = "The matched substring.",
|
doc = "The matched substring.",
|
||||||
type = type("lyng.String"),
|
type = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRegexMatch>().objValue }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRegexMatch>().objValue
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "range",
|
name = "range",
|
||||||
doc = "Range of the match in the input (end-exclusive).",
|
doc = "Range of the match in the input (end-exclusive).",
|
||||||
type = type("lyng.Range"),
|
type = type("lyng.Range"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRegexMatch>().objRange }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRegexMatch>().objRange
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
|
|
||||||
class RingBuffer<T>(val maxSize: Int) : Iterable<T> {
|
class RingBuffer<T>(val maxSize: Int) : Iterable<T> {
|
||||||
@ -96,39 +97,55 @@ class ObjRingBuffer(val capacity: Int) : Obj() {
|
|||||||
doc = "Maximum number of elements the buffer can hold.",
|
doc = "Maximum number of elements the buffer can hold.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRingBuffer>().capacity.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRingBuffer>().capacity.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "Current number of elements in the buffer.",
|
doc = "Current number of elements in the buffer.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { thisAs<ObjRingBuffer>().buffer.size.toObj() }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjRingBuffer>().buffer.size.toObj()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Iterator over elements in insertion order (oldest to newest).",
|
doc = "Iterator over elements in insertion order (oldest to newest).",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val buffer = thisAs<ObjRingBuffer>().buffer
|
override suspend fun call(scp: Scope): Obj {
|
||||||
ObjKotlinObjIterator(buffer.iterator())
|
val buffer = scp.thisAs<ObjRingBuffer>().buffer
|
||||||
}
|
return ObjKotlinObjIterator(buffer.iterator())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "add",
|
name = "add",
|
||||||
doc = "Append an element; if full, the oldest element is dropped.",
|
doc = "Append an element; if full, the oldest element is dropped.",
|
||||||
params = listOf(ParamDoc("value", type("lyng.Any"))),
|
params = listOf(ParamDoc("value", type("lyng.Any"))),
|
||||||
returns = type("lyng.Void"),
|
returns = type("lyng.Void"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) { thisAs<ObjRingBuffer>().apply { buffer.add(requireOnlyArg<Obj>()) } }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj {
|
||||||
|
val self = scp.thisAs<ObjRingBuffer>()
|
||||||
|
self.buffer.add(scp.requireOnlyArg<Obj>())
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "first",
|
name = "first",
|
||||||
doc = "Return the oldest element in the buffer.",
|
doc = "Return the oldest element in the buffer.",
|
||||||
type = type("lyng.Any"),
|
type = type("lyng.Any"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
val buffer = (this.thisObj as ObjRingBuffer).buffer
|
override suspend fun call(scp: Scope): Obj {
|
||||||
if (buffer.size == 0) ObjNull else buffer.first()
|
val buffer = (scp.thisObj as ObjRingBuffer).buffer
|
||||||
|
return if (buffer.size == 0) ObjNull else buffer.iterator().next()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
import net.sergeych.lyng.miniast.ParamDoc
|
||||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
import net.sergeych.lyng.miniast.addFnDoc
|
||||||
@ -46,9 +47,9 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
return set.contains(other)
|
return set.contains(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
|
override suspend fun enumerate(scope: Scope, callback: EnumerateCallback) {
|
||||||
for (item in set) {
|
for (item in set) {
|
||||||
if (!callback(item)) break
|
if (!callback.call(item)) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,56 +165,64 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
name = "size",
|
name = "size",
|
||||||
doc = "Number of elements in this set.",
|
doc = "Number of elements in this set.",
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjSet>().set.size.toObj()
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjSet>().set.size.toObj()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "intersect",
|
name = "intersect",
|
||||||
doc = "Intersection with another set. Returns a new set.",
|
doc = "Intersection with another set. Returns a new set.",
|
||||||
params = listOf(ParamDoc("other", type("lyng.Set"))),
|
params = listOf(ParamDoc("other", type("lyng.Set"))),
|
||||||
returns = type("lyng.Set"),
|
returns = type("lyng.Set"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjSet>().mul(this, args.firstAndOnly())
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjSet>().mul(scp, scp.args.firstAndOnly())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Iterator over elements of this set.",
|
doc = "Iterator over elements of this set.",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjSet>().set.iterator().toObj()
|
override suspend fun call(scp: Scope): Obj = ObjKotlinIterator(scp.thisAs<ObjSet>().set.iterator())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "union",
|
name = "union",
|
||||||
doc = "Union with another set or iterable. Returns a new set.",
|
doc = "Union with another set or iterable. Returns a new set.",
|
||||||
params = listOf(ParamDoc("other")),
|
params = listOf(ParamDoc("other")),
|
||||||
returns = type("lyng.Set"),
|
returns = type("lyng.Set"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjSet>().plus(this, args.firstAndOnly())
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjSet>().plus(scp, scp.args.firstAndOnly())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "subtract",
|
name = "subtract",
|
||||||
doc = "Subtract another set or iterable from this set. Returns a new set.",
|
doc = "Subtract another set or iterable from this set. Returns a new set.",
|
||||||
params = listOf(ParamDoc("other")),
|
params = listOf(ParamDoc("other")),
|
||||||
returns = type("lyng.Set"),
|
returns = type("lyng.Set"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjSet>().minus(this, args.firstAndOnly())
|
override suspend fun call(scp: Scope): Obj = scp.thisAs<ObjSet>().minus(scp, scp.args.firstAndOnly())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "remove",
|
name = "remove",
|
||||||
doc = "Remove one or more elements. Returns true if the set changed.",
|
doc = "Remove one or more elements. Returns true if the set changed.",
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val set = thisAs<ObjSet>().set
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val n = set.size
|
val set = scp.thisAs<ObjSet>().set
|
||||||
for( x in args.list ) set -= x
|
val n = set.size
|
||||||
if( n == set.size ) ObjFalse else ObjTrue
|
for( x in scp.args.list ) set -= x
|
||||||
}
|
return if( n == set.size ) ObjFalse else ObjTrue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,6 +24,7 @@ import kotlinx.serialization.json.JsonPrimitive
|
|||||||
import net.sergeych.lyng.PerfFlags
|
import net.sergeych.lyng.PerfFlags
|
||||||
import net.sergeych.lyng.RegexCache
|
import net.sergeych.lyng.RegexCache
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.ScopeCallable
|
||||||
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
|
||||||
@ -139,205 +140,274 @@ data class ObjString(val value: String) : Obj() {
|
|||||||
name = "iterator",
|
name = "iterator",
|
||||||
doc = "Iterator over characters of this string.",
|
doc = "Iterator over characters of this string.",
|
||||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Char"))),
|
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Char"))),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) { ObjKotlinIterator(thisAs<ObjString>().value.iterator()) }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjKotlinIterator(scp.thisAs<ObjString>().value.iterator())
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toInt",
|
name = "toInt",
|
||||||
doc = "Parse this string as an integer or throw if it is not a valid integer.",
|
doc = "Parse this string as an integer or throw if it is not a valid integer.",
|
||||||
returns = type("lyng.Int"),
|
returns = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjInt.of(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
thisAs<ObjString>().value.toLongOrNull()
|
return ObjInt.of(
|
||||||
?: raiseIllegalArgument("can't convert to int: $thisObj")
|
scp.thisAs<ObjString>().value.toLongOrNull()
|
||||||
)
|
?: scp.raiseIllegalArgument("can't convert to int: ${scp.thisObj}")
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "startsWith",
|
name = "startsWith",
|
||||||
doc = "Whether this string starts with the given prefix.",
|
doc = "Whether this string starts with the given prefix.",
|
||||||
params = listOf(ParamDoc("prefix", type("lyng.String"))),
|
params = listOf(ParamDoc("prefix", type("lyng.String"))),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjBool(thisAs<ObjString>().value.startsWith(requiredArg<ObjString>(0).value))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
return ObjBool(scp.thisAs<ObjString>().value.startsWith(scp.requiredArg<ObjString>(0).value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "endsWith",
|
name = "endsWith",
|
||||||
doc = "Whether this string ends with the given suffix.",
|
doc = "Whether this string ends with the given suffix.",
|
||||||
params = listOf(ParamDoc("suffix", type("lyng.String"))),
|
params = listOf(ParamDoc("suffix", type("lyng.String"))),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjBool(thisAs<ObjString>().value.endsWith(requiredArg<ObjString>(0).value))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
return ObjBool(scp.thisAs<ObjString>().value.endsWith(scp.requiredArg<ObjString>(0).value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "length",
|
name = "length",
|
||||||
doc = "Number of UTF-16 code units in this string.",
|
doc = "Number of UTF-16 code units in this string.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { ObjInt.of((this.thisObj as ObjString).value.length.toLong()) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjInt.of((scp.thisObj as ObjString).value.length.toLong())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "takeLast",
|
name = "takeLast",
|
||||||
doc = "Return a string with the last N characters.",
|
doc = "Return a string with the last N characters.",
|
||||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.takeLast(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
requiredArg<ObjInt>(0).toInt()
|
return ObjString(
|
||||||
).let(::ObjString)
|
scp.thisAs<ObjString>().value.takeLast(
|
||||||
}
|
scp.requiredArg<ObjInt>(0).toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "take",
|
name = "take",
|
||||||
doc = "Return a string with the first N characters.",
|
doc = "Return a string with the first N characters.",
|
||||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.take(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
requiredArg<ObjInt>(0).toInt()
|
return ObjString(
|
||||||
).let(::ObjString)
|
scp.thisAs<ObjString>().value.take(
|
||||||
}
|
scp.requiredArg<ObjInt>(0).toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "drop",
|
name = "drop",
|
||||||
doc = "Drop the first N characters and return the remainder.",
|
doc = "Drop the first N characters and return the remainder.",
|
||||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.drop(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
requiredArg<ObjInt>(0).toInt()
|
return ObjString(
|
||||||
).let(::ObjString)
|
scp.thisAs<ObjString>().value.drop(
|
||||||
}
|
scp.requiredArg<ObjInt>(0).toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "dropLast",
|
name = "dropLast",
|
||||||
doc = "Drop the last N characters and return the remainder.",
|
doc = "Drop the last N characters and return the remainder.",
|
||||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.dropLast(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
requiredArg<ObjInt>(0).toInt()
|
return ObjString(
|
||||||
).let(::ObjString)
|
scp.thisAs<ObjString>().value.dropLast(
|
||||||
}
|
scp.requiredArg<ObjInt>(0).toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "lower",
|
name = "lower",
|
||||||
doc = "Lowercase version of this string (default locale).",
|
doc = "Lowercase version of this string (default locale).",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.lowercase().let(::ObjString)
|
override suspend fun call(scp: Scope): Obj = ObjString(scp.thisAs<ObjString>().value.lowercase())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "lowercase",
|
name = "lowercase",
|
||||||
doc = "Lowercase version of this string (default locale).",
|
doc = "Lowercase version of this string (default locale).",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.lowercase().let(::ObjString)
|
override suspend fun call(scp: Scope): Obj = ObjString(scp.thisAs<ObjString>().value.lowercase())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "upper",
|
name = "upper",
|
||||||
doc = "Uppercase version of this string (default locale).",
|
doc = "Uppercase version of this string (default locale).",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.uppercase().let(::ObjString)
|
override suspend fun call(scp: Scope): Obj = ObjString(scp.thisAs<ObjString>().value.uppercase())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "uppercase",
|
name = "uppercase",
|
||||||
doc = "Uppercase version of this string (default locale).",
|
doc = "Uppercase version of this string (default locale).",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.uppercase().let(::ObjString)
|
override suspend fun call(scp: Scope): Obj = ObjString(scp.thisAs<ObjString>().value.uppercase())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "characters",
|
name = "characters",
|
||||||
doc = "List of characters of this string.",
|
doc = "List of characters of this string.",
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Char"))),
|
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Char"))),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = {
|
getter = object : ScopeCallable {
|
||||||
ObjList(
|
override suspend fun call(scp: Scope): Obj {
|
||||||
(this.thisObj as ObjString).value.map { ObjChar(it) }.toMutableList()
|
return ObjList(
|
||||||
)
|
(scp.thisObj as ObjString).value.map { ObjChar(it) }.toMutableList()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "last",
|
name = "last",
|
||||||
doc = "The last character of this string or throw if the string is empty.",
|
doc = "The last character of this string or throw if the string is empty.",
|
||||||
returns = type("lyng.Char"),
|
returns = type("lyng.Char"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjChar(thisAs<ObjString>().value.lastOrNull() ?: raiseNoSuchElement("empty string"))
|
override suspend fun call(scp: Scope): Obj {
|
||||||
}
|
return ObjChar(scp.thisAs<ObjString>().value.lastOrNull() ?: scp.raiseNoSuchElement("empty string"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "encodeUtf8",
|
name = "encodeUtf8",
|
||||||
doc = "Encode this string as UTF-8 bytes.",
|
doc = "Encode this string as UTF-8 bytes.",
|
||||||
returns = type("lyng.Buffer"),
|
returns = type("lyng.Buffer"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) { ObjBuffer(thisAs<ObjString>().value.encodeToByteArray().asUByteArray()) }
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj =
|
||||||
|
ObjBuffer(scp.thisAs<ObjString>().value.encodeToByteArray().asUByteArray())
|
||||||
|
}
|
||||||
|
)
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "size",
|
name = "size",
|
||||||
doc = "Alias for length: the number of characters (code units) in this string.",
|
doc = "Alias for length: the number of characters (code units) in this string.",
|
||||||
type = type("lyng.Int"),
|
type = type("lyng.Int"),
|
||||||
moduleName = "lyng.stdlib",
|
moduleName = "lyng.stdlib",
|
||||||
getter = { ObjInt.of((this.thisObj as ObjString).value.length.toLong()) }
|
getter = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjInt.of((scp.thisObj as ObjString).value.length.toLong())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "toReal",
|
name = "toReal",
|
||||||
doc = "Parse this string as a real number (floating point).",
|
doc = "Parse this string as a real number (floating point).",
|
||||||
returns = type("lyng.Real"),
|
returns = type("lyng.Real"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
ObjReal.of(thisAs<ObjString>().value.toDouble())
|
override suspend fun call(scp: Scope): Obj = ObjReal.of(scp.thisAs<ObjString>().value.toDouble())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "trim",
|
name = "trim",
|
||||||
doc = "Return a copy of this string with leading and trailing whitespace removed.",
|
doc = "Return a copy of this string with leading and trailing whitespace removed.",
|
||||||
returns = type("lyng.String"),
|
returns = type("lyng.String"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
thisAs<ObjString>().value.trim().let(::ObjString)
|
override suspend fun call(scp: Scope): Obj = ObjString(scp.thisAs<ObjString>().value.trim())
|
||||||
}
|
}
|
||||||
addFnDoc("isBlank", "Whether this string is empty or contains only whitespace characters.",
|
)
|
||||||
returns = type("lyng.Bool"), moduleName = "lyng.stdlib") {
|
addFnDoc(
|
||||||
ObjBool(thisAs<ObjString>().value.isBlank())
|
name = "isBlank",
|
||||||
}
|
doc = "Whether this string is empty or contains only whitespace characters.",
|
||||||
addFnDoc("isEmpty", "Whether this string is empty.",
|
returns = type("lyng.Bool"),
|
||||||
returns = type("lyng.Bool"), moduleName = "lyng.stdlib") {
|
moduleName = "lyng.stdlib",
|
||||||
ObjBool(thisAs<ObjString>().value.isEmpty())
|
code = object : ScopeCallable {
|
||||||
}
|
override suspend fun call(scp: Scope): Obj = ObjBool(scp.thisAs<ObjString>().value.isBlank())
|
||||||
addFnDoc("isNotEmpty", "Whether this string is not empty.",
|
}
|
||||||
returns = type("lyng.Bool"), moduleName = "lyng.stdlib") {
|
)
|
||||||
ObjBool(thisAs<ObjString>().value.isNotEmpty())
|
addFnDoc(
|
||||||
}
|
name = "isEmpty",
|
||||||
|
doc = "Whether this string is empty.",
|
||||||
|
returns = type("lyng.Bool"),
|
||||||
|
moduleName = "lyng.stdlib",
|
||||||
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjBool(scp.thisAs<ObjString>().value.isEmpty())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
addFnDoc(
|
||||||
|
name = "isNotEmpty",
|
||||||
|
doc = "Whether this string is not empty.",
|
||||||
|
returns = type("lyng.Bool"),
|
||||||
|
moduleName = "lyng.stdlib",
|
||||||
|
code = object : ScopeCallable {
|
||||||
|
override suspend fun call(scp: Scope): Obj = ObjBool(scp.thisAs<ObjString>().value.isNotEmpty())
|
||||||
|
}
|
||||||
|
)
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "matches",
|
name = "matches",
|
||||||
doc = "Whether this string matches the given regular expression or pattern string.",
|
doc = "Whether this string matches the given regular expression or pattern string.",
|
||||||
params = listOf(ParamDoc("pattern")),
|
params = listOf(ParamDoc("pattern")),
|
||||||
returns = type("lyng.Bool"),
|
returns = type("lyng.Bool"),
|
||||||
moduleName = "lyng.stdlib"
|
moduleName = "lyng.stdlib",
|
||||||
) {
|
code = object : ScopeCallable {
|
||||||
val s = requireOnlyArg<Obj>()
|
override suspend fun call(scp: Scope): Obj {
|
||||||
val self = thisAs<ObjString>().value
|
val s = scp.requireOnlyArg<Obj>()
|
||||||
ObjBool(
|
val self = scp.thisAs<ObjString>().value
|
||||||
when (s) {
|
return ObjBool(
|
||||||
is ObjRegex -> self.matches(s.regex)
|
when (s) {
|
||||||
is ObjString -> {
|
is ObjRegex -> self.matches(s.regex)
|
||||||
if (s.value == ".*") true
|
is ObjString -> {
|
||||||
else {
|
if (s.value == ".*") true
|
||||||
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(s.value) else s.value.toRegex()
|
else {
|
||||||
self.matches(re)
|
val re = if (PerfFlags.REGEX_CACHE) RegexCache.get(s.value) else s.value.toRegex()
|
||||||
}
|
self.matches(re)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
raiseIllegalArgument("can't match ${s.objClass.className}: required Regex or String")
|
scp.raiseIllegalArgument("can't match ${s.objClass.className}: required Regex or String")
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -21,6 +21,10 @@ import net.sergeych.lyng.*
|
|||||||
import net.sergeych.synctools.ProtectedOp
|
import net.sergeych.synctools.ProtectedOp
|
||||||
import net.sergeych.synctools.withLock
|
import net.sergeych.synctools.withLock
|
||||||
|
|
||||||
|
interface ModuleBuilder {
|
||||||
|
suspend fun build(scope: ModuleScope)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import manager allow to register packages with builder lambdas and act as an
|
* Import manager allow to register packages with builder lambdas and act as an
|
||||||
* [ImportProvider]. Note that packages _must be registered_ first with [addPackage],
|
* [ImportProvider]. Note that packages _must be registered_ first with [addPackage],
|
||||||
@ -40,16 +44,16 @@ class ImportManager(
|
|||||||
|
|
||||||
private inner class Entry(
|
private inner class Entry(
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val builder: suspend (ModuleScope) -> Unit,
|
val builder: ModuleBuilder,
|
||||||
var cachedScope: ModuleScope? = null
|
var cachedScope: ModuleScope? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getScope(pos: Pos): ModuleScope {
|
suspend fun getScope(pos: Pos): ModuleScope {
|
||||||
cachedScope?.let { return it }
|
cachedScope?.let { return it }
|
||||||
return ModuleScope(inner, pos, packageName).apply {
|
val ms = ModuleScope(inner, pos, packageName)
|
||||||
cachedScope = this
|
cachedScope = ms
|
||||||
builder(this)
|
builder.build(ms)
|
||||||
}
|
return ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +94,7 @@ class ImportManager(
|
|||||||
* @param name package name
|
* @param name package name
|
||||||
* @param builder lambda to create actual package using the given [ModuleScope]
|
* @param builder lambda to create actual package using the given [ModuleScope]
|
||||||
*/
|
*/
|
||||||
fun addPackage(name: String, builder: suspend (ModuleScope) -> Unit) {
|
fun addPackage(name: String, builder: ModuleBuilder) {
|
||||||
op.withLock {
|
op.withLock {
|
||||||
if (name in imports)
|
if (name in imports)
|
||||||
throw IllegalArgumentException("Package $name already exists")
|
throw IllegalArgumentException("Package $name already exists")
|
||||||
@ -102,7 +106,7 @@ class ImportManager(
|
|||||||
* Bulk [addPackage] with slightly better performance
|
* Bulk [addPackage] with slightly better performance
|
||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun addPackages(registrationData: List<Pair<String, suspend (ModuleScope) -> Unit>>) {
|
fun addPackages(registrationData: List<Pair<String, ModuleBuilder>>) {
|
||||||
op.withLock {
|
op.withLock {
|
||||||
for (pp in registrationData) {
|
for (pp in registrationData) {
|
||||||
if (pp.first in imports)
|
if (pp.first in imports)
|
||||||
@ -129,9 +133,11 @@ class ImportManager(
|
|||||||
*/
|
*/
|
||||||
fun addSourcePackages(vararg sources: Source) {
|
fun addSourcePackages(vararg sources: Source) {
|
||||||
for (s in sources) {
|
for (s in sources) {
|
||||||
addPackage(s.extractPackageName()) {
|
addPackage(s.extractPackageName(), object : ModuleBuilder {
|
||||||
it.eval(s)
|
override suspend fun build(scope: ModuleScope) {
|
||||||
}
|
scope.eval(s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +150,11 @@ class ImportManager(
|
|||||||
var source = Source("tmp", s)
|
var source = Source("tmp", s)
|
||||||
val packageName = source.extractPackageName()
|
val packageName = source.extractPackageName()
|
||||||
source = Source(packageName, s)
|
source = Source(packageName, s)
|
||||||
addPackage(packageName) { it.eval(source) }
|
addPackage(packageName, object : ModuleBuilder {
|
||||||
|
override suspend fun build(scope: ModuleScope) {
|
||||||
|
scope.eval(source)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,16 +72,16 @@ fun Statement.require(cond: Boolean, message: () -> String) {
|
|||||||
if (!cond) raise(message())
|
if (!cond) raise(message())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend (Scope) -> Obj): Statement =
|
fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false, f: ScopeCallable): Statement =
|
||||||
object : Statement(isStaticConst, isConst) {
|
object : Statement(isStaticConst, isConst) {
|
||||||
override val pos: Pos = pos
|
override val pos: Pos = pos
|
||||||
override suspend fun execute(scope: Scope): Obj = f(scope)
|
override suspend fun execute(scope: Scope): Obj = f.call(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend Scope.() -> Obj): Statement =
|
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: ScopeCallable): Statement =
|
||||||
object : Statement(isStaticConst, isConst) {
|
object : Statement(isStaticConst, isConst) {
|
||||||
override val pos: Pos = Pos.builtIn
|
override val pos: Pos = Pos.builtIn
|
||||||
override suspend fun execute(scope: Scope): Obj = f(scope)
|
override suspend fun execute(scope: Scope): Obj = f.call(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
object NopStatement: Statement(true, true, ObjType.Void) {
|
object NopStatement: Statement(true, true, ObjType.Void) {
|
||||||
|
|||||||
@ -41,11 +41,11 @@ object ObjLynonClass : ObjClass("Lynon") {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
addClassConst("test", ObjString("test_const"))
|
addClassConst("test", ObjString("test_const"))
|
||||||
addClassFn("encode") {
|
addClassFn("encode") { scp ->
|
||||||
encodeAny(this, requireOnlyArg<Obj>())
|
encodeAny(scp, scp.requireOnlyArg<Obj>())
|
||||||
}
|
}
|
||||||
addClassFn("decode") {
|
addClassFn("decode") { scp ->
|
||||||
decodeAny(this, requireOnlyArg<Obj>())
|
decodeAny(scp, scp.requireOnlyArg<Obj>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -171,7 +171,7 @@ class BindingHighlightTest {
|
|||||||
// Find the specific usage inside string-literal invocation: "%s is directory"(name)
|
// Find the specific usage inside string-literal invocation: "%s is directory"(name)
|
||||||
val pattern = "\"%s is directory\"(name)"
|
val pattern = "\"%s is directory\"(name)"
|
||||||
val lineIdx = text.indexOf(pattern)
|
val lineIdx = text.indexOf(pattern)
|
||||||
assertTrue(lineIdx >= 0, "Pattern with string invoke should be present in the snippet")
|
assertTrue(lineIdx >= 0, "Pattern with string invokeCallable should be present in the snippet")
|
||||||
val nameStart = lineIdx + pattern.indexOf("name")
|
val nameStart = lineIdx + pattern.indexOf("name")
|
||||||
val nameEnd = nameStart + "name".length
|
val nameEnd = nameStart + "name".length
|
||||||
|
|
||||||
|
|||||||
@ -865,8 +865,8 @@ class OOTest {
|
|||||||
callBar
|
callBar
|
||||||
""".trimIndent()) as Statement
|
""".trimIndent()) as Statement
|
||||||
val s2 = Script.newScope()
|
val s2 = Script.newScope()
|
||||||
assertEquals(44L, fn.invoke(scope, fn).toKotlin(s2))
|
assertEquals(44L, fn.invokeCallable(scope, fn).toKotlin(s2))
|
||||||
assertEquals(45L, fn.invoke(s2, fn).toKotlin(s2))
|
assertEquals(45L, fn.invokeCallable(s2, fn).toKotlin(s2))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -138,10 +138,10 @@ class ScriptTest {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("TestIterable", ObjIterable).apply {
|
val type = ObjClass("TestIterable", ObjIterable).apply {
|
||||||
addFn("iterator") {
|
addFn("iterator") { scp ->
|
||||||
ObjTestIterator(thisAs<ObjTestIterable>())
|
ObjTestIterator(scp.thisAs<ObjTestIterable>())
|
||||||
}
|
}
|
||||||
addFn("cancelCount") { thisAs<ObjTestIterable>().cancelCount.toObj() }
|
addFn("cancelCount") { scp -> scp.thisAs<ObjTestIterable>().cancelCount.toObj() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,10 +158,10 @@ class ScriptTest {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("TestIterator", ObjIterator).apply {
|
val type = ObjClass("TestIterator", ObjIterator).apply {
|
||||||
addFn("hasNext") { thisAs<ObjTestIterator>().hasNext().toObj() }
|
addFn("hasNext") { scp -> scp.thisAs<ObjTestIterator>().hasNext().toObj() }
|
||||||
addFn("next") { thisAs<ObjTestIterator>().next() }
|
addFn("next") { scp -> scp.thisAs<ObjTestIterator>().next() }
|
||||||
addFn("cancelIteration") {
|
addFn("cancelIteration") { scp ->
|
||||||
thisAs<ObjTestIterator>().cancelIteration()
|
scp.thisAs<ObjTestIterator>().cancelIteration()
|
||||||
ObjVoid
|
ObjVoid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2229,8 +2229,8 @@ class ScriptTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testThrowFromKotlin() = runTest {
|
fun testThrowFromKotlin() = runTest {
|
||||||
val c = Script.newScope()
|
val c = Script.newScope()
|
||||||
c.addFn("callThrow") {
|
c.addFn("callThrow") { scp ->
|
||||||
raiseIllegalArgument("fromKotlin")
|
scp.raiseIllegalArgument("fromKotlin")
|
||||||
}
|
}
|
||||||
c.eval(
|
c.eval(
|
||||||
"""
|
"""
|
||||||
@ -2738,8 +2738,8 @@ class ScriptTest {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val klass = ObjClass("TestFoo").apply {
|
val klass = ObjClass("TestFoo").apply {
|
||||||
addFn("test") {
|
addFn("test") { scp ->
|
||||||
thisAs<ObjTestFoo>().value
|
scp.thisAs<ObjTestFoo>().value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4480,7 +4480,7 @@ class ScriptTest {
|
|||||||
val dummyThis = Obj()
|
val dummyThis = Obj()
|
||||||
// but we should be able to call it directly
|
// but we should be able to call it directly
|
||||||
val otherScope = baseScope.createChildScope()
|
val otherScope = baseScope.createChildScope()
|
||||||
val r = (exports["exportedFunction".toObj()] as Statement).invoke(otherScope, dummyThis, ObjInt(50))
|
val r = (exports["exportedFunction".toObj()] as Statement).invokeCallable(otherScope, dummyThis, ObjInt(50))
|
||||||
println(r)
|
println(r)
|
||||||
assertEquals(51, r.toInt())
|
assertEquals(51, r.toInt())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,7 +90,7 @@ class DelegationTest {
|
|||||||
fun testFunDelegation() = runTest {
|
fun testFunDelegation() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
class ActionDelegate() {
|
class ActionDelegate() {
|
||||||
fun invoke(thisRef, name, args...) {
|
fun invokeCallable(thisRef, name, args...) {
|
||||||
"Called %s with %d args: %s"(name, args.size, args.joinToString(","))
|
"Called %s with %d args: %s"(name, args.size, args.joinToString(","))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,15 +18,15 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
actual object PerfDefaults {
|
actual object PerfDefaults {
|
||||||
actual val LOCAL_SLOT_PIC: Boolean = true
|
actual val LOCAL_SLOT_PIC: Boolean = false
|
||||||
actual val EMIT_FAST_LOCAL_REFS: Boolean = true
|
actual val EMIT_FAST_LOCAL_REFS: Boolean = false
|
||||||
|
|
||||||
actual val ARG_BUILDER: Boolean = true
|
actual val ARG_BUILDER: Boolean = false
|
||||||
actual val SKIP_ARGS_ON_NULL_RECEIVER: Boolean = true
|
actual val SKIP_ARGS_ON_NULL_RECEIVER: Boolean = false
|
||||||
actual val SCOPE_POOL: Boolean = true
|
actual val SCOPE_POOL: Boolean = false
|
||||||
|
|
||||||
actual val FIELD_PIC: Boolean = true
|
actual val FIELD_PIC: Boolean = false
|
||||||
actual val METHOD_PIC: Boolean = true
|
actual val METHOD_PIC: Boolean = false
|
||||||
actual val FIELD_PIC_SIZE_4: Boolean = false
|
actual val FIELD_PIC_SIZE_4: Boolean = false
|
||||||
actual val METHOD_PIC_SIZE_4: Boolean = false
|
actual val METHOD_PIC_SIZE_4: Boolean = false
|
||||||
actual val PIC_ADAPTIVE_2_TO_4: Boolean = false
|
actual val PIC_ADAPTIVE_2_TO_4: Boolean = false
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user