fix #58 improper toString overload processing in Lyng
fix #57 Exception#getStackTrace() ref #56 StackTraceEntry is serializable now
This commit is contained in:
		
							parent
							
								
									8d1cafae80
								
							
						
					
					
						commit
						4b613fda7c
					
				@ -24,7 +24,7 @@ There is a shortcut for the last:
 | 
			
		||||
 | 
			
		||||
    val list = [10, 20, 30]
 | 
			
		||||
    [list.last, list.lastIndex]
 | 
			
		||||
    >>> [30, 2]
 | 
			
		||||
    >>> [30,2]
 | 
			
		||||
 | 
			
		||||
__Important__ negative indexes works wherever indexes are used, e.g. in insertion and removal methods too.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -87,7 +87,7 @@ You can use Char as both ends of the closed range:
 | 
			
		||||
Exclusive end char ranges are supported too:
 | 
			
		||||
 | 
			
		||||
    ('a'..<'c').toList 
 | 
			
		||||
    >>> ['a', 'b']
 | 
			
		||||
    >>> [a,b]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Instance members
 | 
			
		||||
 | 
			
		||||
@ -71,7 +71,7 @@ destructuring arrays when calling functions and lambdas:
 | 
			
		||||
        [ first, last ]
 | 
			
		||||
    }
 | 
			
		||||
    getFirstAndLast( ...(1..10) ) // see "splats" section below
 | 
			
		||||
    >>> [1, 10]
 | 
			
		||||
    >>> [1,10]
 | 
			
		||||
 | 
			
		||||
# Splats
 | 
			
		||||
 | 
			
		||||
@ -83,7 +83,7 @@ or whatever implementing [Iterable], is called _splats_. Here is how we use it:
 | 
			
		||||
    }
 | 
			
		||||
    val array = [1,2,3]
 | 
			
		||||
    testSplat("start", ...array, "end")
 | 
			
		||||
    >>> ["start", 1, 2, 3, "end"]
 | 
			
		||||
    >>> [start,1,2,3,end]
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
There could be any number of splats at any positions. You can splat any other [Iterable] type:
 | 
			
		||||
@ -93,7 +93,7 @@ There could be any number of splats at any positions. You can splat any other [I
 | 
			
		||||
    }
 | 
			
		||||
    val range = 1..3
 | 
			
		||||
    testSplat("start", ...range, "end")
 | 
			
		||||
    >>> ["start", 1, 2, 3, "end"]
 | 
			
		||||
    >>> [start,1,2,3,end]
 | 
			
		||||
    >>> void
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -183,8 +183,8 @@ Important difference from the channels or like, every time you collect the flow,
 | 
			
		||||
    // and again:
 | 
			
		||||
    assertEquals( result, f.toList() )
 | 
			
		||||
 | 
			
		||||
    >>> ["start", 1, 2, 3, 4]
 | 
			
		||||
    >>> ["start", 1, 2, 3, 4]
 | 
			
		||||
    >>> [start,1,2,3,4]
 | 
			
		||||
    >>> [start,1,2,3,4]
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
Notice that flow's lambda is not called until actual collection is started. Cold flows are
 | 
			
		||||
 | 
			
		||||
@ -505,21 +505,21 @@ Notice usage of indexing. You can use negative indexes to offset from the end of
 | 
			
		||||
When you want to "flatten" it to single array, you can use splat syntax:
 | 
			
		||||
 | 
			
		||||
    [1, ...[2,3], 4]
 | 
			
		||||
    >>> [1, 2, 3, 4]
 | 
			
		||||
    >>> [1,2,3,4]
 | 
			
		||||
 | 
			
		||||
Of course, you can splat from anything that is List (or list-like, but it will be defined later):
 | 
			
		||||
 | 
			
		||||
    val a = ["one", "two"]
 | 
			
		||||
    val b = [10.1, 20.2]
 | 
			
		||||
    ["start", ...b, ...a, "end"]
 | 
			
		||||
    >>> ["start", 10.1, 20.2, "one", "two", "end"]
 | 
			
		||||
    >>> [start,10.1,20.2,one,two,end]
 | 
			
		||||
 | 
			
		||||
Of course, you can set any list element:
 | 
			
		||||
 | 
			
		||||
    val a = [1, 2, 3]
 | 
			
		||||
    a[1] = 200
 | 
			
		||||
    a
 | 
			
		||||
    >>> [1, 200, 3]
 | 
			
		||||
    >>> [1,200,3]
 | 
			
		||||
 | 
			
		||||
Lists are comparable, and it works well as long as their respective elements are:
 | 
			
		||||
 | 
			
		||||
@ -609,20 +609,20 @@ Using splat arguments can simplify inserting list in list:
 | 
			
		||||
    val x = [1, 2, 3]
 | 
			
		||||
    x.insertAt( 1, ...[0,100,0])
 | 
			
		||||
    x
 | 
			
		||||
    >>> [1, 0, 100, 0, 2, 3]
 | 
			
		||||
    >>> [1,0,100,0,2,3]
 | 
			
		||||
 | 
			
		||||
Note that to add to the end you still need to use `add` or positive index of the after-last element:
 | 
			
		||||
 | 
			
		||||
    val x = [1,2,3]
 | 
			
		||||
    x.insertAt(3, 10)
 | 
			
		||||
    x
 | 
			
		||||
    >>> [1, 2, 3, 10]
 | 
			
		||||
    >>> [1,2,3,10]
 | 
			
		||||
 | 
			
		||||
