more stdlib and docs, bugfixes
This commit is contained in:
parent
202e70a99a
commit
b07452e66e
@ -26,16 +26,34 @@ 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, for example
|
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!
|
val r = 1..10 // Range is Iterable!
|
||||||
assertEquals( [9,10] r.takeLast(2) )
|
assertEquals( [9,10], r.takeLast(2).toList() )
|
||||||
assertEquals( [1,2,3] r.take(3) )
|
assertEquals( [1,2,3], r.take(3).toList() )
|
||||||
assertEquals( [9,10] r.drop(8) )
|
assertEquals( [9,10], r.drop(8).toList() )
|
||||||
assertEquals( [1,2] r.dropLast(8) )
|
assertEquals( [1,2], r.dropLast(8).toList() )
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
## joinToString
|
||||||
|
|
||||||
|
This methods convert any iterable to a string joining string representation of each element, optionally transforming it and joining using specified suffix.
|
||||||
|
|
||||||
|
Iterable.joinToString(suffux=' ', transform=null)
|
||||||
|
|
||||||
|
- if `Iterable` `isEmpty`, the empty string `""` is returned.
|
||||||
|
- `suffix` is inserted between items when there are more than one.
|
||||||
|
- `transform` of specified is applied to each element, otherwise its `toString()` method is used.
|
||||||
|
|
||||||
|
Here is the sample:
|
||||||
|
|
||||||
|
assertEquals( (1..3).joinToString(), "1 2 3")
|
||||||
|
assertEquals( (1..3).joinToString(":"), "1:2:3")
|
||||||
|
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
## Instance methods:
|
## Instance methods:
|
||||||
|
|
||||||
|
|
||||||
| fun/method | description |
|
| fun/method | description |
|
||||||
|-----------------|---------------------------------------------------------------------------------|
|
|-------------------|---------------------------------------------------------------------------|
|
||||||
| toList() | create a list from iterable |
|
| toList() | create a list from iterable |
|
||||||
| toSet() | create a set from iterable |
|
| toSet() | create a set from iterable |
|
||||||
| contains(i) | check that iterable contains `i` |
|
| contains(i) | check that iterable contains `i` |
|
||||||
@ -52,10 +70,14 @@ Having `Iterable` in base classes allows to use it in for loop. Also, each `Iter
|
|||||||
| taleLast(n) | return [Iterable] of up to n last elements |
|
| taleLast(n) | return [Iterable] of up to n last elements |
|
||||||
| drop(n) | return new [Iterable] without first n elements |
|
| drop(n) | return new [Iterable] without first n elements |
|
||||||
| dropLast(n) | return new [Iterable] without last n elements |
|
| dropLast(n) | return new [Iterable] without last n elements |
|
||||||
|
| joinToString(s,t) | convert iterable to string, see (2) |
|
||||||
|
|
||||||
(1)
|
(1)
|
||||||
: throws `NoSuchElementException` if there is no such element
|
: throws `NoSuchElementException` if there is no such element
|
||||||
|
|
||||||
|
(2)
|
||||||
|
: `joinToString(suffix=" ",transform=null)`: suffix is inserted between items if there are more than one, trasnfom is optional function applied to each item that must return result string for an item, otherwise `item.toString()` is used.
|
||||||
|
|
||||||
fun Iterable.toList(): List
|
fun Iterable.toList(): List
|
||||||
fun Iterable.toSet(): Set
|
fun Iterable.toSet(): Set
|
||||||
fun Iterable.indexOf(element): Int
|
fun Iterable.indexOf(element): Int
|
||||||
|
@ -747,7 +747,7 @@ You can thest that _when expression_ is _contained_, or not contained, in some o
|
|||||||
`!in container`. The container is any object that provides `contains` method, otherwise the runtime exception will be
|
`!in container`. The container is any object that provides `contains` method, otherwise the runtime exception will be
|
||||||
thrown.
|
thrown.
|
||||||
|
|
||||||
Typical builtin types that are containers (e.g. support `conain`):
|
Typical builtin types that are containers (e.g. support `contains`):
|
||||||
|
|
||||||
| class | notes |
|
| class | notes |
|
||||||
|------------|------------------------------------------------|
|
|------------|------------------------------------------------|
|
||||||
@ -756,6 +756,8 @@ Typical builtin types that are containers (e.g. support `conain`):
|
|||||||
| List | faster than Array's |
|
| List | faster than Array's |
|
||||||
| String | character in string or substring in string (3) |
|
| String | character in string or substring in string (3) |
|
||||||
| Range | object is included in the range (2) |
|
| Range | object is included in the range (2) |
|
||||||
|
| Buffer | byte is in buffer |
|
||||||
|
| RingBuffer | object is in buffer |
|
||||||
|
|
||||||
(1)
|
(1)
|
||||||
: Iterable is not the container as it can be infinite
|
: Iterable is not the container as it can be infinite
|
||||||
@ -1297,7 +1299,7 @@ if blank, will be removed too, for example:
|
|||||||
See [math functions](math.md). Other general purpose functions are:
|
See [math functions](math.md). Other general purpose functions are:
|
||||||
|
|
||||||
| name | description |
|
| name | description |
|
||||||
|--------------------------------------------|-----------------------------------------------------------|
|
|----------------------------------------------|------------------------------------------------------------|
|
||||||
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
|
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
|
||||||
| assertEquals(a,b) | |
|
| assertEquals(a,b) | |
|
||||||
| assertNotEquals(a,b) | |
|
| assertNotEquals(a,b) | |
|
||||||
@ -1308,6 +1310,8 @@ See [math functions](math.md). Other general purpose functions are:
|
|||||||
| print(args...) | Open for overriding, it prints to stdout without newline. |
|
| print(args...) | Open for overriding, it prints to stdout without newline. |
|
||||||
| flow {} | create flow sequence, see [parallelism] |
|
| flow {} | create flow sequence, see [parallelism] |
|
||||||
| delay, launch, yield | see [parallelism] |
|
| delay, launch, yield | see [parallelism] |
|
||||||
|
| cached(builder) | remembers builder() on first invocation and return it then |
|
||||||
|
|
||||||
|
|
||||||
# Built-in constants
|
# Built-in constants
|
||||||
|
|
||||||
|
@ -21,7 +21,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.9-SNAPSHOT"
|
version = "0.8.10-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -87,7 +87,7 @@ class Compiler(
|
|||||||
statements += it
|
statements += it
|
||||||
}
|
}
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
when( t.type ) {
|
when (t.type) {
|
||||||
Token.Type.RBRACE, Token.Type.EOF, Token.Type.SEMICOLON -> {}
|
Token.Type.RBRACE, Token.Type.EOF, Token.Type.SEMICOLON -> {}
|
||||||
else ->
|
else ->
|
||||||
throw ScriptError(t.pos, "unexpeced `${t.value}` here")
|
throw ScriptError(t.pos, "unexpeced `${t.value}` here")
|
||||||
@ -114,7 +114,7 @@ class Compiler(
|
|||||||
return result.toString()
|
return result.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastAnnotation: (suspend (Scope, ObjString,Statement) -> Statement)? = null
|
private var lastAnnotation: (suspend (Scope, ObjString, Statement) -> Statement)? = null
|
||||||
|
|
||||||
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
|
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
|
||||||
lastAnnotation = null
|
lastAnnotation = null
|
||||||
@ -138,6 +138,7 @@ class Compiler(
|
|||||||
lastAnnotation = parseAnnotation(t)
|
lastAnnotation = parseAnnotation(t)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
Token.Type.LABEL -> continue
|
Token.Type.LABEL -> continue
|
||||||
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
|
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
|
||||||
|
|
||||||
@ -843,7 +844,7 @@ class Compiler(
|
|||||||
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number")
|
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun parseAnnotation(t: Token): (suspend (Scope, ObjString,Statement)->Statement) {
|
suspend fun parseAnnotation(t: Token): (suspend (Scope, ObjString, Statement) -> Statement) {
|
||||||
val extraArgs = parseArgsOrNull()
|
val extraArgs = parseArgsOrNull()
|
||||||
println("annotation ${t.value}: args: $extraArgs")
|
println("annotation ${t.value}: args: $extraArgs")
|
||||||
return { scope, name, body ->
|
return { scope, name, body ->
|
||||||
@ -851,14 +852,14 @@ class Compiler(
|
|||||||
val required = listOf(name, body)
|
val required = listOf(name, body)
|
||||||
val args = extras?.let { required + it } ?: required
|
val args = extras?.let { required + it } ?: required
|
||||||
val fn = scope.get(t.value)?.value ?: scope.raiseSymbolNotFound("annotation not found: ${t.value}")
|
val fn = scope.get(t.value)?.value ?: scope.raiseSymbolNotFound("annotation not found: ${t.value}")
|
||||||
if( fn !is Statement ) scope.raiseIllegalArgument("annotation must be callable, got ${fn.objClass}")
|
if (fn !is Statement) scope.raiseIllegalArgument("annotation must be callable, got ${fn.objClass}")
|
||||||
(fn.execute(scope.copy(Arguments(args))) as? Statement)
|
(fn.execute(scope.copy(Arguments(args))) as? Statement)
|
||||||
?: scope.raiseClassCastError("function annotation must return callable")
|
?: scope.raiseClassCastError("function annotation must return callable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun parseArgsOrNull(): Pair<List<ParsedArgument>, Boolean>? =
|
suspend fun parseArgsOrNull(): Pair<List<ParsedArgument>, Boolean>? =
|
||||||
if( cc.skipNextIf(Token.Type.LPAREN))
|
if (cc.skipNextIf(Token.Type.LPAREN))
|
||||||
parseArgs()
|
parseArgs()
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
@ -1173,22 +1174,24 @@ class Compiler(
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
val t = cc.skipWsTokens()
|
val t = cc.skipWsTokens()
|
||||||
when(t.type) {
|
when (t.type) {
|
||||||
Token.Type.ID -> {
|
Token.Type.ID -> {
|
||||||
names += t.value
|
names += t.value
|
||||||
val t1 = cc.skipWsTokens()
|
val t1 = cc.skipWsTokens()
|
||||||
when(t1.type) {
|
when (t1.type) {
|
||||||
Token.Type.COMMA ->
|
Token.Type.COMMA ->
|
||||||
continue
|
continue
|
||||||
|
|
||||||
Token.Type.RBRACE -> break
|
Token.Type.RBRACE -> break
|
||||||
else -> {
|
else -> {
|
||||||
t1.raiseSyntax("unexpected token")
|
t1.raiseSyntax("unexpected token")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> t.raiseSyntax("expected enum entry name")
|
else -> t.raiseSyntax("expected enum entry name")
|
||||||
}
|
}
|
||||||
} while(true)
|
} while (true)
|
||||||
|
|
||||||
return statement {
|
return statement {
|
||||||
ObjEnumClass.createSimpleEnum(nameToken.value, names).also {
|
ObjEnumClass.createSimpleEnum(nameToken.value, names).also {
|
||||||
@ -1263,7 +1266,6 @@ class Compiler(
|
|||||||
newClass.classScope = classScope
|
newClass.classScope = classScope
|
||||||
for (s in initScope)
|
for (s in initScope)
|
||||||
s.execute(classScope)
|
s.execute(classScope)
|
||||||
.also { println("executed, ${classScope.objects}") }
|
|
||||||
}
|
}
|
||||||
newClass
|
newClass
|
||||||
}
|
}
|
||||||
@ -1766,6 +1768,10 @@ class Compiler(
|
|||||||
|
|
||||||
val eqToken = cc.next()
|
val eqToken = cc.next()
|
||||||
var setNull = false
|
var setNull = false
|
||||||
|
|
||||||
|
val isDelegate = if (eqToken.isId("by")) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
if (eqToken.type != Token.Type.ASSIGN) {
|
if (eqToken.type != Token.Type.ASSIGN) {
|
||||||
if (!isMutable)
|
if (!isMutable)
|
||||||
throw ScriptError(start, "val must be initialized")
|
throw ScriptError(start, "val must be initialized")
|
||||||
@ -1774,16 +1780,19 @@ class Compiler(
|
|||||||
setNull = true
|
setNull = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
val initialExpression = if (setNull) null else parseStatement(true)
|
val initialExpression = if (setNull) null
|
||||||
|
else parseStatement(true)
|
||||||
?: throw ScriptError(eqToken.pos, "Expected initializer expression")
|
?: throw ScriptError(eqToken.pos, "Expected initializer expression")
|
||||||
|
|
||||||
if (isStatic) {
|
if (isStatic) {
|
||||||
// find objclass instance: this is tricky: this code executes in object initializer,
|
// find objclass instance: this is tricky: this code executes in object initializer,
|
||||||
// when creating instance, but we need to execute it in the class initializer which
|
// when creating instance, but we need to execute it in the class initializer which
|
||||||
// is missing as for now. Add it to the compiler context?
|
// is missing as for now. Add it to the compiler context?
|
||||||
// add there
|
|
||||||
// return
|
if (isDelegate) throw ScriptError(start, "static delegates are not yet implemented")
|
||||||
currentInitScope += statement {
|
currentInitScope += statement {
|
||||||
val initValue = initialExpression?.execute(this)?.byValueCopy() ?: ObjNull
|
val initValue = initialExpression?.execute(this)?.byValueCopy() ?: ObjNull
|
||||||
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, pos)
|
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, pos)
|
||||||
@ -1797,14 +1806,33 @@ class Compiler(
|
|||||||
if (context.containsLocal(name))
|
if (context.containsLocal(name))
|
||||||
throw ScriptError(nameToken.pos, "Variable $name is already defined")
|
throw ScriptError(nameToken.pos, "Variable $name is already defined")
|
||||||
|
|
||||||
|
if (isDelegate) {
|
||||||
|
println("initial expr = $initialExpression")
|
||||||
|
val initValue =
|
||||||
|
(initialExpression?.execute(context.copy(Arguments(ObjString(name)))) as? Statement)
|
||||||
|
?.execute(context.copy(Arguments(ObjString(name))))
|
||||||
|
?: context.raiseError("delegate initialization required")
|
||||||
|
println("delegate init: $initValue")
|
||||||
|
if (!initValue.isInstanceOf(ObjArray))
|
||||||
|
context.raiseIllegalArgument("delegate initialized must be an array")
|
||||||
|
val s = initValue.getAt(context, 1)
|
||||||
|
val setter = if (s == ObjNull) statement { raiseNotImplemented("setter is not provided") }
|
||||||
|
else (s as? Statement) ?: context.raiseClassCastError("setter must be a callable")
|
||||||
|
ObjDelegate(
|
||||||
|
(initValue.getAt(context, 0) as? Statement)
|
||||||
|
?: context.raiseClassCastError("getter must be a callable"), setter
|
||||||
|
).also {
|
||||||
|
context.addItem(name, isMutable, it, visibility, recordType = ObjRecord.Type.Field)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// init value could be a val; when we initialize by-value type var with it, we need to
|
// init value could be a val; when we initialize by-value type var with it, we need to
|
||||||
// create a separate copy:
|
// create a separate copy:
|
||||||
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
||||||
|
|
||||||
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
|
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
|
||||||
initValue
|
initValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class Operator(
|
data class Operator(
|
||||||
val tokenType: Token.Type,
|
val tokenType: Token.Type,
|
||||||
|
@ -24,6 +24,10 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
|
|||||||
|
|
||||||
val isComment: Boolean by lazy { type == Type.SINLGE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }
|
val isComment: Boolean by lazy { type == Type.SINLGE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }
|
||||||
|
|
||||||
|
fun isId(text: String) =
|
||||||
|
type == Type.ID && value == text
|
||||||
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
enum class Type {
|
enum class Type {
|
||||||
ID, INT, REAL, HEX, STRING, CHAR,
|
ID, INT, REAL, HEX, STRING, CHAR,
|
||||||
|
@ -161,6 +161,8 @@ open class Obj {
|
|||||||
|
|
||||||
open suspend fun assign(scope: Scope, other: Obj): Obj? = null
|
open suspend fun assign(scope: Scope, other: Obj): Obj? = null
|
||||||
|
|
||||||
|
open fun getValue(scope: Scope) = this
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a += b
|
* a += b
|
||||||
* if( the operation is not defined, it returns null and the compiler would try
|
* if( the operation is not defined, it returns null and the compiler would try
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 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.obj
|
||||||
|
|
||||||
|
import net.sergeych.lyng.Arguments
|
||||||
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.Statement
|
||||||
|
import net.sergeych.lyng.statement
|
||||||
|
|
||||||
|
class ObjDelegateContext()
|
||||||
|
|
||||||
|
class ObjDelegate(
|
||||||
|
val getter: Statement,
|
||||||
|
val setter: Statement = statement { raiseNotImplemented("setter is not implemented") }
|
||||||
|
): Obj() {
|
||||||
|
|
||||||
|
override suspend fun assign(scope: Scope, other: Obj): Obj? {
|
||||||
|
setter.execute(scope.copy(Arguments(other)))
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val type = object: ObjClass("Delegate") {
|
||||||
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
|
scope.raiseError("Delegate should not be constructed directly")
|
||||||
|
}
|
||||||
|
}.apply {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -56,7 +56,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("RangeIterator", ObjIterable).apply {
|
val type = ObjClass("RangeIterator", ObjIterator).apply {
|
||||||
addFn("hasNext") {
|
addFn("hasNext") {
|
||||||
thisAs<ObjRangeIterator>().hasNext().toObj()
|
thisAs<ObjRangeIterator>().hasNext().toObj()
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,18 @@ package net.sergeych.lyng.stdlib_included
|
|||||||
internal val rootLyng = """
|
internal val rootLyng = """
|
||||||
package lyng.stdlib
|
package lyng.stdlib
|
||||||
|
|
||||||
|
fun cached(builder) {
|
||||||
|
var calculated = false
|
||||||
|
var value = null
|
||||||
|
{
|
||||||
|
if( !calculated ) {
|
||||||
|
value = builder()
|
||||||
|
calculated = true
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Iterable.filter(predicate) {
|
fun Iterable.filter(predicate) {
|
||||||
val list = this
|
val list = this
|
||||||
flow {
|
flow {
|
||||||
@ -38,7 +50,7 @@ fun Iterable.drop(n) {
|
|||||||
fun Iterable.first() {
|
fun Iterable.first() {
|
||||||
val i = iterator()
|
val i = iterator()
|
||||||
if( !i.hasNext() ) throw NoSuchElementException()
|
if( !i.hasNext() ) throw NoSuchElementException()
|
||||||
i.next()
|
i.next().also { i.cancelIteration() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Iterable.last() {
|
fun Iterable.last() {
|
||||||
@ -71,5 +83,15 @@ fun Iterable.takeLast(n) {
|
|||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Iterable.joinToString(prefix=" ", transformer=null) {
|
||||||
|
var result = null
|
||||||
|
for( part in this ) {
|
||||||
|
val transformed = transformer?(part)?.toString() ?: part.toString()
|
||||||
|
if( result == null ) result = transformed
|
||||||
|
else result += prefix + transformed
|
||||||
|
}
|
||||||
|
result ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
|
@ -2913,5 +2913,33 @@ class ScriptTest {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cachedTest() = runTest {
|
||||||
|
eval( """
|
||||||
|
|
||||||
|
var counter = 0
|
||||||
|
var value = cached {
|
||||||
|
counter++
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, counter)
|
||||||
|
assertEquals("ok", value())
|
||||||
|
assertEquals(1, counter)
|
||||||
|
assertEquals("ok", value())
|
||||||
|
assertEquals(1, counter)
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testJoinToString() = runTest {
|
||||||
|
eval("""
|
||||||
|
assertEquals( (1..3).joinToString(), "1 2 3")
|
||||||
|
assertEquals( (1..3).joinToString(":"), "1:2:3")
|
||||||
|
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -40,8 +40,9 @@ class StdlibTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testTake() = runTest {
|
fun testTake() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
assertEquals([1,2,3], (1..8).take(3).toList() )
|
val r = 1..8
|
||||||
assertEquals([7,8], (1..8).takeLast(2).toList() )
|
assertEquals([1,2,3], r.take(3).toList() )
|
||||||
|
assertEquals([7,8], r.takeLast(2).toList() )
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,4 +317,9 @@ class BookTest {
|
|||||||
runDocTests("../docs/RingBuffer.md")
|
runDocTests("../docs/RingBuffer.md")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIterable() = runBlocking {
|
||||||
|
runDocTests("../docs/Iterable.md")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user