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,14 +75,16 @@ 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 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()
@ -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?

View File

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

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
*/
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")
}
}

View File

@ -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 {

View File

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

View File

@ -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)

View File

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

View File

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

View File

@ -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()

View File

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

View File

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

View File

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

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.
* 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))
}
}

View File

@ -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 {

View File

@ -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") {

View File

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

View File

@ -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 {

View File

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

View File

@ -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") {

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
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 {

View File

@ -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) {

View File

@ -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()) {

View File

@ -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)

View File

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

View File

@ -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())

View File

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

View File

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