but it is much simpler, and we recommend to use '+='
 | 
			
		||||
 | 
			
		||||
    val x = [1,2,3]
 | 
			
		||||
    x += 10
 | 
			
		||||
    >>> [1, 2, 3, 10]
 | 
			
		||||
    >>> [1,2,3,10]
 | 
			
		||||
 | 
			
		||||
## Removing list items
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ data class Arguments(val list: List<Obj>, val tailBlockMode: Boolean = false) :
 | 
			
		||||
        return list.map { it.toKotlin(scope) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun inspect(): String = list.joinToString(", ") { it.inspect() }
 | 
			
		||||
    suspend fun inspect(scope: Scope): String = list.map{ it.inspect(scope)}.joinToString(",")
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val EMPTY = Arguments(emptyList())
 | 
			
		||||
 | 
			
		||||
@ -1363,8 +1363,8 @@ class Compiler(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return statement(body.pos) { ctx ->
 | 
			
		||||
                val forContext = ctx.copy(start)
 | 
			
		||||
            return statement(body.pos) { cxt ->
 | 
			
		||||
                val forContext = cxt.copy(start)
 | 
			
		||||
 | 
			
		||||
                // loop var: StoredObject
 | 
			
		||||
                val loopSO = forContext.addItem(tVar.value, true, ObjNull)
 | 
			
		||||
@ -1393,7 +1393,7 @@ class Compiler(
 | 
			
		||||
                            .getOrElse {
 | 
			
		||||
                                throw ScriptError(
 | 
			
		||||
                                    tOp.pos,
 | 
			
		||||
                                    "object is not enumerable: no index access for ${sourceObj.inspect()}",
 | 
			
		||||
                                    "object is not enumerable: no index access for ${sourceObj.inspect(cxt)}",
 | 
			
		||||
                                    it
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
@ -1420,7 +1420,7 @@ class Compiler(
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!breakCaught && elseStatement != null) {
 | 
			
		||||
                        result = elseStatement.execute(ctx)
 | 
			
		||||
                        result = elseStatement.execute(cxt)
 | 
			
		||||
                    }
 | 
			
		||||
                    result
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -57,15 +57,15 @@ class Script(
 | 
			
		||||
            ObjException.addExceptionsToContext(this)
 | 
			
		||||
            addFn("print") {
 | 
			
		||||
                for ((i, a) in args.withIndex()) {
 | 
			
		||||
                    if (i > 0) print(' ' + a.asStr.value)
 | 
			
		||||
                    else print(a.asStr.value)
 | 
			
		||||
                    if (i > 0) print(' ' + a.toString(this).value)
 | 
			
		||||
                    else print(a.toString(this).value)
 | 
			
		||||
                }
 | 
			
		||||
                ObjVoid
 | 
			
		||||
            }
 | 
			
		||||
            addFn("println") {
 | 
			
		||||
                for ((i, a) in args.withIndex()) {
 | 
			
		||||
                    if (i > 0) print(' ' + a.asStr.value)
 | 
			
		||||
                    else print(a.asStr.value)
 | 
			
		||||
                    if (i > 0) print(' ' + a.toString(this).value)
 | 
			
		||||
                    else print(a.toString(this).value)
 | 
			
		||||
                }
 | 
			
		||||
                println()
 | 
			
		||||
                ObjVoid
 | 
			
		||||
@ -165,13 +165,13 @@ class Script(
 | 
			
		||||
                val a = requiredArg<Obj>(0)
 | 
			
		||||
                val b = requiredArg<Obj>(1)
 | 
			
		||||
                if( a.compareTo(this, b) != 0 )
 | 
			
		||||
                    raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect()} == ${b.inspect()}"))
 | 
			
		||||
                    raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
 | 
			
		||||
            }
 | 
			
		||||
            addVoidFn("assertNotEquals") {
 | 
			
		||||
                val a = requiredArg<Obj>(0)
 | 
			
		||||
                val b = requiredArg<Obj>(1)
 | 
			
		||||
                if( a.compareTo(this, b) == 0 )
 | 
			
		||||
                    raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect()} != ${b.inspect()}"))
 | 
			
		||||
                    raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} != ${b.inspect(this)}"))
 | 
			
		||||
            }
 | 
			
		||||
            addFn("assertThrows") {
 | 
			
		||||
                val code = requireOnlyArg<Statement>()
 | 
			
		||||
@ -287,7 +287,7 @@ class Script(
 | 
			
		||||
                            is ObjInt -> delay(a.value * 1000)
 | 
			
		||||
                            is ObjReal -> delay((a.value * 1000).roundToLong())
 | 
			
		||||
                            is ObjDuration -> delay(a.duration)
 | 
			
		||||
                            else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect()}")
 | 
			
		||||
                            else -> raiseIllegalArgument("Expected Duration, Int or Real, got ${a.inspect(this)}")
 | 
			
		||||
                        }
 | 
			
		||||
                        ObjVoid
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -17,10 +17,14 @@
 | 
			
		||||
 | 
			
		||||
package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
import net.sergeych.lyng.obj.ObjString
 | 
			
		||||
 | 
			
		||||
class Source(val fileName: String, text: String) {
 | 
			
		||||
 | 
			
		||||
    val lines = text.lines().map { it.trimEnd() }
 | 
			
		||||
 | 
			
		||||
    val objSourceName by lazy { ObjString(fileName) }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val builtIn: Source by lazy { Source("built-in", "") }
 | 
			
		||||
        val UNKNOWN: Source by lazy { Source("UNKNOWN", "") }
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ import net.sergeych.lyng.*
 | 
			
		||||
import net.sergeych.lynon.LynonDecoder
 | 
			
		||||
import net.sergeych.lynon.LynonEncoder
 | 
			
		||||
import net.sergeych.lynon.LynonType
 | 
			
		||||
import net.sergeych.mptools.CachedExpression
 | 
			
		||||
import net.sergeych.synctools.ProtectedOp
 | 
			
		||||
import net.sergeych.synctools.withLock
 | 
			
		||||
import kotlin.contracts.ExperimentalContracts
 | 
			
		||||
@ -49,7 +50,7 @@ open class Obj {
 | 
			
		||||
 | 
			
		||||
    private val opInstances = ProtectedOp()
 | 
			
		||||
 | 
			
		||||
    open fun inspect(): String = toString()
 | 
			
		||||
    open suspend fun inspect(scope: Scope): String = toString(scope).value
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Some objects are by-value, historically [ObjInt] and [ObjReal] are usually treated as such.
 | 
			
		||||
@ -84,14 +85,14 @@ open class Obj {
 | 
			
		||||
        scope: Scope,
 | 
			
		||||
        name: String,
 | 
			
		||||
        args: Arguments = Arguments.EMPTY,
 | 
			
		||||
        onNotFoundResult: Obj?=null
 | 
			
		||||
        onNotFoundResult: (()->Obj?)? = null
 | 
			
		||||
    ): Obj {
 | 
			
		||||
        return objClass.getInstanceMemberOrNull(name)?.value?.invoke(
 | 
			
		||||
            scope,
 | 
			
		||||
            this,
 | 
			
		||||
            args
 | 
			
		||||
        )
 | 
			
		||||
            ?: onNotFoundResult
 | 
			
		||||
            ?: onNotFoundResult?.invoke()
 | 
			
		||||
            ?: scope.raiseSymbolNotFound(name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -115,8 +116,11 @@ open class Obj {
 | 
			
		||||
        return invokeInstanceMethod(scope, "contains", other).toBool()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open val asStr: ObjString by lazy {
 | 
			
		||||
        if (this is ObjString) this else ObjString(this.toString())
 | 
			
		||||
    suspend open fun toString(scope: Scope): ObjString {
 | 
			
		||||
        return if (this is ObjString) this
 | 
			
		||||
        else invokeInstanceMethod(scope, "toString") {
 | 
			
		||||
            ObjString(this.toString())
 | 
			
		||||
        } as ObjString
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -276,9 +280,9 @@ open class Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun autoInstanceScope(parent: Scope): Scope  {
 | 
			
		||||
       val scope = parent.copy(newThisObj = this, args = parent.args)
 | 
			
		||||
        for( m in objClass.members) {
 | 
			
		||||
    fun autoInstanceScope(parent: Scope): Scope {
 | 
			
		||||
        val scope = parent.copy(newThisObj = this, args = parent.args)
 | 
			
		||||
        for (m in objClass.members) {
 | 
			
		||||
            scope.objects[m.key] = m.value
 | 
			
		||||
        }
 | 
			
		||||
        return scope
 | 
			
		||||
@ -287,11 +291,11 @@ open class Obj {
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        val rootObjectType = ObjClass("Obj").apply {
 | 
			
		||||
            addFn("toString") {
 | 
			
		||||
                thisObj.asStr
 | 
			
		||||
            addFn("toString", true) {
 | 
			
		||||
                ObjString(thisObj.toString())
 | 
			
		||||
            }
 | 
			
		||||
            addFn("inspect", true) {
 | 
			
		||||
                thisObj.inspect().toObj()
 | 
			
		||||
                thisObj.inspect(this).toObj()
 | 
			
		||||
            }
 | 
			
		||||
            addFn("contains") {
 | 
			
		||||
                ObjBool(thisObj.contains(this, args.firstAndOnly()))
 | 
			
		||||
@ -392,9 +396,14 @@ object ObjNull : Obj() {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments, onNotFoundResult: Obj?): Obj {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
//    override suspend fun invokeInstanceMethod(
 | 
			
		||||
//        scope: Scope,
 | 
			
		||||
//        name: String,
 | 
			
		||||
//        args: Arguments,
 | 
			
		||||
//        onNotFoundResult: (()->Obj?)?
 | 
			
		||||
//    ): Obj {
 | 
			
		||||
//        scope.raiseNPE()
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
@ -469,20 +478,51 @@ fun Obj.toBool(): Boolean =
 | 
			
		||||
data class ObjNamespace(val name: String) : Obj() {
 | 
			
		||||
    override val objClass by lazy { ObjClass(name) }
 | 
			
		||||
 | 
			
		||||
    override fun inspect(): String = "Ns[$name]"
 | 
			
		||||
    override suspend fun inspect(scope: Scope): String = "Ns[$name]"
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        return "package $name"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val message: String) : Obj() {
 | 
			
		||||
open class ObjException(
 | 
			
		||||
    val exceptionClass: ExceptionClass,
 | 
			
		||||
    val scope: Scope,
 | 
			
		||||
    val message: String,
 | 
			
		||||
    @Suppress("unused") val extraData: Obj = ObjNull
 | 
			
		||||
) : Obj() {
 | 
			
		||||
    constructor(name: String, scope: Scope, message: String) : this(
 | 
			
		||||
        getOrCreateExceptionClass(name),
 | 
			
		||||
        scope,
 | 
			
		||||
        message
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private val cachedStackTrace = CachedExpression<ObjList>()
 | 
			
		||||
 | 
			
		||||
    suspend fun getStackTrace(): ObjList {
 | 
			
		||||
        return cachedStackTrace.get {
 | 
			
		||||
            val result = ObjList()
 | 
			
		||||
            val cls = scope.get("StackTraceEntry")!!.value as ObjClass
 | 
			
		||||
            var s: Scope? = scope
 | 
			
		||||
            var lastPos: Pos? = null
 | 
			
		||||
            while( s != null ) {
 | 
			
		||||
                val pos = s.pos
 | 
			
		||||
                if( pos != lastPos && !pos.currentLine.isEmpty() ) {
 | 
			
		||||
                    result.list += cls.callWithArgs(
 | 
			
		||||
                        scope,
 | 
			
		||||
                        pos.source.objSourceName,
 | 
			
		||||
                        ObjInt(pos.line.toLong()),
 | 
			
		||||
                        ObjInt(pos.column.toLong()),
 | 
			
		||||
                        ObjString(pos.currentLine)
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                s = s.parent
 | 
			
		||||
                lastPos = pos
 | 
			
		||||
            }
 | 
			
		||||
            result
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constructor(scope: Scope, message: String) : this(Root, scope, message)
 | 
			
		||||
 | 
			
		||||
    fun raise(): Nothing {
 | 
			
		||||
@ -495,6 +535,12 @@ open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val me
 | 
			
		||||
        return "ObjException:${objClass.className}:${scope.pos}@${hashCode().encodeToHex()}"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
 | 
			
		||||
        encoder.encodeAny(scope, ObjString(exceptionClass.name))
 | 
			
		||||
        encoder.encodeAny(scope, ObjString(message))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
 | 
			
		||||
@ -510,6 +556,9 @@ open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val me
 | 
			
		||||
            addConst("message", statement {
 | 
			
		||||
                (thisObj as ObjException).message.toObj()
 | 
			
		||||
            })
 | 
			
		||||
            addFn("getStackTrace") {
 | 
			
		||||
                (thisObj as ObjException).getStackTrace()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private val op = ProtectedOp()
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,5 @@ val ObjArray by lazy {
 | 
			
		||||
        addFn("indices") {
 | 
			
		||||
            ObjRange(0.toObj(), thisObj.invokeInstanceMethod(this, "size"), false)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -23,7 +23,6 @@ import net.sergeych.lynon.LynonEncoder
 | 
			
		||||
import net.sergeych.lynon.LynonType
 | 
			
		||||
 | 
			
		||||
data class ObjBool(val value: Boolean) : Obj() {
 | 
			
		||||
    override val asStr by lazy { ObjString(value.toString()) }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is ObjBool) return -2
 | 
			
		||||
 | 
			
		||||
@ -87,7 +87,7 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
			
		||||
                byteArray + other.toFlow(scope).map { it.toLong().toUByte() }.toList().toTypedArray()
 | 
			
		||||
                    .toUByteArray()
 | 
			
		||||
            )
 | 
			
		||||
        } else scope.raiseIllegalArgument("can't concatenate buffer with ${other.inspect()}")
 | 
			
		||||
        } else scope.raiseIllegalArgument("can't concatenate buffer with ${other.inspect(scope)}")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = base64
 | 
			
		||||
@ -107,7 +107,7 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
			
		||||
        encoder.encodeCachedBytes(byteArray.asByteArray())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun inspect(): String = "Buf($base64)"
 | 
			
		||||
    override suspend fun inspect(scope: Scope): String = "Buf($base64)"
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private suspend fun createBufferFrom(scope: Scope, obj: Obj): ObjBuffer =
 | 
			
		||||
@ -129,7 +129,7 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
			
		||||
                        )
 | 
			
		||||
                    } else
 | 
			
		||||
                        scope.raiseIllegalArgument(
 | 
			
		||||
                            "can't construct buffer from ${obj.inspect()}"
 | 
			
		||||
                            "can't construct buffer from ${obj.inspect(scope)}"
 | 
			
		||||
                        )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -149,7 +149,7 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
			
		||||
                                is ObjChar -> b.value.code.toUByte()
 | 
			
		||||
                                is ObjInt -> b.value.toUByte()
 | 
			
		||||
                                else -> scope.raiseIllegalArgument(
 | 
			
		||||
                                    "invalid byte value for buffer constructor at index $i: ${b.inspect()}"
 | 
			
		||||
                                    "invalid byte value for buffer constructor at index $i: ${b.inspect(scope)}"
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                            data[i] = code
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ class ObjChar(val value: Char): Obj() {
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = value.toString()
 | 
			
		||||
 | 
			
		||||
    override fun inspect(): String = "'$value'"
 | 
			
		||||
    override suspend fun inspect(scope: Scope): String = "'$value'"
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
        return value.hashCode()
 | 
			
		||||
 | 
			
		||||
@ -61,13 +61,17 @@ open class ObjClass(
 | 
			
		||||
 | 
			
		||||
    override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
        val instance = ObjInstance(this)
 | 
			
		||||
        instance.instanceScope = scope.copy(newThisObj = instance,args = scope.args)
 | 
			
		||||
        instance.instanceScope = scope.copy(newThisObj = instance, args = scope.args)
 | 
			
		||||
        if (instanceConstructor != null) {
 | 
			
		||||
            instanceConstructor!!.execute(instance.instanceScope)
 | 
			
		||||
        }
 | 
			
		||||
        return instance
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun callWithArgs(scope: Scope, vararg plainArgs: Obj): Obj {
 | 
			
		||||
        return callOn(scope.copy(Arguments(*plainArgs)))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun createField(
 | 
			
		||||
        name: String,
 | 
			
		||||
@ -77,13 +81,13 @@ open class ObjClass(
 | 
			
		||||
        pos: Pos = Pos.builtIn
 | 
			
		||||
    ) {
 | 
			
		||||
        val existing = members[name] ?: allParentsSet.firstNotNullOfOrNull { it.members[name] }
 | 
			
		||||
        if( existing?.isMutable == false)
 | 
			
		||||
        if (existing?.isMutable == false)
 | 
			
		||||
            throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
 | 
			
		||||
        members[name] = ObjRecord(initialValue, isMutable, visibility)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initClassScope(): Scope {
 | 
			
		||||
        if( classScope == null ) classScope = Scope()
 | 
			
		||||
        if (classScope == null) classScope = Scope()
 | 
			
		||||
        return classScope!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -96,7 +100,7 @@ open class ObjClass(
 | 
			
		||||
    ) {
 | 
			
		||||
        initClassScope()
 | 
			
		||||
        val existing = classScope!!.objects[name]
 | 
			
		||||
        if( existing != null)
 | 
			
		||||
        if (existing != null)
 | 
			
		||||
            throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
 | 
			
		||||
        classScope!!.addItem(name, isMutable, initialValue, visibility)
 | 
			
		||||
    }
 | 
			
		||||
@ -127,26 +131,29 @@ open class ObjClass(
 | 
			
		||||
 | 
			
		||||
    override suspend fun readField(scope: Scope, name: String): ObjRecord {
 | 
			
		||||
        classScope?.objects?.get(name)?.let {
 | 
			
		||||
            if( it.visibility.isPublic ) return it
 | 
			
		||||
            if (it.visibility.isPublic) return it
 | 
			
		||||
        }
 | 
			
		||||
        return super.readField(scope, name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
 | 
			
		||||
        initClassScope().objects[name]?.let {
 | 
			
		||||
            if( it.isMutable) it.value = newValue
 | 
			
		||||
            if (it.isMutable) it.value = newValue
 | 
			
		||||
            else scope.raiseIllegalAssignment("can't assign $name is not mutable")
 | 
			
		||||
        }
 | 
			
		||||
            ?: super.writeField(scope, name, newValue)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments,
 | 
			
		||||
                                              onNotFoundResult: Obj?): Obj {
 | 
			
		||||
    override suspend fun invokeInstanceMethod(
 | 
			
		||||
        scope: Scope, name: String, args: Arguments,
 | 
			
		||||
        onNotFoundResult: (() -> Obj?)?
 | 
			
		||||
    ): Obj {
 | 
			
		||||
        return classScope?.objects?.get(name)?.value?.invoke(scope, this, args)
 | 
			
		||||
            ?: super.invokeInstanceMethod(scope, name, args, onNotFoundResult)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj = scope.raiseNotImplemented()
 | 
			
		||||
    open suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,4 +22,4 @@ package net.sergeych.lyng.obj
 | 
			
		||||
 */
 | 
			
		||||
val ObjCollection = ObjClass("Collection", ObjIterable).apply {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -57,7 +57,7 @@ class ObjDuration(val duration: Duration) : Obj() {
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                val args = scope.args
 | 
			
		||||
                if( args.list.size > 1 )
 | 
			
		||||
                    scope.raiseIllegalArgument("can't construct Duration(${args.inspect()})")
 | 
			
		||||
                    scope.raiseIllegalArgument("can't construct Duration(${args.inspect(scope)})")
 | 
			
		||||
                val a0 = args.list.getOrNull(0)
 | 
			
		||||
 | 
			
		||||
                return ObjDuration(
 | 
			
		||||
@ -66,7 +66,7 @@ class ObjDuration(val duration: Duration) : Obj() {
 | 
			
		||||
                        is ObjInt -> a0.value.seconds
 | 
			
		||||
                        is ObjReal -> a0.value.seconds
 | 
			
		||||
                        else -> {
 | 
			
		||||
                            scope.raiseIllegalArgument("can't construct Instant(${args.inspect()})")
 | 
			
		||||
                            scope.raiseIllegalArgument("can't construct Instant(${args.inspect(scope)})")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments,
 | 
			
		||||
                                              onNotFoundResult: Obj?): Obj =
 | 
			
		||||
                                              onNotFoundResult: (()->Obj?)?): Obj =
 | 
			
		||||
        instanceScope[name]?.let {
 | 
			
		||||
            if (it.visibility.isPublic)
 | 
			
		||||
                it.value.invoke(
 | 
			
		||||
 | 
			
		||||
@ -30,12 +30,19 @@ class ObjInstanceClass(val name: String) : ObjClass(name) {
 | 
			
		||||
    override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
 | 
			
		||||
        val args = decoder.decodeAnyList(scope)
 | 
			
		||||
        val actualSize = constructorMeta?.params?.size ?: 0
 | 
			
		||||
        if( args.size > actualSize )
 | 
			
		||||
        if (args.size > actualSize)
 | 
			
		||||
            scope.raiseIllegalArgument("constructor $name has only $actualSize but serialized version has ${args.size}")
 | 
			
		||||
        val newScope = scope.copy(args = Arguments(args))
 | 
			
		||||
        return (callOn(newScope) as ObjInstance).apply {
 | 
			
		||||
            deserializeStateVars(scope,decoder)
 | 
			
		||||
            invokeInstanceMethod(scope, "onDeserialized", onNotFoundResult = ObjVoid)
 | 
			
		||||
            deserializeStateVars(scope, decoder)
 | 
			
		||||
            invokeInstanceMethod(scope, "onDeserialized") { ObjVoid }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        addFn("toString", true) {
 | 
			
		||||
            println("-------------- tos! --------------")
 | 
			
		||||
            ObjString(thisObj.toString())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -117,7 +117,7 @@ class ObjInstant(val instant: Instant,val truncateMode: LynonSettings.InstantTru
 | 
			
		||||
                        is ObjInstant -> a0.instant
 | 
			
		||||
 | 
			
		||||
                        else -> {
 | 
			
		||||
                            scope.raiseIllegalArgument("can't construct Instant(${args.inspect()})")
 | 
			
		||||
                            scope.raiseIllegalArgument("can't construct Instant(${args.inspect(scope)})")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ import net.sergeych.lynon.LynonEncoder
 | 
			
		||||
import net.sergeych.lynon.LynonType
 | 
			
		||||
 | 
			
		||||
class ObjInt(var value: Long, override val isConst: Boolean = false) : Obj(), Numeric {
 | 
			
		||||
    override val asStr get() = ObjString(value.toString())
 | 
			
		||||
    override val longValue get() = value
 | 
			
		||||
    override val doubleValue get() = value.toDouble()
 | 
			
		||||
    override val toObjInt get() = this
 | 
			
		||||
 | 
			
		||||
@ -82,18 +82,18 @@ fun Obj.toFlow(scope: Scope): Flow<Obj> = flow {
 | 
			
		||||
 *
 | 
			
		||||
 * IF callback returns false, iteration is stopped.
 | 
			
		||||
 */
 | 
			
		||||
suspend fun Obj.enumerate(scope: Scope,callback: suspend (Obj)->Boolean) {
 | 
			
		||||
suspend fun Obj.enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
 | 
			
		||||
    val iterator = invokeInstanceMethod(scope, "iterator")
 | 
			
		||||
    val hasNext = iterator.getInstanceMethod(scope, "hasNext")
 | 
			
		||||
    val next = iterator.getInstanceMethod(scope, "next")
 | 
			
		||||
    var closeIt = false
 | 
			
		||||
    while (hasNext.invoke(scope, iterator).toBool()) {
 | 
			
		||||
        val nextValue = next.invoke(scope, iterator)
 | 
			
		||||
        if( !callback(nextValue) ) {
 | 
			
		||||
        if (!callback(nextValue)) {
 | 
			
		||||
            closeIt = true
 | 
			
		||||
            break
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if( closeIt )
 | 
			
		||||
        iterator.invokeInstanceMethod(scope, "cancelIteration", onNotFoundResult = ObjVoid)
 | 
			
		||||
    if (closeIt)
 | 
			
		||||
        iterator.invokeInstanceMethod(scope, "cancelIteration") { ObjVoid }
 | 
			
		||||
}
 | 
			
		||||
@ -25,10 +25,6 @@ import net.sergeych.lynon.LynonType
 | 
			
		||||
 | 
			
		||||
class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = "[${
 | 
			
		||||
        list.joinToString(separator = ", ") { it.inspect() }
 | 
			
		||||
    }]"
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
        return when (index) {
 | 
			
		||||
            is ObjInt -> {
 | 
			
		||||
@ -65,7 +61,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            else -> scope.raiseIllegalArgument("Illegal index object for a list: ${index.inspect()}")
 | 
			
		||||
            else -> scope.raiseIllegalArgument("Illegal index object for a list: ${index.inspect(scope)}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ class ObjMutableBuffer(byteArray: UByteArray) : ObjBuffer(byteArray) {
 | 
			
		||||
            is ObjInt -> newValue.value.toUByte()
 | 
			
		||||
            is ObjChar -> newValue.value.code.toUByte()
 | 
			
		||||
            else -> scope.raiseIllegalArgument(
 | 
			
		||||
                "invalid byte value for buffer at index ${index.inspect()}: ${newValue.inspect()}"
 | 
			
		||||
                "invalid byte value for buffer at index ${index.inspect(scope)}: ${newValue.inspect(scope)}"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -54,7 +54,7 @@ class ObjMutableBuffer(byteArray: UByteArray) : ObjBuffer(byteArray) {
 | 
			
		||||
                        )
 | 
			
		||||
                    } else
 | 
			
		||||
                        scope.raiseIllegalArgument(
 | 
			
		||||
                            "can't construct buffer from ${obj.inspect()}"
 | 
			
		||||
                            "can't construct buffer from ${obj.inspect(scope)}"
 | 
			
		||||
                        )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -74,7 +74,7 @@ class ObjMutableBuffer(byteArray: UByteArray) : ObjBuffer(byteArray) {
 | 
			
		||||
                                is ObjChar -> b.value.code.toUByte()
 | 
			
		||||
                                is ObjInt -> b.value.toUByte()
 | 
			
		||||
                                else -> scope.raiseIllegalArgument(
 | 
			
		||||
                                    "invalid byte value for buffer constructor at index $i: ${b.inspect()}"
 | 
			
		||||
                                    "invalid byte value for buffer constructor at index $i: ${b.inspect(scope)}"
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                            data[i] = code
 | 
			
		||||
 | 
			
		||||
@ -26,12 +26,12 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass = type
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
    override suspend fun toString(scope: Scope): ObjString {
 | 
			
		||||
        val result = StringBuilder()
 | 
			
		||||
        result.append("${start?.inspect() ?: '∞'} ..")
 | 
			
		||||
        result.append("${start?.inspect(scope) ?: '∞'} ..")
 | 
			
		||||
        if (!isEndInclusive) result.append('<')
 | 
			
		||||
        result.append(" ${end?.inspect() ?: '∞'}")
 | 
			
		||||
        return result.toString()
 | 
			
		||||
        result.append(" ${end?.inspect(scope) ?: '∞'}")
 | 
			
		||||
        return ObjString(result.toString())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -52,11 +52,11 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
 | 
			
		||||
     * if start is not ObjInt, raises [ObjIllegalArgumentException]
 | 
			
		||||
     * otherwise returns start.value.toInt()
 | 
			
		||||
     */
 | 
			
		||||
    fun startInt(scope: Scope): Int =
 | 
			
		||||
    suspend fun startInt(scope: Scope): Int =
 | 
			
		||||
        if( start == null || start is ObjNull) 0
 | 
			
		||||
        else {
 | 
			
		||||
            if( start is ObjInt) start.value.toInt()
 | 
			
		||||
            else scope.raiseIllegalArgument("start is not Int: ${start.inspect()}")
 | 
			
		||||
            else scope.raiseIllegalArgument("start is not Int: ${start.inspect(scope)}")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    suspend fun containsRange(scope: Scope, other: ObjRange): Boolean {
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,6 @@ import kotlin.math.floor
 | 
			
		||||
import kotlin.math.roundToLong
 | 
			
		||||
 | 
			
		||||
data class ObjReal(val value: Double) : Obj(), Numeric {
 | 
			
		||||
    override val asStr by lazy { ObjString(value.toString()) }
 | 
			
		||||
    override val longValue: Long by lazy { floor(value).toLong() }
 | 
			
		||||
    override val doubleValue: Double by lazy { value }
 | 
			
		||||
    override val toObjInt: ObjInt by lazy { ObjInt(longValue) }
 | 
			
		||||
 | 
			
		||||
@ -44,9 +44,7 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = value
 | 
			
		||||
 | 
			
		||||
    override val asStr: ObjString by lazy { this }
 | 
			
		||||
 | 
			
		||||
    override fun inspect(): String {
 | 
			
		||||
    override suspend fun inspect(scope: Scope): String {
 | 
			
		||||
        return "\"$value\""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -54,7 +52,7 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
        get() = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun plus(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        return ObjString(value + other.asStr.value)
 | 
			
		||||
        return ObjString(value + other.toString(scope).value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
@ -159,6 +157,9 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
            addFn("toReal") {
 | 
			
		||||
                ObjReal(thisAs<ObjString>().value.toDouble())
 | 
			
		||||
            }
 | 
			
		||||
            addFn("trim") {
 | 
			
		||||
                thisAs<ObjString>().value.trim().let(::ObjString)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -103,6 +103,21 @@ fun Iterable.any(predicate): Bool {
 | 
			
		||||
fun Iterable.all(predicate): Bool {
 | 
			
		||||
    !any { !predicate(it) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun List.toString() {
 | 
			
		||||
    "[" + joinToString(",") + "]"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StackTraceEntry(
 | 
			
		||||
    val sourceName: String,
 | 
			
		||||
    val line: Int,
 | 
			
		||||
    val column: Int,
 | 
			
		||||
    val sourceString: String
 | 
			
		||||
) {
 | 
			
		||||
    fun toString() {
 | 
			
		||||
        "%s:%d:%d: %s"(sourceName, line, column, sourceString.trim())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
""".trimIndent()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1789,7 +1789,7 @@ class ScriptTest {
 | 
			
		||||
                "e="+e+"f="+f()
 | 
			
		||||
            }
 | 
			
		||||
            assertEquals("e=[]f=xx", f { "xx" })
 | 
			
		||||
            assertEquals("e=[1, 2]f=xx", f(1,2) { "xx" })
 | 
			
		||||
            assertEquals("e=[1,2]f=xx", f(1,2) { "xx" })
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@ -1824,7 +1824,7 @@ class ScriptTest {
 | 
			
		||||
            }
 | 
			
		||||
            val f = Foo()
 | 
			
		||||
            assertEquals("e=[]f=xx", f.f { "xx" })
 | 
			
		||||
            assertEquals("e=[1, 2]f=xx", f.f(1,2) { "xx" })
 | 
			
		||||
            assertEquals("e=[1,2]f=xx", f.f(1,2) { "xx" })
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@ -2661,7 +2661,8 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testBufferEncodings() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            import lyng.buffer
 | 
			
		||||
            
 | 
			
		||||
            val b = Buffer("hello")
 | 
			
		||||
@ -2676,7 +2677,8 @@ class ScriptTest {
 | 
			
		||||
            
 | 
			
		||||
            println(b.inspect())
 | 
			
		||||
                        
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -2900,7 +2902,8 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun tesFunAnnotation() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
        
 | 
			
		||||
            val exportedSymbols = Map()
 | 
			
		||||
            
 | 
			
		||||
@ -2919,7 +2922,8 @@ class ScriptTest {
 | 
			
		||||
            assert( exportedSymbols["getBalance"] != null )
 | 
			
		||||
            assertEquals(122, getBalance(1))
 | 
			
		||||
            
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -2939,12 +2943,14 @@ class ScriptTest {
 | 
			
		||||
                assertEquals( Color.valueOf("GREEN"), Color.GREEN )
 | 
			
		||||
 | 
			
		||||
                
 | 
			
		||||
                """.trimIndent())
 | 
			
		||||
                """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun enumSerializationTest() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            import lyng.serialization
 | 
			
		||||
            
 | 
			
		||||
            enum Color {
 | 
			
		||||
@ -2960,12 +2966,14 @@ class ScriptTest {
 | 
			
		||||
            assert( e1.size / 1000.0 < 6)
 | 
			
		||||
            println(Lynon.encode( (1..100).map { "RED" } ).toDump() )
 | 
			
		||||
            
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun cachedTest() = runTest {
 | 
			
		||||
        eval( """
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
        
 | 
			
		||||
        var counter = 0
 | 
			
		||||
        var value = cached {
 | 
			
		||||
@ -2978,66 +2986,82 @@ class ScriptTest {
 | 
			
		||||
        assertEquals(1, counter)
 | 
			
		||||
        assertEquals("ok", value())
 | 
			
		||||
        assertEquals(1, counter)
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testJoinToString() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        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())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testElvisAndThrow() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            val x = assertThrows {
 | 
			
		||||
                null ?: throw "test" + "x"
 | 
			
		||||
            }
 | 
			
		||||
            assertEquals( "testx", x.message)
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testElvisAndThrow2() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            val t = "112"
 | 
			
		||||
            val x = t ?: run { throw "testx" }
 | 
			
		||||
            }
 | 
			
		||||
            assertEquals( "112", x)
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testElvisAndRunThrow() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            val x = assertThrows {
 | 
			
		||||
                null ?: run { throw "testx" }
 | 
			
		||||
            }
 | 
			
		||||
            assertEquals( "testx", x.message)
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testNewlinesAnsCommentsInExpressions() = runTest {
 | 
			
		||||
        assertEquals( 2, (Scope().eval("""
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            2, (Scope().eval(
 | 
			
		||||
                """
 | 
			
		||||
            val e = 1 + 4 -
 | 
			
		||||
                3
 | 
			
		||||
        """.trimIndent())).toInt())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
            )).toInt()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
                val x = [1,2,3]
 | 
			
		||||
                    .map { it * 10 }
 | 
			
		||||
                    .map { it + 1 }
 | 
			
		||||
                assertEquals( [11,21,31], x)
 | 
			
		||||
            """.trimIndent())
 | 
			
		||||
            """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testNotExpressionWithoutWs() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            fun test() { false }
 | 
			
		||||
            class T(value)
 | 
			
		||||
            assert( !false )
 | 
			
		||||
@ -3046,12 +3070,14 @@ class ScriptTest {
 | 
			
		||||
            val t = T(false)
 | 
			
		||||
            assert( !t.value )
 | 
			
		||||
            assert( !if( true ) false else true )
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testMultilineFnDeclaration() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
            fun test(
 | 
			
		||||
                x = 1,
 | 
			
		||||
                y = 2
 | 
			
		||||
@ -3063,8 +3089,38 @@ class ScriptTest {
 | 
			
		||||
                6,
 | 
			
		||||
                7
 | 
			
		||||
            ) )
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testOverridenListToString() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
            val x = [1,2,3]
 | 
			
		||||
            assertEquals( "[1,2,3]", x.toString() )
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testExceptionSerialization() = runTest {
 | 
			
		||||
        eval(
 | 
			
		||||
            """
 | 
			
		||||
                import lyng.serialization
 | 
			
		||||
                val x = [1,2,3]
 | 
			
		||||
                assertEquals( "[1,2,3]", x.toString() )
 | 
			
		||||
                try {    
 | 
			
		||||
                    require(false)
 | 
			
		||||
                }
 | 
			
		||||
                catch (e) {
 | 
			
		||||
                    println(e)
 | 
			
		||||
                    println(e.getStackTrace())
 | 
			
		||||
                    for( t in e.getStackTrace() ) {
 | 
			
		||||
                        println(t)
 | 
			
		||||
                    }
 | 
			
		||||
//                    val coded = Lynon.encode(e)
 | 
			
		||||
//                    println(coded.toDump())
 | 
			
		||||
                }
 | 
			
		||||
                """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -177,11 +177,11 @@ suspend fun DocTest.test(_scope: Scope? = null) {
 | 
			
		||||
    scope.apply {
 | 
			
		||||
        addFn("println") {
 | 
			
		||||
            if( bookMode ) {
 | 
			
		||||
                println("${currentTest.fileNamePart}:${currentTest.line}> ${args.joinToString(" "){it.asStr.value}}")
 | 
			
		||||
                println("${currentTest.fileNamePart}:${currentTest.line}> ${args.map{it.toString(this).value}.joinToString(" ")}")
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                for ((i, a) in args.withIndex()) {
 | 
			
		||||
                    if (i > 0) collectedOutput.append(' '); collectedOutput.append(a.asStr.value)
 | 
			
		||||
                    if (i > 0) collectedOutput.append(' '); collectedOutput.append(a.toString(this).value)
 | 
			
		||||
                    collectedOutput.append('\n')
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -194,7 +194,7 @@ suspend fun DocTest.test(_scope: Scope? = null) {
 | 
			
		||||
    } catch (e: Throwable) {
 | 
			
		||||
        error = e
 | 
			
		||||
        null
 | 
			
		||||
    }?.inspect()?.replace(Regex("@\\d+"), "@...")
 | 
			
		||||
    }?.inspect(scope)?.replace(Regex("@\\d+"), "@...")
 | 
			
		||||
 | 
			
		||||
    if (bookMode) {
 | 
			
		||||
        if (error != null) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user