109 lines
3.5 KiB
Kotlin

package net.sergeych.lyng
class Context(
val parent: Context?,
val args: Arguments = Arguments.EMPTY,
var pos: Pos = Pos.builtIn,
val thisObj: Obj = ObjVoid
) {
constructor(
args: Arguments = Arguments.EMPTY,
pos: Pos = Pos.builtIn,
)
: this(Script.defaultContext, args, pos)
fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
@Suppress("unused")
fun raiseNPE(): Nothing = raiseError(ObjNullPointerError(this))
@Suppress("unused")
fun raiseIndexOutOfBounds(message: String = "Index out of bounds"): Nothing =
raiseError(ObjIndexOutOfBoundsError(this, message))
@Suppress("unused")
fun raiseArgumentError(message: String = "Illegal argument error"): Nothing =
raiseError(ObjIllegalArgumentError(this, message))
fun raiseClassCastError(msg: String): Nothing = raiseError(ObjClassCastError(this, msg))
fun raiseError(message: String): Nothing {
throw ExecutionError(ObjError(this, message))
}
fun raiseError(obj: ObjError): Nothing {
throw ExecutionError(obj)
}
inline fun <reified T: Obj>requiredArg(index: Int): T {
if( args.list.size <= index ) raiseError("Expected at least ${index+1} argument, got ${args.list.size}")
return (args.list[index].value as? T)
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index].value::class.simpleName}")
}
inline fun <reified T: Obj>requireOnlyArg(): T {
if( args.list.size != 1 ) raiseError("Expected exactly 1 argument, got ${args.list.size}")
return requiredArg(0)
}
@Suppress("unused")
fun requireExactCount(count: Int) {
if( args.list.size != count ) {
raiseError("Expected exactly $count arguments, got ${args.list.size}")
}
}
inline fun <reified T: Obj>thisAs(): T = (thisObj as? T)
?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
private val objects = mutableMapOf<String, StoredObj>()
operator fun get(name: String): StoredObj? =
objects[name]
?: parent?.get(name)
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY,newThisObj: Obj? = null): Context =
Context(this, args, pos, newThisObj ?: thisObj)
fun addItem(name: String, isMutable: Boolean, value: Obj?): StoredObj {
return StoredObj(value, isMutable).also { objects.put(name, it) }
}
fun getOrCreateNamespace(name: String): ObjClass {
val ns = objects.getOrPut(name) { StoredObj(ObjNamespace(name), isMutable = false) }.value
return ns!!.objClass
}
inline fun addVoidFn(vararg names: String, crossinline fn: suspend Context.() -> Unit) {
addFn<ObjVoid>(*names) {
fn(this)
ObjVoid
}
}
inline fun <reified T: Obj> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
val newFn = object : Statement() {
override val pos: Pos = Pos.builtIn
override suspend fun execute(context: Context): Obj = context.fn()
}
for (name in names) {
addItem(
name,
false,
newFn
)
}
}
fun addConst(name: String,value: Obj) = addItem(name, false, value)
suspend fun eval(code: String): Obj =
Compiler().compile(code.toSource()).execute(this)
fun containsLocal(name: String): Boolean = name in objects
}