package ru.dbotthepony.kstarbound.lua import com.google.gson.JsonElement import it.unimi.dsi.fastutil.objects.ObjectIterators import org.classdump.luna.LuaRuntimeException import org.classdump.luna.Table import org.classdump.luna.TableFactory import org.classdump.luna.impl.NonsuspendableFunctionException import org.classdump.luna.lib.ArgumentIterator import org.classdump.luna.lib.TableLib import org.classdump.luna.runtime.AbstractFunction0 import org.classdump.luna.runtime.AbstractFunction1 import org.classdump.luna.runtime.AbstractFunction2 import org.classdump.luna.runtime.AbstractFunction3 import org.classdump.luna.runtime.AbstractFunction4 import org.classdump.luna.runtime.AbstractFunctionAnyArg import org.classdump.luna.runtime.Dispatch import org.classdump.luna.runtime.ExecutionContext import org.classdump.luna.runtime.LuaFunction import org.classdump.luna.runtime.UnresolvedControlThrowable import kotlin.math.max import kotlin.math.min fun ExecutionContext.indexNoYield(table: Any, key: Any): Any? { return try { Dispatch.index(this, table, key) returnBuffer.get0() } catch (err: UnresolvedControlThrowable) { throw RuntimeException("attempt to yield across C boundary", err) } } fun ExecutionContext.indexSetNoYield(table: Any, key: Any, value: Any?) { try { Dispatch.setindex(this, table, key, value) } catch (err: UnresolvedControlThrowable) { throw RuntimeException("attempt to yield across C boundary", err) } } fun ArgumentIterator.nextOptionalFloat(): Double? { if (hasNext()) { return nextFloat() } else { return null } } fun ArgumentIterator.nextOptionalInteger(): Long? { if (hasNext()) { return nextInteger() } else { return null } } operator fun Table.set(index: Any, value: Any?) { rawset(index, value) } @Deprecated("Trying to set JSON to Lua table", level = DeprecationLevel.ERROR) operator fun Table.set(index: Any, value: JsonElement?) { rawset(index, value) } operator fun Table.set(index: Long, value: Any?) { rawset(index, value) } @Deprecated("Trying to set JSON to Lua table", level = DeprecationLevel.ERROR) operator fun Table.set(index: Long, value: JsonElement?) { rawset(index, value) } operator fun Table.set(index: Int, value: Any?) { rawset(index.toLong(), value) } @Deprecated("Trying to set JSON to Lua table", level = DeprecationLevel.ERROR) operator fun Table.set(index: Int, value: JsonElement?) { rawset(index.toLong(), value) } operator fun Table.get(index: Any): Any? = rawget(index) operator fun Table.get(index: Long): Any? = rawget(index) operator fun Table.get(index: Int): Any? = rawget(index.toLong()) operator fun Table.contains(index: Any): Boolean { return rawget(index) != null } operator fun Table.contains(index: Long): Boolean { return rawget(index) != null } operator fun Table.iterator(): Iterator> { var key: Any? = initialKey() ?: return ObjectIterators.emptyIterator() data class Pair(override val key: Any, override val value: Any) : Map.Entry return object : Iterator> { override fun hasNext(): Boolean { return key != null } override fun next(): Map.Entry { val ikey = key ?: throw NoSuchElementException() val value = get(ikey)!! key = successorKeyOf(ikey) return Pair(ikey, value) } } } /** * to be used in places where we need to "unpack" table, like this: * * ```lua * local array = unpack(tab) * ``` * * except this function unpacks using rawget */ fun Table.unpackAsArray(): Array { var min = Long.MAX_VALUE var max = 0L for ((k, v) in this) { if (k is Long) { max = max(k, max) min = min(k, min) } } val length = max - min if (length <= 0L) return arrayOf() val array = arrayOfNulls(length.toInt()) var i2 = 0 for (i in min .. max) { array[i2++] = this[i] } return array } fun TableFactory.tableOf(vararg values: Any?): Table { val table = newTable(values.size, 0) for ((i, v) in values.withIndex()) { table[i + 1L] = v } return table } fun TableFactory.tableMapOf(vararg values: Pair): Table { val table = newTable(0, values.size) for ((k, v) in values) { table[k] = v } return table } fun TableFactory.tableOf(): Table { return newTable() } @Deprecated("Function is a stub") fun luaStub(message: String = "not yet implemented"): LuaFunction { return object : LuaFunction() { override fun resume(context: ExecutionContext?, suspendedState: Any?) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext?) { throw LuaRuntimeException("NYI: $message") } override fun invoke(context: ExecutionContext?, arg1: Any?) { throw LuaRuntimeException("NYI: $message") } override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?) { throw LuaRuntimeException("NYI: $message") } override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?, arg3: Any?) { throw LuaRuntimeException("NYI: $message") } override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?, arg3: Any?, arg4: Any?) { throw LuaRuntimeException("NYI: $message") } override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?, arg3: Any?, arg4: Any?, arg5: Any?) { throw LuaRuntimeException("NYI: $message") } override fun invoke(context: ExecutionContext?, args: Array?) { throw LuaRuntimeException("NYI: $message") } } } fun luaFunctionN(name: String, callable: ExecutionContext.(ArgumentIterator) -> Unit): LuaFunction { return object : AbstractFunctionAnyArg() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext, args: Array) { try { callable.invoke(context, ArgumentIterator.of(context, name, args)) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunctionNS(name: String, callable: ExecutionContext.(ArgumentIterator) -> StateMachine): LuaFunction { return object : AbstractFunctionAnyArg() { override fun resume(context: ExecutionContext, suspendedState: Any) { (suspendedState as StateMachine).run(context) } override fun invoke(context: ExecutionContext, args: Array) { try { callable.invoke(context, ArgumentIterator.of(context, name, args)).run(context) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunctionArray(callable: ExecutionContext.(Array) -> Unit): LuaFunction { return object : AbstractFunctionAnyArg() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext, args: Array) { try { callable.invoke(context, args) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunction(callable: ExecutionContext.() -> Unit): LuaFunction<*, *, *, *, *> { return object : AbstractFunction0() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext) { try { callable.invoke(context) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunction(callable: ExecutionContext.(T) -> Unit): LuaFunction { return object : AbstractFunction1() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext, arg1: T) { try { callable.invoke(context, arg1) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunction(callable: ExecutionContext.(T, T2) -> Unit): LuaFunction { return object : AbstractFunction2() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext, arg1: T, arg2: T2) { try { callable.invoke(context, arg1, arg2) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunction(callable: ExecutionContext.(T, T2, T3) -> Unit): LuaFunction { return object : AbstractFunction3() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext, arg1: T, arg2: T2, arg3: T3) { try { callable.invoke(context, arg1, arg2, arg3) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } } fun luaFunction(callable: ExecutionContext.(T, T2, T3, T4) -> Unit): LuaFunction { return object : AbstractFunction4() { override fun resume(context: ExecutionContext, suspendedState: Any) { throw NonsuspendableFunctionException(this::class.java) } override fun invoke(context: ExecutionContext, arg1: T, arg2: T2, arg3: T3, arg4: T4) { try { callable.invoke(context, arg1, arg2, arg3, arg4) } catch (err: ClassCastException) { throw LuaRuntimeException(err) } catch (err: NullPointerException) { throw LuaRuntimeException(err) } } } }