renaming context to scope, as it is rather scp[e than the context
This commit is contained in:
		
							parent
							
								
									6cf99fbd13
								
							
						
					
					
						commit
						ddbcbf9e4e
					
				
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							@ -75,21 +75,23 @@ runBlocking {
 | 
			
		||||
 | 
			
		||||
### Exchanging information
 | 
			
		||||
 | 
			
		||||
Script is executed over some `Context`. Create instance of the context,
 | 
			
		||||
add your specific vars and functions to it, an call over it:
 | 
			
		||||
Script is executed over some `Scope`. Create instance,
 | 
			
		||||
add your specific vars and functions to it, and call:
 | 
			
		||||
 | 
			
		||||
```kotlin
 | 
			
		||||
import new.sergeych.lyng.* 
 | 
			
		||||
 | 
			
		||||
import com.sun.source.tree.Scope
 | 
			
		||||
import new.sergeych.lyng.*
 | 
			
		||||
 | 
			
		||||
// simple function
 | 
			
		||||
val context = Context().apply {
 | 
			
		||||
val scope = Scope().apply {
 | 
			
		||||
    addFn("addArgs") {
 | 
			
		||||
        var sum = 0.0
 | 
			
		||||
        for( a in args) sum += a.toDouble()
 | 
			
		||||
        for (a in args) sum += a.toDouble()
 | 
			
		||||
        ObjReal(sum)
 | 
			
		||||
    }
 | 
			
		||||
    addConst("LIGHT_SPEED", ObjReal(299_792_458.0))
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // callback back to kotlin to some suspend fn, for example::
 | 
			
		||||
    // suspend fun doSomeWork(text: String): Int
 | 
			
		||||
    addFn("doSomeWork") {
 | 
			
		||||
@ -99,9 +101,9 @@ val context = Context().apply {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
// adding constant:
 | 
			
		||||
context.eval("addArgs(1,2,3)") // <- 6
 | 
			
		||||
scope.eval("addArgs(1,2,3)") // <- 6
 | 
			
		||||
```
 | 
			
		||||
Note that the context stores all changes in it so you can make calls on a single context to preserve state between calls.
 | 
			
		||||
Note that the scope stores all changes in it so you can make calls on a single scope to preserve state between calls.
 | 
			
		||||
 | 
			
		||||
## Why? 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ data class CommandResult(
 | 
			
		||||
    val error: String
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
val baseContext = Context().apply {
 | 
			
		||||
val baseScope = Scope().apply {
 | 
			
		||||
    addFn("exit") {
 | 
			
		||||
        exit(requireOnlyArg<ObjInt>().toInt())
 | 
			
		||||
        ObjVoid
 | 
			
		||||
@ -74,7 +74,7 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
 | 
			
		||||
                val objargs = mutableListOf<String>()
 | 
			
		||||
                script?.let { objargs += it }
 | 
			
		||||
                objargs += args
 | 
			
		||||
                baseContext.addConst(
 | 
			
		||||
                baseScope.addConst(
 | 
			
		||||
                    "ARGV", ObjList(
 | 
			
		||||
                        objargs.map { ObjString(it) }.toMutableList()
 | 
			
		||||
                    )
 | 
			
		||||
@ -82,7 +82,7 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
 | 
			
		||||
                launcher {
 | 
			
		||||
                    // there is no script name, it is a first argument instead:
 | 
			
		||||
                    processErrors {
 | 
			
		||||
                        baseContext.eval(execute!!)
 | 
			
		||||
                        baseScope.eval(execute!!)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -98,7 +98,7 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
 | 
			
		||||
                    )
 | 
			
		||||
                    echoFormattedHelp()
 | 
			
		||||
                } else {
 | 
			
		||||
                    baseContext.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
 | 
			
		||||
                    baseScope.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
 | 
			
		||||
                    launcher { executeFile(script!!) }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -118,7 +118,7 @@ suspend fun executeFile(fileName: String) {
 | 
			
		||||
        text = text.substring(pos + 1)
 | 
			
		||||
    }
 | 
			
		||||
    processErrors {
 | 
			
		||||
        Compiler.compile(Source(fileName, text)).execute(baseContext)
 | 
			
		||||
        Compiler.compile(Source(fileName, text)).execute(baseScope)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,17 +0,0 @@
 | 
			
		||||
package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Special version of the [Context] used to `apply` new this object to
 | 
			
		||||
 * _parent context property.
 | 
			
		||||
 *
 | 
			
		||||
 * @param _parent context to apply to
 | 
			
		||||
 * @param args arguments for the new context
 | 
			
		||||
 * @param appliedContext the new context to apply, it will have lower priority except for `this` which
 | 
			
		||||
 *      will be reset by appliedContext's `this`.
 | 
			
		||||
 */
 | 
			
		||||
class AppliedContext(_parent: Context, args: Arguments, val appliedContext: Context)
 | 
			
		||||
    : Context(_parent, args, appliedContext.pos, appliedContext.thisObj) {
 | 
			
		||||
    override fun get(name: String): ObjRecord? =
 | 
			
		||||
        if (name == "this") thisObj.asReadonly
 | 
			
		||||
        else super.get(name) ?: appliedContext[name]
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Special version of the [Scope] used to `apply` new this object to
 | 
			
		||||
 * _parent context property.
 | 
			
		||||
 *
 | 
			
		||||
 * @param _parent context to apply to
 | 
			
		||||
 * @param args arguments for the new context
 | 
			
		||||
 * @param appliedScope the new context to apply, it will have lower priority except for `this` which
 | 
			
		||||
 *      will be reset by appliedContext's `this`.
 | 
			
		||||
 */
 | 
			
		||||
class AppliedScope(_parent: Scope, args: Arguments, val appliedScope: Scope)
 | 
			
		||||
    : Scope(_parent, args, appliedScope.pos, appliedScope.thisObj) {
 | 
			
		||||
    override fun get(name: String): ObjRecord? =
 | 
			
		||||
        if (name == "this") thisObj.asReadonly
 | 
			
		||||
        else super.get(name) ?: appliedScope[name]
 | 
			
		||||
}
 | 
			
		||||
@ -22,13 +22,13 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
 | 
			
		||||
     * parse args and create local vars in a given context
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun assignToContext(
 | 
			
		||||
        context: Context,
 | 
			
		||||
        arguments: Arguments = context.args,
 | 
			
		||||
        scope: Scope,
 | 
			
		||||
        arguments: Arguments = scope.args,
 | 
			
		||||
        defaultAccessType: AccessType = AccessType.Var,
 | 
			
		||||
        defaultVisibility: Visibility = Visibility.Public
 | 
			
		||||
    ) {
 | 
			
		||||
        fun assign(a: Item, value: Obj) {
 | 
			
		||||
            context.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable, value,
 | 
			
		||||
            scope.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable, value,
 | 
			
		||||
                a.visibility ?: defaultVisibility)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -52,11 +52,11 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
 | 
			
		||||
                if (a.isEllipsis) break
 | 
			
		||||
                val value = when {
 | 
			
		||||
                    i < callArgs.size -> callArgs[i]
 | 
			
		||||
                    a.defaultValue != null -> a.defaultValue.execute(context)
 | 
			
		||||
                    a.defaultValue != null -> a.defaultValue.execute(scope)
 | 
			
		||||
                    else -> {
 | 
			
		||||
                        println("callArgs: ${callArgs.joinToString()}")
 | 
			
		||||
                        println("tailBlockMode: ${arguments.tailBlockMode}")
 | 
			
		||||
                        context.raiseIllegalArgument("too few arguments for the call")
 | 
			
		||||
                        scope.raiseIllegalArgument("too few arguments for the call")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                assign(a, value)
 | 
			
		||||
@ -76,8 +76,8 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
 | 
			
		||||
                        callArgs[j--]
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    a.defaultValue != null -> a.defaultValue.execute(context)
 | 
			
		||||
                    else -> context.raiseIllegalArgument("too few arguments for the call")
 | 
			
		||||
                    a.defaultValue != null -> a.defaultValue.execute(scope)
 | 
			
		||||
                    else -> scope.raiseIllegalArgument("too few arguments for the call")
 | 
			
		||||
                }
 | 
			
		||||
                assign(a, value)
 | 
			
		||||
                i--
 | 
			
		||||
@ -98,7 +98,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
 | 
			
		||||
            processEllipsis(leftIndex, end)
 | 
			
		||||
        } else {
 | 
			
		||||
            if (leftIndex < callArgs.size)
 | 
			
		||||
                context.raiseIllegalArgument("too many arguments for the call")
 | 
			
		||||
                scope.raiseIllegalArgument("too many arguments for the call")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,11 @@ package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false)
 | 
			
		||||
 | 
			
		||||
suspend fun Collection<ParsedArgument>.toArguments(context: Context,tailBlockMode: Boolean): Arguments {
 | 
			
		||||
suspend fun Collection<ParsedArgument>.toArguments(scope: Scope, tailBlockMode: Boolean): Arguments {
 | 
			
		||||
    val list = mutableListOf<Obj>()
 | 
			
		||||
 | 
			
		||||
    for (x in this) {
 | 
			
		||||
        val value = x.value.execute(context)
 | 
			
		||||
        val value = x.value.execute(scope)
 | 
			
		||||
        if (x.isSplat) {
 | 
			
		||||
            when {
 | 
			
		||||
                value is ObjList -> {
 | 
			
		||||
@ -14,11 +14,11 @@ suspend fun Collection<ParsedArgument>.toArguments(context: Context,tailBlockMod
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                value.isInstanceOf(ObjIterable) -> {
 | 
			
		||||
                    val i = (value.invokeInstanceMethod(context, "toList") as ObjList).list
 | 
			
		||||
                    val i = (value.invokeInstanceMethod(scope, "toList") as ObjList).list
 | 
			
		||||
                    i.forEach { list.add(it) }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                else -> context.raiseClassCastError("expected list of objects for splat argument")
 | 
			
		||||
                else -> scope.raiseClassCastError("expected list of objects for splat argument")
 | 
			
		||||
            }
 | 
			
		||||
        } else
 | 
			
		||||
            list.add(value)
 | 
			
		||||
@ -38,8 +38,8 @@ data class Arguments(val list: List<Obj>,val tailBlockMode: Boolean = false) : L
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert to list of kotlin objects, see [Obj.toKotlin].
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun toKotlinList(context: Context): List<Any?> {
 | 
			
		||||
        return list.map { it.toKotlin(context) }
 | 
			
		||||
    suspend fun toKotlinList(scope: Scope): List<Any?> {
 | 
			
		||||
        return list.map { it.toKotlin(scope) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
@ -380,11 +380,11 @@ class Compiler(
 | 
			
		||||
 | 
			
		||||
        val body = parseBlock(skipLeadingBrace = true)
 | 
			
		||||
 | 
			
		||||
        var closure: Context? = null
 | 
			
		||||
        var closure: Scope? = null
 | 
			
		||||
 | 
			
		||||
        val callStatement = statement {
 | 
			
		||||
            // and the source closure of the lambda which might have other thisObj.
 | 
			
		||||
            val context = AppliedContext(closure!!, args, this)
 | 
			
		||||
            val context = AppliedScope(closure!!, args, this)
 | 
			
		||||
            if (argsDeclaration == null) {
 | 
			
		||||
                // no args: automatic var 'it'
 | 
			
		||||
                val l = args.list
 | 
			
		||||
@ -1154,7 +1154,7 @@ class Compiler(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun loopIntRange(
 | 
			
		||||
        forContext: Context, start: Int, end: Int, loopVar: ObjRecord,
 | 
			
		||||
        forScope: Scope, start: Int, end: Int, loopVar: ObjRecord,
 | 
			
		||||
        body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean
 | 
			
		||||
    ): Obj {
 | 
			
		||||
        var result: Obj = ObjVoid
 | 
			
		||||
@ -1164,7 +1164,7 @@ class Compiler(
 | 
			
		||||
            for (i in start..<end) {
 | 
			
		||||
                iVar.value = i.toLong()
 | 
			
		||||
                try {
 | 
			
		||||
                    result = body.execute(forContext)
 | 
			
		||||
                    result = body.execute(forScope)
 | 
			
		||||
                } catch (lbe: LoopBreakContinueException) {
 | 
			
		||||
                    if (lbe.label == label || lbe.label == null) {
 | 
			
		||||
                        if (lbe.doContinue) continue
 | 
			
		||||
@ -1176,24 +1176,24 @@ class Compiler(
 | 
			
		||||
        } else {
 | 
			
		||||
            for (i in start.toLong()..<end.toLong()) {
 | 
			
		||||
                iVar.value = i
 | 
			
		||||
                result = body.execute(forContext)
 | 
			
		||||
                result = body.execute(forScope)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return elseStatement?.execute(forContext) ?: result
 | 
			
		||||
        return elseStatement?.execute(forScope) ?: result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun loopIterable(
 | 
			
		||||
        forContext: Context, sourceObj: Obj, loopVar: ObjRecord,
 | 
			
		||||
        forScope: Scope, sourceObj: Obj, loopVar: ObjRecord,
 | 
			
		||||
        body: Statement, elseStatement: Statement?, label: String?,
 | 
			
		||||
        catchBreak: Boolean
 | 
			
		||||
    ): Obj {
 | 
			
		||||
        val iterObj = sourceObj.invokeInstanceMethod(forContext, "iterator")
 | 
			
		||||
        val iterObj = sourceObj.invokeInstanceMethod(forScope, "iterator")
 | 
			
		||||
        var result: Obj = ObjVoid
 | 
			
		||||
        while (iterObj.invokeInstanceMethod(forContext, "hasNext").toBool()) {
 | 
			
		||||
        while (iterObj.invokeInstanceMethod(forScope, "hasNext").toBool()) {
 | 
			
		||||
            if (catchBreak)
 | 
			
		||||
                try {
 | 
			
		||||
                    loopVar.value = iterObj.invokeInstanceMethod(forContext, "next")
 | 
			
		||||
                    result = body.execute(forContext)
 | 
			
		||||
                    loopVar.value = iterObj.invokeInstanceMethod(forScope, "next")
 | 
			
		||||
                    result = body.execute(forScope)
 | 
			
		||||
                } catch (lbe: LoopBreakContinueException) {
 | 
			
		||||
                    if (lbe.label == label || lbe.label == null) {
 | 
			
		||||
                        if (lbe.doContinue) continue
 | 
			
		||||
@ -1202,11 +1202,11 @@ class Compiler(
 | 
			
		||||
                    throw lbe
 | 
			
		||||
                }
 | 
			
		||||
            else {
 | 
			
		||||
                loopVar.value = iterObj.invokeInstanceMethod(forContext, "next")
 | 
			
		||||
                result = body.execute(forContext)
 | 
			
		||||
                loopVar.value = iterObj.invokeInstanceMethod(forScope, "next")
 | 
			
		||||
                result = body.execute(forScope)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return elseStatement?.execute(forContext) ?: result
 | 
			
		||||
        return elseStatement?.execute(forScope) ?: result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Suppress("UNUSED_VARIABLE")
 | 
			
		||||
@ -1240,11 +1240,11 @@ class Compiler(
 | 
			
		||||
        return statement(body.pos) {
 | 
			
		||||
            var wasBroken = false
 | 
			
		||||
            var result: Obj = ObjVoid
 | 
			
		||||
            lateinit var doContext: Context
 | 
			
		||||
            lateinit var doScope: Scope
 | 
			
		||||
            do {
 | 
			
		||||
                doContext = it.copy().apply { skipContextCreation = true }
 | 
			
		||||
                doScope = it.copy().apply { skipScopeCreation = true }
 | 
			
		||||
                try {
 | 
			
		||||
                    result = body.execute(doContext)
 | 
			
		||||
                    result = body.execute(doScope)
 | 
			
		||||
                } catch (e: LoopBreakContinueException) {
 | 
			
		||||
                    if (e.label == label || e.label == null) {
 | 
			
		||||
                        if (e.doContinue) continue
 | 
			
		||||
@ -1256,7 +1256,7 @@ class Compiler(
 | 
			
		||||
                    }
 | 
			
		||||
                    throw e
 | 
			
		||||
                }
 | 
			
		||||
            } while (condition.execute(doContext).toBool())
 | 
			
		||||
            } while (condition.execute(doScope).toBool())
 | 
			
		||||
            if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) }
 | 
			
		||||
            result
 | 
			
		||||
        }
 | 
			
		||||
@ -1452,7 +1452,7 @@ class Compiler(
 | 
			
		||||
        else
 | 
			
		||||
            parseBlock()
 | 
			
		||||
 | 
			
		||||
        var closure: Context? = null
 | 
			
		||||
        var closure: Scope? = null
 | 
			
		||||
 | 
			
		||||
        val fnBody = statement(t.pos) { callerContext ->
 | 
			
		||||
            callerContext.pos = start
 | 
			
		||||
@ -1497,7 +1497,7 @@ class Compiler(
 | 
			
		||||
        val block = parseScript()
 | 
			
		||||
        return statement(startPos) {
 | 
			
		||||
            // block run on inner context:
 | 
			
		||||
            block.execute(if (it.skipContextCreation) it else it.copy(startPos))
 | 
			
		||||
            block.execute(if (it.skipScopeCreation) it else it.copy(startPos))
 | 
			
		||||
        }.also {
 | 
			
		||||
            val t1 = cc.next()
 | 
			
		||||
            if (t1.type != Token.Type.RBRACE)
 | 
			
		||||
@ -1551,7 +1551,7 @@ class Compiler(
 | 
			
		||||
//        fun isLeftAssociative() = tokenType != Token.Type.OR && tokenType != Token.Type.AND
 | 
			
		||||
 | 
			
		||||
        companion object {
 | 
			
		||||
            fun simple(tokenType: Token.Type, priority: Int, f: suspend (Context, Obj, Obj) -> Obj): Operator =
 | 
			
		||||
            fun simple(tokenType: Token.Type, priority: Int, f: suspend (Scope, Obj, Obj) -> Obj): Operator =
 | 
			
		||||
                Operator(tokenType, priority, 2) { _: Pos, a: Accessor, b: Accessor ->
 | 
			
		||||
                    Accessor { f(it, a.getter(it).value, b.getter(it).value).asReadonly }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -27,13 +27,13 @@ data class ObjRecord(
 | 
			
		||||
 * operator is implemented in [Compiler.allOps].
 | 
			
		||||
 */
 | 
			
		||||
data class Accessor(
 | 
			
		||||
    val getter: suspend (Context) -> ObjRecord,
 | 
			
		||||
    val setterOrNull: (suspend (Context, Obj) -> Unit)?
 | 
			
		||||
    val getter: suspend (Scope) -> ObjRecord,
 | 
			
		||||
    val setterOrNull: (suspend (Scope, Obj) -> Unit)?
 | 
			
		||||
) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Simplified constructor for immutable stores.
 | 
			
		||||
     */
 | 
			
		||||
    constructor(getter: suspend (Context) -> ObjRecord) : this(getter, null)
 | 
			
		||||
    constructor(getter: suspend (Scope) -> ObjRecord) : this(getter, null)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the setter or throw.
 | 
			
		||||
@ -70,41 +70,41 @@ open class Obj {
 | 
			
		||||
            someClass == rootObjectType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj =
 | 
			
		||||
        invokeInstanceMethod(context, name, Arguments(args.toList()))
 | 
			
		||||
    suspend fun invokeInstanceMethod(scope: Scope, name: String, vararg args: Obj): Obj =
 | 
			
		||||
        invokeInstanceMethod(scope, name, Arguments(args.toList()))
 | 
			
		||||
 | 
			
		||||
    suspend inline fun <reified T : Obj> callMethod(
 | 
			
		||||
        context: Context,
 | 
			
		||||
        scope: Scope,
 | 
			
		||||
        name: String,
 | 
			
		||||
        args: Arguments = Arguments.EMPTY
 | 
			
		||||
    ): T = invokeInstanceMethod(context, name, args) as T
 | 
			
		||||
    ): T = invokeInstanceMethod(scope, name, args) as T
 | 
			
		||||
 | 
			
		||||
    open suspend fun invokeInstanceMethod(
 | 
			
		||||
        context: Context,
 | 
			
		||||
        scope: Scope,
 | 
			
		||||
        name: String,
 | 
			
		||||
        args: Arguments = Arguments.EMPTY
 | 
			
		||||
    ): Obj =
 | 
			
		||||
        // note that getInstanceMember traverses the hierarchy
 | 
			
		||||
        objClass.getInstanceMember(context.pos, name).value.invoke(context, this, args)
 | 
			
		||||
        objClass.getInstanceMember(scope.pos, name).value.invoke(scope, this, args)
 | 
			
		||||
 | 
			
		||||
    open suspend fun getInstanceMethod(
 | 
			
		||||
        context: Context,
 | 
			
		||||
        scope: Scope,
 | 
			
		||||
        name: String,
 | 
			
		||||
        args: Arguments = Arguments.EMPTY
 | 
			
		||||
    ): Obj =
 | 
			
		||||
        // note that getInstanceMember traverses the hierarchy
 | 
			
		||||
        objClass.getInstanceMember(context.pos, name).value
 | 
			
		||||
        objClass.getInstanceMember(scope.pos, name).value
 | 
			
		||||
 | 
			
		||||
    fun getMemberOrNull(name: String): Obj? = objClass.getInstanceMemberOrNull(name)?.value
 | 
			
		||||
 | 
			
		||||
    // methods that to override
 | 
			
		||||
 | 
			
		||||
    open suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
        return invokeInstanceMethod(context, "contains", other).toBool()
 | 
			
		||||
    open suspend fun contains(scope: Scope, other: Obj): Boolean {
 | 
			
		||||
        return invokeInstanceMethod(scope, "contains", other).toBool()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open val asStr: ObjString by lazy {
 | 
			
		||||
@ -117,90 +117,90 @@ open class Obj {
 | 
			
		||||
     */
 | 
			
		||||
    open val objClass: ObjClass = rootObjectType
 | 
			
		||||
 | 
			
		||||
    open suspend fun plus(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun plus(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun minus(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun minus(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun mul(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun mul(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun div(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun div(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun mod(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun mod(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun logicalNot(context: Context): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun logicalNot(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun logicalAnd(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun logicalAnd(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun logicalOr(context: Context, other: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun logicalOr(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun assign(context: Context, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun assign(scope: Scope, other: Obj): Obj? = null
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * a += b
 | 
			
		||||
     * if( the operation is not defined, it returns null and the compiler would try
 | 
			
		||||
     * to generate it as 'this = this + other', reassigning its variable
 | 
			
		||||
     */
 | 
			
		||||
    open suspend fun plusAssign(context: Context, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun plusAssign(scope: Scope, other: Obj): Obj? = null
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * `-=` operations, see [plusAssign]
 | 
			
		||||
     */
 | 
			
		||||
    open suspend fun minusAssign(context: Context, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun mulAssign(context: Context, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun divAssign(context: Context, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun modAssign(context: Context, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun minusAssign(scope: Scope, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun mulAssign(scope: Scope, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun divAssign(scope: Scope, other: Obj): Obj? = null
 | 
			
		||||
    open suspend fun modAssign(scope: Scope, other: Obj): Obj? = null
 | 
			
		||||
 | 
			
		||||
    open suspend fun getAndIncrement(context: Context): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun getAndIncrement(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun incrementAndGet(context: Context): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun incrementAndGet(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun decrementAndGet(context: Context): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun decrementAndGet(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun getAndDecrement(context: Context): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun getAndDecrement(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert Lyng object to its Kotlin counterpart
 | 
			
		||||
     */
 | 
			
		||||
    open suspend fun toKotlin(context: Context): Any? {
 | 
			
		||||
    open suspend fun toKotlin(scope: Scope): Any? {
 | 
			
		||||
        return toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun willMutate(context: Context) {
 | 
			
		||||
        if (isFrozen) context.raiseError("attempt to mutate frozen object")
 | 
			
		||||
    fun willMutate(scope: Scope) {
 | 
			
		||||
        if (isFrozen) scope.raiseError("attempt to mutate frozen object")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun <T> sync(block: () -> T): T = monitor.withLock { block() }
 | 
			
		||||
 | 
			
		||||
    open suspend fun readField(context: Context, name: String): ObjRecord {
 | 
			
		||||
    open suspend fun readField(scope: Scope, name: String): ObjRecord {
 | 
			
		||||
        // could be property or class field:
 | 
			
		||||
        val obj = objClass.getInstanceMemberOrNull(name) ?: context.raiseError("no such field: $name")
 | 
			
		||||
        val obj = objClass.getInstanceMemberOrNull(name) ?: scope.raiseError("no such field: $name")
 | 
			
		||||
        return when (val value = obj.value) {
 | 
			
		||||
            is Statement -> {
 | 
			
		||||
                ObjRecord(value.execute(context.copy(context.pos, newThisObj = this)), obj.isMutable)
 | 
			
		||||
                ObjRecord(value.execute(scope.copy(scope.pos, newThisObj = this)), obj.isMutable)
 | 
			
		||||
            }
 | 
			
		||||
            // could be writable property naturally
 | 
			
		||||
//            null -> ObjNull.asReadonly
 | 
			
		||||
@ -208,49 +208,49 @@ open class Obj {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun writeField(context: Context, name: String, newValue: Obj) {
 | 
			
		||||
        willMutate(context)
 | 
			
		||||
        val field = objClass.getInstanceMemberOrNull(name) ?: context.raiseError("no such field: $name")
 | 
			
		||||
        if (field.isMutable) field.value = newValue else context.raiseError("can't assign to read-only field: $name")
 | 
			
		||||
    open suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
 | 
			
		||||
        willMutate(scope)
 | 
			
		||||
        val field = objClass.getInstanceMemberOrNull(name) ?: scope.raiseError("no such field: $name")
 | 
			
		||||
        if (field.isMutable) field.value = newValue else scope.raiseError("can't assign to read-only field: $name")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun getAt(context: Context, index: Obj): Obj {
 | 
			
		||||
        context.raiseNotImplemented("indexing")
 | 
			
		||||
    open suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
        scope.raiseNotImplemented("indexing")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun getAt(context: Context, index: Int): Obj = getAt(context, ObjInt(index.toLong()))
 | 
			
		||||
    suspend fun getAt(scope: Scope, index: Int): Obj = getAt(scope, ObjInt(index.toLong()))
 | 
			
		||||
 | 
			
		||||
    open suspend fun putAt(context: Context, index: Int, newValue: Obj) {
 | 
			
		||||
        context.raiseNotImplemented("indexing")
 | 
			
		||||
    open suspend fun putAt(scope: Scope, index: Int, newValue: Obj) {
 | 
			
		||||
        scope.raiseNotImplemented("indexing")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun callOn(context: Context): Obj {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
    open suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNotImplemented()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun invoke(context: Context, thisObj: Obj, args: Arguments): Obj =
 | 
			
		||||
        callOn(context.copy(context.pos, args = args, newThisObj = thisObj))
 | 
			
		||||
    suspend fun invoke(scope: Scope, thisObj: Obj, args: Arguments): Obj =
 | 
			
		||||
        callOn(scope.copy(scope.pos, args = args, newThisObj = thisObj))
 | 
			
		||||
 | 
			
		||||
    suspend fun invoke(context: Context, thisObj: Obj, vararg args: Obj): Obj =
 | 
			
		||||
    suspend fun invoke(scope: Scope, thisObj: Obj, vararg args: Obj): Obj =
 | 
			
		||||
        callOn(
 | 
			
		||||
            context.copy(
 | 
			
		||||
                context.pos,
 | 
			
		||||
            scope.copy(
 | 
			
		||||
                scope.pos,
 | 
			
		||||
                args = Arguments(args.toList()),
 | 
			
		||||
                newThisObj = thisObj
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    suspend fun invoke(context: Context, thisObj: Obj): Obj =
 | 
			
		||||
    suspend fun invoke(scope: Scope, thisObj: Obj): Obj =
 | 
			
		||||
        callOn(
 | 
			
		||||
            context.copy(
 | 
			
		||||
                context.pos,
 | 
			
		||||
            scope.copy(
 | 
			
		||||
                scope.pos,
 | 
			
		||||
                args = Arguments.EMPTY,
 | 
			
		||||
                newThisObj = thisObj
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    suspend fun invoke(context: Context, atPos: Pos, thisObj: Obj, args: Arguments): Obj =
 | 
			
		||||
        callOn(context.copy(atPos, args = args, newThisObj = thisObj))
 | 
			
		||||
    suspend fun invoke(scope: Scope, atPos: Pos, thisObj: Obj, args: Arguments): Obj =
 | 
			
		||||
        callOn(scope.copy(atPos, args = args, newThisObj = thisObj))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    val asReadonly: ObjRecord by lazy { ObjRecord(this, false) }
 | 
			
		||||
@ -271,7 +271,7 @@ open class Obj {
 | 
			
		||||
                    args.firstAndOnly().callOn(copy(Arguments(thisObj)))
 | 
			
		||||
                }
 | 
			
		||||
                addFn("apply") {
 | 
			
		||||
                    val newContext = ( thisObj as? ObjInstance)?.instanceContext ?: this
 | 
			
		||||
                    val newContext = ( thisObj as? ObjInstance)?.instanceScope ?: this
 | 
			
		||||
                    args.firstAndOnly()
 | 
			
		||||
                        .callOn(newContext)
 | 
			
		||||
                    thisObj
 | 
			
		||||
@ -319,7 +319,7 @@ object ObjVoid : Obj() {
 | 
			
		||||
        return other is ObjVoid || other is Unit
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        return if (other === this) 0 else -1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -329,7 +329,7 @@ object ObjVoid : Obj() {
 | 
			
		||||
@Serializable
 | 
			
		||||
@SerialName("null")
 | 
			
		||||
object ObjNull : Obj() {
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        return if (other === this) 0 else -1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -337,29 +337,29 @@ object ObjNull : Obj() {
 | 
			
		||||
        return other is ObjNull || other == null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun readField(context: Context, name: String): ObjRecord {
 | 
			
		||||
        context.raiseNPE()
 | 
			
		||||
    override suspend fun readField(scope: Scope, name: String): ObjRecord {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun invokeInstanceMethod(context: Context, name: String, args: Arguments): Obj {
 | 
			
		||||
        context.raiseNPE()
 | 
			
		||||
    override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments): Obj {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(context: Context, index: Obj): Obj {
 | 
			
		||||
        context.raiseNPE()
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun putAt(context: Context, index: Int, newValue: Obj) {
 | 
			
		||||
        context.raiseNPE()
 | 
			
		||||
    override suspend fun putAt(scope: Scope, index: Int, newValue: Obj) {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
        context.raiseNPE()
 | 
			
		||||
    override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
        scope.raiseNPE()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = "null"
 | 
			
		||||
 | 
			
		||||
    override suspend fun toKotlin(context: Context): Any? {
 | 
			
		||||
    override suspend fun toKotlin(scope: Scope): Any? {
 | 
			
		||||
        return null
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -401,14 +401,14 @@ data class ObjNamespace(val name: String) : Obj() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
open class ObjException(exceptionClass: ExceptionClass, val context: Context, val message: String) : Obj() {
 | 
			
		||||
    constructor(name: String, context: Context, message: String) : this(
 | 
			
		||||
open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val message: String) : Obj() {
 | 
			
		||||
    constructor(name: String, scope: Scope, message: String) : this(
 | 
			
		||||
        getOrCreateExceptionClass(name),
 | 
			
		||||
        context,
 | 
			
		||||
        scope,
 | 
			
		||||
        message
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    constructor(context: Context, message: String) : this(Root, context, message)
 | 
			
		||||
    constructor(scope: Scope, message: String) : this(Root, scope, message)
 | 
			
		||||
 | 
			
		||||
    fun raise(): Nothing {
 | 
			
		||||
        throw ExecutionError(this)
 | 
			
		||||
@ -417,15 +417,15 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
 | 
			
		||||
    override val objClass: ObjClass = exceptionClass
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        return "ObjException:${objClass.className}:${context.pos}@${hashCode().encodeToHex()}"
 | 
			
		||||
        return "ObjException:${objClass.className}:${scope.pos}@${hashCode().encodeToHex()}"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
 | 
			
		||||
            override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
                val message = context.args.getOrNull(0)?.toString() ?: name
 | 
			
		||||
                return ObjException(this, context, message)
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                val message = scope.args.getOrNull(0)?.toString() ?: name
 | 
			
		||||
                return ObjException(this, scope, message)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun toString(): String = "ExceptionClass[$name]@${hashCode().encodeToHex()}"
 | 
			
		||||
@ -458,8 +458,8 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
 | 
			
		||||
            existingErrorClasses[name]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun addExceptionsToContext(context: Context) {
 | 
			
		||||
            context.addConst("Exception", Root)
 | 
			
		||||
        fun addExceptionsToContext(scope: Scope) {
 | 
			
		||||
            scope.addConst("Exception", Root)
 | 
			
		||||
            existingErrorClasses["Exception"] = Root
 | 
			
		||||
            for (name in listOf(
 | 
			
		||||
                "NullReferenceException",
 | 
			
		||||
@ -474,39 +474,39 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
 | 
			
		||||
                "AccessException",
 | 
			
		||||
                "UnknownException",
 | 
			
		||||
            )) {
 | 
			
		||||
                context.addConst(name, getOrCreateExceptionClass(name))
 | 
			
		||||
                scope.addConst(name, getOrCreateExceptionClass(name))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ObjNullReferenceException(context: Context) : ObjException("NullReferenceException", context, "object is null")
 | 
			
		||||
class ObjNullReferenceException(scope: Scope) : ObjException("NullReferenceException", scope, "object is null")
 | 
			
		||||
 | 
			
		||||
class ObjAssertionFailedException(context: Context, message: String) :
 | 
			
		||||
    ObjException("AssertionFailedException", context, message)
 | 
			
		||||
class ObjAssertionFailedException(scope: Scope, message: String) :
 | 
			
		||||
    ObjException("AssertionFailedException", scope, message)
 | 
			
		||||
 | 
			
		||||
class ObjClassCastException(context: Context, message: String) : ObjException("ClassCastException", context, message)
 | 
			
		||||
class ObjIndexOutOfBoundsException(context: Context, message: String = "index out of bounds") :
 | 
			
		||||
    ObjException("IndexOutOfBoundsException", context, message)
 | 
			
		||||
class ObjClassCastException(scope: Scope, message: String) : ObjException("ClassCastException", scope, message)
 | 
			
		||||
class ObjIndexOutOfBoundsException(scope: Scope, message: String = "index out of bounds") :
 | 
			
		||||
    ObjException("IndexOutOfBoundsException", scope, message)
 | 
			
		||||
 | 
			
		||||
class ObjIllegalArgumentException(context: Context, message: String = "illegal argument") :
 | 
			
		||||
    ObjException("IllegalArgumentException", context, message)
 | 
			
		||||
class ObjIllegalArgumentException(scope: Scope, message: String = "illegal argument") :
 | 
			
		||||
    ObjException("IllegalArgumentException", scope, message)
 | 
			
		||||
 | 
			
		||||
@Suppress("unused")
 | 
			
		||||
class ObjNoSuchElementException(context: Context, message: String = "no such element") :
 | 
			
		||||
    ObjException("IllegalArgumentException", context, message)
 | 
			
		||||
class ObjNoSuchElementException(scope: Scope, message: String = "no such element") :
 | 
			
		||||
    ObjException("IllegalArgumentException", scope, message)
 | 
			
		||||
 | 
			
		||||
class ObjIllegalAssignmentException(context: Context, message: String = "illegal assignment") :
 | 
			
		||||
    ObjException("NoSuchElementException", context, message)
 | 
			
		||||
class ObjIllegalAssignmentException(scope: Scope, message: String = "illegal assignment") :
 | 
			
		||||
    ObjException("NoSuchElementException", scope, message)
 | 
			
		||||
 | 
			
		||||
class ObjSymbolNotDefinedException(context: Context, message: String = "symbol is not defined") :
 | 
			
		||||
    ObjException("SymbolNotDefinedException", context, message)
 | 
			
		||||
class ObjSymbolNotDefinedException(scope: Scope, message: String = "symbol is not defined") :
 | 
			
		||||
    ObjException("SymbolNotDefinedException", scope, message)
 | 
			
		||||
 | 
			
		||||
class ObjIterationFinishedException(context: Context) :
 | 
			
		||||
    ObjException("IterationEndException", context, "iteration finished")
 | 
			
		||||
class ObjIterationFinishedException(scope: Scope) :
 | 
			
		||||
    ObjException("IterationEndException", scope, "iteration finished")
 | 
			
		||||
 | 
			
		||||
class ObjAccessException(context: Context, message: String = "access not allowed error") :
 | 
			
		||||
    ObjException("AccessException", context, message)
 | 
			
		||||
class ObjAccessException(scope: Scope, message: String = "access not allowed error") :
 | 
			
		||||
    ObjException("AccessException", scope, message)
 | 
			
		||||
 | 
			
		||||
class ObjUnknownException(context: Context, message: String = "access not allowed error") :
 | 
			
		||||
    ObjException("UnknownException", context, message)
 | 
			
		||||
class ObjUnknownException(scope: Scope, message: String = "access not allowed error") :
 | 
			
		||||
    ObjException("UnknownException", scope, message)
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,9 @@ class ObjArrayIterator(val array: Obj) : Obj() {
 | 
			
		||||
    private var nextIndex = 0
 | 
			
		||||
    private var lastIndex = 0
 | 
			
		||||
 | 
			
		||||
    suspend fun init(context: Context) {
 | 
			
		||||
    suspend fun init(scope: Scope) {
 | 
			
		||||
        nextIndex = 0
 | 
			
		||||
        lastIndex = array.invokeInstanceMethod(context, "size").toInt()
 | 
			
		||||
        lastIndex = array.invokeInstanceMethod(scope, "size").toInt()
 | 
			
		||||
        ObjVoid
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ package net.sergeych.lyng
 | 
			
		||||
data class ObjBool(val value: Boolean) : Obj() {
 | 
			
		||||
    override val asStr by lazy { ObjString(value.toString()) }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is ObjBool) return -2
 | 
			
		||||
        return value.compareTo(other.value)
 | 
			
		||||
    }
 | 
			
		||||
@ -12,13 +12,13 @@ data class ObjBool(val value: Boolean) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun logicalNot(context: Context): Obj = ObjBool(!value)
 | 
			
		||||
    override suspend fun logicalNot(scope: Scope): Obj = ObjBool(!value)
 | 
			
		||||
 | 
			
		||||
    override suspend fun logicalAnd(context: Context, other: Obj): Obj = ObjBool(value && other.toBool())
 | 
			
		||||
    override suspend fun logicalAnd(scope: Scope, other: Obj): Obj = ObjBool(value && other.toBool())
 | 
			
		||||
 | 
			
		||||
    override suspend fun logicalOr(context: Context, other: Obj): Obj = ObjBool(value || other.toBool())
 | 
			
		||||
    override suspend fun logicalOr(scope: Scope, other: Obj): Obj = ObjBool(value || other.toBool())
 | 
			
		||||
 | 
			
		||||
    override suspend fun toKotlin(context: Context): Any {
 | 
			
		||||
    override suspend fun toKotlin(scope: Scope): Any {
 | 
			
		||||
        return value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ class ObjChar(val value: Char): Obj() {
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int =
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int =
 | 
			
		||||
        (other as? ObjChar)?.let { value.compareTo(it.value) } ?: -1
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = value.toString()
 | 
			
		||||
 | 
			
		||||
@ -21,13 +21,13 @@ open class ObjClass(
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = className
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int = if (other === this) 0 else -1
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int = if (other === this) 0 else -1
 | 
			
		||||
 | 
			
		||||
    override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
    override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
        val instance = ObjInstance(this)
 | 
			
		||||
        instance.instanceContext = context.copy(newThisObj = instance,args = context.args)
 | 
			
		||||
        instance.instanceScope = scope.copy(newThisObj = instance,args = scope.args)
 | 
			
		||||
        if (instanceConstructor != null) {
 | 
			
		||||
            instanceConstructor!!.execute(instance.instanceContext)
 | 
			
		||||
            instanceConstructor!!.execute(instance.instanceScope)
 | 
			
		||||
        }
 | 
			
		||||
        return instance
 | 
			
		||||
    }
 | 
			
		||||
@ -49,7 +49,7 @@ open class ObjClass(
 | 
			
		||||
        members[name] = ObjRecord(initialValue, isMutable, visibility)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun addFn(name: String, isOpen: Boolean = false, code: suspend Context.() -> Obj) {
 | 
			
		||||
    fun addFn(name: String, isOpen: Boolean = false, code: suspend Scope.() -> Obj) {
 | 
			
		||||
        createField(name, statement { code() }, isOpen)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,52 +2,52 @@ package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
class ObjInstance(override val objClass: ObjClass) : Obj() {
 | 
			
		||||
 | 
			
		||||
    internal lateinit var instanceContext: Context
 | 
			
		||||
    internal lateinit var instanceScope: Scope
 | 
			
		||||
 | 
			
		||||
    override suspend fun readField(context: Context, name: String): ObjRecord {
 | 
			
		||||
        return instanceContext[name]?.let {
 | 
			
		||||
    override suspend fun readField(scope: Scope, name: String): ObjRecord {
 | 
			
		||||
        return instanceScope[name]?.let {
 | 
			
		||||
            if (it.visibility.isPublic)
 | 
			
		||||
                it
 | 
			
		||||
            else
 | 
			
		||||
                context.raiseError(ObjAccessException(context, "can't access non-public field $name"))
 | 
			
		||||
                scope.raiseError(ObjAccessException(scope, "can't access non-public field $name"))
 | 
			
		||||
        }
 | 
			
		||||
            ?: super.readField(context, name)
 | 
			
		||||
            ?: super.readField(scope, name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun writeField(context: Context, name: String, newValue: Obj) {
 | 
			
		||||
        instanceContext[name]?.let { f ->
 | 
			
		||||
    override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
 | 
			
		||||
        instanceScope[name]?.let { f ->
 | 
			
		||||
            if (!f.visibility.isPublic)
 | 
			
		||||
                ObjIllegalAssignmentException(context, "can't assign to non-public field $name")
 | 
			
		||||
            if (!f.isMutable) ObjIllegalAssignmentException(context, "can't reassign val $name").raise()
 | 
			
		||||
            if (f.value.assign(context, newValue) == null)
 | 
			
		||||
                ObjIllegalAssignmentException(scope, "can't assign to non-public field $name")
 | 
			
		||||
            if (!f.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
 | 
			
		||||
            if (f.value.assign(scope, newValue) == null)
 | 
			
		||||
                f.value = newValue
 | 
			
		||||
        } ?: super.writeField(context, name, newValue)
 | 
			
		||||
        } ?: super.writeField(scope, name, newValue)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun invokeInstanceMethod(context: Context, name: String, args: Arguments): Obj =
 | 
			
		||||
        instanceContext[name]?.let {
 | 
			
		||||
    override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments): Obj =
 | 
			
		||||
        instanceScope[name]?.let {
 | 
			
		||||
            if (it.visibility.isPublic)
 | 
			
		||||
                it.value.invoke(context, this, args)
 | 
			
		||||
                it.value.invoke(scope, this, args)
 | 
			
		||||
            else
 | 
			
		||||
                context.raiseError(ObjAccessException(context, "can't invoke non-public method $name"))
 | 
			
		||||
                scope.raiseError(ObjAccessException(scope, "can't invoke non-public method $name"))
 | 
			
		||||
        }
 | 
			
		||||
            ?: super.invokeInstanceMethod(context, name, args)
 | 
			
		||||
            ?: super.invokeInstanceMethod(scope, name, args)
 | 
			
		||||
 | 
			
		||||
    private val publicFields: Map<String, ObjRecord>
 | 
			
		||||
        get() = instanceContext.objects.filter { it.value.visibility.isPublic }
 | 
			
		||||
        get() = instanceScope.objects.filter { it.value.visibility.isPublic }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",")
 | 
			
		||||
        return "${objClass.className}($fields)"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is ObjInstance) return -1
 | 
			
		||||
        if (other.objClass != objClass) return -1
 | 
			
		||||
        for (f in publicFields) {
 | 
			
		||||
            val a = f.value.value
 | 
			
		||||
            val b = other.instanceContext[f.key]!!.value
 | 
			
		||||
            val d = a.compareTo(context, b)
 | 
			
		||||
            val b = other.instanceScope[f.key]!!.value
 | 
			
		||||
            val d = a.compareTo(scope, b)
 | 
			
		||||
            if (d != 0) return d
 | 
			
		||||
        }
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
@ -13,23 +13,23 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
 | 
			
		||||
        return value.hashCode()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAndIncrement(context: Context): Obj {
 | 
			
		||||
    override suspend fun getAndIncrement(scope: Scope): Obj {
 | 
			
		||||
        return ObjInt(value).also { value++ }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAndDecrement(context: Context): Obj {
 | 
			
		||||
    override suspend fun getAndDecrement(scope: Scope): Obj {
 | 
			
		||||
        return ObjInt(value).also { value-- }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun incrementAndGet(context: Context): Obj {
 | 
			
		||||
    override suspend fun incrementAndGet(scope: Scope): Obj {
 | 
			
		||||
        return ObjInt(++value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun decrementAndGet(context: Context): Obj {
 | 
			
		||||
    override suspend fun decrementAndGet(scope: Scope): Obj {
 | 
			
		||||
        return ObjInt(--value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is Numeric) return -2
 | 
			
		||||
        return value.compareTo(other.doubleValue)
 | 
			
		||||
    }
 | 
			
		||||
@ -38,29 +38,29 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun plus(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun plus(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        if (other is ObjInt)
 | 
			
		||||
            ObjInt(this.value + other.value)
 | 
			
		||||
        else
 | 
			
		||||
            ObjReal(this.doubleValue + other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun minus(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun minus(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        if (other is ObjInt)
 | 
			
		||||
            ObjInt(this.value - other.value)
 | 
			
		||||
        else
 | 
			
		||||
            ObjReal(this.doubleValue - other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun mul(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun mul(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        if (other is ObjInt) {
 | 
			
		||||
            ObjInt(this.value * other.value)
 | 
			
		||||
        } else ObjReal(this.value * other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun div(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun div(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        if (other is ObjInt)
 | 
			
		||||
            ObjInt(this.value / other.value)
 | 
			
		||||
        else ObjReal(this.value / other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun mod(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun mod(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        if (other is ObjInt)
 | 
			
		||||
            ObjInt(this.value % other.value)
 | 
			
		||||
        else ObjReal(this.value.toDouble() % other.toDouble())
 | 
			
		||||
@ -69,14 +69,14 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
 | 
			
		||||
     * We are by-value type ([byValueCopy] is implemented) so we can do in-place
 | 
			
		||||
     * assignment
 | 
			
		||||
     */
 | 
			
		||||
    override suspend fun assign(context: Context, other: Obj): Obj? {
 | 
			
		||||
    override suspend fun assign(scope: Scope, other: Obj): Obj? {
 | 
			
		||||
        return if (other is ObjInt) {
 | 
			
		||||
            value = other.value
 | 
			
		||||
            this
 | 
			
		||||
        } else null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun toKotlin(context: Context): Any {
 | 
			
		||||
    override suspend fun toKotlin(scope: Scope): Any {
 | 
			
		||||
        return value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,11 +47,11 @@ class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
 | 
			
		||||
 * As Lyng is totally asynchronous, its iterator can't be trivially converted to Kotlin's synchronous iterator.
 | 
			
		||||
 * It is, though, trivially convertible to Kotlin's Flow.
 | 
			
		||||
 */
 | 
			
		||||
fun Obj.toFlow(context: Context): Flow<Obj> = flow {
 | 
			
		||||
    val iterator = invokeInstanceMethod(context, "iterator")
 | 
			
		||||
    val hasNext = iterator.getInstanceMethod(context, "hasNext")
 | 
			
		||||
    val next = iterator.getInstanceMethod(context, "next")
 | 
			
		||||
    while (hasNext.invoke(context, iterator).toBool()) {
 | 
			
		||||
        emit(next.invoke(context, iterator))
 | 
			
		||||
fun Obj.toFlow(scope: Scope): Flow<Obj> = flow {
 | 
			
		||||
    val iterator = invokeInstanceMethod(scope, "iterator")
 | 
			
		||||
    val hasNext = iterator.getInstanceMethod(scope, "hasNext")
 | 
			
		||||
    val next = iterator.getInstanceMethod(scope, "next")
 | 
			
		||||
    while (hasNext.invoke(scope, iterator).toBool()) {
 | 
			
		||||
        emit(next.invoke(scope, iterator))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,7 +11,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
 | 
			
		||||
        list.joinToString(separator = ", ") { it.inspect() }
 | 
			
		||||
    }]"
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(context: Context, index: Obj): Obj {
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
        return when (index) {
 | 
			
		||||
            is ObjInt -> {
 | 
			
		||||
                list[index.toInt()]
 | 
			
		||||
@ -47,23 +47,23 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            else -> context.raiseIllegalArgument("Illegal index object for a list: ${index.inspect()}")
 | 
			
		||||
            else -> scope.raiseIllegalArgument("Illegal index object for a list: ${index.inspect()}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun putAt(context: Context, index: Int, newValue: Obj) {
 | 
			
		||||
    override suspend fun putAt(scope: Scope, index: Int, newValue: Obj) {
 | 
			
		||||
        val i = index
 | 
			
		||||
        list[i] = newValue
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is ObjList) return -2
 | 
			
		||||
        val mySize = list.size
 | 
			
		||||
        val otherSize = other.list.size
 | 
			
		||||
        val commonSize = minOf(mySize, otherSize)
 | 
			
		||||
        for (i in 0..<commonSize) {
 | 
			
		||||
            if (list[i].compareTo(context, other.list[i]) != 0) {
 | 
			
		||||
                return list[i].compareTo(context, other.list[i])
 | 
			
		||||
            if (list[i].compareTo(scope, other.list[i]) != 0) {
 | 
			
		||||
                return list[i].compareTo(scope, other.list[i])
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // equal so far, longer is greater:
 | 
			
		||||
@ -74,44 +74,44 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun plus(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun plus(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        when {
 | 
			
		||||
            other is ObjList ->
 | 
			
		||||
                ObjList((list + other.list).toMutableList())
 | 
			
		||||
 | 
			
		||||
            other.isInstanceOf(ObjIterable) -> {
 | 
			
		||||
                val l = other.callMethod<ObjList>(context, "toList")
 | 
			
		||||
                val l = other.callMethod<ObjList>(scope, "toList")
 | 
			
		||||
                ObjList((list + l.list).toMutableList())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            else ->
 | 
			
		||||
                context.raiseError("'+': can't concatenate $this with $other")
 | 
			
		||||
                scope.raiseError("'+': can't concatenate $this with $other")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override suspend fun plusAssign(context: Context, other: Obj): Obj {
 | 
			
		||||
    override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        // optimization
 | 
			
		||||
        if (other is ObjList) {
 | 
			
		||||
            list += other.list
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
        if (other.isInstanceOf(ObjIterable)) {
 | 
			
		||||
            val otherList = other.invokeInstanceMethod(context, "toList") as ObjList
 | 
			
		||||
            val otherList = other.invokeInstanceMethod(scope, "toList") as ObjList
 | 
			
		||||
            list += otherList.list
 | 
			
		||||
        } else
 | 
			
		||||
            list += other
 | 
			
		||||
        return this
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
    override suspend fun contains(scope: Scope, other: Obj): Boolean {
 | 
			
		||||
        return list.contains(other)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass
 | 
			
		||||
        get() = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun toKotlin(context: Context): Any {
 | 
			
		||||
        return list.map { it.toKotlin(context) }
 | 
			
		||||
    override suspend fun toKotlin(scope: Scope): Any {
 | 
			
		||||
        return list.map { it.toKotlin(scope) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
 | 
			
		||||
@ -2,17 +2,17 @@ package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is ObjMapEntry) return -1
 | 
			
		||||
        val c = key.compareTo(context, other.key)
 | 
			
		||||
        val c = key.compareTo(scope, other.key)
 | 
			
		||||
        if (c != 0) return c
 | 
			
		||||
        return value.compareTo(context, other.value)
 | 
			
		||||
        return value.compareTo(scope, other.value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(context: Context, index: Obj): Obj = when (index.toInt()) {
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj = when (index.toInt()) {
 | 
			
		||||
        0 -> key
 | 
			
		||||
        1 -> value
 | 
			
		||||
        else -> context.raiseIndexOutOfBounds()
 | 
			
		||||
        else -> scope.raiseIndexOutOfBounds()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
@ -23,8 +23,8 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val type = object : ObjClass("MapEntry", ObjArray) {
 | 
			
		||||
            override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
                return ObjMapEntry(context.requiredArg<Obj>(0), context.requiredArg<Obj>(1))
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                return ObjMapEntry(scope.requiredArg<Obj>(0), scope.requiredArg<Obj>(1))
 | 
			
		||||
            }
 | 
			
		||||
        }.apply {
 | 
			
		||||
            addFn("key") { thisAs<ObjMapEntry>().key }
 | 
			
		||||
@ -38,14 +38,14 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override val objClass = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(context: Context, index: Obj): Obj =
 | 
			
		||||
        map.getOrElse(index) { context.raiseNoSuchElement() }
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj =
 | 
			
		||||
        map.getOrElse(index) { scope.raiseNoSuchElement() }
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
    override suspend fun contains(scope: Scope, other: Obj): Boolean {
 | 
			
		||||
        return other in map
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if( other is ObjMap && other.map == map) return 0
 | 
			
		||||
        return -1
 | 
			
		||||
    }
 | 
			
		||||
@ -53,30 +53,30 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        suspend fun listToMap(context: Context, list: List<Obj>): MutableMap<Obj, Obj> {
 | 
			
		||||
        suspend fun listToMap(scope: Scope, list: List<Obj>): MutableMap<Obj, Obj> {
 | 
			
		||||
            val map = mutableMapOf<Obj, Obj>()
 | 
			
		||||
            if (list.isEmpty()) return map
 | 
			
		||||
 | 
			
		||||
            val first = list.first()
 | 
			
		||||
            if (first.isInstanceOf(ObjArray)) {
 | 
			
		||||
                if (first.invokeInstanceMethod(context, "size").toInt() != 2)
 | 
			
		||||
                    context.raiseIllegalArgument(
 | 
			
		||||
                if (first.invokeInstanceMethod(scope, "size").toInt() != 2)
 | 
			
		||||
                    scope.raiseIllegalArgument(
 | 
			
		||||
                        "list to construct map entry should exactly be 2 element Array like [key,value], got $list"
 | 
			
		||||
                    )
 | 
			
		||||
            } else context.raiseIllegalArgument("first element of map list be a Collection of 2 elements; got $first")
 | 
			
		||||
            } else scope.raiseIllegalArgument("first element of map list be a Collection of 2 elements; got $first")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            list.forEach {
 | 
			
		||||
                map[it.getAt(context, ObjInt.Zero)] = it.getAt(context, ObjInt.One)
 | 
			
		||||
                map[it.getAt(scope, ObjInt.Zero)] = it.getAt(scope, ObjInt.One)
 | 
			
		||||
            }
 | 
			
		||||
            return map
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        val type = object : ObjClass("Map", ObjCollection) {
 | 
			
		||||
            override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
                return ObjMap(listToMap(context, context.args.list))
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                return ObjMap(listToMap(scope, scope.args.list))
 | 
			
		||||
            }
 | 
			
		||||
        }.apply {
 | 
			
		||||
            addFn("getOrNull") {
 | 
			
		||||
 | 
			
		||||
@ -15,10 +15,10 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
 | 
			
		||||
        return result.toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun containsRange(context: Context, other: ObjRange): Boolean {
 | 
			
		||||
    suspend fun containsRange(scope: Scope, other: ObjRange): Boolean {
 | 
			
		||||
        if (start != null) {
 | 
			
		||||
            // our start is not -∞ so other start should be GTE or is not contained:
 | 
			
		||||
            if (other.start != null && start.compareTo(context, other.start) > 0) return false
 | 
			
		||||
            if (other.start != null && start.compareTo(scope, other.start) > 0) return false
 | 
			
		||||
        }
 | 
			
		||||
        if (end != null) {
 | 
			
		||||
            // same with the end: if it is open, it can't be contained in ours:
 | 
			
		||||
@ -26,16 +26,16 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
 | 
			
		||||
            // both exists, now there could be 4 cases:
 | 
			
		||||
            return when {
 | 
			
		||||
                other.isEndInclusive && isEndInclusive ->
 | 
			
		||||
                    end.compareTo(context, other.end) >= 0
 | 
			
		||||
                    end.compareTo(scope, other.end) >= 0
 | 
			
		||||
 | 
			
		||||
                !other.isEndInclusive && !isEndInclusive ->
 | 
			
		||||
                    end.compareTo(context, other.end) >= 0
 | 
			
		||||
                    end.compareTo(scope, other.end) >= 0
 | 
			
		||||
 | 
			
		||||
                other.isEndInclusive && !isEndInclusive ->
 | 
			
		||||
                    end.compareTo(context, other.end) > 0
 | 
			
		||||
                    end.compareTo(scope, other.end) > 0
 | 
			
		||||
 | 
			
		||||
                !other.isEndInclusive && isEndInclusive ->
 | 
			
		||||
                    end.compareTo(context, other.end) >= 0
 | 
			
		||||
                    end.compareTo(scope, other.end) >= 0
 | 
			
		||||
 | 
			
		||||
                else -> throw IllegalStateException("unknown comparison")
 | 
			
		||||
            }
 | 
			
		||||
@ -43,17 +43,17 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
    override suspend fun contains(scope: Scope, other: Obj): Boolean {
 | 
			
		||||
 | 
			
		||||
        if (other is ObjRange)
 | 
			
		||||
            return containsRange(context, other)
 | 
			
		||||
            return containsRange(scope, other)
 | 
			
		||||
 | 
			
		||||
        if (start == null && end == null) return true
 | 
			
		||||
        if (start != null) {
 | 
			
		||||
            if (start.compareTo(context, other) > 0) return false
 | 
			
		||||
            if (start.compareTo(scope, other) > 0) return false
 | 
			
		||||
        }
 | 
			
		||||
        if (end != null) {
 | 
			
		||||
            val cmp = end.compareTo(context, other)
 | 
			
		||||
            val cmp = end.compareTo(scope, other)
 | 
			
		||||
            if (isEndInclusive && cmp < 0 || !isEndInclusive && cmp <= 0) return false
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
@ -67,7 +67,7 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
 | 
			
		||||
        start is ObjChar && end is ObjChar
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        return (other as? ObjRange)?.let {
 | 
			
		||||
            if( start == other.start && end == other.end ) 0 else -1
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass = type
 | 
			
		||||
 | 
			
		||||
    fun Context.init() {
 | 
			
		||||
    fun Scope.init() {
 | 
			
		||||
        if (self.start == null || self.end == null)
 | 
			
		||||
            raiseError("next is only available for finite ranges")
 | 
			
		||||
        isCharRange = self.isCharRange
 | 
			
		||||
@ -24,7 +24,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
 | 
			
		||||
 | 
			
		||||
    fun hasNext(): Boolean = nextIndex < lastIndex
 | 
			
		||||
 | 
			
		||||
    fun next(context: Context): Obj =
 | 
			
		||||
    fun next(scope: Scope): Obj =
 | 
			
		||||
        if (nextIndex < lastIndex) {
 | 
			
		||||
            val x = if (self.isEndInclusive)
 | 
			
		||||
                self.start!!.toLong() + nextIndex++
 | 
			
		||||
@ -33,7 +33,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
 | 
			
		||||
            if( isCharRange ) ObjChar(x.toInt().toChar()) else ObjInt(x)
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            context.raiseError(ObjIterationFinishedException(context))
 | 
			
		||||
            scope.raiseError(ObjIterationFinishedException(scope))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
 | 
			
		||||
 | 
			
		||||
    override fun byValueCopy(): Obj = ObjReal(value)
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is Numeric) return -2
 | 
			
		||||
        return value.compareTo(other.doubleValue)
 | 
			
		||||
    }
 | 
			
		||||
@ -25,25 +25,25 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
 | 
			
		||||
        return value.hashCode()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun plus(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun plus(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        ObjReal(this.value + other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun minus(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun minus(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        ObjReal(this.value - other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun mul(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun mul(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        ObjReal(this.value * other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun div(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun div(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        ObjReal(this.value / other.toDouble())
 | 
			
		||||
 | 
			
		||||
    override suspend fun mod(context: Context, other: Obj): Obj =
 | 
			
		||||
    override suspend fun mod(scope: Scope, other: Obj): Obj =
 | 
			
		||||
        ObjReal(this.value % other.toDouble())
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns unboxed Double value
 | 
			
		||||
     */
 | 
			
		||||
    override suspend fun toKotlin(context: Context): Any {
 | 
			
		||||
    override suspend fun toKotlin(scope: Scope): Any {
 | 
			
		||||
        return value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,11 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
 | 
			
		||||
 | 
			
		||||
    override val objClass = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
    override suspend fun contains(scope: Scope, other: Obj): Boolean {
 | 
			
		||||
        return set.contains(other)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun plus(context: Context, other: Obj): Obj {
 | 
			
		||||
    override suspend fun plus(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        return ObjSet(
 | 
			
		||||
            if (other is ObjSet)
 | 
			
		||||
                (set + other.set).toMutableSet()
 | 
			
		||||
@ -17,7 +17,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun plusAssign(context: Context, other: Obj): Obj {
 | 
			
		||||
    override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        when (other) {
 | 
			
		||||
            is ObjSet -> {
 | 
			
		||||
                set += other.set
 | 
			
		||||
@ -29,9 +29,9 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
 | 
			
		||||
 | 
			
		||||
            else -> {
 | 
			
		||||
                if (other.isInstanceOf(ObjIterable)) {
 | 
			
		||||
                    val i = other.invokeInstanceMethod(context, "iterable")
 | 
			
		||||
                    while (i.invokeInstanceMethod(context, "hasNext").toBool()) {
 | 
			
		||||
                        set += i.invokeInstanceMethod(context, "next")
 | 
			
		||||
                    val i = other.invokeInstanceMethod(scope, "iterable")
 | 
			
		||||
                    while (i.invokeInstanceMethod(scope, "hasNext").toBool()) {
 | 
			
		||||
                        set += i.invokeInstanceMethod(scope, "next")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                set += other
 | 
			
		||||
@ -40,16 +40,16 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
 | 
			
		||||
        return this
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun mul(context: Context, other: Obj): Obj {
 | 
			
		||||
    override suspend fun mul(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        return if (other is ObjSet) {
 | 
			
		||||
            ObjSet(set.intersect(other.set).toMutableSet())
 | 
			
		||||
        } else
 | 
			
		||||
            context.raiseIllegalArgument("set operator * requires another set")
 | 
			
		||||
            scope.raiseIllegalArgument("set operator * requires another set")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun minus(context: Context, other: Obj): Obj {
 | 
			
		||||
    override suspend fun minus(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        if (other !is ObjSet)
 | 
			
		||||
            context.raiseIllegalArgument("set operator - requires another set")
 | 
			
		||||
            scope.raiseIllegalArgument("set operator - requires another set")
 | 
			
		||||
        return ObjSet(set.minus(other.set).toMutableSet())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
 | 
			
		||||
        return "Set(${set.joinToString(", ")})"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        return if (other !is ObjSet) -1
 | 
			
		||||
        else {
 | 
			
		||||
            if (set == other.set) 0
 | 
			
		||||
@ -69,8 +69,8 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        val type = object : ObjClass("Set", ObjCollection) {
 | 
			
		||||
            override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
                return ObjSet(context.args.list.toMutableSet())
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                return ObjSet(scope.args.list.toMutableSet())
 | 
			
		||||
            }
 | 
			
		||||
        }.apply {
 | 
			
		||||
            addFn("size") {
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context, other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        if (other !is ObjString) return -2
 | 
			
		||||
        return this.value.compareTo(other.value)
 | 
			
		||||
    }
 | 
			
		||||
@ -32,11 +32,11 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
    override val objClass: ObjClass
 | 
			
		||||
        get() = type
 | 
			
		||||
 | 
			
		||||
    override suspend fun plus(context: Context, other: Obj): Obj {
 | 
			
		||||
    override suspend fun plus(scope: Scope, other: Obj): Obj {
 | 
			
		||||
        return ObjString(value + other.asStr.value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getAt(context: Context, index: Obj): Obj {
 | 
			
		||||
    override suspend fun getAt(scope: Scope, index: Obj): Obj {
 | 
			
		||||
        if( index is ObjInt ) return ObjChar(value[index.toInt()])
 | 
			
		||||
        if( index is ObjRange ) {
 | 
			
		||||
            val start = if(index.start == null || index.start.isNull) 0 else  index.start.toInt()
 | 
			
		||||
@ -46,23 +46,23 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
            }
 | 
			
		||||
            return ObjString(value.substring(start, end))
 | 
			
		||||
        }
 | 
			
		||||
        context.raiseIllegalArgument("String index must be Int or Range")
 | 
			
		||||
        scope.raiseIllegalArgument("String index must be Int or Range")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
        return value.hashCode()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
        return ObjString(this.value.sprintf(*context.args.toKotlinList(context).toTypedArray()))
 | 
			
		||||
    override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
        return ObjString(this.value.sprintf(*scope.args.toKotlinList(scope).toTypedArray()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
    override suspend fun contains(scope: Scope, other: Obj): Boolean {
 | 
			
		||||
        return if (other is ObjString)
 | 
			
		||||
            value.contains(other.value)
 | 
			
		||||
        else if (other is ObjChar)
 | 
			
		||||
            value.contains(other.value)
 | 
			
		||||
        else context.raiseIllegalArgument("String.contains can't take $other")
 | 
			
		||||
        else scope.raiseIllegalArgument("String.contains can't take $other")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,17 @@
 | 
			
		||||
package net.sergeych.lyng
 | 
			
		||||
 | 
			
		||||
open class Context(
 | 
			
		||||
    val parent: Context?,
 | 
			
		||||
open class Scope(
 | 
			
		||||
    val parent: Scope?,
 | 
			
		||||
    val args: Arguments = Arguments.EMPTY,
 | 
			
		||||
    var pos: Pos = Pos.builtIn,
 | 
			
		||||
    var thisObj: Obj = ObjVoid,
 | 
			
		||||
    var skipContextCreation: Boolean = false,
 | 
			
		||||
    var skipScopeCreation: Boolean = false,
 | 
			
		||||
) {
 | 
			
		||||
    constructor(
 | 
			
		||||
        args: Arguments = Arguments.EMPTY,
 | 
			
		||||
        pos: Pos = Pos.builtIn,
 | 
			
		||||
    )
 | 
			
		||||
            : this(Script.defaultContext, args, pos)
 | 
			
		||||
            : this(Script.defaultScope, args, pos)
 | 
			
		||||
 | 
			
		||||
    fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
 | 
			
		||||
 | 
			
		||||
@ -75,13 +75,13 @@ open class Context(
 | 
			
		||||
                ?: thisObj.objClass.getInstanceMemberOrNull(name)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
 | 
			
		||||
        Context(this, args, pos, newThisObj ?: thisObj)
 | 
			
		||||
    fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
 | 
			
		||||
        Scope(this, args, pos, newThisObj ?: thisObj)
 | 
			
		||||
 | 
			
		||||
    fun copy(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
 | 
			
		||||
        Context(this, args, pos, newThisObj ?: thisObj)
 | 
			
		||||
    fun copy(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
 | 
			
		||||
        Scope(this, args, pos, newThisObj ?: thisObj)
 | 
			
		||||
 | 
			
		||||
    fun copy() = Context(this, args, pos, thisObj)
 | 
			
		||||
    fun copy() = Scope(this, args, pos, thisObj)
 | 
			
		||||
 | 
			
		||||
    fun addItem(
 | 
			
		||||
        name: String,
 | 
			
		||||
@ -97,18 +97,18 @@ open class Context(
 | 
			
		||||
        return ns.objClass
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun addVoidFn(vararg names: String, crossinline fn: suspend Context.() -> Unit) {
 | 
			
		||||
    inline fun addVoidFn(vararg names: String, crossinline fn: suspend Scope.() -> Unit) {
 | 
			
		||||
        addFn<ObjVoid>(*names) {
 | 
			
		||||
            fn(this)
 | 
			
		||||
            ObjVoid
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun <reified T : Obj> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
 | 
			
		||||
    inline fun <reified T : Obj> addFn(vararg names: String, crossinline fn: suspend Scope.() -> T) {
 | 
			
		||||
        val newFn = object : Statement() {
 | 
			
		||||
            override val pos: Pos = Pos.builtIn
 | 
			
		||||
 | 
			
		||||
            override suspend fun execute(context: Context): Obj = context.fn()
 | 
			
		||||
            override suspend fun execute(scope: Scope): Obj = scope.fn()
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        for (name in names) {
 | 
			
		||||
@ -9,18 +9,18 @@ class Script(
 | 
			
		||||
//    private val catchReturn: Boolean = false,
 | 
			
		||||
) : Statement() {
 | 
			
		||||
 | 
			
		||||
    override suspend fun execute(context: Context): Obj {
 | 
			
		||||
    override suspend fun execute(scope: Scope): Obj {
 | 
			
		||||
        var lastResult: Obj = ObjVoid
 | 
			
		||||
        for (s in statements) {
 | 
			
		||||
            lastResult = s.execute(context)
 | 
			
		||||
            lastResult = s.execute(scope)
 | 
			
		||||
        }
 | 
			
		||||
        return lastResult
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun execute() = execute(defaultContext.copy(pos = pos))
 | 
			
		||||
    suspend fun execute() = execute(defaultScope.copy(pos = pos))
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val defaultContext: Context = Context().apply {
 | 
			
		||||
        val defaultScope: Scope = Scope().apply {
 | 
			
		||||
            ObjException.addExceptionsToContext(this)
 | 
			
		||||
            addFn("println") {
 | 
			
		||||
                for ((i, a) in args.withIndex()) {
 | 
			
		||||
 | 
			
		||||
@ -12,4 +12,4 @@ open class ScriptError(val pos: Pos, val errorMessage: String,cause: Throwable?=
 | 
			
		||||
    cause
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
class ExecutionError(val errorObject: ObjException) : ScriptError(errorObject.context.pos, errorObject.message)
 | 
			
		||||
class ExecutionError(val errorObject: ObjException) : ScriptError(errorObject.scope.pos, errorObject.message)
 | 
			
		||||
 | 
			
		||||
@ -18,14 +18,14 @@ abstract class Statement(
 | 
			
		||||
    override val objClass: ObjClass = type
 | 
			
		||||
 | 
			
		||||
    abstract val pos: Pos
 | 
			
		||||
    abstract suspend fun execute(context: Context): Obj
 | 
			
		||||
    abstract suspend fun execute(scope: Scope): Obj
 | 
			
		||||
 | 
			
		||||
    override suspend fun compareTo(context: Context,other: Obj): Int {
 | 
			
		||||
    override suspend fun compareTo(scope: Scope, other: Obj): Int {
 | 
			
		||||
        throw UnsupportedOperationException("not comparable")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
        return execute(context)
 | 
			
		||||
    override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
        return execute(scope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = "Callable@${this.hashCode()}"
 | 
			
		||||
@ -34,7 +34,7 @@ abstract class Statement(
 | 
			
		||||
        val type = ObjClass("Callable")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun call(context: Context,vararg args: Obj) = execute(context.copy(args =  Arguments(*args)))
 | 
			
		||||
    suspend fun call(scope: Scope, vararg args: Obj) = execute(scope.copy(args =  Arguments(*args)))
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -47,16 +47,16 @@ fun Statement.require(cond: Boolean, message: () -> String) {
 | 
			
		||||
    if (!cond) raise(message())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend (Context) -> Obj): Statement =
 | 
			
		||||
fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend (Scope) -> Obj): Statement =
 | 
			
		||||
    object : Statement(isStaticConst, isConst) {
 | 
			
		||||
        override val pos: Pos = pos
 | 
			
		||||
        override suspend fun execute(context: Context): Obj = f(context)
 | 
			
		||||
        override suspend fun execute(scope: Scope): Obj = f(scope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend Context.() -> Obj): Statement =
 | 
			
		||||
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend Scope.() -> Obj): Statement =
 | 
			
		||||
    object : Statement(isStaticConst, isConst) {
 | 
			
		||||
        override val pos: Pos = Pos.builtIn
 | 
			
		||||
        override suspend fun execute(context: Context): Obj = f(context)
 | 
			
		||||
        override suspend fun execute(scope: Scope): Obj = f(scope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -173,55 +173,55 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun varsAndConstsTest() = runTest {
 | 
			
		||||
        val context = Context(pos = Pos.builtIn)
 | 
			
		||||
        val scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            ObjInt(3L), context.eval(
 | 
			
		||||
            ObjInt(3L), scope.eval(
 | 
			
		||||
                """
 | 
			
		||||
            val a = 17
 | 
			
		||||
            var b = 3
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals(17, context.eval("a").toInt())
 | 
			
		||||
        assertEquals(20, context.eval("b + a").toInt())
 | 
			
		||||
        assertEquals(17, scope.eval("a").toInt())
 | 
			
		||||
        assertEquals(20, scope.eval("b + a").toInt())
 | 
			
		||||
        assertFailsWith<ScriptError> {
 | 
			
		||||
            context.eval("a = 10")
 | 
			
		||||
            scope.eval("a = 10")
 | 
			
		||||
        }
 | 
			
		||||
        assertEquals(17, context.eval("a").toInt())
 | 
			
		||||
        assertEquals(5, context.eval("b = a - 7 - 5").toInt())
 | 
			
		||||
        assertEquals(5, context.eval("b").toInt())
 | 
			
		||||
        assertEquals(17, scope.eval("a").toInt())
 | 
			
		||||
        assertEquals(5, scope.eval("b = a - 7 - 5").toInt())
 | 
			
		||||
        assertEquals(5, scope.eval("b").toInt())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun functionTest() = runTest {
 | 
			
		||||
        val context = Context(pos = Pos.builtIn)
 | 
			
		||||
        context.eval(
 | 
			
		||||
        val scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            fun foo(a, b) {
 | 
			
		||||
                a + b
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals(17, context.eval("foo(3,14)").toInt())
 | 
			
		||||
        assertEquals(17, scope.eval("foo(3,14)").toInt())
 | 
			
		||||
        assertFailsWith<ScriptError> {
 | 
			
		||||
            assertEquals(17, context.eval("foo(3)").toInt())
 | 
			
		||||
            assertEquals(17, scope.eval("foo(3)").toInt())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        context.eval(
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            fn bar(a, b=10) {
 | 
			
		||||
                a + b + 1
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals(10, context.eval("bar(3, 6)").toInt())
 | 
			
		||||
        assertEquals(14, context.eval("bar(3)").toInt())
 | 
			
		||||
        assertEquals(10, scope.eval("bar(3, 6)").toInt())
 | 
			
		||||
        assertEquals(14, scope.eval("bar(3)").toInt())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun simpleClosureTest() = runTest {
 | 
			
		||||
        val context = Context(pos = Pos.builtIn)
 | 
			
		||||
        context.eval(
 | 
			
		||||
        val scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            var global = 10
 | 
			
		||||
            
 | 
			
		||||
@ -230,16 +230,16 @@ class ScriptTest {
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals(27, context.eval("foo(3,14)").toInt())
 | 
			
		||||
        context.eval("global = 20")
 | 
			
		||||
        assertEquals(37, context.eval("foo(3,14)").toInt())
 | 
			
		||||
        assertEquals(27, scope.eval("foo(3,14)").toInt())
 | 
			
		||||
        scope.eval("global = 20")
 | 
			
		||||
        assertEquals(37, scope.eval("foo(3,14)").toInt())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun nullAndVoidTest() = runTest {
 | 
			
		||||
        val context = Context(pos = Pos.builtIn)
 | 
			
		||||
        assertEquals(ObjVoid, context.eval("void"))
 | 
			
		||||
        assertEquals(ObjNull, context.eval("null"))
 | 
			
		||||
        val scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        assertEquals(ObjVoid, scope.eval("void"))
 | 
			
		||||
        assertEquals(ObjNull, scope.eval("null"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -346,8 +346,8 @@ class ScriptTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun ifTest() = runTest {
 | 
			
		||||
        // if - single line
 | 
			
		||||
        var context = Context(pos = Pos.builtIn)
 | 
			
		||||
        context.eval(
 | 
			
		||||
        var scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            fn test1(n) {
 | 
			
		||||
                var result = "more"
 | 
			
		||||
@ -357,12 +357,12 @@ class ScriptTest {
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals("enough", context.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("more", context.eval("test1(1)").toString())
 | 
			
		||||
        assertEquals("enough", scope.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("more", scope.eval("test1(1)").toString())
 | 
			
		||||
 | 
			
		||||
        // if - multiline (block)
 | 
			
		||||
        context = Context(pos = Pos.builtIn)
 | 
			
		||||
        context.eval(
 | 
			
		||||
        scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            fn test1(n) {
 | 
			
		||||
                var prefix = "answer: "
 | 
			
		||||
@ -376,12 +376,12 @@ class ScriptTest {
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals("answer: enough", context.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("answer: more", context.eval("test1(1)").toString())
 | 
			
		||||
        assertEquals("answer: enough", scope.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("answer: more", scope.eval("test1(1)").toString())
 | 
			
		||||
 | 
			
		||||
        // else single line1
 | 
			
		||||
        context = Context(pos = Pos.builtIn)
 | 
			
		||||
        context.eval(
 | 
			
		||||
        scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            fn test1(n) {
 | 
			
		||||
                if( n >= 10 )
 | 
			
		||||
@ -391,12 +391,12 @@ class ScriptTest {
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals("enough", context.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("more", context.eval("test1(1)").toString())
 | 
			
		||||
        assertEquals("enough", scope.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("more", scope.eval("test1(1)").toString())
 | 
			
		||||
 | 
			
		||||
        // if/else with blocks
 | 
			
		||||
        context = Context(pos = Pos.builtIn)
 | 
			
		||||
        context.eval(
 | 
			
		||||
        scope = Scope(pos = Pos.builtIn)
 | 
			
		||||
        scope.eval(
 | 
			
		||||
            """
 | 
			
		||||
            fn test1(n) {
 | 
			
		||||
                if( n > 20 ) {
 | 
			
		||||
@ -410,9 +410,9 @@ class ScriptTest {
 | 
			
		||||
            }
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
        assertEquals("enough", context.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("more", context.eval("test1(1)").toString())
 | 
			
		||||
        assertEquals("too much", context.eval("test1(100)").toString())
 | 
			
		||||
        assertEquals("enough", scope.eval("test1(11)").toString())
 | 
			
		||||
        assertEquals("more", scope.eval("test1(1)").toString())
 | 
			
		||||
        assertEquals("too much", scope.eval("test1(100)").toString())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -512,13 +512,13 @@ class ScriptTest {
 | 
			
		||||
                ArgsDeclaration.Item("c"),
 | 
			
		||||
            ), ttEnd
 | 
			
		||||
        )
 | 
			
		||||
        var c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        var c = Scope(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        assertEquals(ObjInt(1), c["a"]?.value)
 | 
			
		||||
        assertEquals(ObjInt(2), c["b"]?.value)
 | 
			
		||||
        assertEquals(ObjInt(3), c["c"]?.value)
 | 
			
		||||
        // less args: error
 | 
			
		||||
        c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2).map { it.toObj() }))
 | 
			
		||||
        c = Scope(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2).map { it.toObj() }))
 | 
			
		||||
        assertFailsWith<ScriptError> {
 | 
			
		||||
            pa.assignToContext(c)
 | 
			
		||||
        }
 | 
			
		||||
@ -535,7 +535,7 @@ class ScriptTest {
 | 
			
		||||
        assertEquals(ObjInt(2), c["b"]?.value)
 | 
			
		||||
        assertEquals(ObjInt(100), c["c"]?.value)
 | 
			
		||||
        // enough args. default value is ignored:
 | 
			
		||||
        c = Context(pos = Pos.builtIn, args = Arguments.from(listOf(10, 2, 5).map { it.toObj() }))
 | 
			
		||||
        c = Scope(pos = Pos.builtIn, args = Arguments.from(listOf(10, 2, 5).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        assertEquals(ObjInt(10), c["a"]?.value)
 | 
			
		||||
        assertEquals(ObjInt(2), c["b"]?.value)
 | 
			
		||||
@ -553,17 +553,17 @@ class ScriptTest {
 | 
			
		||||
                ArgsDeclaration.Item("b", isEllipsis = true),
 | 
			
		||||
            ), ttEnd
 | 
			
		||||
        )
 | 
			
		||||
        var c = Context(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        var c = Scope(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assert( a == 1 ); println(b)")
 | 
			
		||||
        c.eval("assert( b == [2,3] )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(1, 2).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(1, 2).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( a, 1 ); println(b)")
 | 
			
		||||
        c.eval("assertEquals( b, [2] )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(1).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(1).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assert( a == 1 ); println(b)")
 | 
			
		||||
        c.eval("assert( b == [] )")
 | 
			
		||||
@ -579,25 +579,25 @@ class ScriptTest {
 | 
			
		||||
                ArgsDeclaration.Item("c"),
 | 
			
		||||
            ), ttEnd
 | 
			
		||||
        )
 | 
			
		||||
        var c = Context(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        var c = Scope(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( a,[0,1] )")
 | 
			
		||||
        c.eval("assertEquals( b, 2 )")
 | 
			
		||||
        c.eval("assertEquals( c, 3 )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( a,[1] )")
 | 
			
		||||
        c.eval("assertEquals( b, 2 )")
 | 
			
		||||
        c.eval("assertEquals( c, 3 )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(2, 3).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( a,[] )")
 | 
			
		||||
        c.eval("assertEquals( b, 2 )")
 | 
			
		||||
        c.eval("assertEquals( c, 3 )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(3).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(3).map { it.toObj() }))
 | 
			
		||||
        assertFailsWith<ExecutionError> {
 | 
			
		||||
            pa.assignToContext(c)
 | 
			
		||||
        }
 | 
			
		||||
@ -614,28 +614,28 @@ class ScriptTest {
 | 
			
		||||
                ArgsDeclaration.Item("c"),
 | 
			
		||||
            ), ttEnd
 | 
			
		||||
        )
 | 
			
		||||
        var c = Context(args = Arguments.from(listOf(-1, 0, 1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        var c = Scope(args = Arguments.from(listOf(-1, 0, 1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( i, -1 )")
 | 
			
		||||
        c.eval("assertEquals( a,[0,1] )")
 | 
			
		||||
        c.eval("assertEquals( b, 2 )")
 | 
			
		||||
        c.eval("assertEquals( c, 3 )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( i, 0 )")
 | 
			
		||||
        c.eval("assertEquals( a,[1] )")
 | 
			
		||||
        c.eval("assertEquals( b, 2 )")
 | 
			
		||||
        c.eval("assertEquals( c, 3 )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
 | 
			
		||||
        pa.assignToContext(c)
 | 
			
		||||
        c.eval("assertEquals( i, 1)")
 | 
			
		||||
        c.eval("assertEquals( a,[] )")
 | 
			
		||||
        c.eval("assertEquals( b, 2 )")
 | 
			
		||||
        c.eval("assertEquals( c, 3 )")
 | 
			
		||||
 | 
			
		||||
        c = Context(args = Arguments.from(listOf(2, 3).map { it.toObj() }))
 | 
			
		||||
        c = Scope(args = Arguments.from(listOf(2, 3).map { it.toObj() }))
 | 
			
		||||
        assertFailsWith<ExecutionError> {
 | 
			
		||||
            pa.assignToContext(c)
 | 
			
		||||
        }
 | 
			
		||||
@ -756,7 +756,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testIncr() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval("var x = 10")
 | 
			
		||||
        assertEquals(10, c.eval("x++").toInt())
 | 
			
		||||
        assertEquals(11, c.eval("x++").toInt())
 | 
			
		||||
@ -768,7 +768,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testDecr() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval("var x = 9")
 | 
			
		||||
        assertEquals(9, c.eval("x--").toInt())
 | 
			
		||||
        assertEquals(8, c.eval("x--").toInt())
 | 
			
		||||
@ -779,7 +779,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testDecrIncr() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval("var x = 9")
 | 
			
		||||
        assertEquals(9, c.eval("x++").toInt())
 | 
			
		||||
        assertEquals(10, c.eval("x++").toInt())
 | 
			
		||||
@ -793,7 +793,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testDecrIncr2() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval("var x = 9")
 | 
			
		||||
        assertEquals(9, c.eval("x--").toInt())
 | 
			
		||||
        assertEquals(8, c.eval("x--").toInt())
 | 
			
		||||
@ -810,7 +810,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testDecrIncr3() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval("var x = 9")
 | 
			
		||||
        assertEquals(9, c.eval("x++").toInt())
 | 
			
		||||
        assertEquals(10, c.eval("x++").toInt())
 | 
			
		||||
@ -822,7 +822,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testIncrAndDecr() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        assertEquals(
 | 
			
		||||
            "8", c.eval(
 | 
			
		||||
                """
 | 
			
		||||
@ -857,7 +857,7 @@ class ScriptTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testAssign1() = runTest {
 | 
			
		||||
        assertEquals(10, eval("var x = 5; x=10; x").toInt())
 | 
			
		||||
        val ctx = Context()
 | 
			
		||||
        val ctx = Scope()
 | 
			
		||||
        ctx.eval(
 | 
			
		||||
            """
 | 
			
		||||
            var a = 1
 | 
			
		||||
@ -871,7 +871,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testAssign2() = runTest {
 | 
			
		||||
        val ctx = Context()
 | 
			
		||||
        val ctx = Scope()
 | 
			
		||||
        ctx.eval("var x = 10")
 | 
			
		||||
        assertEquals(14, ctx.eval("x += 4").toInt())
 | 
			
		||||
        assertEquals(14, ctx.eval("x").toInt())
 | 
			
		||||
@ -889,7 +889,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testVals() = runTest {
 | 
			
		||||
        val cxt = Context()
 | 
			
		||||
        val cxt = Scope()
 | 
			
		||||
        cxt.eval("val x = 11")
 | 
			
		||||
        assertEquals(11, cxt.eval("x").toInt())
 | 
			
		||||
        assertFails { cxt.eval("x = 12") }
 | 
			
		||||
@ -1436,7 +1436,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testSimpleStruct() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval(
 | 
			
		||||
            """
 | 
			
		||||
            class Point(x,y)
 | 
			
		||||
@ -1457,7 +1457,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testNonAssignalbeFieldInStruct() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval(
 | 
			
		||||
            """
 | 
			
		||||
            class Point(x,y)
 | 
			
		||||
@ -1474,7 +1474,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testStructBodyVal() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval(
 | 
			
		||||
            """
 | 
			
		||||
            class Point(x,y) {
 | 
			
		||||
@ -1496,7 +1496,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testStructBodyFun() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval(
 | 
			
		||||
            """
 | 
			
		||||
            class Point(x,y) {
 | 
			
		||||
@ -1516,7 +1516,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testPrivateConstructorParams() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.eval(
 | 
			
		||||
            """
 | 
			
		||||
            class Point(private var x,y)
 | 
			
		||||
@ -1871,7 +1871,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testTryFinally() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        assertFails {
 | 
			
		||||
            c.eval(
 | 
			
		||||
                """
 | 
			
		||||
@ -1894,7 +1894,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testThrowFromKotlin() = runTest {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        c.addFn("callThrow") {
 | 
			
		||||
            raiseIllegalArgument("fromKotlin")
 | 
			
		||||
        }
 | 
			
		||||
@ -2316,7 +2316,7 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testToFlow() = runTest() {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
        val arr = c.eval("[1,2,3]")
 | 
			
		||||
        // array is iterable so we can:
 | 
			
		||||
        assertEquals(listOf(1,2,3),  arr.toFlow(c).map { it.toInt() }.toList())
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,8 @@ import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.flow
 | 
			
		||||
import kotlinx.coroutines.flow.flowOn
 | 
			
		||||
import kotlinx.coroutines.test.runTest
 | 
			
		||||
import net.sergeych.lyng.Context
 | 
			
		||||
import net.sergeych.lyng.ObjVoid
 | 
			
		||||
import net.sergeych.lyng.Scope
 | 
			
		||||
import java.nio.file.Files
 | 
			
		||||
import java.nio.file.Files.readAllLines
 | 
			
		||||
import java.nio.file.Paths
 | 
			
		||||
@ -158,10 +158,10 @@ fun parseDocTests(fileName: String, bookMode: Boolean = false): Flow<DocTest> =
 | 
			
		||||
}
 | 
			
		||||
    .flowOn(Dispatchers.IO)
 | 
			
		||||
 | 
			
		||||
suspend fun DocTest.test(context: Context = Context()) {
 | 
			
		||||
suspend fun DocTest.test(scope: Scope = Scope()) {
 | 
			
		||||
    val collectedOutput = StringBuilder()
 | 
			
		||||
    val currentTest = this
 | 
			
		||||
    context.apply {
 | 
			
		||||
    scope.apply {
 | 
			
		||||
        addFn("println") {
 | 
			
		||||
            if( bookMode ) {
 | 
			
		||||
                println("${currentTest.fileNamePart}:${currentTest.line}> ${args.joinToString(" "){it.asStr.value}}")
 | 
			
		||||
@ -177,7 +177,7 @@ suspend fun DocTest.test(context: Context = Context()) {
 | 
			
		||||
    }
 | 
			
		||||
    var error: Throwable? = null
 | 
			
		||||
    val result = try {
 | 
			
		||||
        context.eval(code)
 | 
			
		||||
        scope.eval(code)
 | 
			
		||||
    } catch (e: Throwable) {
 | 
			
		||||
        error = e
 | 
			
		||||
        null
 | 
			
		||||
@ -204,10 +204,10 @@ suspend fun DocTest.test(context: Context = Context()) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun runDocTests(fileName: String, bookMode: Boolean = false) {
 | 
			
		||||
    val bookContext = Context()
 | 
			
		||||
    val bookScope = Scope()
 | 
			
		||||
    var count = 0
 | 
			
		||||
    parseDocTests(fileName, bookMode).collect { dt ->
 | 
			
		||||
        if (bookMode) dt.test(bookContext)
 | 
			
		||||
        if (bookMode) dt.test(bookScope)
 | 
			
		||||
        else dt.test()
 | 
			
		||||
        count++
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.runBlocking
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
import kotlinx.datetime.Clock
 | 
			
		||||
import net.sergeych.lyng.Context
 | 
			
		||||
import net.sergeych.lyng.Scope
 | 
			
		||||
import java.nio.file.Files
 | 
			
		||||
import java.nio.file.Paths
 | 
			
		||||
import kotlin.io.path.extension
 | 
			
		||||
@ -13,7 +13,7 @@ suspend fun executeSampleTests(fileName: String) {
 | 
			
		||||
        Files.readString(Paths.get(fileName))
 | 
			
		||||
    }
 | 
			
		||||
    runBlocking {
 | 
			
		||||
        val c = Context()
 | 
			
		||||
        val c = Scope()
 | 
			
		||||
            val start = Clock.System.now()
 | 
			
		||||
            c.eval(sample)
 | 
			
		||||
            val time = Clock.System.now() - start
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user