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