fixed many bugs in closures processing also in flows

This commit is contained in:
Sergey Chernov 2025-08-09 15:48:41 +03:00
parent 9aae33d564
commit e0ed27a01f
7 changed files with 130 additions and 15 deletions

View File

@ -205,6 +205,20 @@ open class Scope(
return "S[this=$thisObj $contents]" return "S[this=$thisObj $contents]"
} }
fun trace(text: String="") {
println("trace Scope: $text ------------------")
var p = this.parent
var level = 0
while (p != null) {
println(" parent#${++level}: $p")
println(" ( ${p.args.list} )")
p = p.parent
}
println("--------------------")
ObjVoid
}
companion object { companion object {
fun new(): Scope = fun new(): Scope =

View File

@ -26,8 +26,15 @@ class Script(
companion object { companion object {
private val rootScope: Scope = Scope(null).apply { internal val rootScope: Scope = Scope(null).apply {
ObjException.addExceptionsToContext(this) ObjException.addExceptionsToContext(this)
addFn("print") {
for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + a.asStr.value)
else print(a.asStr.value)
}
ObjVoid
}
addFn("println") { addFn("println") {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {
if (i > 0) print(' ' + a.asStr.value) if (i > 0) print(' ' + a.asStr.value)
@ -153,16 +160,26 @@ class Script(
} }
result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown")) result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown"))
} }
addFn("traceScope") { addFn("require") {
println("trace Scope: $this") val condition = requiredArg<ObjBool>(0)
var p = this.parent if( !condition.value ) {
var level = 0 val message = args.list.getOrNull(1)?.toString() ?: "requirement not met"
while (p != null) { raiseIllegalArgument(message)
println(" parent#${++level}: $p")
p = p.parent
} }
ObjVoid ObjVoid
} }
addFn("check") {
val condition = requiredArg<ObjBool>(0)
if( !condition.value ) {
val message = args.list.getOrNull(1)?.toString() ?: "check failed"
raiseIllegalState(message)
}
ObjVoid
}
addFn("traceScope") {
this.trace(args.get(0)?.toString() ?: "")
ObjVoid
}
addVoidFn("delay") { addVoidFn("delay") {
delay((this.args.firstAndOnly().toDouble()/1000.0).roundToLong()) delay((this.args.firstAndOnly().toDouble()/1000.0).roundToLong())
@ -204,7 +221,9 @@ class Script(
} }
addFn("flow") { addFn("flow") {
ObjFlow(requireOnlyArg<Statement>()) // important is: current context contains closure often used in call;
// we'll need it for the producer
ObjFlow(requireOnlyArg<Statement>(), this)
} }
val pi = ObjReal(PI) val pi = ObjReal(PI)

View File

@ -7,9 +7,7 @@ import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import net.sergeych.lyng.Scope import net.sergeych.lyng.*
import net.sergeych.lyng.ScriptFlowIsNoMoreCollected
import net.sergeych.lyng.Statement
import net.sergeych.mp_tools.globalLaunch import net.sergeych.mp_tools.globalLaunch
import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.cancellation.CancellationException
@ -60,7 +58,7 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
return channel return channel
} }
class ObjFlow(val producer: Statement) : Obj() { class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
override val objClass = type override val objClass = type
@ -71,7 +69,8 @@ class ObjFlow(val producer: Statement) : Obj() {
} }
}.apply { }.apply {
addFn("iterator") { addFn("iterator") {
ObjFlowIterator(thisAs<ObjFlow>().producer) val objFlow = thisAs<ObjFlow>()
ObjFlowIterator( statement { objFlow.producer.execute(ClosureScope(this,objFlow.scope)) } )
} }
} }
} }

View File

@ -111,6 +111,5 @@ val ObjIterable by lazy {
.not() .not()
) )
} }
} }
} }

View File

@ -0,0 +1,5 @@
package net.sergeych.lyng.stdlib_included
internal val rootLyng = """
""".trimIndent()

View File

@ -0,0 +1,16 @@
fun Iterable.filter( predicate ) {
flow {
for( item in this )
if( predicate(item) )
emit(item)
}
}
fun Iterable.drop(n) {
require( n >= 0, "drop amount must be non-negative")
var count = 0
filter {
count++ < N
}
}

View File

@ -106,4 +106,67 @@ class TestCoroutines {
assertEquals( result, f.toList()) assertEquals( result, f.toList())
""".trimIndent()) """.trimIndent())
} }
@Test
fun testFlowClosures() = runTest {
eval("""
fun filter( a, b ) {
println("filter: %s, %s"(a,b))
flow {
emit(a)
emit(b)
}
}
assertEquals( [5, 1], filter(5,1).toList() )
assertEquals( [2, 3], filter(2,3).toList() )
""".trimIndent())
}
@Test
fun testFilterFlow() = runTest {
eval("""
fun filter( list, predicate ) {
val p = predicate
println("predicate "+predicate+" / "+p)
flow {
// here p is captured only once and does not change!
for( item in list ) {
print("filter "+p+" "+item+": ")
if( p(item) ) {
println("OK")
emit(item)
}
else println("NO")
}
}
}
// fun drop(i, n) {
// require( n >= 0, "drop amount must be non-negative")
// var count = 0
// println("drop %d"(n))
// filter(i) {
// count++ >= n
// }
// }
val src = (1..1).toList()
assertEquals( 1, filter(src) { true }.toList().size )
println("----------------------------------------------------------")
println("----------------------------------------------------------")
println("----------------------------------------------------------")
println("----------------------------------------------------------")
assertEquals( 0, filter(src) { false }.toList().size )
// assertEquals( 3, filter(src) { true }.size() )
// assertEquals( [7,8], drop((1..8).toList(),6).toList())
// assertEquals( [1,3,5,7], filter((1..8).toList()) {
// println("call2")
// it % 2 == 1
// }.toList())
""".trimIndent())
}
} }