renaming context to scope, as it is rather scp[e than the context

This commit is contained in:
Sergey Chernov 2025-07-03 21:46:25 +03:00
parent 6cf99fbd13
commit ddbcbf9e4e
29 changed files with 414 additions and 412 deletions

View File

@ -75,21 +75,23 @@ runBlocking {
### Exchanging information ### Exchanging information
Script is executed over some `Context`. Create instance of the context, Script is executed over some `Scope`. Create instance,
add your specific vars and functions to it, an call over it: add your specific vars and functions to it, and call:
```kotlin ```kotlin
import new.sergeych.lyng.*
import com.sun.source.tree.Scope
import new.sergeych.lyng.*
// simple function // simple function
val context = Context().apply { val scope = Scope().apply {
addFn("addArgs") { addFn("addArgs") {
var sum = 0.0 var sum = 0.0
for( a in args) sum += a.toDouble() for (a in args) sum += a.toDouble()
ObjReal(sum) ObjReal(sum)
} }
addConst("LIGHT_SPEED", ObjReal(299_792_458.0)) addConst("LIGHT_SPEED", ObjReal(299_792_458.0))
// callback back to kotlin to some suspend fn, for example:: // callback back to kotlin to some suspend fn, for example::
// suspend fun doSomeWork(text: String): Int // suspend fun doSomeWork(text: String): Int
addFn("doSomeWork") { addFn("doSomeWork") {
@ -99,9 +101,9 @@ val context = Context().apply {
} }
} }
// adding constant: // 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? ## Why?

View File

@ -31,7 +31,7 @@ data class CommandResult(
val error: String val error: String
) )
val baseContext = Context().apply { val baseScope = Scope().apply {
addFn("exit") { addFn("exit") {
exit(requireOnlyArg<ObjInt>().toInt()) exit(requireOnlyArg<ObjInt>().toInt())
ObjVoid ObjVoid
@ -74,7 +74,7 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
val objargs = mutableListOf<String>() val objargs = mutableListOf<String>()
script?.let { objargs += it } script?.let { objargs += it }
objargs += args objargs += args
baseContext.addConst( baseScope.addConst(
"ARGV", ObjList( "ARGV", ObjList(
objargs.map { ObjString(it) }.toMutableList() objargs.map { ObjString(it) }.toMutableList()
) )
@ -82,7 +82,7 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
launcher { launcher {
// there is no script name, it is a first argument instead: // there is no script name, it is a first argument instead:
processErrors { processErrors {
baseContext.eval(execute!!) baseScope.eval(execute!!)
} }
} }
} }
@ -98,7 +98,7 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
) )
echoFormattedHelp() echoFormattedHelp()
} else { } else {
baseContext.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList())) baseScope.addConst("ARGV", ObjList(args.map { ObjString(it) }.toMutableList()))
launcher { executeFile(script!!) } launcher { executeFile(script!!) }
} }
} }
@ -118,7 +118,7 @@ suspend fun executeFile(fileName: String) {
text = text.substring(pos + 1) text = text.substring(pos + 1)
} }
processErrors { processErrors {
Compiler.compile(Source(fileName, text)).execute(baseContext) Compiler.compile(Source(fileName, text)).execute(baseScope)
} }
} }

View File

@ -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]
}

View File

@ -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]
}

View File

@ -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 * parse args and create local vars in a given context
*/ */
suspend fun assignToContext( suspend fun assignToContext(
context: Context, scope: Scope,
arguments: Arguments = context.args, arguments: Arguments = scope.args,
defaultAccessType: AccessType = AccessType.Var, defaultAccessType: AccessType = AccessType.Var,
defaultVisibility: Visibility = Visibility.Public defaultVisibility: Visibility = Visibility.Public
) { ) {
fun assign(a: Item, value: Obj) { 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) a.visibility ?: defaultVisibility)
} }
@ -52,11 +52,11 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
if (a.isEllipsis) break if (a.isEllipsis) break
val value = when { val value = when {
i < callArgs.size -> callArgs[i] i < callArgs.size -> callArgs[i]
a.defaultValue != null -> a.defaultValue.execute(context) a.defaultValue != null -> a.defaultValue.execute(scope)
else -> { else -> {
println("callArgs: ${callArgs.joinToString()}") println("callArgs: ${callArgs.joinToString()}")
println("tailBlockMode: ${arguments.tailBlockMode}") println("tailBlockMode: ${arguments.tailBlockMode}")
context.raiseIllegalArgument("too few arguments for the call") scope.raiseIllegalArgument("too few arguments for the call")
} }
} }
assign(a, value) assign(a, value)
@ -76,8 +76,8 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
callArgs[j--] callArgs[j--]
} }
a.defaultValue != null -> a.defaultValue.execute(context) a.defaultValue != null -> a.defaultValue.execute(scope)
else -> context.raiseIllegalArgument("too few arguments for the call") else -> scope.raiseIllegalArgument("too few arguments for the call")
} }
assign(a, value) assign(a, value)
i-- i--
@ -98,7 +98,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
processEllipsis(leftIndex, end) processEllipsis(leftIndex, end)
} else { } else {
if (leftIndex < callArgs.size) if (leftIndex < callArgs.size)
context.raiseIllegalArgument("too many arguments for the call") scope.raiseIllegalArgument("too many arguments for the call")
} }
} }

View File

