0.8.4: stadlib in Lyng! added universal Iterable functions
This commit is contained in:
parent
f9198fe583
commit
3948283481
@ -17,7 +17,14 @@ Iterator itself is a simple interface that should provide only to method:
|
|||||||
|
|
||||||
Just remember at this stage typed declarations are not yet supported.
|
Just remember at this stage typed declarations are not yet supported.
|
||||||
|
|
||||||
Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions available:
|
Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions available, for example
|
||||||
|
|
||||||
|
val r = 1..10 // Range is Iterable!
|
||||||
|
assertEquals( [9,10] r.takeLast(2) )
|
||||||
|
assertEquals( [1,2,3] r.take(3) )
|
||||||
|
assertEquals( [9,10] r.drop(8) )
|
||||||
|
assertEquals( [1,2] r.dropLast(8) )
|
||||||
|
>>> void
|
||||||
|
|
||||||
## Instance methods
|
## Instance methods
|
||||||
|
|
||||||
@ -33,7 +40,15 @@ Having `Iterable` in base classes allows to use it in for loop. Also, each `Iter
|
|||||||
| map(f) | create a list of values returned by `f` called for each element of the iterable |
|
| map(f) | create a list of values returned by `f` called for each element of the iterable |
|
||||||
| indexOf(i) | return index if the first encounter of i or a negative value if not found |
|
| indexOf(i) | return index if the first encounter of i or a negative value if not found |
|
||||||
| associateBy(kf) | create a map where keys are returned by kf that will be called for each element |
|
| associateBy(kf) | create a map where keys are returned by kf that will be called for each element |
|
||||||
|
| first | first element (1) |
|
||||||
|
| last | last element (1) |
|
||||||
|
| take(n) | return [Iterable] of up to n first elements |
|
||||||
|
| taleLast(n) | return [Iterable] of up to n last elements |
|
||||||
|
| drop(n) | return new [Iterable] without first n elements |
|
||||||
|
| dropLast(n) | return new [Iterable] without last n elements |
|
||||||
|
|
||||||
|
(1)
|
||||||
|
: throws `NoSuchElementException` if there is no such element
|
||||||
|
|
||||||
fun Iterable.toList(): List
|
fun Iterable.toList(): List
|
||||||
fun Iterable.toSet(): Set
|
fun Iterable.toSet(): Set
|
||||||
|
52
docs/RingBuffer.md
Normal file
52
docs/RingBuffer.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# RingBuffer
|
||||||
|
|
||||||
|
This is a fixed size buffer that allow to store N last elements with _O(1)_ effectiveness (no data shifting).
|
||||||
|
|
||||||
|
Here is the sample:
|
||||||
|
|
||||||
|
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() )
|
||||||
|
|
||||||
|
// now first value is lost:
|
||||||
|
r += 40
|
||||||
|
assertEquals(3, r.size)
|
||||||
|
assertEquals( [20, 30, 40], r.toList() )
|
||||||
|
assertEquals(3, r.capacity)
|
||||||
|
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
Ring buffer implements [Iterable], so any of its methods are available for `RingBuffer`, e.g. `first`, `last`, `toList`,
|
||||||
|
`take`, `drop`, `takelast`, `dropLast`, etc.
|
||||||
|
|
||||||
|
## Constructor
|
||||||
|
|
||||||
|
RinbBuffer(capacity: Int)
|
||||||
|
|
||||||
|
## Instance methods
|
||||||
|
|
||||||
|
| method | description | remarks |
|
||||||
|
|-------------|------------------------|---------|
|
||||||
|
| capacity | max size of the buffer | |
|
||||||
|
| size | current size | (1) |
|
||||||
|
| operator += | add new item | (1) |
|
||||||
|
| add(item) | add new item | (1) |
|
||||||
|
| iterator() | return iterator | (1) |
|
||||||
|
|
||||||
|
(1)
|
||||||
|
: Ringbuffer is not threadsafe, protect it with a mutex to avoid RC where necessary.
|
||||||
|
|
||||||
|
[Iterable]: Iterable.md
|
@ -189,3 +189,19 @@ Important difference from the channels or like, every time you collect the flow,
|
|||||||
|
|
||||||
Notice that flow's lambda is not called until actual collection is started. Cold flows are
|
Notice that flow's lambda is not called until actual collection is started. Cold flows are
|
||||||
better in terms of resource consumption.
|
better in terms of resource consumption.
|
||||||
|
|
||||||
|
Flows allow easy transforming of any [Iterable]. See how the standard Lyng library functions use it:
|
||||||
|
|
||||||
|
fun Iterable.filter(predicate) {
|
||||||
|
val list = this
|
||||||
|
flow {
|
||||||
|
for( item in list ) {
|
||||||
|
if( predicate(item) ) {
|
||||||
|
emit(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Iterable]: Iterable.md
|
@ -14,8 +14,9 @@ __Other documents to read__ maybe after this one:
|
|||||||
- [Advanced topics](advanced_topics.md), [declaring arguments](declaring_arguments.md)
|
- [Advanced topics](advanced_topics.md), [declaring arguments](declaring_arguments.md)
|
||||||
- [OOP notes](OOP.md), [exception handling](exceptions_handling.md)
|
- [OOP notes](OOP.md), [exception handling](exceptions_handling.md)
|
||||||
- [math in Lyng](math.md)
|
- [math in Lyng](math.md)
|
||||||
|
- [time](time.md) and [parallelism](parallelism.md)
|
||||||
- [parallelism] - multithreaded code, coroutines, etc.
|
- [parallelism] - multithreaded code, coroutines, etc.
|
||||||
- Some class references: [List], [Set], [Map], [Real], [Range], [Iterable], [Iterator], [time manipulation](time.md)
|
- Some class references: [List], [Set], [Map], [Real], [Range], [Iterable], [Iterator], [time manipulation](time.md), [RingBuffer], [Buffer].
|
||||||
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and
|
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and
|
||||||
loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
|
loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
|
||||||
|
|
||||||
@ -1305,3 +1306,5 @@ See [math functions](math.md). Other general purpose functions are:
|
|||||||
[Buffer]: Buffer.md
|
[Buffer]: Buffer.md
|
||||||
|
|
||||||
[parallelism]: parallelism.md
|
[parallelism]: parallelism.md
|
||||||
|
|
||||||
|
[RingBuffer]: RingBuffer.md
|
@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.8.3-SNAPSHOT"
|
version = "0.8.4-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -163,13 +163,14 @@ open class Scope(
|
|||||||
fun addConst(name: String, value: Obj) = addItem(name, false, value)
|
fun addConst(name: String, value: Obj) = addItem(name, false, value)
|
||||||
|
|
||||||
suspend fun eval(code: String): Obj =
|
suspend fun eval(code: String): Obj =
|
||||||
Compiler.compile(code.toSource(), currentImportProvider).execute(this)
|
eval(code.toSource())
|
||||||
|
|
||||||
suspend fun eval(source: Source): Obj =
|
suspend fun eval(source: Source): Obj {
|
||||||
Compiler.compile(
|
return Compiler.compile(
|
||||||
source,
|
source,
|
||||||
currentImportProvider
|
currentImportProvider
|
||||||
).execute(this)
|
).execute(this)
|
||||||
|
}
|
||||||
|
|
||||||
fun containsLocal(name: String): Boolean = name in objects
|
fun containsLocal(name: String): Boolean = name in objects
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import net.sergeych.lyng.obj.*
|
import net.sergeych.lyng.obj.*
|
||||||
import net.sergeych.lyng.pacman.ImportManager
|
import net.sergeych.lyng.pacman.ImportManager
|
||||||
|
import net.sergeych.lyng.stdlib_included.rootLyng
|
||||||
import net.sergeych.lynon.ObjLynonClass
|
import net.sergeych.lynon.ObjLynonClass
|
||||||
import net.sergeych.mp_tools.globalDefer
|
import net.sergeych.mp_tools.globalDefer
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
@ -22,10 +23,14 @@ class Script(
|
|||||||
return lastResult
|
return lastResult
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun execute() = execute(defaultImportManager.newModule())
|
suspend fun execute() = execute(
|
||||||
|
defaultImportManager.newStdScope()
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
suspend fun newScope(pos: Pos = Pos.builtIn) = defaultImportManager.newStdScope(pos)
|
||||||
|
|
||||||
internal val rootScope: Scope = Scope(null).apply {
|
internal val rootScope: Scope = Scope(null).apply {
|
||||||
ObjException.addExceptionsToContext(this)
|
ObjException.addExceptionsToContext(this)
|
||||||
addFn("print") {
|
addFn("print") {
|
||||||
@ -202,6 +207,7 @@ class Script(
|
|||||||
addConst("Iterable", ObjIterable)
|
addConst("Iterable", ObjIterable)
|
||||||
addConst("Collection", ObjCollection)
|
addConst("Collection", ObjCollection)
|
||||||
addConst("Array", ObjArray)
|
addConst("Array", ObjArray)
|
||||||
|
addConst("RingBuffer", ObjRingBuffer.type)
|
||||||
addConst("Class", ObjClassType)
|
addConst("Class", ObjClassType)
|
||||||
|
|
||||||
addConst("Deferred", ObjDeferred.type)
|
addConst("Deferred", ObjDeferred.type)
|
||||||
@ -235,6 +241,9 @@ class Script(
|
|||||||
|
|
||||||
val defaultImportManager: ImportManager by lazy {
|
val defaultImportManager: ImportManager by lazy {
|
||||||
ImportManager(rootScope, SecurityManager.allowAll).apply {
|
ImportManager(rootScope, SecurityManager.allowAll).apply {
|
||||||
|
addTextPackages(
|
||||||
|
rootLyng
|
||||||
|
)
|
||||||
addPackage("lyng.buffer") {
|
addPackage("lyng.buffer") {
|
||||||
it.addConst("Buffer", ObjBuffer.type)
|
it.addConst("Buffer", ObjBuffer.type)
|
||||||
it.addConst("MutableBuffer", ObjMutableBuffer.type)
|
it.addConst("MutableBuffer", ObjMutableBuffer.type)
|
||||||
|
@ -36,7 +36,9 @@ class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
|
|||||||
addFn("next") {
|
addFn("next") {
|
||||||
thisAs<ObjKotlinObjIterator>().iterator.next()
|
thisAs<ObjKotlinObjIterator>().iterator.next()
|
||||||
}
|
}
|
||||||
addFn("hasNext") { thisAs<ObjKotlinIterator>().iterator.hasNext().toObj() }
|
addFn("hasNext") {
|
||||||
|
thisAs<ObjKotlinObjIterator>().iterator.hasNext().toObj()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
package net.sergeych.lyng.pacman
|
||||||
|
|
||||||
import net.sergeych.lyng.*
|
import net.sergeych.lyng.*
|
||||||
|
import net.sergeych.mptools.CachedExpression
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package manager INTERFACE (abstract class). Performs import routines
|
* Package manager INTERFACE (abstract class). Performs import routines
|
||||||
@ -45,6 +46,15 @@ abstract class ImportProvider(
|
|||||||
|
|
||||||
fun newModuleAt(pos: Pos): ModuleScope =
|
fun newModuleAt(pos: Pos): ModuleScope =
|
||||||
ModuleScope(this, pos, "unknown")
|
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
|
package net.sergeych.lyng.stdlib_included
|
||||||
|
|
||||||
internal val rootLyng = """
|
internal val rootLyng = """
|
||||||
|
package lyng.stdlib
|
||||||
|
|
||||||
|
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()
|
""".trimIndent()
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class TestCoroutines {
|
|||||||
val done = CompletableDeferred()
|
val done = CompletableDeferred()
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
delay(10)
|
delay(30)
|
||||||
done.complete("ok")
|
done.complete("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,6 @@ class TestCoroutines {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFilterFlow() = runTest {
|
fun testFilterFlow() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
|
65
lynglib/src/commonTest/kotlin/StdlibTest.kt
Normal file
65
lynglib/src/commonTest/kotlin/StdlibTest.kt
Normal file
@ -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())
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.flowOn
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.obj.ObjVoid
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Files.readAllLines
|
import java.nio.file.Files.readAllLines
|
||||||
@ -163,9 +164,10 @@ fun parseDocTests(fileName: String, bookMode: Boolean = false): Flow<DocTest> =
|
|||||||
}
|
}
|
||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
suspend fun DocTest.test(scope: Scope = Scope()) {
|
suspend fun DocTest.test(_scope: Scope? = null) {
|
||||||
val collectedOutput = StringBuilder()
|
val collectedOutput = StringBuilder()
|
||||||
val currentTest = this
|
val currentTest = this
|
||||||
|
val scope = _scope ?: Script.newScope()
|
||||||
scope.apply {
|
scope.apply {
|
||||||
addFn("println") {
|
addFn("println") {
|
||||||
if( bookMode ) {
|
if( bookMode ) {
|
||||||
@ -304,4 +306,9 @@ class BookTest {
|
|||||||
runDocTests("../docs/parallelism.md")
|
runDocTests("../docs/parallelism.md")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRingBuffer() = runBlocking {
|
||||||
|
runDocTests("../docs/RingBuffer.md")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user