diff --git a/lynglib/src/commonTest/kotlin/WebsiteSamplesTest.kt b/lynglib/src/commonTest/kotlin/WebsiteSamplesTest.kt new file mode 100644 index 0000000..ea796cb --- /dev/null +++ b/lynglib/src/commonTest/kotlin/WebsiteSamplesTest.kt @@ -0,0 +1,260 @@ +/* + * Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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. + * + */ + +package net.sergeych.lyng + +import kotlinx.coroutines.test.runTest +import net.sergeych.lyng.obj.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class WebsiteSamplesTest { + + @Test + fun testEverythingIsAnExpression() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Everything is an expression + val x: Int = 10 + val status: String = if (x > 0) "Positive" else "Zero or Negative" + + // Even loops return values! + val result = for (i in 1..5) { + if (i == 3) break "Found 3!" + } else "Not found" + + result + """.trimIndent()) + assertEquals("Found 3!", (result as ObjString).value) + } + + @Test + fun testFunctionalPower() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Functional power with generics and collections + val squares: List = (1..10) + .filter { it % 2 == 0 } + .map { it * it } + + squares + """.trimIndent()) + assertTrue(result is ObjList) + val list = (result as ObjList).list.map { (it as ObjInt).value } + assertEquals(listOf(4L, 16L, 36L, 64L, 100L), list) + } + + @Test + fun testGenericsAndTypeAliases() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Generics and type aliases + type Num = Int | Real + + class Box(val value: T) { + fun get(): T = value + } + + val intBox = Box(42) + val realBox = Box(3.14) + [intBox.get(), realBox.get()] + """.trimIndent()) + assertTrue(result is ObjList) + val l = (result as ObjList).list + assertEquals(42L, (l[0] as ObjInt).value) + assertEquals(3.14, (l[1] as ObjReal).value) + } + + @Test + fun testStrictCompileTimeTypes() = runTest { + val scope = Script.newScope() + scope.eval(""" + // Strict compile-time types and symbol resolution + fun greet(name: String, count: Int) { + for (i in 1..count) { + println("Hello, " + name + "!") + } + } + + greet("Lyng", 3) + """.trimIndent()) + } + + @Test + fun testFlexibleMapLiterals() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Flexible map literals and shorthands + val id = 101 + val name = "Lyng" + val base = { id:, name: } // Shorthand for id: id, name: name + + val full = { ...base, version: "1.5.0-SNAPSHOT", status: "active" } + full + """.trimIndent()) + assertTrue(result is ObjMap) + val m = (result as ObjMap).map + assertEquals(101L, (m[ObjString("id")] as ObjInt).value) + assertEquals("Lyng", (m[ObjString("name")] as ObjString).value) + assertEquals("1.5.0-SNAPSHOT", (m[ObjString("version")] as ObjString).value) + assertEquals("active", (m[ObjString("status")] as ObjString).value) + } + + @Test + fun testModernNullSafety() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Modern null safety + var config: Map? = null + config ?= { timeout: 30 } // Assign only if null + + val timeout = config?["timeout"] ?: 60 + timeout + """.trimIndent()) + assertEquals(30L, (result as ObjInt).value) + } + + @Test + fun testDestructuringWithSplat() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Destructuring with splat operator + val [first, middle..., last] = [1, 2, 3, 4, 5, 6] + [first, middle, last] + """.trimIndent()) + assertTrue(result is ObjList) + val rl = (result as ObjList).list + assertEquals(1L, (rl[0] as ObjInt).value) + val middle = rl[1] as ObjList + assertEquals(listOf(2L, 3L, 4L, 5L), middle.list.map { (it as ObjInt).value }) + assertEquals(6L, (rl[2] as ObjInt).value) + } + + @Test + fun testDiamondSafeMultipleInheritance() = runTest { + val scope = Script.newScope() + scope.eval(""" + // Diamond-safe Multiple Inheritance (C3 MRO) + interface Logger { + fun log(m: String) = println("[LOG] " + m) + } + interface Auth { + fun login(u: String) = println("Login: " + u) + } + + class App() : Logger, Auth { + override fun run() { + log("Starting...") + login("admin") + } + } + App().run() + """.trimIndent()) + } + + @Test + fun testExtensionFunctionsAndProperties() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Extension functions and properties + fun String.shout(): String = this.uppercase() + "!!!" + + val List.second: T get() = this[1] + ["hello".shout(), [10, 20, 30].second] + """.trimIndent()) + assertTrue(result is ObjList) + val el = (result as ObjList).list + assertEquals("HELLO!!!", (el[0] as ObjString).value) + assertEquals(20L, (el[1] as ObjInt).value) + } + + @Test + fun testNonLocalReturns() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Non-local returns from closures + fun findFirst(list: Iterable, predicate: (T)->Bool): T? { + list.forEach { + if (predicate(it)) return@findFirst it + } + null + } + + val found: Int? = findFirst([1, 5, 8, 12]) { it > 10 } + found + """.trimIndent()) + assertEquals(12L, (result as ObjInt).value) + } + + @Test + fun testEasyOperatorOverloading() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Easy operator overloading + class Vector(val x: Real, val y: Real) { + fun plus(other: Vector): Vector = Vector(x + other.x, y + other.y) + override fun toString(): String = "Vector(%g, %g)"(x, y) + } + + val v1 = Vector(1, 2) + val v2 = Vector(3, 4) + (v1 + v2).toString() + """.trimIndent()) + assertEquals("Vector(4, 6)", (result as ObjString).value) + } + + @Test + fun testPropertyDelegationToMap() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Property delegation to Map + class User() { + var name: String by Map() + } + + val u = User() + u.name = "Sergey" + u.name + """.trimIndent()) + assertEquals("Sergey", (result as ObjString).value) + } + + @Test + fun testImplicitCoroutines() = runTest { + val scope = Script.newScope() + val result = scope.eval(""" + // Implicit coroutines: parallelism without ceremony + // import lyng.time // we don't need it for delay if it's available globally + + val d1 = launch { + delay(100.milliseconds) + "Task A finished" + } + val d2 = launch { + delay(50.milliseconds) + "Task B finished" + } + + [d1.await(), d2.await()] + """.trimIndent()) + assertTrue(result is ObjList) + val dl = (result as ObjList).list + assertEquals("Task A finished", (dl[0] as ObjString).value) + assertEquals("Task B finished", (dl[1] as ObjString).value) + } +} diff --git a/site/src/jsMain/kotlin/HomePage.kt b/site/src/jsMain/kotlin/HomePage.kt index aef031a..c03f4ea 100644 --- a/site/src/jsMain/kotlin/HomePage.kt +++ b/site/src/jsMain/kotlin/HomePage.kt @@ -106,7 +106,7 @@ fun HomePage() { } class App() : Logger, Auth { - fun run() { + override fun run() { log("Starting...") login("admin") } @@ -137,7 +137,7 @@ fun HomePage() { """ // Easy operator overloading class Vector(val x: Real, val y: Real) { - override fun plus(other: Vector): Vector = Vector(x + other.x, y + other.y) + fun plus(other: Vector): Vector = Vector(x + other.x, y + other.y) override fun toString(): String = "Vector(%g, %g)"(x, y) }