@ -2,11 +2,11 @@ package net.sergeych.lyng
data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false) 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>() val list = mutableListOf<Obj>()
for (x in this) { for (x in this) {
val value = x.value.execute(context) val value = x.value.execute(scope)
if (x.isSplat) { if (x.isSplat) {
when { when {
value is ObjList -> { value is ObjList -> {
@ -14,11 +14,11 @@ suspend fun Collection<ParsedArgument>.toArguments(context: Context,tailBlockMod
} }
value.isInstanceOf(ObjIterable) -> { 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) } 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 } else
list.add(value) 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]. * Convert to list of kotlin objects, see [Obj.toKotlin].
*/ */
suspend fun toKotlinList(context: Context): List<Any?> { suspend fun toKotlinList(scope: Scope): List<Any?> {
return list.map { it.toKotlin(context) } return list.map { it.toKotlin(scope) }
} }
companion object { companion object {

View File

@ -380,11 +380,11 @@ class Compiler(
val body = parseBlock(skipLeadingBrace = true) val body = parseBlock(skipLeadingBrace = true)
var closure: Context? = null var closure: Scope? = null
val callStatement = statement { val callStatement = statement {
// and the source closure of the lambda which might have other thisObj. // 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) { if (argsDeclaration == null) {
// no args: automatic var 'it' // no args: automatic var 'it'
val l = args.list val l = args.list
@ -1154,7 +1154,7 @@ class Compiler(
} }
private suspend fun loopIntRange( 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 body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean
): Obj { ): Obj {
var result: Obj = ObjVoid var result: Obj = ObjVoid
@ -1164,7 +1164,7 @@ class Compiler(
for (i in start..<end) { for (i in start..<end) {
iVar.value = i.toLong() iVar.value = i.toLong()
try { try {
result = body.execute(forContext) result = body.execute(forScope)
} catch (lbe: LoopBreakContinueException) { } catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) { if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue if (lbe.doContinue) continue
@ -1176,24 +1176,24 @@ class Compiler(
} else { } else {
for (i in start.toLong()..<end.toLong()) { for (i in start.toLong()..<end.toLong()) {
iVar.value = i 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( private suspend fun loopIterable(
forContext: Context, sourceObj: Obj, loopVar: ObjRecord, forScope: Scope, sourceObj: Obj, loopVar: ObjRecord,
body: Statement, elseStatement: Statement?, label: String?, body: Statement, elseStatement: Statement?, label: String?,
catchBreak: Boolean catchBreak: Boolean
): Obj { ): Obj {
val iterObj = sourceObj.invokeInstanceMethod(forContext, "iterator") val iterObj = sourceObj.invokeInstanceMethod(forScope, "iterator")
var result: Obj = ObjVoid var result: Obj = ObjVoid
while (iterObj.invokeInstanceMethod(forContext, "hasNext").toBool()) { while (iterObj.invokeInstanceMethod(forScope, "hasNext").toBool()) {
if (catchBreak) if (catchBreak)
try { try {
loopVar.value = iterObj.invokeInstanceMethod(forContext, "next") loopVar.value = iterObj.invokeInstanceMethod(forScope, "next")
result = body.execute(forContext) result = body.execute(forScope)
} catch (lbe: LoopBreakContinueException) { } catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) { if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue if (lbe.doContinue) continue
@ -1202,11 +1202,11 @@ class Compiler(
throw lbe throw lbe
} }
else { else {
loopVar.value = iterObj.invokeInstanceMethod(forContext, "next") loopVar.value = iterObj.invokeInstanceMethod(forScope, "next")
result = body.execute(forContext) result = body.execute(forScope)
} }
} }
return elseStatement?.execute(forContext) ?: result return elseStatement?.execute(forScope) ?: result
} }
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
@ -1240,11 +1240,11 @@ class Compiler(
return statement(body.pos) { return statement(body.pos) {
var wasBroken = false var wasBroken = false
var result: Obj = ObjVoid var result: Obj = ObjVoid
lateinit var doContext: Context lateinit var doScope: Scope
do { do {
doContext = it.copy().apply { skipContextCreation = true } doScope = it.copy().apply { skipScopeCreation = true }
try { try {
result = body.execute(doContext) result = body.execute(doScope)
} catch (e: LoopBreakContinueException) { } catch (e: LoopBreakContinueException) {
if (e.label == label || e.label == null) { if (e.label == label || e.label == null) {
if (e.doContinue) continue if (e.doContinue) continue
@ -1256,7 +1256,7 @@ class Compiler(
} }
throw e throw e
} }
} while (condition.execute(doContext).toBool()) } while (condition.execute(doScope).toBool())
if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) } if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) }
result result
} }
@ -1452,7 +1452,7 @@ class Compiler(
else else
parseBlock() parseBlock()
var closure: Context? = null var closure: Scope? = null
val fnBody = statement(t.pos) { callerContext -> val fnBody = statement(t.pos) { callerContext ->
callerContext.pos = start callerContext.pos = start
@ -1497,7 +1497,7 @@ class Compiler(
val block = parseScript() val block = parseScript()
return statement(startPos) { return statement(startPos) {
// block run on inner context: // 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 { }.also {
val t1 = cc.next() val t1 = cc.next()
if (t1.type != Token.Type.RBRACE) if (t1.type != Token.Type.RBRACE)
@ -1551,7 +1551,7 @@ class Compiler(
// fun isLeftAssociative() = tokenType != Token.Type.OR && tokenType != Token.Type.AND // fun isLeftAssociative() = tokenType != Token.Type.OR && tokenType != Token.Type.AND
companion object { 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 -> Operator(tokenType, priority, 2) { _: Pos, a: Accessor, b: Accessor ->
Accessor { f(it, a.getter(it).value, b.getter(it).value).asReadonly } Accessor { f(it, a.getter(it).value, b.getter(it).value).asReadonly }
} }

View File

@ -27,13 +27,13 @@ data class ObjRecord(
* operator is implemented in [Compiler.allOps]. * operator is implemented in [Compiler.allOps].
*/ */
data class Accessor( data class Accessor(
val getter: suspend (Context) -> ObjRecord, val getter: suspend (Scope) -> ObjRecord,
val setterOrNull: (suspend (Context, Obj) -> Unit)? val setterOrNull: (suspend (Scope, Obj) -> Unit)?
) { ) {
/** /**
* Simplified constructor for immutable stores. * 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. * Get the setter or throw.
@ -70,41 +70,41 @@ open class Obj {
someClass == rootObjectType someClass == rootObjectType
suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj = suspend fun invokeInstanceMethod(scope: Scope, name: String, vararg args: Obj): Obj =
invokeInstanceMethod(context, name, Arguments(args.toList())) invokeInstanceMethod(scope, name, Arguments(args.toList()))
suspend inline fun <reified T : Obj> callMethod( suspend inline fun <reified T : Obj> callMethod(
context: Context, scope: Scope,
name: String, name: String,
args: Arguments = Arguments.EMPTY args: Arguments = Arguments.EMPTY
): T = invokeInstanceMethod(context, name, args) as T ): T = invokeInstanceMethod(scope, name, args) as T
open suspend fun invokeInstanceMethod( open suspend fun invokeInstanceMethod(
context: Context, scope: Scope,
name: String, name: String,
args: Arguments = Arguments.EMPTY args: Arguments = Arguments.EMPTY
): Obj = ): Obj =
// note that getInstanceMember traverses the hierarchy // 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( open suspend fun getInstanceMethod(
context: Context, scope: Scope,
name: String, name: String,
args: Arguments = Arguments.EMPTY args: Arguments = Arguments.EMPTY
): Obj = ): Obj =
// note that getInstanceMember traverses the hierarchy // 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 fun getMemberOrNull(name: String): Obj? = objClass.getInstanceMemberOrNull(name)?.value
// methods that to override // methods that to override
open suspend fun compareTo(context: Context, other: Obj): Int { open suspend fun compareTo(scope: Scope, other: Obj): Int {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun contains(context: Context, other: Obj): Boolean { open suspend fun contains(scope: Scope, other: Obj): Boolean {
return invokeInstanceMethod(context, "contains", other).toBool() return invokeInstanceMethod(scope, "contains", other).toBool()
} }
open val asStr: ObjString by lazy { open val asStr: ObjString by lazy {
@ -117,90 +117,90 @@ open class Obj {
*/ */
open val objClass: ObjClass = rootObjectType open val objClass: ObjClass = rootObjectType
open suspend fun plus(context: Context, other: Obj): Obj { open suspend fun plus(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun minus(context: Context, other: Obj): Obj { open suspend fun minus(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun mul(context: Context, other: Obj): Obj { open suspend fun mul(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun div(context: Context, other: Obj): Obj { open suspend fun div(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun mod(context: Context, other: Obj): Obj { open suspend fun mod(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun logicalNot(context: Context): Obj { open suspend fun logicalNot(scope: Scope): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun logicalAnd(context: Context, other: Obj): Obj { open suspend fun logicalAnd(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun logicalOr(context: Context, other: Obj): Obj { open suspend fun logicalOr(scope: Scope, other: Obj): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun assign(context: Context, other: Obj): Obj? = null open suspend fun assign(scope: Scope, other: Obj): Obj? = null
/** /**
* a += b * a += b
* if( the operation is not defined, it returns null and the compiler would try * if( the operation is not defined, it returns null and the compiler would try
* to generate it as 'this = this + other', reassigning its variable * 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] * `-=` operations, see [plusAssign]
*/ */
open suspend fun minusAssign(context: Context, other: Obj): Obj? = null open suspend fun minusAssign(scope: Scope, other: Obj): Obj? = null
open suspend fun mulAssign(context: Context, other: Obj): Obj? = null open suspend fun mulAssign(scope: Scope, other: Obj): Obj? = null
open suspend fun divAssign(context: Context, other: Obj): Obj? = null open suspend fun divAssign(scope: Scope, other: Obj): Obj? = null
open suspend fun modAssign(context: Context, other: Obj): Obj? = null open suspend fun modAssign(scope: Scope, other: Obj): Obj? = null
open suspend fun getAndIncrement(context: Context): Obj { open suspend fun getAndIncrement(scope: Scope): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun incrementAndGet(context: Context): Obj { open suspend fun incrementAndGet(scope: Scope): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun decrementAndGet(context: Context): Obj { open suspend fun decrementAndGet(scope: Scope): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
open suspend fun getAndDecrement(context: Context): Obj { open suspend fun getAndDecrement(scope: Scope): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
/** /**
* Convert Lyng object to its Kotlin counterpart * Convert Lyng object to its Kotlin counterpart
*/ */
open suspend fun toKotlin(context: Context): Any? { open suspend fun toKotlin(scope: Scope): Any? {
return toString() return toString()
} }
fun willMutate(context: Context) { fun willMutate(scope: Scope) {
if (isFrozen) context.raiseError("attempt to mutate frozen object") if (isFrozen) scope.raiseError("attempt to mutate frozen object")
} }
suspend fun <T> sync(block: () -> T): T = monitor.withLock { block() } 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: // 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) { return when (val value = obj.value) {
is Statement -> { 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 // could be writable property naturally
// null -> ObjNull.asReadonly // null -> ObjNull.asReadonly
@ -208,49 +208,49 @@ open class Obj {
} }
} }
open suspend fun writeField(context: Context, name: String, newValue: Obj) { open suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
willMutate(context) willMutate(scope)
val field = objClass.getInstanceMemberOrNull(name) ?: context.raiseError("no such field: $name") val field = objClass.getInstanceMemberOrNull(name) ?: scope.raiseError("no such field: $name")
if (field.isMutable) field.value = newValue else context.raiseError("can't assign to read-only 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 { open suspend fun getAt(scope: Scope, index: Obj): Obj {
context.raiseNotImplemented("indexing") 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) { open suspend fun putAt(scope: Scope, index: Int, newValue: Obj) {
context.raiseNotImplemented("indexing") scope.raiseNotImplemented("indexing")
} }
open suspend fun callOn(context: Context): Obj { open suspend fun callOn(scope: Scope): Obj {
context.raiseNotImplemented() scope.raiseNotImplemented()
} }
suspend fun invoke(context: Context, thisObj: Obj, args: Arguments): Obj = suspend fun invoke(scope: Scope, thisObj: Obj, args: Arguments): Obj =
callOn(context.copy(context.pos, args = args, newThisObj = thisObj)) 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( callOn(
context.copy( scope.copy(
context.pos, scope.pos,
args = Arguments(args.toList()), args = Arguments(args.toList()),
newThisObj = thisObj newThisObj = thisObj
) )
) )
suspend fun invoke(context: Context, thisObj: Obj): Obj = suspend fun invoke(scope: Scope, thisObj: Obj): Obj =
callOn( callOn(
context.copy( scope.copy(
context.pos, scope.pos,
args = Arguments.EMPTY, args = Arguments.EMPTY,
newThisObj = thisObj newThisObj = thisObj
) )
) )
suspend fun invoke(context: Context, atPos: Pos, thisObj: Obj, args: Arguments): Obj = suspend fun invoke(scope: Scope, atPos: Pos, thisObj: Obj, args: Arguments): Obj =
callOn(context.copy(atPos, args = args, newThisObj = thisObj)) callOn(scope.copy(atPos, args = args, newThisObj = thisObj))
val asReadonly: ObjRecord by lazy { ObjRecord(this, false) } val asReadonly: ObjRecord by lazy { ObjRecord(this, false) }
@ -271,7 +271,7 @@ open class Obj {
args.firstAndOnly().callOn(copy(Arguments(thisObj))) args.firstAndOnly().callOn(copy(Arguments(thisObj)))
} }
addFn("apply") { addFn("apply") {
val newContext = ( thisObj as? ObjInstance)?.instanceContext ?: this val newContext = ( thisObj as? ObjInstance)?.instanceScope ?: this
args.firstAndOnly() args.firstAndOnly()
.callOn(newContext) .callOn(newContext)
thisObj thisObj
@ -319,7 +319,7 @@ object ObjVoid : Obj() {
return other is ObjVoid || other is Unit 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 return if (other === this) 0 else -1
} }
@ -329,7 +329,7 @@ object ObjVoid : Obj() {
@Serializable @Serializable
@SerialName("null") @SerialName("null")
object ObjNull : Obj() { 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 return if (other === this) 0 else -1
} }
@ -337,29 +337,29 @@ object ObjNull : Obj() {
return other is ObjNull || other == null return other is ObjNull || other == null
} }
override suspend fun readField(context: Context, name: String): ObjRecord { override suspend fun readField(scope: Scope, name: String): ObjRecord {
context.raiseNPE() scope.raiseNPE()
} }
override suspend fun invokeInstanceMethod(context: Context, name: String, args: Arguments): Obj { override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments): Obj {
context.raiseNPE() scope.raiseNPE()
} }
override suspend fun getAt(context: Context, index: Obj): Obj { override suspend fun getAt(scope: Scope, index: Obj): Obj {
context.raiseNPE() scope.raiseNPE()
} }
override suspend fun putAt(context: Context, index: Int, newValue: Obj) { override suspend fun putAt(scope: Scope, index: Int, newValue: Obj) {
context.raiseNPE() scope.raiseNPE()
} }
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
context.raiseNPE() scope.raiseNPE()
} }
override fun toString(): String = "null" override fun toString(): String = "null"
override suspend fun toKotlin(context: Context): Any? { override suspend fun toKotlin(scope: Scope): Any? {
return null 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() { open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val message: String) : Obj() {
constructor(name: String, context: Context, message: String) : this( constructor(name: String, scope: Scope, message: String) : this(
getOrCreateExceptionClass(name), getOrCreateExceptionClass(name),
context, scope,
message message
) )
constructor(context: Context, message: String) : this(Root, context, message) constructor(scope: Scope, message: String) : this(Root, scope, message)
fun raise(): Nothing { fun raise(): Nothing {
throw ExecutionError(this) throw ExecutionError(this)
@ -417,15 +417,15 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
override val objClass: ObjClass = exceptionClass override val objClass: ObjClass = exceptionClass
override fun toString(): String { override fun toString(): String {
return "ObjException:${objClass.className}:${context.pos}@${hashCode().encodeToHex()}" return "ObjException:${objClass.className}:${scope.pos}@${hashCode().encodeToHex()}"
} }
companion object { companion object {
class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) { class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) {
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
val message = context.args.getOrNull(0)?.toString() ?: name val message = scope.args.getOrNull(0)?.toString() ?: name
return ObjException(this, context, message) return ObjException(this, scope, message)
} }
override fun toString(): String = "ExceptionClass[$name]@${hashCode().encodeToHex()}" override fun toString(): String = "ExceptionClass[$name]@${hashCode().encodeToHex()}"
@ -458,8 +458,8 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
existingErrorClasses[name] existingErrorClasses[name]
} }
fun addExceptionsToContext(context: Context) { fun addExceptionsToContext(scope: Scope) {
context.addConst("Exception", Root) scope.addConst("Exception", Root)
existingErrorClasses["Exception"] = Root existingErrorClasses["Exception"] = Root
for (name in listOf( for (name in listOf(
"NullReferenceException", "NullReferenceException",
@ -474,39 +474,39 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
"AccessException", "AccessException",
"UnknownException", "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) : class ObjAssertionFailedException(scope: Scope, message: String) :
ObjException("AssertionFailedException", context, message) ObjException("AssertionFailedException", scope, message)
class ObjClassCastException(context: Context, message: String) : ObjException("ClassCastException", context, message) class ObjClassCastException(scope: Scope, message: String) : ObjException("ClassCastException", scope, message)
class ObjIndexOutOfBoundsException(context: Context, message: String = "index out of bounds") : class ObjIndexOutOfBoundsException(scope: Scope, message: String = "index out of bounds") :
ObjException("IndexOutOfBoundsException", context, message) ObjException("IndexOutOfBoundsException", scope, message)
class ObjIllegalArgumentException(context: Context, message: String = "illegal argument") : class ObjIllegalArgumentException(scope: Scope, message: String = "illegal argument") :
ObjException("IllegalArgumentException", context, message) ObjException("IllegalArgumentException", scope, message)
@Suppress("unused") @Suppress("unused")
class ObjNoSuchElementException(context: Context, message: String = "no such element") : class ObjNoSuchElementException(scope: Scope, message: String = "no such element") :
ObjException("IllegalArgumentException", context, message) ObjException("IllegalArgumentException", scope, message)
class ObjIllegalAssignmentException(context: Context, message: String = "illegal assignment") : class ObjIllegalAssignmentException(scope: Scope, message: String = "illegal assignment") :
ObjException("NoSuchElementException", context, message) ObjException("NoSuchElementException", scope, message)
class ObjSymbolNotDefinedException(context: Context, message: String = "symbol is not defined") : class ObjSymbolNotDefinedException(scope: Scope, message: String = "symbol is not defined") :
ObjException("SymbolNotDefinedException", context, message) ObjException("SymbolNotDefinedException", scope, message)
class ObjIterationFinishedException(context: Context) : class ObjIterationFinishedException(scope: Scope) :
ObjException("IterationEndException", context, "iteration finished") ObjException("IterationEndException", scope, "iteration finished")
class ObjAccessException(context: Context, message: String = "access not allowed error") : class ObjAccessException(scope: Scope, message: String = "access not allowed error") :
ObjException("AccessException", context, message) ObjException("AccessException", scope, message)
class ObjUnknownException(context: Context, message: String = "access not allowed error") : class ObjUnknownException(scope: Scope, message: String = "access not allowed error") :
ObjException("UnknownException", context, message) ObjException("UnknownException", scope, message)

View File

@ -7,9 +7,9 @@ class ObjArrayIterator(val array: Obj) : Obj() {
private var nextIndex = 0 private var nextIndex = 0
private var lastIndex = 0 private var lastIndex = 0
suspend fun init(context: Context) { suspend fun init(scope: Scope) {
nextIndex = 0 nextIndex = 0
lastIndex = array.invokeInstanceMethod(context, "size").toInt() lastIndex = array.invokeInstanceMethod(scope, "size").toInt()
ObjVoid ObjVoid
} }

View File

@ -3,7 +3,7 @@ package net.sergeych.lyng
data class ObjBool(val value: Boolean) : Obj() { data class ObjBool(val value: Boolean) : Obj() {
override val asStr by lazy { ObjString(value.toString()) } 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 if (other !is ObjBool) return -2
return value.compareTo(other.value) return value.compareTo(other.value)
} }
@ -12,13 +12,13 @@ data class ObjBool(val value: Boolean) : Obj() {
override val objClass: ObjClass = type 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 return value
} }

View File

@ -4,7 +4,7 @@ class ObjChar(val value: Char): Obj() {
override val objClass: ObjClass = type 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 (other as? ObjChar)?.let { value.compareTo(it.value) } ?: -1
override fun toString(): String = value.toString() override fun toString(): String = value.toString()

View File

@ -21,13 +21,13 @@ open class ObjClass(
override fun toString(): String = className 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) 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) { if (instanceConstructor != null) {
instanceConstructor!!.execute(instance.instanceContext) instanceConstructor!!.execute(instance.instanceScope)
} }
return instance return instance
} }
@ -49,7 +49,7 @@ open class ObjClass(
members[name] = ObjRecord(initialValue, isMutable, visibility) 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) createField(name, statement { code() }, isOpen)
} }

View File

@ -2,52 +2,52 @@ package net.sergeych.lyng
class ObjInstance(override val objClass: ObjClass) : Obj() { 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 { override suspend fun readField(scope: Scope, name: String): ObjRecord {
return instanceContext[name]?.let { return instanceScope[name]?.let {
if (it.visibility.isPublic) if (it.visibility.isPublic)
it it
else 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) { override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
instanceContext[name]?.let { f -> instanceScope[name]?.let { f ->
if (!f.visibility.isPublic) if (!f.visibility.isPublic)
ObjIllegalAssignmentException(context, "can't assign to non-public field $name") ObjIllegalAssignmentException(scope, "can't assign to non-public field $name")
if (!f.isMutable) ObjIllegalAssignmentException(context, "can't reassign val $name").raise() if (!f.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
if (f.value.assign(context, newValue) == null) if (f.value.assign(scope, newValue) == null)
f.value = newValue f.value = newValue
} ?: super.writeField(context, name, newValue) } ?: super.writeField(scope, name, newValue)
} }
override suspend fun invokeInstanceMethod(context: Context, name: String, args: Arguments): Obj = override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments): Obj =
instanceContext[name]?.let { instanceScope[name]?.let {
if (it.visibility.isPublic) if (it.visibility.isPublic)
it.value.invoke(context, this, args) it.value.invoke(scope, this, args)
else 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> 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 { override fun toString(): String {
val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",") val fields = publicFields.map { "${it.key}=${it.value.value}" }.joinToString(",")
return "${objClass.className}($fields)" 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 !is ObjInstance) return -1
if (other.objClass != objClass) return -1 if (other.objClass != objClass) return -1
for (f in publicFields) { for (f in publicFields) {
val a = f.value.value val a = f.value.value
val b = other.instanceContext[f.key]!!.value val b = other.instanceScope[f.key]!!.value
val d = a.compareTo(context, b) val d = a.compareTo(scope, b)
if (d != 0) return d if (d != 0) return d
} }
return 0 return 0

View File

@ -13,23 +13,23 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
return value.hashCode() return value.hashCode()
} }
override suspend fun getAndIncrement(context: Context): Obj { override suspend fun getAndIncrement(scope: Scope): Obj {
return ObjInt(value).also { value++ } return ObjInt(value).also { value++ }
} }
override suspend fun getAndDecrement(context: Context): Obj { override suspend fun getAndDecrement(scope: Scope): Obj {
return ObjInt(value).also { value-- } return ObjInt(value).also { value-- }
} }
override suspend fun incrementAndGet(context: Context): Obj { override suspend fun incrementAndGet(scope: Scope): Obj {
return ObjInt(++value) return ObjInt(++value)
} }
override suspend fun decrementAndGet(context: Context): Obj { override suspend fun decrementAndGet(scope: Scope): Obj {
return ObjInt(--value) 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 if (other !is Numeric) return -2
return value.compareTo(other.doubleValue) return value.compareTo(other.doubleValue)
} }
@ -38,29 +38,29 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
override val objClass: ObjClass = type 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) if (other is ObjInt)
ObjInt(this.value + other.value) ObjInt(this.value + other.value)
else else
ObjReal(this.doubleValue + other.toDouble()) 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) if (other is ObjInt)
ObjInt(this.value - other.value) ObjInt(this.value - other.value)
else else
ObjReal(this.doubleValue - other.toDouble()) 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) { if (other is ObjInt) {
ObjInt(this.value * other.value) ObjInt(this.value * other.value)
} else ObjReal(this.value * other.toDouble()) } 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) if (other is ObjInt)
ObjInt(this.value / other.value) ObjInt(this.value / other.value)
else ObjReal(this.value / other.toDouble()) 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) if (other is ObjInt)
ObjInt(this.value % other.value) ObjInt(this.value % other.value)
else ObjReal(this.value.toDouble() % other.toDouble()) 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 * We are by-value type ([byValueCopy] is implemented) so we can do in-place
* assignment * assignment
*/ */
override suspend fun assign(context: Context, other: Obj): Obj? { override suspend fun assign(scope: Scope, other: Obj): Obj? {
return if (other is ObjInt) { return if (other is ObjInt) {
value = other.value value = other.value
this this
} else null } else null
} }
override suspend fun toKotlin(context: Context): Any { override suspend fun toKotlin(scope: Scope): Any {
return value return value
} }

View File

@ -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. * 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. * It is, though, trivially convertible to Kotlin's Flow.
*/ */
fun Obj.toFlow(context: Context): Flow<Obj> = flow { fun Obj.toFlow(scope: Scope): Flow<Obj> = flow {
val iterator = invokeInstanceMethod(context, "iterator") val iterator = invokeInstanceMethod(scope, "iterator")
val hasNext = iterator.getInstanceMethod(context, "hasNext") val hasNext = iterator.getInstanceMethod(scope, "hasNext")
val next = iterator.getInstanceMethod(context, "next") val next = iterator.getInstanceMethod(scope, "next")
while (hasNext.invoke(context, iterator).toBool()) { while (hasNext.invoke(scope, iterator).toBool()) {
emit(next.invoke(context, iterator)) emit(next.invoke(scope, iterator))
} }
} }

View File

@ -11,7 +11,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
list.joinToString(separator = ", ") { it.inspect() } 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) { return when (index) {
is ObjInt -> { is ObjInt -> {
list[index.toInt()] 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 val i = index
list[i] = newValue 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 if (other !is ObjList) return -2
val mySize = list.size val mySize = list.size
val otherSize = other.list.size val otherSize = other.list.size
val commonSize = minOf(mySize, otherSize) val commonSize = minOf(mySize, otherSize)
for (i in 0..<commonSize) { for (i in 0..<commonSize) {
if (list[i].compareTo(context, other.list[i]) != 0) { if (list[i].compareTo(scope, other.list[i]) != 0) {
return list[i].compareTo(context, other.list[i]) return list[i].compareTo(scope, other.list[i])
} }
} }
// equal so far, longer is greater: // 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 { when {
other is ObjList -> other is ObjList ->
ObjList((list + other.list).toMutableList()) ObjList((list + other.list).toMutableList())
other.isInstanceOf(ObjIterable) -> { other.isInstanceOf(ObjIterable) -> {
val l = other.callMethod<ObjList>(context, "toList") val l = other.callMethod<ObjList>(scope, "toList")
ObjList((list + l.list).toMutableList()) ObjList((list + l.list).toMutableList())
} }
else -> 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 // optimization
if (other is ObjList) { if (other is ObjList) {
list += other.list list += other.list
return this return this
} }
if (other.isInstanceOf(ObjIterable)) { if (other.isInstanceOf(ObjIterable)) {
val otherList = other.invokeInstanceMethod(context, "toList") as ObjList val otherList = other.invokeInstanceMethod(scope, "toList") as ObjList
list += otherList.list list += otherList.list
} else } else
list += other list += other
return this return this
} }
override suspend fun contains(context: Context, other: Obj): Boolean { override suspend fun contains(scope: Scope, other: Obj): Boolean {
return list.contains(other) return list.contains(other)
} }
override val objClass: ObjClass override val objClass: ObjClass
get() = type get() = type
override suspend fun toKotlin(context: Context): Any { override suspend fun toKotlin(scope: Scope): Any {
return list.map { it.toKotlin(context) } return list.map { it.toKotlin(scope) }
} }
override fun hashCode(): Int { override fun hashCode(): Int {

View File

@ -2,17 +2,17 @@ package net.sergeych.lyng
class ObjMapEntry(val key: Obj, val value: Obj) : Obj() { 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 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 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 0 -> key
1 -> value 1 -> value
else -> context.raiseIndexOutOfBounds() else -> scope.raiseIndexOutOfBounds()
} }
override fun toString(): String { override fun toString(): String {
@ -23,8 +23,8 @@ class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
companion object { companion object {
val type = object : ObjClass("MapEntry", ObjArray) { val type = object : ObjClass("MapEntry", ObjArray) {
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
return ObjMapEntry(context.requiredArg<Obj>(0), context.requiredArg<Obj>(1)) return ObjMapEntry(scope.requiredArg<Obj>(0), scope.requiredArg<Obj>(1))
} }
}.apply { }.apply {
addFn("key") { thisAs<ObjMapEntry>().key } addFn("key") { thisAs<ObjMapEntry>().key }
@ -38,14 +38,14 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
override val objClass = type override val objClass = type
override suspend fun getAt(context: Context, index: Obj): Obj = override suspend fun getAt(scope: Scope, index: Obj): Obj =
map.getOrElse(index) { context.raiseNoSuchElement() } 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 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 if( other is ObjMap && other.map == map) return 0
return -1 return -1
} }
@ -53,30 +53,30 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
companion object { 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>() val map = mutableMapOf<Obj, Obj>()
if (list.isEmpty()) return map if (list.isEmpty()) return map
val first = list.first() val first = list.first()
if (first.isInstanceOf(ObjArray)) { if (first.isInstanceOf(ObjArray)) {
if (first.invokeInstanceMethod(context, "size").toInt() != 2) if (first.invokeInstanceMethod(scope, "size").toInt() != 2)
context.raiseIllegalArgument( scope.raiseIllegalArgument(
"list to construct map entry should exactly be 2 element Array like [key,value], got $list" "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 { 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 return map
} }
val type = object : ObjClass("Map", ObjCollection) { val type = object : ObjClass("Map", ObjCollection) {
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
return ObjMap(listToMap(context, context.args.list)) return ObjMap(listToMap(scope, scope.args.list))
} }
}.apply { }.apply {
addFn("getOrNull") { addFn("getOrNull") {

View File

@ -15,10 +15,10 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
return result.toString() return result.toString()
} }
suspend fun containsRange(context: Context, other: ObjRange): Boolean { suspend fun containsRange(scope: Scope, other: ObjRange): Boolean {
if (start != null) { if (start != null) {
// our start is not -∞ so other start should be GTE or is not contained: // 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) { if (end != null) {
// same with the end: if it is open, it can't be contained in ours: // 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: // both exists, now there could be 4 cases:
return when { return when {
other.isEndInclusive && isEndInclusive -> other.isEndInclusive && isEndInclusive ->
end.compareTo(context, other.end) >= 0 end.compareTo(scope, other.end) >= 0
!other.isEndInclusive && !isEndInclusive -> !other.isEndInclusive && !isEndInclusive ->
end.compareTo(context, other.end) >= 0 end.compareTo(scope, other.end) >= 0
other.isEndInclusive && !isEndInclusive -> other.isEndInclusive && !isEndInclusive ->
end.compareTo(context, other.end) > 0 end.compareTo(scope, other.end) > 0
!other.isEndInclusive && isEndInclusive -> !other.isEndInclusive && isEndInclusive ->
end.compareTo(context, other.end) >= 0 end.compareTo(scope, other.end) >= 0
else -> throw IllegalStateException("unknown comparison") else -> throw IllegalStateException("unknown comparison")
} }
@ -43,17 +43,17 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
return true return true
} }
override suspend fun contains(context: Context, other: Obj): Boolean { override suspend fun contains(scope: Scope, other: Obj): Boolean {
if (other is ObjRange) if (other is ObjRange)
return containsRange(context, other) return containsRange(scope, other)
if (start == null && end == null) return true if (start == null && end == null) return true
if (start != null) { if (start != null) {
if (start.compareTo(context, other) > 0) return false if (start.compareTo(scope, other) > 0) return false
} }
if (end != null) { if (end != null) {
val cmp = end.compareTo(context, other) val cmp = end.compareTo(scope, other)
if (isEndInclusive && cmp < 0 || !isEndInclusive && cmp <= 0) return false if (isEndInclusive && cmp < 0 || !isEndInclusive && cmp <= 0) return false
} }
return true return true
@ -67,7 +67,7 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
start is ObjChar && end is ObjChar 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 { return (other as? ObjRange)?.let {
if( start == other.start && end == other.end ) 0 else -1 if( start == other.start && end == other.end ) 0 else -1
} }

View File

@ -8,7 +8,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
override val objClass: ObjClass = type override val objClass: ObjClass = type
fun Context.init() { fun Scope.init() {
if (self.start == null || self.end == null) if (self.start == null || self.end == null)
raiseError("next is only available for finite ranges") raiseError("next is only available for finite ranges")
isCharRange = self.isCharRange isCharRange = self.isCharRange
@ -24,7 +24,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
fun hasNext(): Boolean = nextIndex < lastIndex fun hasNext(): Boolean = nextIndex < lastIndex
fun next(context: Context): Obj = fun next(scope: Scope): Obj =
if (nextIndex < lastIndex) { if (nextIndex < lastIndex) {
val x = if (self.isEndInclusive) val x = if (self.isEndInclusive)
self.start!!.toLong() + nextIndex++ self.start!!.toLong() + nextIndex++
@ -33,7 +33,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
if( isCharRange ) ObjChar(x.toInt().toChar()) else ObjInt(x) if( isCharRange ) ObjChar(x.toInt().toChar()) else ObjInt(x)
} }
else { else {
context.raiseError(ObjIterationFinishedException(context)) scope.raiseError(ObjIterationFinishedException(scope))
} }
companion object { companion object {

View File

@ -14,7 +14,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
override fun byValueCopy(): Obj = ObjReal(value) 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 if (other !is Numeric) return -2
return value.compareTo(other.doubleValue) return value.compareTo(other.doubleValue)
} }
@ -25,25 +25,25 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
return value.hashCode() 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()) 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()) 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()) 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()) 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()) ObjReal(this.value % other.toDouble())
/** /**
* Returns unboxed Double value * Returns unboxed Double value
*/ */
override suspend fun toKotlin(context: Context): Any { override suspend fun toKotlin(scope: Scope): Any {
return value return value
} }

View File

@ -4,11 +4,11 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
override val objClass = type 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) return set.contains(other)
} }
override suspend fun plus(context: Context, other: Obj): Obj { override suspend fun plus(scope: Scope, other: Obj): Obj {
return ObjSet( return ObjSet(
if (other is ObjSet) if (other is ObjSet)
(set + other.set).toMutableSet() (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) { when (other) {
is ObjSet -> { is ObjSet -> {
set += other.set set += other.set
@ -29,9 +29,9 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
else -> { else -> {
if (other.isInstanceOf(ObjIterable)) { if (other.isInstanceOf(ObjIterable)) {
val i = other.invokeInstanceMethod(context, "iterable") val i = other.invokeInstanceMethod(scope, "iterable")
while (i.invokeInstanceMethod(context, "hasNext").toBool()) { while (i.invokeInstanceMethod(scope, "hasNext").toBool()) {
set += i.invokeInstanceMethod(context, "next") set += i.invokeInstanceMethod(scope, "next")
} }
} }
set += other set += other
@ -40,16 +40,16 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
return this 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) { return if (other is ObjSet) {
ObjSet(set.intersect(other.set).toMutableSet()) ObjSet(set.intersect(other.set).toMutableSet())
} else } 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) 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()) return ObjSet(set.minus(other.set).toMutableSet())
} }
@ -57,7 +57,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
return "Set(${set.joinToString(", ")})" 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 return if (other !is ObjSet) -1
else { else {
if (set == other.set) 0 if (set == other.set) 0
@ -69,8 +69,8 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
val type = object : ObjClass("Set", ObjCollection) { val type = object : ObjClass("Set", ObjCollection) {
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
return ObjSet(context.args.list.toMutableSet()) return ObjSet(scope.args.list.toMutableSet())
} }
}.apply { }.apply {
addFn("size") { addFn("size") {

View File

@ -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 if (other !is ObjString) return -2
return this.value.compareTo(other.value) return this.value.compareTo(other.value)
} }
@ -32,11 +32,11 @@ data class ObjString(val value: String) : Obj() {
override val objClass: ObjClass override val objClass: ObjClass
get() = type 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) 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 ObjInt ) return ObjChar(value[index.toInt()])
if( index is ObjRange ) { if( index is ObjRange ) {
val start = if(index.start == null || index.start.isNull) 0 else index.start.toInt() 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)) 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 { override fun hashCode(): Int {
return value.hashCode() return value.hashCode()
} }
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
return ObjString(this.value.sprintf(*context.args.toKotlinList(context).toTypedArray())) 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) return if (other is ObjString)
value.contains(other.value) value.contains(other.value)
else if (other is ObjChar) else if (other is ObjChar)
value.contains(other.value) 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 { override fun equals(other: Any?): Boolean {

View File

@ -1,17 +1,17 @@
package net.sergeych.lyng package net.sergeych.lyng
open class Context( open class Scope(
val parent: Context?, val parent: Scope?,
val args: Arguments = Arguments.EMPTY, val args: Arguments = Arguments.EMPTY,
var pos: Pos = Pos.builtIn, var pos: Pos = Pos.builtIn,
var thisObj: Obj = ObjVoid, var thisObj: Obj = ObjVoid,
var skipContextCreation: Boolean = false, var skipScopeCreation: Boolean = false,
) { ) {
constructor( constructor(
args: Arguments = Arguments.EMPTY, args: Arguments = Arguments.EMPTY,
pos: Pos = Pos.builtIn, 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") fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
@ -75,13 +75,13 @@ open class Context(
?: thisObj.objClass.getInstanceMemberOrNull(name) ?: thisObj.objClass.getInstanceMemberOrNull(name)
} }
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context = fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
Context(this, args, pos, newThisObj ?: thisObj) Scope(this, args, pos, newThisObj ?: thisObj)
fun copy(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context = fun copy(args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
Context(this, args, pos, newThisObj ?: thisObj) Scope(this, args, pos, newThisObj ?: thisObj)
fun copy() = Context(this, args, pos, thisObj) fun copy() = Scope(this, args, pos, thisObj)
fun addItem( fun addItem(
name: String, name: String,
@ -97,18 +97,18 @@ open class Context(
return ns.objClass 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) { addFn<ObjVoid>(*names) {
fn(this) fn(this)
ObjVoid 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() { val newFn = object : Statement() {
override val pos: Pos = Pos.builtIn 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) { for (name in names) {

View File

@ -9,18 +9,18 @@ class Script(
// private val catchReturn: Boolean = false, // private val catchReturn: Boolean = false,
) : Statement() { ) : Statement() {
override suspend fun execute(context: Context): Obj { override suspend fun execute(scope: Scope): Obj {
var lastResult: Obj = ObjVoid var lastResult: Obj = ObjVoid
for (s in statements) { for (s in statements) {
lastResult = s.execute(context) lastResult = s.execute(scope)
} }
return lastResult return lastResult
} }
suspend fun execute() = execute(defaultContext.copy(pos = pos)) suspend fun execute() = execute(defaultScope.copy(pos = pos))
companion object { companion object {
val defaultContext: Context = Context().apply { val defaultScope: Scope = Scope().apply {
ObjException.addExceptionsToContext(this) ObjException.addExceptionsToContext(this)
addFn("println") { addFn("println") {
for ((i, a) in args.withIndex()) { for ((i, a) in args.withIndex()) {

View File

@ -12,4 +12,4 @@ open class ScriptError(val pos: Pos, val errorMessage: String,cause: Throwable?=
cause cause
) )
class ExecutionError(val errorObject: ObjException) : ScriptError(errorObject.context.pos, errorObject.message) class ExecutionError(val errorObject: ObjException) : ScriptError(errorObject.scope.pos, errorObject.message)

View File

@ -18,14 +18,14 @@ abstract class Statement(
override val objClass: ObjClass = type override val objClass: ObjClass = type
abstract val pos: Pos 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") throw UnsupportedOperationException("not comparable")
} }
override suspend fun callOn(context: Context): Obj { override suspend fun callOn(scope: Scope): Obj {
return execute(context) return execute(scope)
} }
override fun toString(): String = "Callable@${this.hashCode()}" override fun toString(): String = "Callable@${this.hashCode()}"
@ -34,7 +34,7 @@ abstract class Statement(
val type = ObjClass("Callable") 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()) 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) { object : Statement(isStaticConst, isConst) {
override val pos: Pos = pos 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) { object : Statement(isStaticConst, isConst) {
override val pos: Pos = Pos.builtIn override val pos: Pos = Pos.builtIn
override suspend fun execute(context: Context): Obj = f(context) override suspend fun execute(scope: Scope): Obj = f(scope)
} }

View File

@ -173,55 +173,55 @@ class ScriptTest {
@Test @Test
fun varsAndConstsTest() = runTest { fun varsAndConstsTest() = runTest {
val context = Context(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
assertEquals( assertEquals(
ObjInt(3L), context.eval( ObjInt(3L), scope.eval(
""" """
val a = 17 val a = 17
var b = 3 var b = 3
""".trimIndent() """.trimIndent()
) )
) )
assertEquals(17, context.eval("a").toInt()) assertEquals(17, scope.eval("a").toInt())
assertEquals(20, context.eval("b + a").toInt()) assertEquals(20, scope.eval("b + a").toInt())
assertFailsWith<ScriptError> { assertFailsWith<ScriptError> {
context.eval("a = 10") scope.eval("a = 10")
} }
assertEquals(17, context.eval("a").toInt()) assertEquals(17, scope.eval("a").toInt())
assertEquals(5, context.eval("b = a - 7 - 5").toInt()) assertEquals(5, scope.eval("b = a - 7 - 5").toInt())
assertEquals(5, context.eval("b").toInt()) assertEquals(5, scope.eval("b").toInt())
} }
@Test @Test
fun functionTest() = runTest { fun functionTest() = runTest {
val context = Context(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
context.eval( scope.eval(
""" """
fun foo(a, b) { fun foo(a, b) {
a + b a + b
} }
""".trimIndent() """.trimIndent()
) )
assertEquals(17, context.eval("foo(3,14)").toInt()) assertEquals(17, scope.eval("foo(3,14)").toInt())
assertFailsWith<ScriptError> { 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) { fn bar(a, b=10) {
a + b + 1 a + b + 1
} }
""".trimIndent() """.trimIndent()
) )
assertEquals(10, context.eval("bar(3, 6)").toInt()) assertEquals(10, scope.eval("bar(3, 6)").toInt())
assertEquals(14, context.eval("bar(3)").toInt()) assertEquals(14, scope.eval("bar(3)").toInt())
} }
@Test @Test
fun simpleClosureTest() = runTest { fun simpleClosureTest() = runTest {
val context = Context(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
context.eval( scope.eval(
""" """
var global = 10 var global = 10
@ -230,16 +230,16 @@ class ScriptTest {
} }
""".trimIndent() """.trimIndent()
) )
assertEquals(27, context.eval("foo(3,14)").toInt()) assertEquals(27, scope.eval("foo(3,14)").toInt())
context.eval("global = 20") scope.eval("global = 20")
assertEquals(37, context.eval("foo(3,14)").toInt()) assertEquals(37, scope.eval("foo(3,14)").toInt())
} }
@Test @Test
fun nullAndVoidTest() = runTest { fun nullAndVoidTest() = runTest {
val context = Context(pos = Pos.builtIn) val scope = Scope(pos = Pos.builtIn)
assertEquals(ObjVoid, context.eval("void")) assertEquals(ObjVoid, scope.eval("void"))
assertEquals(ObjNull, context.eval("null")) assertEquals(ObjNull, scope.eval("null"))
} }
@Test @Test
@ -346,8 +346,8 @@ class ScriptTest {
@Test @Test
fun ifTest() = runTest { fun ifTest() = runTest {
// if - single line // if - single line
var context = Context(pos = Pos.builtIn) var scope = Scope(pos = Pos.builtIn)
context.eval( scope.eval(
""" """
fn test1(n) { fn test1(n) {
var result = "more" var result = "more"
@ -357,12 +357,12 @@ class ScriptTest {
} }
""".trimIndent() """.trimIndent()
) )
assertEquals("enough", context.eval("test1(11)").toString()) assertEquals("enough", scope.eval("test1(11)").toString())
assertEquals("more", context.eval("test1(1)").toString()) assertEquals("more", scope.eval("test1(1)").toString())
// if - multiline (block) // if - multiline (block)
context = Context(pos = Pos.builtIn) scope = Scope(pos = Pos.builtIn)
context.eval( scope.eval(
""" """
fn test1(n) { fn test1(n) {
var prefix = "answer: " var prefix = "answer: "
@ -376,12 +376,12 @@ class ScriptTest {
} }
""".trimIndent() """.trimIndent()
) )
assertEquals("answer: enough", context.eval("test1(11)").toString()) assertEquals("answer: enough", scope.eval("test1(11)").toString())
assertEquals("answer: more", context.eval("test1(1)").toString()) assertEquals("answer: more", scope.eval("test1(1)").toString())
// else single line1 // else single line1
context = Context(pos = Pos.builtIn) scope = Scope(pos = Pos.builtIn)
context.eval( scope.eval(
""" """
fn test1(n) { fn test1(n) {
if( n >= 10 ) if( n >= 10 )
@ -391,12 +391,12 @@ class ScriptTest {
} }
""".trimIndent() """.trimIndent()
) )
assertEquals("enough", context.eval("test1(11)").toString()) assertEquals("enough", scope.eval("test1(11)").toString())
assertEquals("more", context.eval("test1(1)").toString()) assertEquals("more", scope.eval("test1(1)").toString())
// if/else with blocks // if/else with blocks
context = Context(pos = Pos.builtIn) scope = Scope(pos = Pos.builtIn)
context.eval( scope.eval(
""" """
fn test1(n) { fn test1(n) {
if( n > 20 ) { if( n > 20 ) {
@ -410,9 +410,9 @@ class ScriptTest {
} }
""".trimIndent() """.trimIndent()
) )
assertEquals("enough", context.eval("test1(11)").toString()) assertEquals("enough", scope.eval("test1(11)").toString())
assertEquals("more", context.eval("test1(1)").toString()) assertEquals("more", scope.eval("test1(1)").toString())
assertEquals("too much", context.eval("test1(100)").toString()) assertEquals("too much", scope.eval("test1(100)").toString())
} }
@Test @Test
@ -512,13 +512,13 @@ class ScriptTest {
ArgsDeclaration.Item("c"), ArgsDeclaration.Item("c"),
), ttEnd ), 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) pa.assignToContext(c)
assertEquals(ObjInt(1), c["a"]?.value) assertEquals(ObjInt(1), c["a"]?.value)
assertEquals(ObjInt(2), c["b"]?.value) assertEquals(ObjInt(2), c["b"]?.value)
assertEquals(ObjInt(3), c["c"]?.value) assertEquals(ObjInt(3), c["c"]?.value)
// less args: error // 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> { assertFailsWith<ScriptError> {
pa.assignToContext(c) pa.assignToContext(c)
} }
@ -535,7 +535,7 @@ class ScriptTest {
assertEquals(ObjInt(2), c["b"]?.value) assertEquals(ObjInt(2), c["b"]?.value)
assertEquals(ObjInt(100), c["c"]?.value) assertEquals(ObjInt(100), c["c"]?.value)
// enough args. default value is ignored: // 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) pa.assignToContext(c)
assertEquals(ObjInt(10), c["a"]?.value) assertEquals(ObjInt(10), c["a"]?.value)
assertEquals(ObjInt(2), c["b"]?.value) assertEquals(ObjInt(2), c["b"]?.value)
@ -553,17 +553,17 @@ class ScriptTest {
ArgsDeclaration.Item("b", isEllipsis = true), ArgsDeclaration.Item("b", isEllipsis = true),
), ttEnd ), 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) pa.assignToContext(c)
c.eval("assert( a == 1 ); println(b)") c.eval("assert( a == 1 ); println(b)")
c.eval("assert( b == [2,3] )") 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) pa.assignToContext(c)
c.eval("assertEquals( a, 1 ); println(b)") c.eval("assertEquals( a, 1 ); println(b)")
c.eval("assertEquals( b, [2] )") 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) pa.assignToContext(c)
c.eval("assert( a == 1 ); println(b)") c.eval("assert( a == 1 ); println(b)")
c.eval("assert( b == [] )") c.eval("assert( b == [] )")
@ -579,25 +579,25 @@ class ScriptTest {
ArgsDeclaration.Item("c"), ArgsDeclaration.Item("c"),
), ttEnd ), 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) pa.assignToContext(c)
c.eval("assertEquals( a,[0,1] )") c.eval("assertEquals( a,[0,1] )")
c.eval("assertEquals( b, 2 )") c.eval("assertEquals( b, 2 )")
c.eval("assertEquals( c, 3 )") 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) pa.assignToContext(c)
c.eval("assertEquals( a,[1] )") c.eval("assertEquals( a,[1] )")
c.eval("assertEquals( b, 2 )") c.eval("assertEquals( b, 2 )")
c.eval("assertEquals( c, 3 )") 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) pa.assignToContext(c)
c.eval("assertEquals( a,[] )") c.eval("assertEquals( a,[] )")
c.eval("assertEquals( b, 2 )") c.eval("assertEquals( b, 2 )")
c.eval("assertEquals( c, 3 )") 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> { assertFailsWith<ExecutionError> {
pa.assignToContext(c) pa.assignToContext(c)
} }
@ -614,28 +614,28 @@ class ScriptTest {
ArgsDeclaration.Item("c"), ArgsDeclaration.Item("c"),
), ttEnd ), 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) pa.assignToContext(c)
c.eval("assertEquals( i, -1 )") c.eval("assertEquals( i, -1 )")
c.eval("assertEquals( a,[0,1] )") c.eval("assertEquals( a,[0,1] )")
c.eval("assertEquals( b, 2 )") c.eval("assertEquals( b, 2 )")
c.eval("assertEquals( c, 3 )") 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) pa.assignToContext(c)
c.eval("assertEquals( i, 0 )") c.eval("assertEquals( i, 0 )")
c.eval("assertEquals( a,[1] )") c.eval("assertEquals( a,[1] )")
c.eval("assertEquals( b, 2 )") c.eval("assertEquals( b, 2 )")
c.eval("assertEquals( c, 3 )") 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) pa.assignToContext(c)
c.eval("assertEquals( i, 1)") c.eval("assertEquals( i, 1)")
c.eval("assertEquals( a,[] )") c.eval("assertEquals( a,[] )")
c.eval("assertEquals( b, 2 )") c.eval("assertEquals( b, 2 )")
c.eval("assertEquals( c, 3 )") 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> { assertFailsWith<ExecutionError> {
pa.assignToContext(c) pa.assignToContext(c)
} }
@ -756,7 +756,7 @@ class ScriptTest {
@Test @Test
fun testIncr() = runTest { fun testIncr() = runTest {
val c = Context() val c = Scope()
c.eval("var x = 10") c.eval("var x = 10")
assertEquals(10, c.eval("x++").toInt()) assertEquals(10, c.eval("x++").toInt())
assertEquals(11, c.eval("x++").toInt()) assertEquals(11, c.eval("x++").toInt())
@ -768,7 +768,7 @@ class ScriptTest {
@Test @Test
fun testDecr() = runTest { fun testDecr() = runTest {
val c = Context() val c = Scope()
c.eval("var x = 9") c.eval("var x = 9")
assertEquals(9, c.eval("x--").toInt()) assertEquals(9, c.eval("x--").toInt())
assertEquals(8, c.eval("x--").toInt()) assertEquals(8, c.eval("x--").toInt())
@ -779,7 +779,7 @@ class ScriptTest {
@Test @Test
fun testDecrIncr() = runTest { fun testDecrIncr() = runTest {
val c = Context() val c = Scope()
c.eval("var x = 9") c.eval("var x = 9")
assertEquals(9, c.eval("x++").toInt()) assertEquals(9, c.eval("x++").toInt())
assertEquals(10, c.eval("x++").toInt()) assertEquals(10, c.eval("x++").toInt())
@ -793,7 +793,7 @@ class ScriptTest {
@Test @Test
fun testDecrIncr2() = runTest { fun testDecrIncr2() = runTest {
val c = Context() val c = Scope()
c.eval("var x = 9") c.eval("var x = 9")
assertEquals(9, c.eval("x--").toInt()) assertEquals(9, c.eval("x--").toInt())
assertEquals(8, c.eval("x--").toInt()) assertEquals(8, c.eval("x--").toInt())
@ -810,7 +810,7 @@ class ScriptTest {
@Test @Test
fun testDecrIncr3() = runTest { fun testDecrIncr3() = runTest {
val c = Context() val c = Scope()
c.eval("var x = 9") c.eval("var x = 9")
assertEquals(9, c.eval("x++").toInt()) assertEquals(9, c.eval("x++").toInt())
assertEquals(10, c.eval("x++").toInt()) assertEquals(10, c.eval("x++").toInt())
@ -822,7 +822,7 @@ class ScriptTest {
@Test @Test
fun testIncrAndDecr() = runTest { fun testIncrAndDecr() = runTest {
val c = Context() val c = Scope()
assertEquals( assertEquals(
"8", c.eval( "8", c.eval(
""" """
@ -857,7 +857,7 @@ class ScriptTest {
@Test @Test
fun testAssign1() = runTest { fun testAssign1() = runTest {
assertEquals(10, eval("var x = 5; x=10; x").toInt()) assertEquals(10, eval("var x = 5; x=10; x").toInt())
val ctx = Context() val ctx = Scope()
ctx.eval( ctx.eval(
""" """
var a = 1 var a = 1
@ -871,7 +871,7 @@ class ScriptTest {
@Test @Test
fun testAssign2() = runTest { fun testAssign2() = runTest {
val ctx = Context() val ctx = Scope()
ctx.eval("var x = 10") ctx.eval("var x = 10")
assertEquals(14, ctx.eval("x += 4").toInt()) assertEquals(14, ctx.eval("x += 4").toInt())
assertEquals(14, ctx.eval("x").toInt()) assertEquals(14, ctx.eval("x").toInt())
@ -889,7 +889,7 @@ class ScriptTest {
@Test @Test
fun testVals() = runTest { fun testVals() = runTest {
val cxt = Context() val cxt = Scope()
cxt.eval("val x = 11") cxt.eval("val x = 11")
assertEquals(11, cxt.eval("x").toInt()) assertEquals(11, cxt.eval("x").toInt())
assertFails { cxt.eval("x = 12") } assertFails { cxt.eval("x = 12") }
@ -1436,7 +1436,7 @@ class ScriptTest {
@Test @Test
fun testSimpleStruct() = runTest { fun testSimpleStruct() = runTest {
val c = Context() val c = Scope()
c.eval( c.eval(
""" """
class Point(x,y) class Point(x,y)
@ -1457,7 +1457,7 @@ class ScriptTest {
@Test @Test
fun testNonAssignalbeFieldInStruct() = runTest { fun testNonAssignalbeFieldInStruct() = runTest {
val c = Context() val c = Scope()
c.eval( c.eval(
""" """
class Point(x,y) class Point(x,y)
@ -1474,7 +1474,7 @@ class ScriptTest {
@Test @Test
fun testStructBodyVal() = runTest { fun testStructBodyVal() = runTest {
val c = Context() val c = Scope()
c.eval( c.eval(
""" """
class Point(x,y) { class Point(x,y) {
@ -1496,7 +1496,7 @@ class ScriptTest {
@Test @Test
fun testStructBodyFun() = runTest { fun testStructBodyFun() = runTest {
val c = Context() val c = Scope()
c.eval( c.eval(
""" """
class Point(x,y) { class Point(x,y) {
@ -1516,7 +1516,7 @@ class ScriptTest {
@Test @Test
fun testPrivateConstructorParams() = runTest { fun testPrivateConstructorParams() = runTest {
val c = Context() val c = Scope()
c.eval( c.eval(
""" """
class Point(private var x,y) class Point(private var x,y)
@ -1871,7 +1871,7 @@ class ScriptTest {
@Test @Test
fun testTryFinally() = runTest { fun testTryFinally() = runTest {
val c = Context() val c = Scope()
assertFails { assertFails {
c.eval( c.eval(
""" """
@ -1894,7 +1894,7 @@ class ScriptTest {
@Test @Test
fun testThrowFromKotlin() = runTest { fun testThrowFromKotlin() = runTest {
val c = Context() val c = Scope()
c.addFn("callThrow") { c.addFn("callThrow") {
raiseIllegalArgument("fromKotlin") raiseIllegalArgument("fromKotlin")
} }
@ -2316,7 +2316,7 @@ class ScriptTest {
@Test @Test
fun testToFlow() = runTest() { fun testToFlow() = runTest() {
val c = Context() val c = Scope()
val arr = c.eval("[1,2,3]") val arr = c.eval("[1,2,3]")
// array is iterable so we can: // array is iterable so we can:
assertEquals(listOf(1,2,3), arr.toFlow(c).map { it.toInt() }.toList()) assertEquals(listOf(1,2,3), arr.toFlow(c).map { it.toInt() }.toList())

View File

@ -3,8 +3,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Context
import net.sergeych.lyng.ObjVoid import net.sergeych.lyng.ObjVoid
import net.sergeych.lyng.Scope
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Files.readAllLines import java.nio.file.Files.readAllLines
import java.nio.file.Paths import java.nio.file.Paths
@ -158,10 +158,10 @@ fun parseDocTests(fileName: String, bookMode: Boolean = false): Flow<DocTest> =
} }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
suspend fun DocTest.test(context: Context = Context()) { suspend fun DocTest.test(scope: Scope = Scope()) {
val collectedOutput = StringBuilder() val collectedOutput = StringBuilder()
val currentTest = this val currentTest = this
context.apply { scope.apply {
addFn("println") { addFn("println") {
if( bookMode ) { if( bookMode ) {
println("${currentTest.fileNamePart}:${currentTest.line}> ${args.joinToString(" "){it.asStr.value}}") 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 var error: Throwable? = null
val result = try { val result = try {
context.eval(code) scope.eval(code)
} catch (e: Throwable) { } catch (e: Throwable) {
error = e error = e
null null
@ -204,10 +204,10 @@ suspend fun DocTest.test(context: Context = Context()) {
} }
suspend fun runDocTests(fileName: String, bookMode: Boolean = false) { suspend fun runDocTests(fileName: String, bookMode: Boolean = false) {
val bookContext = Context() val bookScope = Scope()
var count = 0 var count = 0
parseDocTests(fileName, bookMode).collect { dt -> parseDocTests(fileName, bookMode).collect { dt ->
if (bookMode) dt.test(bookContext) if (bookMode) dt.test(bookScope)
else dt.test() else dt.test()
count++ count++
} }

View File

@ -2,7 +2,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import net.sergeych.lyng.Context import net.sergeych.lyng.Scope
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.io.path.extension import kotlin.io.path.extension
@ -13,7 +13,7 @@ suspend fun executeSampleTests(fileName: String) {
Files.readString(Paths.get(fileName)) Files.readString(Paths.get(fileName))
} }
runBlocking { runBlocking {
val c = Context() val c = Scope()
val start = Clock.System.now() val start = Clock.System.now()
c.eval(sample) c.eval(sample)
val time = Clock.System.now() - start val time = Clock.System.now() - start