fix #82 refactored and added builtin docs to all symbols
This commit is contained in:
parent
e25fc95cbf
commit
40f11b6f29
@ -322,7 +322,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
||||
// mkdirs(mustCreate: Bool=false)
|
||||
addFnDoc(
|
||||
name = "mkdirs",
|
||||
doc = "Create directories (like `mkdir -p`). Optional `mustCreate` enforces error if target 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"))),
|
||||
moduleName = module.packageName
|
||||
) {
|
||||
@ -336,7 +336,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
||||
// move(to: Path|String, overwrite: Bool=false)
|
||||
addFnDoc(
|
||||
name = "move",
|
||||
doc = "Move this path to a new location. `to` may be a `Path` or `String`. Use `overwrite` to replace existing target.",
|
||||
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
|
||||
params = listOf(ParamDoc("to"), ParamDoc("overwrite", type("lyng.Bool"))),
|
||||
moduleName = module.packageName
|
||||
@ -352,7 +352,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
||||
// delete(mustExist: Bool=false, recursively: Bool=false)
|
||||
addFnDoc(
|
||||
name = "delete",
|
||||
doc = "Delete this path. Optional flags: `mustExist` and `recursively`.",
|
||||
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"))),
|
||||
moduleName = module.packageName
|
||||
) {
|
||||
@ -367,7 +367,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
|
||||
// copy(to: Path|String, overwrite: Bool=false)
|
||||
addFnDoc(
|
||||
name = "copy",
|
||||
doc = "Copy this path to a new location. `to` may be a `Path` or `String`. Use `overwrite` to replace existing target.",
|
||||
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"))),
|
||||
moduleName = module.packageName
|
||||
) {
|
||||
|
||||
@ -20,6 +20,9 @@ package net.sergeych.lyng
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.yield
|
||||
import net.sergeych.lyng.Script.Companion.defaultImportManager
|
||||
import net.sergeych.lyng.miniast.addConstDoc
|
||||
import net.sergeych.lyng.miniast.addVoidFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lyng.obj.*
|
||||
import net.sergeych.lyng.pacman.ImportManager
|
||||
import net.sergeych.lyng.stdlib_included.rootLyng
|
||||
@ -276,9 +279,19 @@ class Script(
|
||||
}
|
||||
|
||||
val pi = ObjReal(PI)
|
||||
addConst("π", pi)
|
||||
addConstDoc(
|
||||
name = "π",
|
||||
value = pi,
|
||||
doc = "The mathematical constant pi (π).",
|
||||
type = type("lyng.Real")
|
||||
)
|
||||
getOrCreateNamespace("Math").apply {
|
||||
addConst("PI", pi)
|
||||
addConstDoc(
|
||||
name = "PI",
|
||||
value = pi,
|
||||
doc = "The mathematical constant pi (π) in the Math namespace.",
|
||||
type = type("lyng.Real")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,16 +301,44 @@ class Script(
|
||||
rootLyng
|
||||
)
|
||||
addPackage("lyng.buffer") {
|
||||
it.addConst("Buffer", ObjBuffer.type)
|
||||
it.addConst("MutableBuffer", ObjMutableBuffer.type)
|
||||
it.addConstDoc(
|
||||
name = "Buffer",
|
||||
value = ObjBuffer.type,
|
||||
doc = "Immutable sequence of bytes. Use for binary data and IO.",
|
||||
type = type("lyng.Class")
|
||||
)
|
||||
it.addConstDoc(
|
||||
name = "MutableBuffer",
|
||||
value = ObjMutableBuffer.type,
|
||||
doc = "Mutable byte buffer. Supports in-place modifications.",
|
||||
type = type("lyng.Class")
|
||||
)
|
||||
}
|
||||
addPackage("lyng.serialization") {
|
||||
it.addConst("Lynon", ObjLynonClass)
|
||||
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.addConst("Instant", ObjInstant.type)
|
||||
it.addConst("Duration", ObjDuration.type)
|
||||
it.addFn("delay") {
|
||||
it.addConstDoc(
|
||||
name = "Instant",
|
||||
value = ObjInstant.type,
|
||||
doc = "Point in time (epoch-based).",
|
||||
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)
|
||||
@ -305,7 +346,6 @@ class Script(
|
||||
is ObjDuration -> delay(a.duration)
|
||||
else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect(this)}")
|
||||
}
|
||||
ObjVoid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
package net.sergeych.lyng.miniast
|
||||
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.stdlib_included.rootLyng
|
||||
|
||||
// ---------------- Types DSL ----------------
|
||||
|
||||
@ -262,7 +263,125 @@ internal fun TypeDoc.toMiniTypeRef(): MiniTypeRef = when (this) {
|
||||
|
||||
// ---------------- Built-in module doc seeds ----------------
|
||||
|
||||
// Seed docs for lyng.stdlib lazily to avoid init-order coupling.
|
||||
// ---------------- Inline docs support (.lyng source) ----------------
|
||||
|
||||
/**
|
||||
* Lightweight, single-pass scanner that extracts inline doc comments from the stdlib .lyng source
|
||||
* and associates them with declarations (top-level functions, classes, and class methods).
|
||||
* It is intentionally conservative and only recognizes simple patterns present in stdlib sources.
|
||||
*
|
||||
* The scan is cached; performed at most once per process.
|
||||
*/
|
||||
private object StdlibInlineDocIndex {
|
||||
private var built = false
|
||||
|
||||
// Keys for matching declaration docs
|
||||
private sealed interface Key {
|
||||
data class TopFun(val name: String) : Key
|
||||
data class Clazz(val name: String) : Key
|
||||
data class Method(val className: String, val name: String) : Key
|
||||
}
|
||||
|
||||
private val docs: MutableMap<Key, String> = mutableMapOf()
|
||||
|
||||
private fun putIfAbsent(k: Key, doc: String) {
|
||||
if (doc.isBlank()) return
|
||||
if (!docs.containsKey(k)) docs[k] = doc.trim()
|
||||
}
|
||||
|
||||
private fun buildOnce() {
|
||||
if (built) return
|
||||
built = true
|
||||
val text = try { rootLyng } catch (_: Throwable) { null } ?: return
|
||||
|
||||
// Simple line-based scan. Collect a doc block immediately preceding a declaration.
|
||||
val lines = text.lines()
|
||||
val buf = mutableListOf<String>()
|
||||
var inBlock = false
|
||||
var prevWasComment = false
|
||||
|
||||
fun flushTo(key: Key) {
|
||||
if (buf.isNotEmpty()) {
|
||||
val raw = buf.joinToString("\n").trimEnd()
|
||||
putIfAbsent(key, raw)
|
||||
buf.clear()
|
||||
}
|
||||
}
|
||||
|
||||
for (rawLine in lines) {
|
||||
val line = rawLine.trim()
|
||||
when {
|
||||
// Multiline block comment begin/end
|
||||
line.startsWith("/*") && !inBlock -> {
|
||||
inBlock = true
|
||||
val inner = line.removePrefix("/*").let { l -> if (l.endsWith("*/")) l.removeSuffix("*/") else l }
|
||||
buf += inner
|
||||
prevWasComment = true
|
||||
if (line.endsWith("*/")) inBlock = false
|
||||
continue
|
||||
}
|
||||
inBlock -> {
|
||||
val content = if (line.endsWith("*/")) {
|
||||
inBlock = false
|
||||
line.removeSuffix("*/")
|
||||
} else line
|
||||
// Trim leading '*' like Javadoc style
|
||||
val t = content.trimStart()
|
||||
buf += if (t.startsWith("*")) t.removePrefix("*").trimStart() else content
|
||||
prevWasComment = true
|
||||
continue
|
||||
}
|
||||
line.startsWith("//") -> {
|
||||
buf += line.removePrefix("//")
|
||||
prevWasComment = true
|
||||
continue
|
||||
}
|
||||
line.isBlank() -> {
|
||||
// Blank line breaks doc association unless it immediately follows a comment
|
||||
if (!prevWasComment) buf.clear()
|
||||
prevWasComment = false
|
||||
continue
|
||||
}
|
||||
else -> {
|
||||
// Non-comment, non-blank: try to match a declaration just after comments
|
||||
if (buf.isNotEmpty()) {
|
||||
// fun Class.name( ... )
|
||||
val mExt = Regex("^fun\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)\\s*\\(").find(line)
|
||||
if (mExt != null) {
|
||||
val (cls, name) = mExt.destructured
|
||||
flushTo(Key.Method(cls, name))
|
||||
} else {
|
||||
// fun name( ... )
|
||||
val mTop = Regex("^fun\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(").find(line)
|
||||
if (mTop != null) {
|
||||
val (name) = mTop.destructured
|
||||
flushTo(Key.TopFun(name))
|
||||
} else {
|
||||
// class Name
|
||||
val mClass = Regex("^class\\s+([A-Za-z_][A-Za-z0-9_]*)\\b").find(line)
|
||||
if (mClass != null) {
|
||||
val (name) = mClass.destructured
|
||||
flushTo(Key.Clazz(name))
|
||||
} else {
|
||||
// Unrecognized line – drop buffer to avoid leaking to unrelated code
|
||||
buf.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prevWasComment = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Public API: fetch doc text if present
|
||||
fun topFunDoc(name: String): String? { buildOnce(); return docs[Key.TopFun(name)] }
|
||||
fun classDoc(name: String): String? { buildOnce(); return docs[Key.Clazz(name)] }
|
||||
fun methodDoc(className: String, name: String): String? { buildOnce(); return docs[Key.Method(className, name)] }
|
||||
}
|
||||
|
||||
// Seed docs for lyng.stdlib lazily to avoid init-order coupling and prefer inline docs where present.
|
||||
private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
val decls = mutableListOf<MiniDecl>()
|
||||
// Use the same DSL builders to construct decls
|
||||
@ -270,7 +389,7 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
// Printing
|
||||
mod.funDoc(
|
||||
name = "print",
|
||||
doc = """
|
||||
doc = StdlibInlineDocIndex.topFunDoc("print") ?: """
|
||||
Print values to the standard output without a trailing newline.
|
||||
Accepts any number of arguments and prints them separated by a space.
|
||||
""".trimIndent(),
|
||||
@ -279,7 +398,7 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "println",
|
||||
doc = """
|
||||
doc = StdlibInlineDocIndex.topFunDoc("println") ?: """
|
||||
Print values to the standard output and append a newline.
|
||||
Accepts any number of arguments and prints them separated by a space.
|
||||
""".trimIndent(),
|
||||
@ -288,7 +407,7 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
// Caching helper
|
||||
mod.funDoc(
|
||||
name = "cached",
|
||||
doc = """
|
||||
doc = StdlibInlineDocIndex.topFunDoc("cached") ?: """
|
||||
Wrap a `builder` into a zero-argument thunk that computes once and caches the result.
|
||||
The first call invokes `builder()` and stores the value; subsequent calls return the cached value.
|
||||
""".trimIndent(),
|
||||
@ -298,44 +417,44 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
// Math helpers (scalar versions)
|
||||
fun math1(name: String) = mod.funDoc(
|
||||
name = name,
|
||||
doc = "Compute $name(x).",
|
||||
doc = StdlibInlineDocIndex.topFunDoc(name) ?: "Compute $name(x).",
|
||||
params = listOf(ParamDoc("x", type("lyng.Number")))
|
||||
)
|
||||
math1("sin"); math1("cos"); math1("tan"); math1("asin"); math1("acos"); math1("atan")
|
||||
mod.funDoc(name = "floor", doc = "Round down the number to the nearest integer.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "ceil", doc = "Round up the number to the nearest integer.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "round", doc = "Round the number to the nearest integer.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "floor", doc = StdlibInlineDocIndex.topFunDoc("floor") ?: "Round down the number to the nearest integer.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "ceil", doc = StdlibInlineDocIndex.topFunDoc("ceil") ?: "Round up the number to the nearest integer.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "round", doc = StdlibInlineDocIndex.topFunDoc("round") ?: "Round the number to the nearest integer.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
|
||||
// Hyperbolic and inverse hyperbolic
|
||||
math1("sinh"); math1("cosh"); math1("tanh"); math1("asinh"); math1("acosh"); math1("atanh")
|
||||
|
||||
// Exponentials and logarithms
|
||||
mod.funDoc(name = "exp", doc = "Euler's exponential e^x.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "ln", doc = "Natural logarithm (base e).", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "log10", doc = "Logarithm base 10.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "log2", doc = "Logarithm base 2.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "exp", doc = StdlibInlineDocIndex.topFunDoc("exp") ?: "Euler's exponential e^x.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "ln", doc = StdlibInlineDocIndex.topFunDoc("ln") ?: "Natural logarithm (base e).", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "log10", doc = StdlibInlineDocIndex.topFunDoc("log10") ?: "Logarithm base 10.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
mod.funDoc(name = "log2", doc = StdlibInlineDocIndex.topFunDoc("log2") ?: "Logarithm base 2.", params = listOf(ParamDoc("x", type("lyng.Number"))))
|
||||
|
||||
// Power/roots and absolute value
|
||||
mod.funDoc(
|
||||
name = "pow",
|
||||
doc = "Raise `x` to the power `y`.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("pow") ?: "Raise `x` to the power `y`.",
|
||||
params = listOf(ParamDoc("x", type("lyng.Number")), ParamDoc("y", type("lyng.Number")))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "sqrt",
|
||||
doc = "Square root of `x`.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("sqrt") ?: "Square root of `x`.",
|
||||
params = listOf(ParamDoc("x", type("lyng.Number")))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "abs",
|
||||
doc = "Absolute value of a number (works for Int and Real).",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("abs") ?: "Absolute value of a number (works for Int and Real).",
|
||||
params = listOf(ParamDoc("x", type("lyng.Number")))
|
||||
)
|
||||
|
||||
// Assertions and checks
|
||||
mod.funDoc(
|
||||
name = "assert",
|
||||
doc = """
|
||||
doc = StdlibInlineDocIndex.topFunDoc("assert") ?: """
|
||||
Assert that `cond` is true, otherwise throw an `AssertionFailedException`.
|
||||
Optionally provide a `message`.
|
||||
""".trimIndent(),
|
||||
@ -343,17 +462,17 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "assertEquals",
|
||||
doc = "Assert that `a == b`, otherwise throw an assertion error.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("assertEquals") ?: "Assert that `a == b`, otherwise throw an assertion error.",
|
||||
params = listOf(ParamDoc("a"), ParamDoc("b"))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "assertNotEquals",
|
||||
doc = "Assert that `a != b`, otherwise throw an assertion error.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("assertNotEquals") ?: "Assert that `a != b`, otherwise throw an assertion error.",
|
||||
params = listOf(ParamDoc("a"), ParamDoc("b"))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "assertThrows",
|
||||
doc = """
|
||||
doc = StdlibInlineDocIndex.topFunDoc("assertThrows") ?: """
|
||||
Execute `code` and return the thrown `Exception` object.
|
||||
If nothing is thrown, an assertion error is raised.
|
||||
""".trimIndent(),
|
||||
@ -364,119 +483,125 @@ private fun buildStdlibDocs(): List<MiniDecl> {
|
||||
// Utilities
|
||||
mod.funDoc(
|
||||
name = "dynamic",
|
||||
doc = "Wrap a value into a dynamic object that defers resolution to runtime.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("dynamic") ?: "Wrap a value into a dynamic object that defers resolution to runtime.",
|
||||
params = listOf(ParamDoc("value"))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "require",
|
||||
doc = "Require `cond` to be true, else throw `IllegalArgumentException` with optional `message`.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("require") ?: "Require `cond` to be true, else throw `IllegalArgumentException` with optional `message`.",
|
||||
params = listOf(ParamDoc("cond", type("lyng.Bool")), ParamDoc("message"))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "check",
|
||||
doc = "Check `cond` is true, else throw `IllegalStateException` with optional `message`.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("check") ?: "Check `cond` is true, else throw `IllegalStateException` with optional `message`.",
|
||||
params = listOf(ParamDoc("cond", type("lyng.Bool")), ParamDoc("message"))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "traceScope",
|
||||
doc = "Print a debug trace of the current scope chain with an optional label.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("traceScope") ?: "Print a debug trace of the current scope chain with an optional label.",
|
||||
params = listOf(ParamDoc("label", type("lyng.String")))
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "delay",
|
||||
doc = "Suspend for the specified number of milliseconds.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("delay") ?: "Suspend for the specified number of milliseconds.",
|
||||
params = listOf(ParamDoc("ms", type("lyng.Number")))
|
||||
)
|
||||
|
||||
// Concurrency helpers
|
||||
mod.funDoc(
|
||||
name = "launch",
|
||||
doc = "Launch an asynchronous task and return a `Deferred`.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("launch") ?: "Launch an asynchronous task and return a `Deferred`.",
|
||||
params = listOf(ParamDoc("code")),
|
||||
returns = type("lyng.Deferred")
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "yield",
|
||||
doc = "Yield to the scheduler, allowing other tasks to run."
|
||||
doc = StdlibInlineDocIndex.topFunDoc("yield") ?: "Yield to the scheduler, allowing other tasks to run."
|
||||
)
|
||||
mod.funDoc(
|
||||
name = "flow",
|
||||
doc = "Create a lazy iterable stream using the provided `builder`.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("flow") ?: "Create a lazy iterable stream using the provided `builder`.",
|
||||
params = listOf(ParamDoc("builder")),
|
||||
returns = type("lyng.Iterable")
|
||||
)
|
||||
|
||||
// Common Iterable helpers (document top-level extension-like APIs as class members)
|
||||
mod.classDoc(name = "Iterable", doc = "Helper operations for iterable collections.") {
|
||||
method(name = "filter", doc = "Filter elements by predicate.", params = listOf(ParamDoc("predicate")), returns = type("lyng.Iterable"))
|
||||
method(name = "drop", doc = "Skip the first N elements.", params = listOf(ParamDoc("n", type("lyng.Int"))), returns = type("lyng.Iterable"))
|
||||
method(name = "first", doc = "Return the first element or throw if empty.")
|
||||
method(name = "last", doc = "Return the last element or throw if empty.")
|
||||
method(name = "dropLast", doc = "Drop the last N elements.", params = listOf(ParamDoc("n", type("lyng.Int"))), returns = type("lyng.Iterable"))
|
||||
method(name = "takeLast", doc = "Take the last N elements.", params = listOf(ParamDoc("n", type("lyng.Int"))), returns = type("lyng.List"))
|
||||
method(name = "joinToString", doc = "Join elements into a string with an optional separator and transformer.", params = listOf(ParamDoc("prefix", type("lyng.String")), ParamDoc("transformer")), returns = type("lyng.String"))
|
||||
method(name = "any", doc = "Return true if any element matches the predicate.", params = listOf(ParamDoc("predicate")), returns = type("lyng.Bool"))
|
||||
method(name = "all", doc = "Return true if all elements match the predicate.", params = listOf(ParamDoc("predicate")), returns = type("lyng.Bool"))
|
||||
method(name = "sum", doc = "Sum all elements; returns null for empty collections.", returns = type("lyng.Number", nullable = true))
|
||||
method(name = "sumOf", doc = "Sum mapped values of elements; returns null for empty collections.", params = listOf(ParamDoc("f")))
|
||||
method(name = "minOf", doc = "Minimum of mapped values.", params = listOf(ParamDoc("lambda")))
|
||||
method(name = "maxOf", doc = "Maximum of mapped values.", params = listOf(ParamDoc("lambda")))
|
||||
method(name = "sorted", doc = "Return elements sorted by natural order.", returns = type("lyng.Iterable"))
|
||||
method(name = "sortedBy", doc = "Return elements sorted by the key selector.", params = listOf(ParamDoc("predicate")), returns = type("lyng.Iterable"))
|
||||
method(name = "shuffled", doc = "Return a shuffled copy as a list.", returns = type("lyng.List"))
|
||||
method(name = "map", doc = "Transform elements by applying `transform`.", params = listOf(ParamDoc("transform")), returns = type("lyng.Iterable"))
|
||||
method(name = "toList", doc = "Collect elements of this iterable into a new list.", returns = type("lyng.List"))
|
||||
mod.classDoc(name = "Iterable", doc = StdlibInlineDocIndex.classDoc("Iterable") ?: "Helper operations for iterable collections.") {
|
||||
fun md(name: String, fallback: String) = StdlibInlineDocIndex.methodDoc("Iterable", name) ?: fallback
|
||||
method(name = "filter", doc = md("filter", "Filter elements by predicate."), params = listOf(ParamDoc("predicate")), returns = type("lyng.Iterable"))
|
||||
method(name = "drop", doc = md("drop", "Skip the first N elements."), params = listOf(ParamDoc("n", type("lyng.Int"))), returns = type("lyng.Iterable"))
|
||||
method(name = "first", doc = md("first", "Return the first element or throw if empty."))
|
||||
method(name = "last", doc = md("last", "Return the last element or throw if empty."))
|
||||
method(name = "dropLast", doc = md("dropLast", "Drop the last N elements."), params = listOf(ParamDoc("n", type("lyng.Int"))), returns = type("lyng.Iterable"))
|
||||
method(name = "takeLast", doc = md("takeLast", "Take the last N elements."), params = listOf(ParamDoc("n", type("lyng.Int"))), returns = type("lyng.List"))
|
||||
method(name = "joinToString", doc = md("joinToString", "Join elements into a string with an optional separator and transformer."), params = listOf(ParamDoc("prefix", type("lyng.String")), ParamDoc("transformer")), returns = type("lyng.String"))
|
||||
method(name = "any", doc = md("any", "Return true if any element matches the predicate."), params = listOf(ParamDoc("predicate")), returns = type("lyng.Bool"))
|
||||
method(name = "all", doc = md("all", "Return true if all elements match the predicate."), params = listOf(ParamDoc("predicate")), returns = type("lyng.Bool"))
|
||||
method(name = "sum", doc = md("sum", "Sum all elements; returns null for empty collections."), returns = type("lyng.Number", nullable = true))
|
||||
method(name = "sumOf", doc = md("sumOf", "Sum mapped values of elements; returns null for empty collections."), params = listOf(ParamDoc("f")))
|
||||
method(name = "minOf", doc = md("minOf", "Minimum of mapped values."), params = listOf(ParamDoc("lambda")))
|
||||
method(name = "maxOf", doc = md("maxOf", "Maximum of mapped values."), params = listOf(ParamDoc("lambda")))
|
||||
method(name = "sorted", doc = md("sorted", "Return elements sorted by natural order."), returns = type("lyng.Iterable"))
|
||||
method(name = "sortedBy", doc = md("sortedBy", "Return elements sorted by the key selector."), params = listOf(ParamDoc("predicate")), returns = type("lyng.Iterable"))
|
||||
method(name = "shuffled", doc = md("shuffled", "Return a shuffled copy as a list."), returns = type("lyng.List"))
|
||||
method(name = "map", doc = md("map", "Transform elements by applying `transform`."), params = listOf(ParamDoc("transform")), returns = type("lyng.Iterable"))
|
||||
method(name = "toList", doc = md("toList", "Collect elements of this iterable into a new list."), returns = type("lyng.List"))
|
||||
}
|
||||
|
||||
// List helpers
|
||||
mod.classDoc(name = "List", doc = "List-specific operations.", bases = listOf(type("Collection"), type("Iterable"))) {
|
||||
method(name = "toString", doc = "Return string representation like [a,b,c].", returns = type("lyng.String"))
|
||||
method(name = "sortBy", doc = "Sort list in-place by key selector.", params = listOf(ParamDoc("predicate")))
|
||||
method(name = "sort", doc = "Sort list in-place by natural order.")
|
||||
method(name = "toList", doc = "Return a shallow copy of this list (new list with the same elements).", returns = type("lyng.List"))
|
||||
mod.classDoc(name = "List", doc = StdlibInlineDocIndex.classDoc("List") ?: "List-specific operations.", bases = listOf(type("Collection"), type("Iterable"))) {
|
||||
fun md(name: String, fallback: String) = StdlibInlineDocIndex.methodDoc("List", name) ?: fallback
|
||||
method(name = "toString", doc = md("toString", "Return string representation like [a,b,c]."), returns = type("lyng.String"))
|
||||
method(name = "sortBy", doc = md("sortBy", "Sort list in-place by key selector."), params = listOf(ParamDoc("predicate")))
|
||||
method(name = "sort", doc = md("sort", "Sort list in-place by natural order."))
|
||||
method(name = "toList", doc = md("toList", "Return a shallow copy of this list (new list with the same elements)."), returns = type("lyng.List"))
|
||||
}
|
||||
|
||||
// Collection helpers (supertype for sized collections)
|
||||
mod.classDoc(name = "Collection", doc = "Collection operations common to sized collections.", bases = listOf(type("Iterable"))) {
|
||||
method(name = "size", doc = "Number of elements in the collection.", returns = type("lyng.Int"))
|
||||
method(name = "toList", doc = "Collect elements into a new list.", returns = type("lyng.List"))
|
||||
mod.classDoc(name = "Collection", doc = StdlibInlineDocIndex.classDoc("Collection") ?: "Collection operations common to sized collections.", bases = listOf(type("Iterable"))) {
|
||||
fun md(name: String, fallback: String) = StdlibInlineDocIndex.methodDoc("Collection", name) ?: fallback
|
||||
method(name = "size", doc = md("size", "Number of elements in the collection."), returns = type("lyng.Int"))
|
||||
method(name = "toList", doc = md("toList", "Collect elements into a new list."), returns = type("lyng.List"))
|
||||
}
|
||||
|
||||
// Iterator helpers
|
||||
mod.classDoc(name = "Iterator", doc = "Iterator protocol for sequential access.") {
|
||||
method(name = "hasNext", doc = "Whether another element is available.", returns = type("lyng.Bool"))
|
||||
method(name = "next", doc = "Return the next element.")
|
||||
method(name = "cancelIteration", doc = "Stop the iteration early.")
|
||||
method(name = "toList", doc = "Consume this iterator and collect elements into a list.", returns = type("lyng.List"))
|
||||
mod.classDoc(name = "Iterator", doc = StdlibInlineDocIndex.classDoc("Iterator") ?: "Iterator protocol for sequential access.") {
|
||||
fun md(name: String, fallback: String) = StdlibInlineDocIndex.methodDoc("Iterator", name) ?: fallback
|
||||
method(name = "hasNext", doc = md("hasNext", "Whether another element is available."), returns = type("lyng.Bool"))
|
||||
method(name = "next", doc = md("next", "Return the next element."))
|
||||
method(name = "cancelIteration", doc = md("cancelIteration", "Stop the iteration early."))
|
||||
method(name = "toList", doc = md("toList", "Consume this iterator and collect elements into a list."), returns = type("lyng.List"))
|
||||
}
|
||||
|
||||
// Exceptions and utilities
|
||||
mod.classDoc(name = "Exception", doc = "Exception helpers.") {
|
||||
method(name = "printStackTrace", doc = "Print this exception and its stack trace to standard output.")
|
||||
mod.classDoc(name = "Exception", doc = StdlibInlineDocIndex.classDoc("Exception") ?: "Exception helpers.") {
|
||||
method(name = "printStackTrace", doc = StdlibInlineDocIndex.methodDoc("Exception", "printStackTrace") ?: "Print this exception and its stack trace to standard output.")
|
||||
}
|
||||
|
||||
mod.classDoc(name = "String", doc = "String helpers.") {
|
||||
method(name = "re", doc = "Compile this string into a regular expression.", returns = type("lyng.Regex"))
|
||||
mod.classDoc(name = "String", doc = StdlibInlineDocIndex.classDoc("String") ?: "String helpers.") {
|
||||
// Only include inline-source method here; Kotlin-embedded methods are now documented via DocHelpers near definitions.
|
||||
method(name = "re", doc = StdlibInlineDocIndex.methodDoc("String", "re") ?: "Compile this string into a regular expression.", returns = type("lyng.Regex"))
|
||||
}
|
||||
|
||||
// StackTraceEntry structure
|
||||
mod.classDoc(name = "StackTraceEntry", doc = "Represents a single stack trace element.") {
|
||||
mod.classDoc(name = "StackTraceEntry", doc = StdlibInlineDocIndex.classDoc("StackTraceEntry") ?: "Represents a single stack trace element.") {
|
||||
// Fields are not present as declarations in root.lyng's class header docs. Keep seeded defaults.
|
||||
field(name = "sourceName", doc = "Source (file) name.", type = type("lyng.String"))
|
||||
field(name = "line", doc = "Line number (1-based).", type = type("lyng.Int"))
|
||||
field(name = "column", doc = "Column number (0-based).", type = type("lyng.Int"))
|
||||
field(name = "sourceString", doc = "The source line text.", type = type("lyng.String"))
|
||||
method(name = "toString", doc = "Formatted representation: source:line:column: text.", returns = type("lyng.String"))
|
||||
method(name = "toString", doc = StdlibInlineDocIndex.methodDoc("StackTraceEntry", "toString") ?: "Formatted representation: source:line:column: text.", returns = type("lyng.String"))
|
||||
}
|
||||
|
||||
// Constants and namespaces
|
||||
mod.valDoc(
|
||||
name = "π",
|
||||
doc = "The mathematical constant pi.",
|
||||
doc = StdlibInlineDocIndex.topFunDoc("π") ?: "The mathematical constant pi.",
|
||||
type = type("lyng.Real"),
|
||||
mutable = false
|
||||
)
|
||||
mod.classDoc(name = "Math", doc = "Mathematical constants and helpers.") {
|
||||
field(name = "PI", doc = "The mathematical constant pi.", type = type("lyng.Real"), isStatic = true)
|
||||
field(name = "PI", doc = StdlibInlineDocIndex.methodDoc("Math", "PI") ?: "The mathematical constant pi.", type = type("lyng.Real"), isStatic = true)
|
||||
}
|
||||
|
||||
decls += mod.build()
|
||||
|
||||
@ -17,6 +17,11 @@
|
||||
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
val ObjArray by lazy {
|
||||
|
||||
/**
|
||||
@ -25,19 +30,34 @@ val ObjArray by lazy {
|
||||
ObjClass("Array", ObjCollection).apply {
|
||||
// we can create iterators using size/getat:
|
||||
|
||||
addFn("iterator") {
|
||||
ObjArrayIterator(thisObj).also { it.init(this) }
|
||||
}
|
||||
addFnDoc(
|
||||
name = "iterator",
|
||||
doc = "Iterator over elements of this array using its indexer.",
|
||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { ObjArrayIterator(thisObj).also { it.init(this) } }
|
||||
|
||||
addFn("contains", isOpen = true) {
|
||||
addFnDoc(
|
||||
name = "contains",
|
||||
doc = "Whether the array contains the given element (by equality).",
|
||||
params = listOf(ParamDoc("element")),
|
||||
returns = type("lyng.Bool"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val obj = args.firstAndOnly()
|
||||
for (i in 0..<thisObj.invokeInstanceMethod(this, "size").toInt()) {
|
||||
if (thisObj.getAt(this, ObjInt(i.toLong())).compareTo(this, obj) == 0) return@addFn ObjTrue
|
||||
if (thisObj.getAt(this, ObjInt(i.toLong())).compareTo(this, obj) == 0) return@addFnDoc ObjTrue
|
||||
}
|
||||
ObjFalse
|
||||
}
|
||||
|
||||
addFn("last") {
|
||||
addFnDoc(
|
||||
name = "last",
|
||||
doc = "The last element of this array.",
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisObj.invokeInstanceMethod(
|
||||
this,
|
||||
"getAt",
|
||||
@ -45,13 +65,27 @@ val ObjArray by lazy {
|
||||
)
|
||||
}
|
||||
|
||||
addFn("lastIndex") { (thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj() }
|
||||
addFnDoc(
|
||||
name = "lastIndex",
|
||||
doc = "Index of the last element (size - 1).",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { (thisObj.invokeInstanceMethod(this, "size").toInt() - 1).toObj() }
|
||||
|
||||
addFn("indices") {
|
||||
ObjRange(0.toObj(), thisObj.invokeInstanceMethod(this, "size"), false)
|
||||
}
|
||||
addFnDoc(
|
||||
name = "indices",
|
||||
doc = "Range of valid indices for this array.",
|
||||
returns = type("lyng.Range"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { ObjRange(0.toObj(), thisObj.invokeInstanceMethod(this, "size"), false) }
|
||||
|
||||
addFn("binarySearch") {
|
||||
addFnDoc(
|
||||
name = "binarySearch",
|
||||
doc = "Binary search for a target in a sorted array. Returns index or negative insertion point - 1.",
|
||||
params = listOf(ParamDoc("target")),
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val target = args.firstAndOnly()
|
||||
var low = 0
|
||||
var high = thisObj.invokeInstanceMethod(this, "size").toInt() - 1
|
||||
@ -62,13 +96,12 @@ val ObjArray by lazy {
|
||||
|
||||
val cmp = midVal.compareTo(this, target)
|
||||
when {
|
||||
cmp == 0 -> return@addFn (mid).toObj()
|
||||
cmp == 0 -> return@addFnDoc (mid).toObj()
|
||||
cmp > 0 -> high = mid - 1
|
||||
else -> low = mid + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Элемент не найден, возвращаем -(точка вставки) - 1
|
||||
(-low - 1).toObj()
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
class ObjChar(val value: Char): Obj() {
|
||||
|
||||
@ -45,7 +47,12 @@ class ObjChar(val value: Char): Obj() {
|
||||
|
||||
companion object {
|
||||
val type = ObjClass("Char").apply {
|
||||
addFn("code") { ObjInt(thisAs<ObjChar>().value.code.toLong()) }
|
||||
addFnDoc(
|
||||
name = "code",
|
||||
doc = "Unicode code point (UTF-16 code unit) of this character.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { ObjInt(thisAs<ObjChar>().value.code.toLong()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,13 +18,73 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.*
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
|
||||
// Simple id generator for class identities (not thread-safe; fine for scripts)
|
||||
private object ClassIdGen { var c: Long = 1L; fun nextId(): Long = c++ }
|
||||
|
||||
val ObjClassType by lazy { ObjClass("Class") }
|
||||
val ObjClassType by lazy {
|
||||
ObjClass("Class").apply {
|
||||
addFnDoc(
|
||||
name = "name",
|
||||
doc = "Simple name of this class (without package).",
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjClass>().classNameObj }
|
||||
|
||||
addFnDoc(
|
||||
name = "fields",
|
||||
doc = "Declared instance fields of this class and its ancestors (C3 order), without duplicates.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val cls = thisAs<ObjClass>()
|
||||
val seen = hashSetOf<String>()
|
||||
val names = mutableListOf<Obj>()
|
||||
for (c in cls.mro) {
|
||||
for ((n, rec) in c.members) {
|
||||
if (rec.value !is Statement && seen.add(n)) names += ObjString(n)
|
||||
}
|
||||
}
|
||||
ObjList(names.toMutableList())
|
||||
}
|
||||
|
||||
addFnDoc(
|
||||
name = "methods",
|
||||
doc = "Declared instance methods of this class and its ancestors (C3 order), without duplicates.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val cls = thisAs<ObjClass>()
|
||||
val seen = hashSetOf<String>()
|
||||
val names = mutableListOf<Obj>()
|
||||
for (c in cls.mro) {
|
||||
for ((n, rec) in c.members) {
|
||||
if (rec.value is Statement && seen.add(n)) names += ObjString(n)
|
||||
}
|
||||
}
|
||||
ObjList(names.toMutableList())
|
||||
}
|
||||
|
||||
addFnDoc(
|
||||
name = "get",
|
||||
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"))),
|
||||
returns = type("lyng.Any", nullable = true),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val cls = thisAs<ObjClass>()
|
||||
val name = requiredArg<ObjString>(0).value
|
||||
val rec = cls.getInstanceMemberOrNull(name)
|
||||
rec?.value ?: ObjNull
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class ObjClass(
|
||||
val className: String,
|
||||
|
||||
@ -19,6 +19,9 @@ package net.sergeych.lyng.obj
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
class ObjCompletableDeferred(val completableDeferred: CompletableDeferred<Obj>): ObjDeferred(completableDeferred) {
|
||||
|
||||
@ -30,7 +33,13 @@ class ObjCompletableDeferred(val completableDeferred: CompletableDeferred<Obj>):
|
||||
return ObjCompletableDeferred(CompletableDeferred())
|
||||
}
|
||||
}.apply {
|
||||
addFn("complete") {
|
||||
addFnDoc(
|
||||
name = "complete",
|
||||
doc = "Complete this deferred with the given value. Subsequent calls have no effect.",
|
||||
params = listOf(ParamDoc("value")),
|
||||
returns = type("lyng.Void"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjCompletableDeferred>().completableDeferred.complete(args.firstAndOnly())
|
||||
ObjVoid
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ package net.sergeych.lyng.obj
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
|
||||
|
||||
@ -30,20 +32,33 @@ open class ObjDeferred(val deferred: Deferred<Obj>): Obj() {
|
||||
scope.raiseError("Deferred constructor is not directly callable")
|
||||
}
|
||||
}.apply {
|
||||
addFn("await") {
|
||||
thisAs<ObjDeferred>().deferred.await()
|
||||
}
|
||||
addFn("isCompleted") {
|
||||
thisAs<ObjDeferred>().deferred.isCompleted.toObj()
|
||||
}
|
||||
addFn("isActive") {
|
||||
addFnDoc(
|
||||
name = "await",
|
||||
doc = "Suspend until completion and return the result value (or throw if failed).",
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjDeferred>().deferred.await() }
|
||||
addFnDoc(
|
||||
name = "isCompleted",
|
||||
doc = "Whether this deferred has completed (successfully or with an error).",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjDeferred>().deferred.isCompleted.toObj() }
|
||||
addFnDoc(
|
||||
name = "isActive",
|
||||
doc = "Whether this deferred is currently active (not completed and not cancelled).",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val d = thisAs<ObjDeferred>().deferred
|
||||
// Cross-engine tolerant: prefer Deferred.isActive; otherwise treat any not-yet-completed and not-cancelled as active
|
||||
(d.isActive || (!d.isCompleted && !d.isCancelled)).toObj()
|
||||
}
|
||||
addFn("isCancelled") {
|
||||
thisAs<ObjDeferred>().deferred.isCancelled.toObj()
|
||||
}
|
||||
addFnDoc(
|
||||
name = "isCancelled",
|
||||
doc = "Whether this deferred was cancelled.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjDeferred>().deferred.isCancelled.toObj() }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
@ -72,91 +74,169 @@ class ObjDuration(val duration: Duration) : Obj() {
|
||||
)
|
||||
}
|
||||
}.apply {
|
||||
addFn("days") {
|
||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.DAYS).toObj()
|
||||
}
|
||||
addFn("hours") {
|
||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.HOURS).toObj()
|
||||
}
|
||||
addFn("minutes") {
|
||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.MINUTES).toObj()
|
||||
}
|
||||
addFn("seconds") {
|
||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.SECONDS).toObj()
|
||||
}
|
||||
addFn("milliseconds") {
|
||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.MILLISECONDS).toObj()
|
||||
}
|
||||
addFn("microseconds") {
|
||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.MICROSECONDS).toObj()
|
||||
}
|
||||
addFnDoc(
|
||||
name = "days",
|
||||
doc = "Return this duration as a real number of days.",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.time"
|
||||
) { thisAs<ObjDuration>().duration.toDouble(DurationUnit.DAYS).toObj() }
|
||||
addFnDoc(
|
||||
name = "hours",
|
||||
doc = "Return this duration as a real number of hours.",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.time"
|
||||
) { thisAs<ObjDuration>().duration.toDouble(DurationUnit.HOURS).toObj() }
|
||||
addFnDoc(
|
||||
name = "minutes",
|
||||
doc = "Return this duration as a real number of minutes.",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.time"
|
||||
) { thisAs<ObjDuration>().duration.toDouble(DurationUnit.MINUTES).toObj() }
|
||||
addFnDoc(
|
||||
name = "seconds",
|
||||
doc = "Return this duration as a real number of seconds.",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.time"
|
||||
) { thisAs<ObjDuration>().duration.toDouble(DurationUnit.SECONDS).toObj() }
|
||||
addFnDoc(
|
||||
name = "milliseconds",
|
||||
doc = "Return this duration as a real number of milliseconds.",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.time"
|
||||
) { thisAs<ObjDuration>().duration.toDouble(DurationUnit.MILLISECONDS).toObj() }
|
||||
addFnDoc(
|
||||
name = "microseconds",
|
||||
doc = "Return this duration as a real number of microseconds.",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.time"
|
||||
) { thisAs<ObjDuration>().duration.toDouble(DurationUnit.MICROSECONDS).toObj() }
|
||||
// extensions
|
||||
|
||||
ObjInt.type.addFn("seconds") {
|
||||
ObjDuration(thisAs<ObjInt>().value.seconds)
|
||||
}
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "seconds",
|
||||
doc = "Construct a `Duration` equal to this integer number of seconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.seconds) }
|
||||
|
||||
ObjInt.type.addFn("second") {
|
||||
ObjDuration(thisAs<ObjInt>().value.seconds)
|
||||
}
|
||||
ObjInt.type.addFn("milliseconds") {
|
||||
ObjDuration(thisAs<ObjInt>().value.milliseconds)
|
||||
}
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "second",
|
||||
doc = "Construct a `Duration` equal to this integer number of seconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.seconds) }
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "milliseconds",
|
||||
doc = "Construct a `Duration` equal to this integer number of milliseconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.milliseconds) }
|
||||
|
||||
ObjInt.type.addFn("millisecond") {
|
||||
ObjDuration(thisAs<ObjInt>().value.milliseconds)
|
||||
}
|
||||
ObjReal.type.addFn("seconds") {
|
||||
ObjDuration(thisAs<ObjReal>().value.seconds)
|
||||
}
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "millisecond",
|
||||
doc = "Construct a `Duration` equal to this integer number of milliseconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.milliseconds) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "seconds",
|
||||
doc = "Construct a `Duration` equal to this real number of seconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.seconds) }
|
||||
|
||||
ObjReal.type.addFn("second") {
|
||||
ObjDuration(thisAs<ObjReal>().value.seconds)
|
||||
}
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "second",
|
||||
doc = "Construct a `Duration` equal to this real number of seconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.seconds) }
|
||||
|
||||
ObjReal.type.addFn("milliseconds") {
|
||||
ObjDuration(thisAs<ObjReal>().value.milliseconds)
|
||||
}
|
||||
ObjReal.type.addFn("millisecond") {
|
||||
ObjDuration(thisAs<ObjReal>().value.milliseconds)
|
||||
}
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "milliseconds",
|
||||
doc = "Construct a `Duration` equal to this real number of milliseconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.milliseconds) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "millisecond",
|
||||
doc = "Construct a `Duration` equal to this real number of milliseconds.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.milliseconds) }
|
||||
|
||||
ObjInt.type.addFn("minutes") {
|
||||
ObjDuration(thisAs<ObjInt>().value.minutes)
|
||||
}
|
||||
ObjReal.type.addFn("minutes") {
|
||||
ObjDuration(thisAs<ObjReal>().value.minutes)
|
||||
}
|
||||
ObjInt.type.addFn("minute") {
|
||||
ObjDuration(thisAs<ObjInt>().value.minutes)
|
||||
}
|
||||
ObjReal.type.addFn("minute") {
|
||||
ObjDuration(thisAs<ObjReal>().value.minutes)
|
||||
}
|
||||
ObjInt.type.addFn("hours") {
|
||||
ObjDuration(thisAs<ObjInt>().value.hours)
|
||||
}
|
||||
ObjReal.type.addFn("hours") {
|
||||
ObjDuration(thisAs<ObjReal>().value.hours)
|
||||
}
|
||||
ObjInt.type.addFn("hour") {
|
||||
ObjDuration(thisAs<ObjInt>().value.hours)
|
||||
}
|
||||
ObjReal.type.addFn("hour") {
|
||||
ObjDuration(thisAs<ObjReal>().value.hours)
|
||||
}
|
||||
ObjInt.type.addFn("days") {
|
||||
ObjDuration(thisAs<ObjInt>().value.days)
|
||||
}
|
||||
ObjReal.type.addFn("days") {
|
||||
ObjDuration(thisAs<ObjReal>().value.days)
|
||||
}
|
||||
ObjInt.type.addFn("day") {
|
||||
ObjDuration(thisAs<ObjInt>().value.days)
|
||||
}
|
||||
ObjReal.type.addFn("day") {
|
||||
ObjDuration(thisAs<ObjReal>().value.days)
|
||||
}
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "minutes",
|
||||
doc = "Construct a `Duration` equal to this integer number of minutes.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.minutes) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "minutes",
|
||||
doc = "Construct a `Duration` equal to this real number of minutes.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.minutes) }
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "minute",
|
||||
doc = "Construct a `Duration` equal to this integer number of minutes.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.minutes) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "minute",
|
||||
doc = "Construct a `Duration` equal to this real number of minutes.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.minutes) }
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "hours",
|
||||
doc = "Construct a `Duration` equal to this integer number of hours.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.hours) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "hours",
|
||||
doc = "Construct a `Duration` equal to this real number of hours.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.hours) }
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "hour",
|
||||
doc = "Construct a `Duration` equal to this integer number of hours.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.hours) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "hour",
|
||||
doc = "Construct a `Duration` equal to this real number of hours.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.hours) }
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "days",
|
||||
doc = "Construct a `Duration` equal to this integer number of days.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.days) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "days",
|
||||
doc = "Construct a `Duration` equal to this real number of days.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.days) }
|
||||
ObjInt.type.addFnDoc(
|
||||
name = "day",
|
||||
doc = "Construct a `Duration` equal to this integer number of days.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjInt>().value.days) }
|
||||
ObjReal.type.addFnDoc(
|
||||
name = "day",
|
||||
doc = "Construct a `Duration` equal to this real number of days.",
|
||||
returns = type("lyng.Duration"),
|
||||
moduleName = "lyng.time"
|
||||
) { ObjDuration(thisAs<ObjReal>().value.days) }
|
||||
|
||||
|
||||
// addFn("epochSeconds") {
|
||||
|
||||
@ -19,6 +19,10 @@ package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.bintools.encodeToHex
|
||||
import net.sergeych.lyng.*
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addConstDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
@ -133,10 +137,21 @@ open class ObjException(
|
||||
}
|
||||
|
||||
val Root = ExceptionClass("Exception").apply {
|
||||
addConst("message", statement {
|
||||
(thisObj as ObjException).message.toObj()
|
||||
})
|
||||
addFn("stackTrace") {
|
||||
addConstDoc(
|
||||
name = "message",
|
||||
value = statement {
|
||||
(thisObj as ObjException).message.toObj()
|
||||
},
|
||||
doc = "Human‑readable error message.",
|
||||
type = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
)
|
||||
addFnDoc(
|
||||
name = "stackTrace",
|
||||
doc = "Stack trace captured at throw site as a list of `StackTraceEntry`.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.StackTraceEntry"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
(thisObj as ObjException).getStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@ import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.sergeych.lyng.*
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.mp_tools.globalLaunch
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@ -36,7 +40,13 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
|
||||
companion object {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
val type = object : ObjClass("FlowBuilder") {}.apply {
|
||||
addFn("emit") {
|
||||
addFnDoc(
|
||||
name = "emit",
|
||||
doc = "Send a value to the flow consumer. Suspends if back‑pressured; no‑ops after consumer stops.",
|
||||
params = listOf(ParamDoc("value", type("lyng.Any"))),
|
||||
returns = type("lyng.Void"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val data = requireOnlyArg<Obj>()
|
||||
try {
|
||||
val channel = thisAs<ObjFlowBuilder>().output
|
||||
@ -47,7 +57,6 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
|
||||
throw ScriptFlowIsNoMoreCollected()
|
||||
} catch (x: Exception) {
|
||||
// Any failure to send (including closed channel) should gracefully stop the producer.
|
||||
// Do not print stack traces here to keep test output clean on JVM.
|
||||
if (x is CancellationException) {
|
||||
// Cancellation is a normal control-flow event
|
||||
throw ScriptFlowIsNoMoreCollected()
|
||||
@ -90,7 +99,12 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
|
||||
scope.raiseError("Flow constructor is not available")
|
||||
}
|
||||
}.apply {
|
||||
addFn("iterator") {
|
||||
addFnDoc(
|
||||
name = "iterator",
|
||||
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"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val objFlow = thisAs<ObjFlow>()
|
||||
ObjFlowIterator(statement {
|
||||
objFlow.producer.execute(
|
||||
@ -146,14 +160,27 @@ class ObjFlowIterator(val producer: Statement) : Obj() {
|
||||
val type = object : ObjClass("FlowIterator", ObjIterator) {
|
||||
|
||||
}.apply {
|
||||
addFn("hasNext") {
|
||||
thisAs<ObjFlowIterator>().hasNext(this).toObj()
|
||||
}
|
||||
addFn("next") {
|
||||
addFnDoc(
|
||||
name = "hasNext",
|
||||
doc = "Whether another element is available from the flow.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjFlowIterator>().hasNext(this).toObj() }
|
||||
addFnDoc(
|
||||
name = "next",
|
||||
doc = "Receive the next element from the flow or throw if completed.",
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val x = thisAs<ObjFlowIterator>()
|
||||
x.next(this)
|
||||
}
|
||||
addFn("cancelIteration") {
|
||||
addFnDoc(
|
||||
name = "cancelIteration",
|
||||
doc = "Stop iteration and cancel the underlying flow producer.",
|
||||
returns = type("lyng.Void"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val x = thisAs<ObjFlowIterator>()
|
||||
x.cancel()
|
||||
ObjVoid
|
||||
|
||||
@ -19,6 +19,9 @@ package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Arguments
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
/**
|
||||
* Abstract class that must provide `iterator` method that returns [ObjIterator] instance.
|
||||
@ -26,7 +29,12 @@ import net.sergeych.lyng.Statement
|
||||
val ObjIterable by lazy {
|
||||
ObjClass("Iterable").apply {
|
||||
|
||||
addFn("toList") {
|
||||
addFnDoc(
|
||||
name = "toList",
|
||||
doc = "Collect elements of this iterable into a new list.",
|
||||
returns = type("lyng.List"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val result = mutableListOf<Obj>()
|
||||
val iterator = thisObj.invokeInstanceMethod(this, "iterator")
|
||||
|
||||
@ -36,29 +44,48 @@ val ObjIterable by lazy {
|
||||
}
|
||||
|
||||
// it is not effective, but it is open:
|
||||
addFn("contains", isOpen = true) {
|
||||
addFnDoc(
|
||||
name = "contains",
|
||||
doc = "Whether the iterable contains the given element (by equality).",
|
||||
params = listOf(ParamDoc("element")),
|
||||
returns = type("lyng.Bool"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val obj = args.firstAndOnly()
|
||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
||||
if (obj.compareTo(this, it.invokeInstanceMethod(this, "next")) == 0)
|
||||
return@addFn ObjTrue
|
||||
return@addFnDoc ObjTrue
|
||||
}
|
||||
ObjFalse
|
||||
}
|
||||
|
||||
addFn("indexOf", isOpen = true) {
|
||||
addFnDoc(
|
||||
name = "indexOf",
|
||||
doc = "Index of the first occurrence of the given element, or -1 if not found.",
|
||||
params = listOf(ParamDoc("element")),
|
||||
returns = type("lyng.Int"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val obj = args.firstAndOnly()
|
||||
var index = 0
|
||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
||||
if (obj.compareTo(this, it.invokeInstanceMethod(this, "next")) == 0)
|
||||
return@addFn ObjInt(index.toLong())
|
||||
return@addFnDoc ObjInt(index.toLong())
|
||||
index++
|
||||
}
|
||||
ObjInt(-1L)
|
||||
}
|
||||
|
||||
addFn("toSet") {
|
||||
addFnDoc(
|
||||
name = "toSet",
|
||||
doc = "Collect elements of this iterable into a new set.",
|
||||
returns = type("lyng.Set"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
if( thisObj.isInstanceOf(ObjSet.type) )
|
||||
thisObj
|
||||
else {
|
||||
@ -71,7 +98,12 @@ val ObjIterable by lazy {
|
||||
}
|
||||
}
|
||||
|
||||
addFn("toMap") {
|
||||
addFnDoc(
|
||||
name = "toMap",
|
||||
doc = "Collect pairs into a map using [0] as key and [1] as value for each element.",
|
||||
returns = type("lyng.Map"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val result = ObjMap()
|
||||
thisObj.toFlow(this).collect { pair ->
|
||||
result.map[pair.getAt(this, 0)] = pair.getAt(this, 1)
|
||||
@ -79,7 +111,13 @@ val ObjIterable by lazy {
|
||||
result
|
||||
}
|
||||
|
||||
addFn("associateBy") {
|
||||
addFnDoc(
|
||||
name = "associateBy",
|
||||
doc = "Build a map from elements using the lambda result as key.",
|
||||
params = listOf(ParamDoc("keySelector")),
|
||||
returns = type("lyng.Map"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val association = requireOnlyArg<Statement>()
|
||||
val result = ObjMap()
|
||||
thisObj.toFlow(this).collect {
|
||||
@ -88,7 +126,13 @@ val ObjIterable by lazy {
|
||||
result
|
||||
}
|
||||
|
||||
addFn("forEach", isOpen = true) {
|
||||
addFnDoc(
|
||||
name = "forEach",
|
||||
doc = "Apply the lambda to each element in iteration order.",
|
||||
params = listOf(ParamDoc("action")),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||
val fn = requiredArg<Statement>(0)
|
||||
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
||||
@ -98,7 +142,14 @@ val ObjIterable by lazy {
|
||||
ObjVoid
|
||||
}
|
||||
|
||||
addFn("map", isOpen = true) {
|
||||
addFnDoc(
|
||||
name = "map",
|
||||
doc = "Transform elements by applying the given lambda.",
|
||||
params = listOf(ParamDoc("transform")),
|
||||
returns = type("lyng.List"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val fn = requiredArg<Statement>(0)
|
||||
val result = mutableListOf<Obj>()
|
||||
thisObj.toFlow(this).collect {
|
||||
@ -107,7 +158,13 @@ val ObjIterable by lazy {
|
||||
ObjList(result)
|
||||
}
|
||||
|
||||
addFn("take") {
|
||||
addFnDoc(
|
||||
name = "take",
|
||||
doc = "Take the first N elements and return them as a list.",
|
||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||
returns = type("lyng.List"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
var n = requireOnlyArg<ObjInt>().value.toInt()
|
||||
val result = mutableListOf<Obj>()
|
||||
if (n > 0) {
|
||||
@ -119,7 +176,12 @@ val ObjIterable by lazy {
|
||||
ObjList(result)
|
||||
}
|
||||
|
||||
addFn("isEmpty") {
|
||||
addFnDoc(
|
||||
name = "isEmpty",
|
||||
doc = "Whether the iterable has no elements.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjBool(
|
||||
thisObj.invokeInstanceMethod(this, "iterator")
|
||||
.invokeInstanceMethod(this, "hasNext").toBool()
|
||||
@ -127,7 +189,13 @@ val ObjIterable by lazy {
|
||||
)
|
||||
}
|
||||
|
||||
addFn("sortedWith") {
|
||||
addFnDoc(
|
||||
name = "sortedWith",
|
||||
doc = "Return a new list sorted using the provided comparator `(a, b) -> Int`.",
|
||||
params = listOf(ParamDoc("comparator")),
|
||||
returns = type("lyng.List"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val list = thisObj.callMethod<ObjList>(this, "toList")
|
||||
val comparator = requireOnlyArg<Statement>()
|
||||
list.quicksort { a, b ->
|
||||
@ -136,7 +204,12 @@ val ObjIterable by lazy {
|
||||
list
|
||||
}
|
||||
|
||||
addFn("reversed") {
|
||||
addFnDoc(
|
||||
name = "reversed",
|
||||
doc = "Return a new list with elements in reverse order.",
|
||||
returns = type("lyng.List"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val list = thisObj.callMethod<ObjList>(this, "toList")
|
||||
list.list.reverse()
|
||||
list
|
||||
|
||||
@ -17,6 +17,10 @@
|
||||
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
/**
|
||||
* Iterator should provide lyng-level iterator functions:
|
||||
*
|
||||
@ -28,15 +32,50 @@ package net.sergeych.lyng.obj
|
||||
*/
|
||||
val ObjIterator by lazy {
|
||||
ObjClass("Iterator").apply {
|
||||
addFn("cancelIteration", true) {
|
||||
// Base protocol methods; actual iterators override these.
|
||||
addFnDoc(
|
||||
name = "cancelIteration",
|
||||
doc = "Optional hint to stop iteration early and free resources.",
|
||||
returns = type("lyng.Void"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjVoid
|
||||
}
|
||||
addFn("hasNext", true) {
|
||||
addFnDoc(
|
||||
name = "hasNext",
|
||||
doc = "Whether another element is available.",
|
||||
returns = type("lyng.Bool"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
raiseNotImplemented("hasNext() is not implemented")
|
||||
}
|
||||
addFn("next", true) {
|
||||
addFnDoc(
|
||||
name = "next",
|
||||
doc = "Return the next element.",
|
||||
returns = type("lyng.Any"),
|
||||
isOpen = true,
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
raiseNotImplemented("next() is not implemented")
|
||||
}
|
||||
// Helper to consume iterator into a list
|
||||
addFnDoc(
|
||||
name = "toList",
|
||||
doc = "Consume this iterator and collect elements into a list.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val out = mutableListOf<Obj>()
|
||||
while (true) {
|
||||
val has = thisObj.invokeInstanceMethod(this, "hasNext").toBool()
|
||||
if (!has) break
|
||||
val v = thisObj.invokeInstanceMethod(this, "next")
|
||||
out += v
|
||||
}
|
||||
ObjList(out.toMutableList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,10 @@ import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addConstDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lyng.statement
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
@ -200,19 +204,32 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
return ObjList(decoder.decodeAnyList(scope))
|
||||
}
|
||||
}.apply {
|
||||
createField("size",
|
||||
statement {
|
||||
addConstDoc(
|
||||
name = "size",
|
||||
value = statement {
|
||||
(thisObj as ObjList).list.size.toObj()
|
||||
}
|
||||
},
|
||||
doc = "Number of elements in this list.",
|
||||
type = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
)
|
||||
createField("add",
|
||||
statement {
|
||||
addConstDoc(
|
||||
name = "add",
|
||||
value = statement {
|
||||
val l = thisAs<ObjList>().list
|
||||
for (a in args) l.add(a)
|
||||
ObjVoid
|
||||
}
|
||||
},
|
||||
doc = "Append one or more elements to the end of this list.",
|
||||
type = type("lyng.Callable"),
|
||||
moduleName = "lyng.stdlib"
|
||||
)
|
||||
addFn("insertAt") {
|
||||
addFnDoc(
|
||||
name = "insertAt",
|
||||
doc = "Insert elements starting at the given index.",
|
||||
params = listOf(ParamDoc("index", type("lyng.Int"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
if (args.size < 2) raiseError("addAt takes 2+ arguments")
|
||||
val l = thisAs<ObjList>()
|
||||
var index = requiredArg<ObjInt>(0).value.toInt()
|
||||
@ -220,7 +237,12 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
ObjVoid
|
||||
}
|
||||
|
||||
addFn("removeAt") {
|
||||
addFnDoc(
|
||||
name = "removeAt",
|
||||
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"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val self = thisAs<ObjList>()
|
||||
val start = requiredArg<ObjInt>(0).value.toInt()
|
||||
if (args.size == 2) {
|
||||
@ -231,7 +253,12 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
self
|
||||
}
|
||||
|
||||
addFn("removeLast") {
|
||||
addFnDoc(
|
||||
name = "removeLast",
|
||||
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"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val self = thisAs<ObjList>()
|
||||
if (args.isNotEmpty()) {
|
||||
val count = requireOnlyArg<ObjInt>().value.toInt()
|
||||
@ -242,7 +269,12 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
self
|
||||
}
|
||||
|
||||
addFn("removeRange") {
|
||||
addFnDoc(
|
||||
name = "removeRange",
|
||||
doc = "Remove a range of elements. Accepts a Range or (start, endInclusive). Returns the list.",
|
||||
params = listOf(ParamDoc("range")),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val self = thisAs<ObjList>()
|
||||
val list = self.list
|
||||
val range = requiredArg<Obj>(0)
|
||||
@ -283,19 +315,32 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
self
|
||||
}
|
||||
|
||||
addFn("sortWith") {
|
||||
addFnDoc(
|
||||
name = "sortWith",
|
||||
doc = "Sort this list in-place using a comparator function (a, b) -> Int.",
|
||||
params = listOf(ParamDoc("comparator")),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val comparator = requireOnlyArg<Statement>()
|
||||
thisAs<ObjList>().quicksort { a, b -> comparator.call(this, a, b).toInt() }
|
||||
ObjVoid
|
||||
}
|
||||
addFn("shuffle") {
|
||||
addFnDoc(
|
||||
name = "shuffle",
|
||||
doc = "Shuffle elements of this list in-place.",
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjList>().list.shuffle()
|
||||
ObjVoid
|
||||
}
|
||||
addFn("sum") {
|
||||
addFnDoc(
|
||||
name = "sum",
|
||||
doc = "Sum elements using dynamic '+' or optimized integer path. Returns null for empty lists.",
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val self = thisAs<ObjList>()
|
||||
val l = self.list
|
||||
if (l.isEmpty()) return@addFn ObjNull
|
||||
if (l.isEmpty()) return@addFnDoc ObjNull
|
||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||
// Fast path: all ints → accumulate as long
|
||||
var i = 0
|
||||
@ -312,10 +357,10 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
res = res.plus(this, l[i])
|
||||
i++
|
||||
}
|
||||
return@addFn res
|
||||
return@addFnDoc res
|
||||
}
|
||||
}
|
||||
return@addFn ObjInt(acc)
|
||||
return@addFnDoc ObjInt(acc)
|
||||
}
|
||||
// Generic path: dynamic '+' starting from first element
|
||||
var res: Obj = l[0]
|
||||
@ -326,9 +371,13 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
}
|
||||
res
|
||||
}
|
||||
addFn("min") {
|
||||
addFnDoc(
|
||||
name = "min",
|
||||
doc = "Minimum element by natural order. Returns null for empty lists.",
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val l = thisAs<ObjList>().list
|
||||
if (l.isEmpty()) return@addFn ObjNull
|
||||
if (l.isEmpty()) return@addFnDoc ObjNull
|
||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||
var i = 0
|
||||
var hasOnlyInts = true
|
||||
@ -343,7 +392,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (hasOnlyInts) return@addFn ObjInt(minVal)
|
||||
if (hasOnlyInts) return@addFnDoc ObjInt(minVal)
|
||||
}
|
||||
var res: Obj = l[0]
|
||||
var i = 1
|
||||
@ -354,9 +403,13 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
}
|
||||
res
|
||||
}
|
||||
addFn("max") {
|
||||
addFnDoc(
|
||||
name = "max",
|
||||
doc = "Maximum element by natural order. Returns null for empty lists.",
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val l = thisAs<ObjList>().list
|
||||
if (l.isEmpty()) return@addFn ObjNull
|
||||
if (l.isEmpty()) return@addFnDoc ObjNull
|
||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||
var i = 0
|
||||
var hasOnlyInts = true
|
||||
@ -371,7 +424,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (hasOnlyInts) return@addFn ObjInt(maxVal)
|
||||
if (hasOnlyInts) return@addFnDoc ObjInt(maxVal)
|
||||
}
|
||||
var res: Obj = l[0]
|
||||
var i = 1
|
||||
@ -382,21 +435,27 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||
}
|
||||
res
|
||||
}
|
||||
addFn("indexOf") {
|
||||
addFnDoc(
|
||||
name = "indexOf",
|
||||
doc = "Index of the first occurrence of the given element, or -1 if not found.",
|
||||
params = listOf(ParamDoc("element")),
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val l = thisAs<ObjList>().list
|
||||
val needle = args.firstAndOnly()
|
||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && needle is ObjInt) {
|
||||
var i = 0
|
||||
while (i < l.size) {
|
||||
val v = l[i]
|
||||
if (v is ObjInt && v.value == needle.value) return@addFn ObjInt(i.toLong())
|
||||
if (v is ObjInt && v.value == needle.value) return@addFnDoc ObjInt(i.toLong())
|
||||
i++
|
||||
}
|
||||
return@addFn ObjInt((-1).toLong())
|
||||
return@addFnDoc ObjInt((-1).toLong())
|
||||
}
|
||||
var i = 0
|
||||
while (i < l.size) {
|
||||
if (l[i].compareTo(this, needle) == 0) return@addFn ObjInt(i.toLong())
|
||||
if (l[i].compareTo(this, needle) == 0) return@addFnDoc ObjInt(i.toLong())
|
||||
i++
|
||||
}
|
||||
ObjInt((-1).toLong())
|
||||
|
||||
@ -21,6 +21,10 @@ import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
@ -72,9 +76,24 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
||||
)
|
||||
}
|
||||
}.apply {
|
||||
addFn("key") { thisAs<ObjMapEntry>().key }
|
||||
addFn("value") { thisAs<ObjMapEntry>().value }
|
||||
addFn("size") { 2.toObj() }
|
||||
addFnDoc(
|
||||
name = "key",
|
||||
doc = "Key component of this map entry.",
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjMapEntry>().key }
|
||||
addFnDoc(
|
||||
name = "value",
|
||||
doc = "Value component of this map entry.",
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjMapEntry>().value }
|
||||
addFnDoc(
|
||||
name = "size",
|
||||
doc = "Number of components in this entry (always 2).",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { 2.toObj() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,34 +203,77 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
||||
return ObjMap(keys.zip(values).toMap().toMutableMap())
|
||||
}
|
||||
}.apply {
|
||||
addFn("getOrNull") {
|
||||
addFnDoc(
|
||||
name = "getOrNull",
|
||||
doc = "Get value by key or return null if the key is absent.",
|
||||
params = listOf(ParamDoc("key")),
|
||||
returns = type("lyng.Any", nullable = true),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val key = args.firstAndOnly(pos)
|
||||
thisAs<ObjMap>().map.getOrElse(key) { ObjNull }
|
||||
}
|
||||
addFn("getOrPut") {
|
||||
addFnDoc(
|
||||
name = "getOrPut",
|
||||
doc = "Get value by key or compute, store, and return the default from a lambda.",
|
||||
params = listOf(ParamDoc("key"), ParamDoc("default")),
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val key = requiredArg<Obj>(0)
|
||||
thisAs<ObjMap>().map.getOrPut(key) {
|
||||
val lambda = requiredArg<Statement>(1)
|
||||
lambda.execute(this)
|
||||
}
|
||||
}
|
||||
addFn("size") {
|
||||
addFnDoc(
|
||||
name = "size",
|
||||
doc = "Number of entries in the map.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjMap>().map.size.toObj()
|
||||
}
|
||||
addFn("remove") {
|
||||
addFnDoc(
|
||||
name = "remove",
|
||||
doc = "Remove the entry by key and return the previous value or null if absent.",
|
||||
params = listOf(ParamDoc("key")),
|
||||
returns = type("lyng.Any", nullable = true),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjMap>().map.remove(requiredArg<Obj>(0))?.toObj() ?: ObjNull
|
||||
}
|
||||
addFn("clear") {
|
||||
addFnDoc(
|
||||
name = "clear",
|
||||
doc = "Remove all entries from this map. Returns the map.",
|
||||
returns = type("lyng.Map"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjMap>().map.clear()
|
||||
thisObj
|
||||
}
|
||||
addFn("keys") {
|
||||
addFnDoc(
|
||||
name = "keys",
|
||||
doc = "List of keys in this map.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjMap>().map.keys.toObj()
|
||||
}
|
||||
addFn("values") {
|
||||
addFnDoc(
|
||||
name = "values",
|
||||
doc = "List of values in this map.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjList(thisAs<ObjMap>().map.values.toMutableList())
|
||||
}
|
||||
addFn("iterator") {
|
||||
addFnDoc(
|
||||
name = "iterator",
|
||||
doc = "Iterator over map entries as MapEntry objects.",
|
||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.MapEntry"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjKotlinIterator(thisAs<ObjMap>().map.entries.iterator())
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,9 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Statement
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
class ObjMutex(val mutex: Mutex): Obj() {
|
||||
override val objClass = type
|
||||
@ -31,7 +34,13 @@ class ObjMutex(val mutex: Mutex): Obj() {
|
||||
return ObjMutex(Mutex())
|
||||
}
|
||||
}.apply {
|
||||
addFn("withLock") {
|
||||
addFnDoc(
|
||||
name = "withLock",
|
||||
doc = "Run the given lambda while holding the mutex and return its result.",
|
||||
params = listOf(ParamDoc("action")),
|
||||
returns = type("lyng.Any"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val f = requiredArg<Statement>(0)
|
||||
// Execute user lambda directly in the current scope to preserve the active scope
|
||||
// ancestry across suspension points. The lambda still constructs a ClosureScope
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Obj() {
|
||||
|
||||
@ -141,25 +144,60 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
|
||||
|
||||
companion object {
|
||||
val type = ObjClass("Range", ObjIterable).apply {
|
||||
addFn("start") {
|
||||
addFnDoc(
|
||||
name = "start",
|
||||
doc = "Start bound of the range or null if open.",
|
||||
returns = type("lyng.Any", nullable = true),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRange>().start ?: ObjNull
|
||||
}
|
||||
addFn("end") {
|
||||
addFnDoc(
|
||||
name = "end",
|
||||
doc = "End bound of the range or null if open.",
|
||||
returns = type("lyng.Any", nullable = true),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRange>().end ?: ObjNull
|
||||
}
|
||||
addFn("isOpen") {
|
||||
addFnDoc(
|
||||
name = "isOpen",
|
||||
doc = "Whether the range is open on either side (no start or no end).",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRange>().let { it.start == null || it.end == null }.toObj()
|
||||
}
|
||||
addFn("isIntRange") {
|
||||
addFnDoc(
|
||||
name = "isIntRange",
|
||||
doc = "True if both bounds are Int values.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRange>().isIntRange.toObj()
|
||||
}
|
||||
addFn("isCharRange") {
|
||||
addFnDoc(
|
||||
name = "isCharRange",
|
||||
doc = "True if both bounds are Char values.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRange>().isCharRange.toObj()
|
||||
}
|
||||
addFn("isEndInclusive") {
|
||||
addFnDoc(
|
||||
name = "isEndInclusive",
|
||||
doc = "Whether the end bound is inclusive.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRange>().isEndInclusive.toObj()
|
||||
}
|
||||
addFn("iterator") {
|
||||
addFnDoc(
|
||||
name = "iterator",
|
||||
doc = "Iterator over elements in this range (optimized for Int ranges).",
|
||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val self = thisAs<ObjRange>()
|
||||
if (net.sergeych.lyng.PerfFlags.RANGE_FAST_ITER) {
|
||||
val s = self.start
|
||||
@ -169,7 +207,7 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
|
||||
val endExclusive = (if (self.isEndInclusive) e.value.toInt() + 1 else e.value.toInt())
|
||||
// Only for ascending simple ranges; fall back otherwise
|
||||
if (start <= endExclusive) {
|
||||
return@addFn ObjFastIntRangeIterator(start, endExclusive)
|
||||
return@addFnDoc ObjFastIntRangeIterator(start, endExclusive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,9 @@ import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import net.sergeych.lyng.Pos
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.addConstDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lyng.statement
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
@ -111,13 +114,22 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
|
||||
ObjReal(decoder.unpackDouble())
|
||||
}.apply {
|
||||
createField(
|
||||
"roundToInt",
|
||||
statement(Pos.builtIn) {
|
||||
// roundToInt: number rounded to the nearest integer
|
||||
addConstDoc(
|
||||
name = "roundToInt",
|
||||
value = statement(Pos.builtIn) {
|
||||
(it.thisObj as ObjReal).value.roundToLong().toObj()
|
||||
},
|
||||
doc = "This real number rounded to the nearest integer.",
|
||||
type = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
)
|
||||
addFn("toInt") {
|
||||
addFnDoc(
|
||||
name = "toInt",
|
||||
doc = "Truncate this real number toward zero to an integer.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjInt(thisAs<ObjReal>().value.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,10 @@ package net.sergeych.lyng.obj
|
||||
import net.sergeych.lyng.PerfFlags
|
||||
import net.sergeych.lyng.RegexCache
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
class ObjRegex(val regex: Regex) : Obj() {
|
||||
override val objClass = type
|
||||
@ -43,13 +47,31 @@ class ObjRegex(val regex: Regex) : Obj() {
|
||||
return ObjRegex(re)
|
||||
}
|
||||
}.apply {
|
||||
addFn("matches") {
|
||||
addFnDoc(
|
||||
name = "matches",
|
||||
doc = "Whether the entire string matches this regular expression.",
|
||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjBool(args.firstAndOnly().toString().matches(thisAs<ObjRegex>().regex))
|
||||
}
|
||||
addFn("find") {
|
||||
addFnDoc(
|
||||
name = "find",
|
||||
doc = "Find the first match in the given string.",
|
||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||
returns = type("lyng.RegexMatch", nullable = true),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRegex>().find(requireOnlyArg<ObjString>())
|
||||
}
|
||||
addFn("findAll") {
|
||||
addFnDoc(
|
||||
name = "findAll",
|
||||
doc = "Find all matches in the given string.",
|
||||
params = listOf(ParamDoc("text", type("lyng.String"))),
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.RegexMatch"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val s = requireOnlyArg<ObjString>().value
|
||||
ObjList(thisAs<ObjRegex>().regex.findAll(s).map { ObjRegexMatch(it) }.toMutableList())
|
||||
}
|
||||
@ -101,13 +123,28 @@ class ObjRegexMatch(val match: MatchResult) : Obj() {
|
||||
scope.raiseError("RegexMatch can't be constructed directly")
|
||||
}
|
||||
}.apply {
|
||||
addFn("groups") {
|
||||
addFnDoc(
|
||||
name = "groups",
|
||||
doc = "List of captured groups with index 0 as the whole match.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.String"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRegexMatch>().objGroups
|
||||
}
|
||||
addFn("value") {
|
||||
addFnDoc(
|
||||
name = "value",
|
||||
doc = "The matched substring.",
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRegexMatch>().objValue
|
||||
}
|
||||
addFn("range") {
|
||||
addFnDoc(
|
||||
name = "range",
|
||||
doc = "Range of the match in the input (end-exclusive).",
|
||||
returns = type("lyng.Range"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjRegexMatch>().objRange
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,10 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
|
||||
class RingBuffer<T>(val maxSize: Int) : Iterable<T> {
|
||||
private val data = arrayOfNulls<Any>(maxSize)
|
||||
@ -90,19 +94,34 @@ class ObjRingBuffer(val capacity: Int) : Obj() {
|
||||
return ObjRingBuffer(scope.requireOnlyArg<ObjInt>().toInt())
|
||||
}
|
||||
}.apply {
|
||||
addFn("capacity") {
|
||||
thisAs<ObjRingBuffer>().capacity.toObj()
|
||||
}
|
||||
addFn("size") {
|
||||
thisAs<ObjRingBuffer>().buffer.size.toObj()
|
||||
}
|
||||
addFn("iterator") {
|
||||
addFnDoc(
|
||||
name = "capacity",
|
||||
doc = "Maximum number of elements the buffer can hold.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjRingBuffer>().capacity.toObj() }
|
||||
addFnDoc(
|
||||
name = "size",
|
||||
doc = "Current number of elements in the buffer.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjRingBuffer>().buffer.size.toObj() }
|
||||
addFnDoc(
|
||||
name = "iterator",
|
||||
doc = "Iterator over elements in insertion order (oldest to newest).",
|
||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val buffer = thisAs<ObjRingBuffer>().buffer
|
||||
ObjKotlinObjIterator(buffer.iterator())
|
||||
}
|
||||
addFn("add") {
|
||||
thisAs<ObjRingBuffer>().apply { buffer.add(requireOnlyArg<Obj>()) }
|
||||
}
|
||||
addFnDoc(
|
||||
name = "add",
|
||||
doc = "Append an element; if full, the oldest element is dropped.",
|
||||
params = listOf(ParamDoc("value", type("lyng.Any"))),
|
||||
returns = type("lyng.Void"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { thisAs<ObjRingBuffer>().apply { buffer.add(requireOnlyArg<Obj>()) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,10 @@
|
||||
package net.sergeych.lyng.obj
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.ParamDoc
|
||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
||||
import net.sergeych.lyng.miniast.addFnDoc
|
||||
import net.sergeych.lyng.miniast.type
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
import net.sergeych.lynon.LynonType
|
||||
@ -140,22 +144,55 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
|
||||
ObjSet(decoder.decodeAnyList(scope).toMutableSet())
|
||||
}.apply {
|
||||
addFn("size") {
|
||||
addFnDoc(
|
||||
name = "size",
|
||||
doc = "Number of elements in this set.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjSet>().set.size.toObj()
|
||||
}
|
||||
addFn("intersect") {
|
||||
addFnDoc(
|
||||
name = "intersect",
|
||||
doc = "Intersection with another set. Returns a new set.",
|
||||
params = listOf(ParamDoc("other", type("lyng.Set"))),
|
||||
returns = type("lyng.Set"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjSet>().mul(this, args.firstAndOnly())
|
||||
}
|
||||
addFn("iterator") {
|
||||
addFnDoc(
|
||||
name = "iterator",
|
||||
doc = "Iterator over elements of this set.",
|
||||
returns = TypeGenericDoc(type("lyng.Iterator"), listOf(type("lyng.Any"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjSet>().set.iterator().toObj()
|
||||
}
|
||||
addFn("union") {
|
||||
addFnDoc(
|
||||
name = "union",
|
||||
doc = "Union with another set or iterable. Returns a new set.",
|
||||
params = listOf(ParamDoc("other")),
|
||||
returns = type("lyng.Set"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjSet>().plus(this, args.firstAndOnly())
|
||||
}
|
||||
addFn("subtract") {
|
||||
addFnDoc(
|
||||
name = "subtract",
|
||||
doc = "Subtract another set or iterable from this set. Returns a new set.",
|
||||
params = listOf(ParamDoc("other")),
|
||||
returns = type("lyng.Set"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjSet>().minus(this, args.firstAndOnly())
|
||||
}
|
||||
addFn("remove") {
|
||||
addFnDoc(
|
||||
name = "remove",
|
||||
doc = "Remove one or more elements. Returns true if the set changed.",
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val set = thisAs<ObjSet>().set
|
||||
val n = set.size
|
||||
for( x in args.list ) set -= x
|
||||
|
||||
@ -24,6 +24,7 @@ import kotlinx.serialization.json.JsonPrimitive
|
||||
import net.sergeych.lyng.PerfFlags
|
||||
import net.sergeych.lyng.RegexCache
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.miniast.*
|
||||
import net.sergeych.lyng.statement
|
||||
import net.sergeych.lynon.LynonDecoder
|
||||
import net.sergeych.lynon.LynonEncoder
|
||||
@ -127,64 +128,155 @@ data class ObjString(val value: String) : Obj() {
|
||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
|
||||
ObjString(decoder.unpackBinaryData().decodeToString())
|
||||
}.apply {
|
||||
addFn("toInt") {
|
||||
addFnDoc(
|
||||
name = "toInt",
|
||||
doc = "Parse this string as an integer or throw if it is not a valid integer.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjInt(
|
||||
thisAs<ObjString>().value.toLongOrNull()
|
||||
?: raiseIllegalArgument("can't convert to int: $thisObj")
|
||||
)
|
||||
}
|
||||
addFn("startsWith") {
|
||||
addFnDoc(
|
||||
name = "startsWith",
|
||||
doc = "Whether this string starts with the given prefix.",
|
||||
params = listOf(ParamDoc("prefix", type("lyng.String"))),
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjBool(thisAs<ObjString>().value.startsWith(requiredArg<ObjString>(0).value))
|
||||
}
|
||||
addFn("endsWith") {
|
||||
addFnDoc(
|
||||
name = "endsWith",
|
||||
doc = "Whether this string ends with the given suffix.",
|
||||
params = listOf(ParamDoc("suffix", type("lyng.String"))),
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjBool(thisAs<ObjString>().value.endsWith(requiredArg<ObjString>(0).value))
|
||||
}
|
||||
addConst("length",
|
||||
statement { ObjInt(thisAs<ObjString>().value.length.toLong()) }
|
||||
addConstDoc(
|
||||
name = "length",
|
||||
value = statement { ObjInt(thisAs<ObjString>().value.length.toLong()) },
|
||||
doc = "Number of UTF-16 code units in this string.",
|
||||
type = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
)
|
||||
addFn("takeLast") {
|
||||
addFnDoc(
|
||||
name = "takeLast",
|
||||
doc = "Return a string with the last N characters.",
|
||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.takeLast(
|
||||
requiredArg<ObjInt>(0).toInt()
|
||||
).let(::ObjString)
|
||||
}
|
||||
addFn("take") {
|
||||
addFnDoc(
|
||||
name = "take",
|
||||
doc = "Return a string with the first N characters.",
|
||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.take(
|
||||
requiredArg<ObjInt>(0).toInt()
|
||||
).let(::ObjString)
|
||||
}
|
||||
addFn("drop") {
|
||||
addFnDoc(
|
||||
name = "drop",
|
||||
doc = "Drop the first N characters and return the remainder.",
|
||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.drop(
|
||||
requiredArg<ObjInt>(0).toInt()
|
||||
).let(::ObjString)
|
||||
}
|
||||
addFn("dropLast") {
|
||||
addFnDoc(
|
||||
name = "dropLast",
|
||||
doc = "Drop the last N characters and return the remainder.",
|
||||
params = listOf(ParamDoc("n", type("lyng.Int"))),
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.dropLast(
|
||||
requiredArg<ObjInt>(0).toInt()
|
||||
).let(::ObjString)
|
||||
}
|
||||
addFn("lower") {
|
||||
addFnDoc(
|
||||
name = "lower",
|
||||
doc = "Lowercase version of this string (default locale).",
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.lowercase().let(::ObjString)
|
||||
}
|
||||
addFn("upper") {
|
||||
addFnDoc(
|
||||
name = "upper",
|
||||
doc = "Uppercase version of this string (default locale).",
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.uppercase().let(::ObjString)
|
||||
}
|
||||
addFn("characters") {
|
||||
addFnDoc(
|
||||
name = "characters",
|
||||
doc = "List of characters of this string.",
|
||||
returns = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Char"))),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjList(
|
||||
thisAs<ObjString>().value.map { ObjChar(it) }.toMutableList()
|
||||
)
|
||||
}
|
||||
addFn("last") {
|
||||
addFnDoc(
|
||||
name = "last",
|
||||
doc = "The last character of this string or throw if the string is empty.",
|
||||
returns = type("lyng.Char"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjChar(thisAs<ObjString>().value.lastOrNull() ?: raiseNoSuchElement("empty string"))
|
||||
}
|
||||
addFn("encodeUtf8") { ObjBuffer(thisAs<ObjString>().value.encodeToByteArray().asUByteArray()) }
|
||||
addFn("size") { ObjInt(thisAs<ObjString>().value.length.toLong()) }
|
||||
addFn("toReal") {
|
||||
addFnDoc(
|
||||
name = "encodeUtf8",
|
||||
doc = "Encode this string as UTF-8 bytes.",
|
||||
returns = type("lyng.Buffer"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { ObjBuffer(thisAs<ObjString>().value.encodeToByteArray().asUByteArray()) }
|
||||
addFnDoc(
|
||||
name = "size",
|
||||
doc = "Alias for length: the number of characters (code units) in this string.",
|
||||
returns = type("lyng.Int"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) { ObjInt(thisAs<ObjString>().value.length.toLong()) }
|
||||
addFnDoc(
|
||||
name = "toReal",
|
||||
doc = "Parse this string as a real number (floating point).",
|
||||
returns = type("lyng.Real"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
ObjReal(thisAs<ObjString>().value.toDouble())
|
||||
}
|
||||
addFn("trim") {
|
||||
addFnDoc(
|
||||
name = "trim",
|
||||
doc = "Return a copy of this string with leading and trailing whitespace removed.",
|
||||
returns = type("lyng.String"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
thisAs<ObjString>().value.trim().let(::ObjString)
|
||||
}
|
||||
addFn("matches") {
|
||||
addFnDoc(
|
||||
name = "matches",
|
||||
doc = "Whether this string matches the given regular expression or pattern string.",
|
||||
params = listOf(ParamDoc("pattern")),
|
||||
returns = type("lyng.Bool"),
|
||||
moduleName = "lyng.stdlib"
|
||||
) {
|
||||
val s = requireOnlyArg<Obj>()
|
||||
val self = thisAs<ObjString>().value
|
||||
ObjBool(
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
package lyng.stdlib
|
||||
|
||||
/*
|
||||
Wrap a builder into a zero-argument thunk that computes once and caches the result.
|
||||
The first call invokes builder() and stores the value; subsequent calls return the cached value.
|
||||
*/
|
||||
fun cached(builder) {
|
||||
var calculated = false
|
||||
var value = null
|
||||
@ -11,7 +15,7 @@ fun cached(builder) {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
/* Filter elements of this iterable using the provided predicate. */
|
||||
fun Iterable.filter(predicate) {
|
||||
val list = this
|
||||
flow {
|
||||
@ -23,17 +27,20 @@ fun Iterable.filter(predicate) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip the first N elements of this iterable. */
|
||||
fun Iterable.drop(n) {
|
||||
var cnt = 0
|
||||
filter { cnt++ >= n }
|
||||
}
|
||||
|
||||
/* Return the first element or throw if the iterable is empty. */
|
||||
fun Iterable.first() {
|
||||
val i = iterator()
|
||||
if( !i.hasNext() ) throw NoSuchElementException()
|
||||
i.next().also { i.cancelIteration() }
|
||||
}
|
||||
|
||||
/* Return the last element or throw if the iterable is empty. */
|
||||
fun Iterable.last() {
|
||||
var found = false
|
||||
var element = null
|
||||
@ -45,6 +52,7 @@ fun Iterable.last() {
|
||||
element
|
||||
}
|
||||
|
||||
/* Emit all but the last N elements of this iterable. */
|
||||
fun Iterable.dropLast(n) {
|
||||
val list = this
|
||||
val buffer = RingBuffer(n)
|
||||
@ -57,12 +65,14 @@ fun Iterable.dropLast(n) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the last N elements of this iterable as a buffer/list. */
|
||||
fun Iterable.takeLast(n) {
|
||||
val buffer = RingBuffer(n)
|
||||
for( item in this ) buffer += item
|
||||
buffer
|
||||
}
|
||||
|
||||
/* Join elements into a string with a separator (prefix parameter) and optional transformer. */
|
||||
fun Iterable.joinToString(prefix=" ", transformer=null) {
|
||||
var result = null
|
||||
for( part in this ) {
|
||||
@ -73,6 +83,7 @@ fun Iterable.joinToString(prefix=" ", transformer=null) {
|
||||
result ?: ""
|
||||
}
|
||||
|
||||
/* Return true if any element matches the predicate. */
|
||||
fun Iterable.any(predicate): Bool {
|
||||
for( i in this ) {
|
||||
if( predicate(i) )
|
||||
@ -80,10 +91,12 @@ fun Iterable.any(predicate): Bool {
|
||||
} else false
|
||||
}
|
||||
|
||||
/* Return true if all elements match the predicate. */
|
||||
fun Iterable.all(predicate): Bool {
|
||||
!any { !predicate(it) }
|
||||
}
|
||||
|
||||
/* Sum all elements; returns null for empty collections. */
|
||||
fun Iterable.sum() {
|
||||
val i = iterator()
|
||||
if( i.hasNext() ) {
|
||||
@ -94,6 +107,7 @@ fun Iterable.sum() {
|
||||
else null
|
||||
}
|
||||
|
||||
/* Sum mapped values of elements; returns null for empty collections. */
|
||||
fun Iterable.sumOf(f) {
|
||||
val i = iterator()
|
||||
if( i.hasNext() ) {
|
||||
@ -104,6 +118,7 @@ fun Iterable.sumOf(f) {
|
||||
else null
|
||||
}
|
||||
|
||||
/* Minimum value of the given function applied to elements of the collection. */
|
||||
fun Iterable.minOf( lambda ) {
|
||||
val i = iterator()
|
||||
var minimum = lambda( i.next() )
|
||||
@ -114,9 +129,7 @@ fun Iterable.minOf( lambda ) {
|
||||
minimum
|
||||
}
|
||||
|
||||
/*
|
||||
Return maximum value of the given function applied to elements of the collection.
|
||||
*/
|
||||
/* Maximum value of the given function applied to elements of the collection. */
|
||||
fun Iterable.maxOf( lambda ) {
|
||||
val i = iterator()
|
||||
var maximum = lambda( i.next() )
|
||||
@ -127,41 +140,50 @@ fun Iterable.maxOf( lambda ) {
|
||||
maximum
|
||||
}
|
||||
|
||||
/* Return elements sorted by natural order. */
|
||||
fun Iterable.sorted() {
|
||||
sortedWith { a, b -> a <=> b }
|
||||
}
|
||||
|
||||
/* Return elements sorted by the key selector. */
|
||||
fun Iterable.sortedBy(predicate) {
|
||||
sortedWith { a, b -> predicate(a) <=> predicate(b) }
|
||||
}
|
||||
|
||||
/* Return a shuffled copy of the iterable as a list. */
|
||||
fun Iterable.shuffled() {
|
||||
toList().apply { shuffle() }
|
||||
}
|
||||
|
||||
/* Return string representation like [a,b,c]. */
|
||||
fun List.toString() {
|
||||
"[" + joinToString(",") + "]"
|
||||
}
|
||||
|
||||
/* Sort list in-place by key selector. */
|
||||
fun List.sortBy(predicate) {
|
||||
sortWith { a, b -> predicate(a) <=> predicate(b) }
|
||||
}
|
||||
|
||||
/* Sort list in-place by natural order. */
|
||||
fun List.sort() {
|
||||
sortWith { a, b -> a <=> b }
|
||||
}
|
||||
|
||||
/* Represents a single stack trace element. */
|
||||
class StackTraceEntry(
|
||||
val sourceName: String,
|
||||
val line: Int,
|
||||
val column: Int,
|
||||
val sourceString: String
|
||||
) {
|
||||
/* Formatted representation: source:line:column: text. */
|
||||
fun toString() {
|
||||
"%s:%d:%d: %s"(sourceName, line, column, sourceString.trim())
|
||||
}
|
||||
}
|
||||
|
||||
/* Print this exception and its stack trace to standard output. */
|
||||
fun Exception.printStackTrace() {
|
||||
println(this)
|
||||
for( entry in stackTrace() ) {
|
||||
@ -169,6 +191,7 @@ fun Exception.printStackTrace() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Compile this string into a regular expression. */
|
||||
fun String.re() { Regex(this) }
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user