KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/lua/Functions.kt
2024-04-20 21:31:26 +07:00

353 lines
9.9 KiB
Kotlin

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<Map.Entry<Any, Any>> {
var key: Any? = initialKey() ?: return ObjectIterators.emptyIterator()
data class Pair(override val key: Any, override val value: Any) : Map.Entry<Any, Any>
return object : Iterator<Map.Entry<Any, Any>> {
override fun hasNext(): Boolean {
return key != null
}
override fun next(): Map.Entry<Any, Any> {
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<Any?> {
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<Any>(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<Any, Any?>): 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<Any?, Any?, Any?, Any?, Any?> {
return object : LuaFunction<Any?, Any?, Any?, Any?, Any?>() {
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<out Any>?) {
throw LuaRuntimeException("NYI: $message")
}
}
}
fun luaFunctionN(name: String, callable: ExecutionContext.(ArgumentIterator) -> Unit): LuaFunction<Any, Any, Any, Any, Any> {
return object : AbstractFunctionAnyArg() {
override fun resume(context: ExecutionContext, suspendedState: Any) {
throw NonsuspendableFunctionException(this::class.java)
}
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
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<Any, Any, Any, Any, Any> {
return object : AbstractFunctionAnyArg() {
override fun resume(context: ExecutionContext, suspendedState: Any) {
(suspendedState as StateMachine).run(context)
}
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
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<out Any?>) -> Unit): LuaFunction<Any, Any, Any, Any, Any> {
return object : AbstractFunctionAnyArg() {
override fun resume(context: ExecutionContext, suspendedState: Any) {
throw NonsuspendableFunctionException(this::class.java)
}
override fun invoke(context: ExecutionContext, args: Array<out Any?>) {
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 <T> luaFunction(callable: ExecutionContext.(T) -> Unit): LuaFunction<T, *, *, *, *> {
return object : AbstractFunction1<T>() {
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 <T, T2> luaFunction(callable: ExecutionContext.(T, T2) -> Unit): LuaFunction<T, T2, *, *, *> {
return object : AbstractFunction2<T, T2>() {
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 <T, T2, T3> luaFunction(callable: ExecutionContext.(T, T2, T3) -> Unit): LuaFunction<T, T2, T3, *, *> {
return object : AbstractFunction3<T, T2, T3>() {
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 <T, T2, T3, T4> luaFunction(callable: ExecutionContext.(T, T2, T3, T4) -> Unit): LuaFunction<T, T2, T3, T4, *> {
return object : AbstractFunction4<T, T2, T3, T4>() {
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)
}
}
}
}