package net.sergeych.ling 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)) fun raiseIndexOutOfBounds(message: String = "Index out of bounds"): Nothing = raiseError(ObjIndexOutOfBoundsError(this, message)) 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 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 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 thisAs(): T = (thisObj as? T) ?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}") private val objects = mutableMapOf() 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): ObjNamespace = (objects.getOrPut(name) { StoredObj( ObjNamespace(name), isMutable = false ) }.value as ObjNamespace) inline fun addVoidFn(vararg names: String, crossinline fn: suspend Context.() -> Unit) { addFn(*names) { fn(this) ObjVoid } } inline fun 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 }