summaryrefslogtreecommitdiffstats
path: root/src/scripting/ffi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/scripting/ffi.c')
-rw-r--r--src/scripting/ffi.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/scripting/ffi.c b/src/scripting/ffi.c
new file mode 100644
index 0000000..9632893
--- /dev/null
+++ b/src/scripting/ffi.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2015 Patrick "P. J." McDermott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <lua.h>
+#include "../logging.h"
+#include "../resources/script.h"
+#include "ffi.h"
+
+/* LUA_OK is defined in Lua 5.2 but not 5.1. */
+#ifndef LUA_OK
+#define LUA_OK 0
+#endif
+
+static struct script *ctx;
+static struct ffi_namespace root = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+static int call_arg_index;
+
+static void ffi_register_functions_in_namespace(lua_State *l,
+ struct ffi_namespace *parent);
+
+void
+ffi_add_namespace(struct ffi_namespace *parent, const char *name)
+{
+ struct ffi_namespace *new_ns;
+
+ if (parent == NULL) {
+ parent = &root;
+ }
+
+ new_ns = malloc(sizeof(*new_ns));
+ if (new_ns == NULL) {
+ err(1, "Failed to allocate FFI namespace \"%s\"", name);
+ return;
+ }
+ new_ns->name = strdup(name);
+ new_ns->ns_head = NULL;
+ new_ns->ns_tail = NULL;
+ new_ns->fn_head = NULL;
+ new_ns->fn_tail = NULL;
+ new_ns->next = NULL;
+
+ if (parent->ns_head == NULL) {
+ parent->ns_head = new_ns;
+ } else {
+ parent->ns_tail->next = new_ns;
+ }
+ parent->ns_tail = new_ns;
+}
+
+void
+ffi_add_function(struct ffi_namespace *parent, const char *name,
+ void (*func)(void))
+{
+ struct ffi_function *new_fn;
+
+ new_fn = malloc(sizeof(*new_fn));
+ if (new_fn == NULL) {
+ err(1, "Failed to allocate FFI function \"%s\"", name);
+ return;
+ }
+ new_fn->name = strdup(name);
+ new_fn->func = func;
+ new_fn->next = NULL;
+
+ if (parent->fn_head == NULL) {
+ parent->fn_head = new_fn;
+ } else {
+ parent->fn_tail->next = new_fn;
+ }
+ parent->fn_tail = new_fn;
+}
+
+void
+ffi_register_functions(struct script *script)
+{
+ ffi_register_functions_in_namespace(script->lua_state, &root);
+}
+
+static void
+ffi_register_functions_in_namespace(lua_State *l, struct ffi_namespace *parent)
+{
+ struct ffi_namespace *ns;
+ struct ffi_function *fn;
+
+ for (ns = parent->ns_head; ns != NULL; ns = ns->next) {
+ }
+ for (fn = ns->fn_head; fn != NULL; fn = fn->next) {
+ }
+ l = l;
+}
+
+void ffi_context_switch(struct script *script)
+{
+ ctx = script;
+}
+
+int
+ffi_stack_get_bool(void)
+{
+ if (lua_gettop(ctx->lua_state) < ++call_arg_index) {
+ warn("Bad argument #%d to %s (no value)");
+ return 0;
+ }
+ if (!lua_isboolean(ctx->lua_state, call_arg_index)) {
+ warn("Bad argument #%d to %s (boolean expected)");
+ return 0;
+ }
+ return lua_toboolean(ctx->lua_state, call_arg_index);
+}
+
+int
+ffi_stack_get_int(void)
+{
+#if LUA_VERSION_NUM <= 501
+ if (lua_gettop(ctx->lua_state) < ++call_arg_index) {
+ warn("Bad argument #%d to %s (no value)");
+ return 0;
+ }
+ return lua_tointeger(ctx->lua_state, call_arg_index);
+#elif LUA_VERSION_NUM == 502
+ int v;
+ int is_int;
+
+ if (lua_gettop(ctx->lua_state) < ++call_arg_index) {
+ warn("Bad argument #%d to %s (no value)");
+ return 0;
+ }
+ v = lua_tointegerx(ctx->lua_state, call_arg_index, &is_int);
+ if (!is_int) {
+ warn("Bad argument #%d to %s (integer expected)");
+ return 0;
+ }
+ return v;
+#elif LUA_VERSION_NUM >= 503
+ if (lua_gettop(ctx->lua_state) < ++call_arg_index) {
+ warn("Bad argument #%d to %s (no value)");
+ return 0;
+ }
+ if (!lua_isinteger(ctx->lua_state, call_arg_index)) {
+ warn("Bad argument #%d to %s (integer expected)");
+ return 0;
+ }
+ return lua_tointeger(ctx->lua_state, call_arg_index);
+#endif
+}
+
+double
+ffi_stack_get_float(void)
+{
+ if (lua_gettop(ctx->lua_state) < ++call_arg_index) {
+ warn("Bad argument #%d to %s (no value)");
+ return 0;
+ }
+ if (!lua_isnumber(ctx->lua_state, call_arg_index)) {
+ warn("Bad argument #%d to %s (number expected)");
+ return 0;
+ }
+ return lua_tonumber(ctx->lua_state, call_arg_index);
+}
+
+const char *
+ffi_stack_get_string(void)
+{
+ if (lua_gettop(ctx->lua_state) < ++call_arg_index) {
+ warn("Bad argument #%d to %s (no value)");
+ return 0;
+ }
+ if (!lua_isstring(ctx->lua_state, call_arg_index)) {
+ warn("Bad argument #%d to %s (string expected)");
+ return 0;
+ }
+ return lua_tostring(ctx->lua_state, call_arg_index);
+}
+
+void
+ffi_prepare_call(const char *func)
+{
+ lua_getglobal(ctx->lua_state, func);
+ call_arg_index = 0;
+}
+
+void
+ffi_stack_set_bool(int v)
+{
+ lua_pushboolean(ctx->lua_state, v);
+}
+
+void
+ffi_stack_set_int(int v)
+{
+ lua_pushinteger(ctx->lua_state, v);
+}
+
+void
+ffi_stack_set_float(double v)
+{
+ lua_pushnumber(ctx->lua_state, v);
+}
+
+void
+ffi_stack_set_string(const char *v)
+{
+ lua_pushstring(ctx->lua_state, v);
+}
+
+void
+ffi_call(void)
+{
+ if (lua_pcall(ctx->lua_state, call_arg_index, 0, 0) != LUA_OK) {
+ err(1, "Error calling function: %s\n",
+ lua_tostring(ctx->lua_state, -1));
+ }
+}