0.8.4: stadlib in Lyng! added universal Iterable functions

This commit is contained in:
sergeych committed 2025-08-09 22:50:48 +03:00
1 parent f9198fe583
commit 3948283481
15 files changed
+338 -15

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych"
version = "0.8.3-SNAPSHOT"
version = "0.8.4-SNAPSHOT"
buildscript {
repositories {
@@ -163,13 +163,14 @@ open class Scope(
fun addConst(name: String, value: Obj) = addItem(name, false, value)
suspend fun eval(code: String): Obj =
Compiler.compile(code.toSource(), currentImportProvider).execute(this)
eval(code.toSource())
suspend fun eval(source: Source): Obj =
Compiler.compile(
suspend fun eval(source: Source): Obj {
return Compiler.compile(
source,
currentImportProvider
).execute(this)
}
fun containsLocal(name: String): Boolean = name in objects
@@ -4,6 +4,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.yield
import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.stdlib_included.rootLyng
import net.sergeych.lynon.ObjLynonClass
import net.sergeych.mp_tools.globalDefer
import kotlin.math.*
@@ -22,10 +23,14 @@ class Script(
return lastResult
}
suspend fun execute() = execute(defaultImportManager.newModule())
suspend fun execute() = execute(
defaultImportManager.newStdScope()
)
companion object {
suspend fun newScope(pos: Pos = Pos.builtIn) = defaultImportManager.newStdScope(pos)
internal val rootScope: Scope = Scope(null).apply {
ObjException.addExceptionsToContext(this)
addFn("print") {
@@ -202,6 +207,7 @@ class Script(
addConst("Iterable", ObjIterable)
addConst("Collection", ObjCollection)
addConst("Array", ObjArray)
addConst("RingBuffer", ObjRingBuffer.type)
addConst("Class", ObjClassType)
addConst("Deferred", ObjDeferred.type)
@@ -235,6 +241,9 @@ class Script(
val defaultImportManager: ImportManager by lazy {
ImportManager(rootScope, SecurityManager.allowAll).apply {
addTextPackages(
rootLyng
)
addPackage("lyng.buffer") {
it.addConst("Buffer", ObjBuffer.type)
it.addConst("MutableBuffer", ObjMutableBuffer.type)
@@ -36,7 +36,9 @@ class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
addFn("next") {
thisAs<ObjKotlinObjIterator>().iterator.next()
}
addFn("hasNext") { thisAs<ObjKotlinIterator>().iterator.hasNext().toObj() }
addFn("hasNext") {
thisAs<ObjKotlinObjIterator>().iterator.hasNext().toObj()
}
}
}
@@ -68,4 +68,4 @@ class ObjMutableBuffer(byteArray: UByteArray) : ObjBuffer(byteArray) {
}
}
}
}
}
@@ -0,0 +1,91 @@
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
class RingBuffer<T>(val maxSize: Int) : Iterable<T> {
private val data = arrayOfNulls<Any>(maxSize)
var size = 0
private set
private var start = 0
init {
check(maxSize > 0) { "Max size should be a positive number: $maxSize" }
}
fun add(item: T) {
if (size < maxSize)
size++
else
start = (start + 1) % maxSize
data[(start + size - 1) % maxSize] = item
}
@Suppress("unused")
fun addAll(vararg items: T) {
for (i in items) add(i)
}
@Suppress("unused")
fun addAll(elements: Iterable<T>) {
elements.forEach { add(it) }
}
@Suppress("unused")
fun clear() {
start = 0
size = 0
for (i in data.indices) {
data[i] = null
}
}
override fun iterator(): Iterator<T> =
object : Iterator<T> {
private var i = 0
override fun hasNext(): Boolean = i < size
override fun next(): T {
if (!hasNext()) throw NoSuchElementException()
@Suppress("UNCHECKED_CAST")
return data[(start + i++) % maxSize] as T
}
}
}
class ObjRingBuffer(val capacity: Int) : Obj() {
val buffer = RingBuffer<Obj>(capacity)
override val objClass: ObjClass = type
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
buffer.add(other.byValueCopy())
return this
}
companion object {
val type = object : ObjClass("RingBuffer", ObjIterable) {
override suspend fun callOn(scope: Scope): Obj {
return ObjRingBuffer(scope.requireOnlyArg<ObjInt>().toInt())
}
}.apply {
addFn("capacity") {
thisAs<ObjRingBuffer>().capacity.toObj()
}
addFn("size") {
thisAs<ObjRingBuffer>().buffer.size.toObj()
}
addFn("iterator") {
val buffer = thisAs<ObjRingBuffer>().buffer
ObjKotlinObjIterator(buffer.iterator())
}
addFn("add") {
thisAs<ObjRingBuffer>().apply { buffer.add(requireOnlyArg<Obj>()) }
}
}
}
}
@@ -1,6 +1,7 @@
package net.sergeych.lyng.pacman
import net.sergeych.lyng.*
import net.sergeych.mptools.CachedExpression
/**
* Package manager INTERFACE (abstract class). Performs import routines
@@ -45,6 +46,15 @@ abstract class ImportProvider(
fun newModuleAt(pos: Pos): ModuleScope =
ModuleScope(this, pos, "unknown")
private var cachedStdScope = CachedExpression<Scope>()
suspend fun newStdScope(pos: Pos = Pos.builtIn): Scope =
cachedStdScope.get {
newModuleAt(pos).also {
it.eval("import lyng.stdlib\n")
}
}.copy()
}
@@ -1,5 +1,58 @@
package net.sergeych.lyng.stdlib_included
internal val rootLyng = """
package lyng.stdlib
""".trimIndent()
fun Iterable.filter(predicate) {
val list = this
flow {
for( item in list ) {
if( predicate(item) ) {
emit(item)
}
}
}
}
fun Iterable.drop(n) {
var cnt = 0
filter { cnt++ >= n }
}
fun Iterable.first() {
val i = iterator()
if( !i.hasNext() ) throw NoSuchElementException()
i.next()
}
fun Iterable.last() {
var found = false
var element = null
for( i in this ) {
element = i
found = true
}
if( !found ) throw NoSuchElementException()
element
}
fun Iterable.dropLast(n) {
val list = this
val buffer = RingBuffer(n)
flow {
for( item in list ) {
if( buffer.size == n )
emit( buffer.first() )
buffer += item
}
}
}
fun Iterable.takeLast(n) {
val list = this
val buffer = RingBuffer(n)
for( item in list ) buffer += item
buffer
}
""".trimIndent()
@@ -29,7 +29,7 @@ class TestCoroutines {
val done = CompletableDeferred()
launch {
delay(10)
delay(30)
done.complete("ok")
}
@@ -124,7 +124,6 @@ class TestCoroutines {
""".trimIndent())
}
@Test
fun testFilterFlow() = runTest {
eval("""
@@ -0,0 +1,65 @@
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.eval
import kotlin.test.Test
class StdlibTest {
@Test
fun testIterableFilter() = runTest {
eval("""
assertEquals([1,3,5,7], (1..8).filter{ it % 2 == 1 }.toList() )
assertEquals([2,4,6,8], (1..8).filter{ it % 2 == 0 }.toList() )
""".trimIndent())
}
@Test
fun testFirstLast() = runTest {
eval("""
assertEquals(1, (1..8).first )
assertEquals(8, (1..8).last )
""".trimIndent())
}
@Test
fun testTake() = runTest {
eval("""
assertEquals([1,2,3], (1..8).take(3).toList() )
assertEquals([7,8], (1..8).takeLast(2).toList() )
""".trimIndent())
}
@Test
fun testRingBuffer() = runTest {
eval("""
val r = RingBuffer(3)
assert( r is RingBuffer )
assertEquals(0, r.size)
assertEquals(3, r.capacity)
r += 10
assertEquals(1, r.size)
assertEquals(10, r.first)
r += 20
assertEquals(2, r.size)
assertEquals( [10, 20], r.toList() )
r += 30
assertEquals(3, r.size)
assertEquals( [10, 20, 30], r.toList() )
r += 40
assertEquals(3, r.size)
assertEquals( [20, 30, 40], r.toList() )
""".trimIndent())
}
@Test
fun testDrop() = runTest {
eval("""
assertEquals([7,8], (1..8).drop(6).toList() )
assertEquals([1,2], (1..8).dropLast(6).toList() )
""".trimIndent())
}
}
+8 -1
View File
@@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Script
import net.sergeych.lyng.obj.ObjVoid
import java.nio.file.Files
import java.nio.file.Files.readAllLines
@@ -163,9 +164,10 @@ fun parseDocTests(fileName: String, bookMode: Boolean = false): Flow<DocTest> =
}
.flowOn(Dispatchers.IO)
suspend fun DocTest.test(scope: Scope = Scope()) {
suspend fun DocTest.test(_scope: Scope? = null) {
val collectedOutput = StringBuilder()
val currentTest = this
val scope = _scope ?: Script.newScope()
scope.apply {
addFn("println") {
if( bookMode ) {
@@ -304,4 +306,9 @@ class BookTest {
runDocTests("../docs/parallelism.md")
}
@Test
fun testRingBuffer() = runBlocking {
runDocTests("../docs/RingBuffer.md")
}
}