261 lines
8.3 KiB
Kotlin
261 lines
8.3 KiB
Kotlin
/*
|
|
* 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<Int> = (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<out T: Num>(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<String, Int>? = 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<T>.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<T>(list: Iterable<T>, 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)
|
|
}
|
|
}
|