Compare commits

..

16 Commits

Author SHA1 Message Date
fa91afa92b migrated to Kotlin 2.3.0 2025-12-18 01:33:49 +01:00
00503fa105 cosmetics and docs 2025-12-17 22:12:08 +01:00
f7a4497f3f Migrate to kotlin.time.Instant and kotlin.time.Clock, update dependencies to support Kotlin 2.2, and adopt JVM 17. 2025-12-17 19:53:36 +01:00
1aef64e0fa Add lowercase and uppercase string functions, update documentation accordingly. 2025-12-17 17:59:51 +01:00
5d8fdce637 Move qualified identifier resolution to Scope as resolveQualifiedIdentifier, replace inline logic in LynonDecoder. 2025-12-14 00:20:43 +01:00
5a8881bfd5 Refactor decodeClassObj to mimic compiler behavior for qualified names, add evaluateQualifiedNameAsCompiled. 2025-12-14 00:06:46 +01:00
d487886c8f some more trace on strange decpdeClassObj behavior 2025-12-13 23:41:52 +01:00
180471e4cd Merge remote-tracking branch 'origin/fix_decodeClassObj' 2025-12-13 23:20:55 +01:00
71a37a2906 Revert "Improve decodeClassObj class resolution in LynonDecoder, add fallback lookup mechanisms, and refine related tests"
This reverts commit dd1a1544c6d49641783d221b15e23c7150010161.
2025-12-13 23:14:12 +01:00
ab05f83e77 Revert "Add documentation for Lynon class-name resolution behavior and future plans for fully-qualified name support"
This reverts commit a2d26fc7775508c4fc9c5bb62496ad4b88d74662.
2025-12-13 23:13:56 +01:00
9e11519608 revert to wirking ugly fix for decodeClassObj 2025-12-13 23:11:28 +01:00
a2d26fc777 Add documentation for Lynon class-name resolution behavior and future plans for fully-qualified name support 2025-12-13 17:16:10 +01:00
dd1a1544c6 Improve decodeClassObj class resolution in LynonDecoder, add fallback lookup mechanisms, and refine related tests 2025-12-13 17:12:44 +01:00
fba44622e5 Refactor toString implementations to support Scope context, add inspect, and improve assertions readability. 2025-12-13 13:48:57 +01:00
2737aaa14e add mapNotNull to ObjIterable with documentation 2025-12-12 13:48:02 +01:00
bce88ced43 Merge pull request 'fix/scope-parent-cycle' (#92) from fix/scope-parent-cycle into master
Reviewed-on: #92
2025-12-11 03:09:45 +03:00
26 changed files with 269 additions and 80 deletions

View File

@ -0,0 +1,28 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tests in 'lyng.lynglib.jvmTest'" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":lynglib:cleanJvmTest" />
<option value=":lynglib:jvmTest" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<ExternalSystemDebugDisabled>false</ExternalSystemDebugDisabled>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<GradleProfilingDisabled>false</GradleProfilingDisabled>
<GradleCoverageDisabled>false</GradleCoverageDisabled>
<method v="2" />
</configuration>
</component>

View File

@ -1,3 +1,20 @@
<!--
~ Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="lyng:site [jsBrowserDevelopmentRun]" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
@ -17,8 +34,11 @@
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<ExternalSystemDebugDisabled>false</ExternalSystemDebugDisabled>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<GradleProfilingDisabled>true</GradleProfilingDisabled>
<GradleCoverageDisabled>true</GradleCoverageDisabled>
<method v="2" />
</configuration>
</component>

View File

@ -101,7 +101,7 @@ die() { echo "ERROR: $*" 1>&2 ; exit 1; }
# Update the IDEA plugin download link in docs (temporarily), then build, then restore the doc
updateIdeaPluginDownloadLink || echo "WARN: proceeding without updating IDEA plugin download link"
./gradlew site:clean site:jsBrowserDistribution
./gradlew site:jsBrowserDistribution
BUILD_RC=$?
# Always restore original doc if backup exists

View File

@ -68,6 +68,20 @@ These, again, does the thing:
>>> void
## map and mapNotNull
Used to transform either the whole iterable stream or also skipping som elements from it:
val source = [1,2,3,4]
// transform every element to string or null:
assertEquals(["n1", "n2", null, "n4"], source.map { if( it == 3 ) null else "n"+it } )
// transform every element to stirng, skipping 3:
assertEquals(["n1", "n2", "n4"], source.mapNotNull { if( it == 3 ) null else "n"+it } )
>>> void
## Instance methods:
| fun/method | description |

View File

@ -0,0 +1,25 @@
# Migration of Instant and Clock
## History
Before kotlin 2.0, there was an excellent library, kotlinx.datetime, which was widely used everywhere, also in Lyng and its dependencies.
When kotlin 2.0 was released, or soon after, JetBrains made an exptic decision to remove `Instant` and `Clock` from kotlinx.datetime and replace it with _yet experimental_ analogs in `kotlin.time`.
The problem is, these were not quite the same (these weren't `@Serializable`!), so people didn't migrate with ease. Okay, then JetBrains decided to not only deprecate it but also make them unusable on Apple targets. It sort of split auditories of many published libraries to those who hate JetBrains and Apple and continue to use 1.9-2.0 compatible versions that no longer work with Kotlin 2.2 on Apple targets (but work pretty well with earlier Kotlin or on other platforms).
Later JetBrains added serializers for their new `Instant` and `Clock` types, but strangely not in the stdlib, but in newer versions of `kotlinx.serialization`. This means that plain upgrade of dependencies to 2.2 is not enough to make them work.
## Solution
We hereby publish a new version of Lyng, 1.0.8-SNAPSHOT, which uses `ktlin.time.Instant` and `kotlin.time.Clock` instead of `kotlinx.datetime.Instant` and `kotlinx.datetime.Clock; it is in other aspects compatible also with Lynon encoded binaries. Still you might need to migrate your code to use `kotlinx.datetime` types.
So, if you are getting errors with new version, plase do:
- upgrade to Kotlin 2.2
- upgrade to Lyng 1.0.8-SNAPSHOT
- replace in your code imports (or other uses) of`kotlinx.datetime.Clock` to `kotlin.time.Clock` and `kotlinx.datetime.Instant` to `kotlin.time.Instant`.
This should solve the problem and hopefully we'll see no more suh a brillant ideas from IDEA ideologspersons.
Sorry for inconvenicence and send a ray of hate to JetBrains ;)

View File

@ -1379,9 +1379,9 @@ Part match:
Typical set of String functions includes:
| fun/prop | description / notes |
|--------------------|------------------------------------------------------------|
| lower() | change case to unicode upper |
| upper() | change case to unicode lower |
|----------------------|------------------------------------------------------------|
| lower(), lowercase() | change case to unicode upper |
| upper(), uppercase() | change case to unicode lower |
| trim() | trim space chars from both ends |
| startsWith(prefix) | true if starts with a prefix |
| endsWith(prefix) | true if ends with a prefix |

View File

@ -1,11 +1,11 @@
[versions]
agp = "8.5.2"
clikt = "5.0.3"
kotlin = "2.2.21"
kotlin = "2.3.0"
android-minSdk = "24"
android-compileSdk = "34"
kotlinx-coroutines = "1.10.2"
mp_bintools = "0.1.12"
mp_bintools = "0.3.2"
firebaseCrashlyticsBuildtools = "3.0.3"
okioVersion = "3.10.2"
compiler = "3.2.0-alpha11"

View File

@ -38,7 +38,7 @@ kotlin {
publishLibraryVariants("release")
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
jvmTarget.set(JvmTarget.JVM_17)
}
}
iosX64()
@ -71,11 +71,7 @@ kotlin {
sourceSets {
all {
languageSettings.optIn("kotlin.ExperimentalUnsignedTypes")
// languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
// Correct opt-in markers for coroutines
// languageSettings.optIn("kotlinx.coroutines.DelicateCoroutinesApi")
// languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
// languageSettings.optIn("kotlinx.coroutines.FlowPreview")
languageSettings.optIn("kotlin.time.ExperimentalTime")
}
val commonMain by getting {
dependencies {
@ -115,8 +111,8 @@ android {
minSdk = libs.versions.android.minSdk.get().toInt()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
lint {
// Prevent Android Lint from failing the build due to Kotlin toolchain

View File

@ -163,7 +163,7 @@ private suspend fun buildFsModule(module: ModuleScope, policy: FsAccessPolicy) {
fsGuard {
val self = this.thisObj as ObjPath
val m = self.ensureMetadata()
m.createdAtMillis?.let { ObjInstant(kotlinx.datetime.Instant.fromEpochMilliseconds(it)) } ?: ObjNull
m.createdAtMillis?.let { ObjInstant(kotlin.time.Instant.fromEpochMilliseconds(it)) } ?: ObjNull
}
}
// createdAtMillis(): Int? — milliseconds since epoch or null

View File

@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych"
version = "1.0.7-SNAPSHOT"
version = "1.0.8-SNAPSHOT"
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
@ -51,7 +51,7 @@ kotlin {
publishLibraryVariants("release")
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
jvmTarget.set(JvmTarget.JVM_17)
}
}
iosX64()
@ -89,16 +89,16 @@ kotlin {
languageSettings.optIn("kotlinx.coroutines.DelicateCoroutinesApi")
languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
languageSettings.optIn("kotlinx.coroutines.FlowPreview")
languageSettings.optIn("kotlin.time.ExperimentalTime")
}
val commonMain by getting {
kotlin.srcDir("$buildDir/generated/buildConfig/commonMain/kotlin")
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
//put your multiplatform dependencies here
api(libs.kotlinx.coroutines.core)
api(libs.mp.bintools)
api("net.sergeych:mp_stools:1.5.2")
}
}
val commonTest by getting {
@ -116,6 +116,18 @@ kotlin {
}
}
android {
namespace = "net.sergeych.lynglib"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
// ---- Build-time generation of stdlib text from .lyng files into a Kotlin constant ----
// Implemented as a proper task type compatible with Gradle Configuration Cache

View File

@ -428,13 +428,13 @@ open class Scope(
}
inline fun addVoidFn(vararg names: String, crossinline fn: suspend Scope.() -> Unit) {
addFn<ObjVoid>(*names) {
addFn(*names) {
fn(this)
ObjVoid
}
}
inline fun <reified T : Obj> addFn(vararg names: String, crossinline fn: suspend Scope.() -> T) {
fun addFn(vararg names: String, fn: suspend Scope.() -> Obj) {
val newFn = object : Statement() {
override val pos: Pos = Pos.builtIn
@ -516,6 +516,23 @@ open class Scope(
open fun applyClosure(closure: Scope): Scope = ClosureScope(this, closure)
/**
* Resolve and evaluate a qualified identifier exactly as compiled code would.
* For input like `A.B.C`, it builds the same ObjRef chain the compiler emits:
* `LocalVarRef("A", Pos.builtIn)` followed by `FieldRef` for each segment, then evaluates it.
* This mirrors `eval("A.B.C")` resolution semantics without invoking the compiler.
*/
suspend fun resolveQualifiedIdentifier(qualifiedName: String): Obj {
val trimmed = qualifiedName.trim()
if (trimmed.isEmpty()) raiseSymbolNotFound("empty identifier")
val parts = trimmed.split('.')
var ref: ObjRef = LocalVarRef(parts[0], Pos.builtIn)
for (i in 1 until parts.size) {
ref = FieldRef(ref, parts[i], false)
}
return ref.evalValue(this)
}
companion object {
fun new(): Scope =

View File

@ -155,52 +155,65 @@ class Script(
sqrt(args.firstAndOnly().toDouble())
)
}
addFn( "abs" ) {
addFn("abs") {
val x = args.firstAndOnly()
if( x is ObjInt) ObjInt( x.value.absoluteValue ) else ObjReal( x.toDouble().absoluteValue )
if (x is ObjInt) ObjInt(x.value.absoluteValue) else ObjReal(x.toDouble().absoluteValue)
}
addVoidFn("assert") {
val cond = requiredArg<ObjBool>(0)
val message = if( args.size > 1 )
val message = if (args.size > 1)
": " + (args[1] as Statement).execute(this).toString(this).value
else ""
if( !cond.value == true )
if (!cond.value == true)
raiseError(ObjAssertionFailedException(this, "Assertion failed$message"))
}
addVoidFn("assertEquals") {
val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1)
if( a.compareTo(this, b) != 0 )
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
if (a.compareTo(this, b) != 0)
raiseError(
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
)
)
}
// alias used in tests
addVoidFn("assertEqual") {
val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1)
if( a.compareTo(this, b) != 0 )
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
if (a.compareTo(this, b) != 0)
raiseError(
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"
)
)
}
addVoidFn("assertNotEquals") {
val a = requiredArg<Obj>(0)
val b = requiredArg<Obj>(1)
if( a.compareTo(this, b) == 0 )
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"))
if (a.compareTo(this, b) == 0)
raiseError(
ObjAssertionFailedException(
this,
"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"
)
)
}
addFn("assertThrows") {
val code = requireOnlyArg<Statement>()
val result =try {
val result = try {
code.execute(this)
null
}
catch( e: ExecutionError ) {
} catch (e: ExecutionError) {
e.errorObject
}
catch (_: ScriptError) {
} catch (_: ScriptError) {
ObjNull
}
result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown"))
result ?: raiseError(ObjAssertionFailedException(this, "Expected exception but nothing was thrown"))
}
addFn("dynamic") {
@ -209,7 +222,7 @@ class Script(
addFn("require") {
val condition = requiredArg<ObjBool>(0)
if( !condition.value ) {
if (!condition.value) {
val message = args.list.getOrNull(1)?.toString() ?: "requirement not met"
raiseIllegalArgument(message)
}
@ -217,7 +230,7 @@ class Script(
}
addFn("check") {
val condition = requiredArg<ObjBool>(0)
if( !condition.value ) {
if (!condition.value) {
val message = args.list.getOrNull(1)?.toString() ?: "check failed"
raiseIllegalState(message)
}
@ -340,7 +353,7 @@ class Script(
doc = "Suspend for the given time. Accepts Duration, Int seconds, or Real seconds."
) {
val a = args.firstAndOnly()
when(a) {
when (a) {
is ObjInt -> delay(a.value * 1000)
is ObjReal -> delay((a.value * 1000).roundToLong())
is ObjDuration -> delay(a.duration)

View File

@ -131,7 +131,7 @@ object CompletionEngineLight {
}
is MiniClassDecl -> add(CompletionItem(d.name, Kind.Class_))
is MiniValDecl -> add(CompletionItem(d.name, Kind.Value, typeText = typeOf(d.type)))
else -> add(CompletionItem(d.name, Kind.Value))
// else -> add(CompletionItem(d.name, Kind.Value))
}
}

View File

@ -197,9 +197,20 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
!it.key.contains("::") && it.value.visibility.isPublic && it.value.type.serializable
}
override fun toString(): String {
val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",")
return "${objClass.className}($fields)"
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString {
return ObjString(buildString {
append("${objClass.className}(")
var first = true
for ((name, value) in publicFields) {
if (first) first = false else append(",")
append("$name=${value.value.toString(scope)}")
}
append(")")
})
}
override suspend fun inspect(scope: Scope): String {
return toString(scope).value
}
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {

View File

@ -17,10 +17,6 @@
package net.sergeych.lyng.obj
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.isDistantFuture
import kotlinx.datetime.isDistantPast
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import net.sergeych.lyng.Scope
@ -28,6 +24,10 @@ import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder
import net.sergeych.lynon.LynonSettings
import net.sergeych.lynon.LynonType
import kotlin.time.Clock
import kotlin.time.Instant
import kotlin.time.isDistantFuture
import kotlin.time.isDistantPast
class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTruncateMode=LynonSettings.InstantTruncateMode.Microsecond) : Obj() {
override val objClass: ObjClass get() = type

View File

@ -158,6 +158,23 @@ val ObjIterable by lazy {
ObjList(result)
}
addFnDoc(
name = "mapNotNull",
doc = "Transform elements by applying the given lambda unless it returns null.",
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 {
val transformed = fn.call(this, it)
if( transformed != ObjNull) result += transformed
}
ObjList(result)
}
addFnDoc(
name = "take",
doc = "Take the first N elements and return them as a list.",

View File

@ -198,6 +198,18 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
return JsonArray(list.map { it.toJson(scope) })
}
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString {
return ObjString(buildString {
append("[")
var first = true
for (v in list) {
if (first) first = false else append(",")
append(v.toString(scope).value)
}
append("]")
})
}
companion object {
val type = object : ObjClass("List", ObjArray) {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {

View File

@ -52,8 +52,8 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
else -> scope.raiseIndexOutOfBounds()
}
override fun toString(): String {
return "$key=>$value"
override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString {
return ObjString("(${key.toString(scope).value} => ${value.toString(scope).value})")
}
override val objClass = type

View File

@ -1625,7 +1625,7 @@ class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
when (elements) {
is ObjList -> {
// Grow underlying array once when possible
if (list is ArrayList) list.ensureCapacity(list.size + elements.list.size)
list.ensureCapacity(list.size + elements.list.size)
list.addAll(elements.list)
}
else -> scope.raiseError("Spread element must be list")

View File

@ -216,6 +216,14 @@ data class ObjString(val value: String) : Obj() {
) {
thisAs<ObjString>().value.lowercase().let(::ObjString)
}
addFnDoc(
name = "lowercase",
doc = "Lowercase version of this string (default locale).",
returns = type("lyng.String"),
moduleName = "lyng.stdlib"
) {
thisAs<ObjString>().value.lowercase().let(::ObjString)
}
addFnDoc(
name = "upper",
doc = "Uppercase version of this string (default locale).",
@ -224,6 +232,14 @@ data class ObjString(val value: String) : Obj() {
) {
thisAs<ObjString>().value.uppercase().let(::ObjString)
}
addFnDoc(
name = "uppercase",
doc = "Uppercase version of this string (default locale).",
returns = type("lyng.String"),
moduleName = "lyng.stdlib"
) {
thisAs<ObjString>().value.uppercase().let(::ObjString)
}
addFnDoc(
name = "characters",
doc = "List of characters of this string.",

View File

@ -82,8 +82,16 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
if (it !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}")
it
} ?: scope.raiseSymbolNotFound("can't deserialize: not found type $className")
} ?: run {
// Use Scope API that mirrors compiler-emitted ObjRef chain for qualified identifiers
val evaluated = scope.resolveQualifiedIdentifier(className.value)
if (evaluated !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${evaluated::class.simpleName}")
evaluated
}
}
// helper moved to Scope as resolveQualifiedIdentifier
suspend fun decodeAnyList(scope: Scope, fixedSize: Int? = null): MutableList<Obj> {
return if (bin.getBit() == 1) {

View File

@ -17,7 +17,7 @@
package net.sergeych.tools
import kotlinx.datetime.Clock
import kotlin.time.Clock
inline fun bm(text: String="", f: ()->Unit) {
val start = Clock.System.now()

View File

@ -23,8 +23,6 @@ import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
@ -36,7 +34,9 @@ import net.sergeych.lyng.pacman.InlineSourcesImportProvider
import net.sergeych.mp_tools.globalDefer
import net.sergeych.tools.bm
import kotlin.test.*
import kotlin.time.Clock
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com

View File

@ -18,12 +18,12 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock
import net.sergeych.lyng.Scope
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.io.path.extension
import kotlin.test.Test
import kotlin.time.Clock
suspend fun executeSampleTests(fileName: String) {
val sample = withContext(Dispatchers.IO) {

View File

@ -36,7 +36,7 @@ fun HomePage() {
// Benefits pills
listOf(
"Clean, familiar syntax",
"Immutable-first collections",
"both FP and OOP",
"Batteries-included standard library",
"Embeddable and testable"
).forEach { b ->

View File

@ -329,7 +329,7 @@
<!-- Top-left version ribbon -->
<div class="corner-ribbon bg-danger text-white">
<span style="margin-left: -5em">
v1.0.6-SNAPSHOT
v1.0.8-SNAPSHOT
</span>
</div>
<!-- Fixed top navbar for the whole site -->