2024-12-23 17:59:00 +00:00
|
|
|
#!/bin/env python3
|
|
|
|
|
|
|
|
import sys, json
|
|
|
|
|
|
|
|
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
|
|
|
|
api_source = f.read()
|
|
|
|
|
|
|
|
api = json.loads(api_source)
|
|
|
|
|
|
|
|
|
2025-01-11 23:44:41 +00:00
|
|
|
# TODO: use lua_to--x functions for defaults, so that fewer function calls are made
|
|
|
|
# TODO: use lua_createtable for preallocation
|
|
|
|
|
2024-12-23 17:59:00 +00:00
|
|
|
def default(parameter):
|
|
|
|
basetype = parameter["type"].rsplit(' *', 1)[0]
|
|
|
|
if parameter["type"] == "float":
|
|
|
|
return parameter["default"]
|
|
|
|
elif parameter["type"] == "bool":
|
|
|
|
return "true" if parameter["default"] else "false"
|
|
|
|
elif parameter["type"] == "char *":
|
|
|
|
if parameter["default"] == {}:
|
|
|
|
return "NULL"
|
|
|
|
else: return '"' + parameter["default"] + '"'
|
|
|
|
elif basetype in api["types"]:
|
2025-01-03 08:45:10 +00:00
|
|
|
if parameter["type"].endswith(" *"):
|
2024-12-23 17:59:00 +00:00
|
|
|
if parameter["default"] == {}:
|
|
|
|
return "NULL"
|
|
|
|
else:
|
|
|
|
return "&(%s){\n%s\n }" % \
|
|
|
|
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
|
|
|
|
else:
|
|
|
|
return "(%s){\n%s\n }" % \
|
|
|
|
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
|
|
|
|
raise BaseException("Unhandled default value of type '%s'" % parameter["type"])
|
|
|
|
|
|
|
|
|
2025-01-09 18:47:08 +00:00
|
|
|
def to_table(typedesc, variable, indent = 0):
|
2025-01-11 23:44:41 +00:00
|
|
|
binding = ' ' * indent + "lua_createtable(L, 0, %i);\n" % len(typedesc["fields"])
|
2025-01-09 18:47:08 +00:00
|
|
|
for field in typedesc["fields"]:
|
|
|
|
if field["type"] == "float":
|
|
|
|
binding += ' ' * indent + "lua_pushnumber(L, (double)(%s));\n" % (variable + ".%s" % field["name"])
|
2025-01-11 13:01:41 +00:00
|
|
|
elif field["type"] == "bool":
|
|
|
|
binding += ' ' * indent + "lua_pushboolean(L, (%s));\n" % (variable + ".%s" % field["name"])
|
2025-01-09 18:47:08 +00:00
|
|
|
elif field["type"] in api["types"]:
|
|
|
|
binding += to_table(api["types"][field["type"]], variable + ".%s" % field["name"], indent + 4)
|
|
|
|
else:
|
|
|
|
raise BaseException("Unhandled return field type '%s'" % (field["type"]))
|
|
|
|
binding += ' ' * indent + "lua_setfield(L, -2, \"%s\");\n" % field["name"]
|
|
|
|
# binding += ' ' * indent + "lua_pop(L, 1);\n"
|
|
|
|
return binding
|
|
|
|
|
|
|
|
|
2024-12-23 19:02:17 +00:00
|
|
|
print('#include "twn_game_api.h"\n')
|
2025-01-11 23:44:41 +00:00
|
|
|
# TODO: reuse implementation from the engine, this also breaks with statically compiled build
|
|
|
|
print('#define STB_DS_IMPLEMENTATION')
|
2024-12-24 07:03:19 +00:00
|
|
|
print('#include <stb_ds.h>')
|
2024-12-23 17:59:00 +00:00
|
|
|
print('#include <lua.h>')
|
|
|
|
print('#include <lualib.h>')
|
|
|
|
print('#include <lauxlib.h>\n')
|
|
|
|
|
2025-01-11 13:01:41 +00:00
|
|
|
bindings, used_converters = [], {}
|
2024-12-23 17:59:00 +00:00
|
|
|
for procedure, procedure_desc in api["procedures"].items():
|
|
|
|
binding = "static int binding_%s(lua_State *L) {\n" % procedure
|
|
|
|
binding += " luaL_checktype(L, 1, LUA_TTABLE);\n"
|
|
|
|
|
|
|
|
if "params" in procedure_desc:
|
|
|
|
for parameter in procedure_desc["params"]:
|
|
|
|
basetype = parameter["type"].rsplit(' *', 1)[0]
|
|
|
|
|
|
|
|
if parameter["type"].endswith("*"):
|
|
|
|
binding += " %s %s_value;\n" % (basetype, parameter["name"])
|
|
|
|
binding += " %s %s;\n" % (parameter["type"], parameter["name"])
|
|
|
|
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
|
|
|
|
|
|
|
|
if "default" in parameter:
|
|
|
|
binding += " if (lua_isnoneornil(L, -1))\n"
|
|
|
|
binding += " %s = %s;\n" % (parameter["name"], default(parameter))
|
|
|
|
binding += " else\n "
|
|
|
|
|
|
|
|
if parameter["type"] == "float":
|
|
|
|
binding += " %s = (float)lua_tonumber(L, -1);\n" % (parameter["name"]);
|
|
|
|
elif parameter["type"] == "bool":
|
|
|
|
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
|
|
|
|
elif parameter["type"] == "char *":
|
|
|
|
binding += " %s = lua_tostring(L, -1);\n" % (parameter["name"]);
|
|
|
|
elif basetype in api["types"]:
|
2025-01-11 13:01:41 +00:00
|
|
|
used_converters[basetype] = api["types"][basetype]
|
2025-01-03 08:45:10 +00:00
|
|
|
if parameter["type"].endswith(" *"):
|
2024-12-23 19:02:17 +00:00
|
|
|
binding += " { %s_value = to_%s(L, -1); %s = &%s_value; }\n" % (parameter["name"], basetype.lower(), parameter["name"], parameter["name"]);
|
2024-12-23 17:59:00 +00:00
|
|
|
else:
|
2024-12-23 19:02:17 +00:00
|
|
|
binding += " %s = to_%s(L, -1);\n" % (parameter["name"], basetype.lower());
|
2024-12-23 17:59:00 +00:00
|
|
|
else:
|
|
|
|
raise BaseException("Unhandled parameter type '%s'" % (parameter["type"]))
|
|
|
|
|
2025-01-11 23:44:41 +00:00
|
|
|
# binding += " lua_pop(L, %i);\n" % (1 + len(procedure_desc["params"]) if "params" in procedure_desc else 0)
|
|
|
|
|
2024-12-23 19:02:17 +00:00
|
|
|
if "return" in procedure_desc:
|
|
|
|
if procedure_desc["return"] == "bool":
|
2025-01-11 23:44:41 +00:00
|
|
|
binding += " lua_pushboolean(L, (int)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
2024-12-23 19:02:17 +00:00
|
|
|
elif procedure_desc["return"] == "float":
|
2025-01-11 23:44:41 +00:00
|
|
|
binding += " lua_pushnumber(L, (double)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
2025-01-09 18:47:08 +00:00
|
|
|
elif procedure_desc["return"] == "char *":
|
|
|
|
binding += " lua_pushstring(L, %s(%s));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
|
|
|
elif type(procedure_desc["return"]) is dict or procedure_desc["return"] in api["types"]:
|
|
|
|
# TODO: handle enums
|
|
|
|
type_desc = procedure_desc["return"] if type(procedure_desc["return"]) is dict else api["types"][procedure_desc["return"]]
|
|
|
|
binding += " %s result = %s(%s);\n" % (type_desc["c_type"], procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
|
|
|
binding += to_table(type_desc, "result", 4)
|
2024-12-23 19:02:17 +00:00
|
|
|
else:
|
2025-01-09 18:47:08 +00:00
|
|
|
raise BaseException("Unhandled return type '%s'" % (procedure_desc["return"]))
|
2024-12-23 19:02:17 +00:00
|
|
|
binding += " return 1;\n}\n"
|
|
|
|
else:
|
|
|
|
binding += " %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
|
|
|
binding += " return 0;\n}\n"
|
|
|
|
|
2025-01-11 13:01:41 +00:00
|
|
|
bindings += [binding]
|
|
|
|
|
|
|
|
|
|
|
|
storages, converters, initializers, deinitializers = [], [], [], []
|
|
|
|
|
|
|
|
for typename, typedesc in used_converters.items():
|
|
|
|
if "no_convert" in typedesc and typedesc["no_convert"]:
|
|
|
|
continue
|
|
|
|
|
|
|
|
converter = "static %s to_%s(lua_State *L, int idx) {\n" % (typename, typename.lower())
|
|
|
|
converter += " %s %s;\n" % (typename, typename.lower());
|
|
|
|
|
|
|
|
if "fields" in typedesc:
|
|
|
|
for field in typedesc["fields"]:
|
|
|
|
converter += " lua_getfield(L, idx, \"%s\");\n" % (field["name"]);
|
|
|
|
if field["type"] == "float":
|
|
|
|
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
|
|
|
|
elif field["type"] == "uint8_t":
|
|
|
|
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
|
|
|
|
else:
|
|
|
|
raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
|
|
|
|
converter += " lua_pop(L, 1);\n";
|
|
|
|
|
|
|
|
# TODO: wild idea: use compile time built hash table
|
|
|
|
elif "enums" in typedesc:
|
|
|
|
storages += ["struct %sHashItem { char *key; %s value; };\nstatic struct %sHashItem *%s_map = NULL;\n" % (typename, typename, typename, typename.lower())]
|
|
|
|
|
|
|
|
# TODO: use arena
|
|
|
|
for enum in typedesc["enums"]:
|
|
|
|
initializer = " shput(%s_map, \"%s\", %s);" % (typename.lower(), enum, typedesc["enums"][enum])
|
|
|
|
initializers += [initializer]
|
|
|
|
|
|
|
|
deinitializers += [" shfree(%s_map);" % typename.lower()]
|
|
|
|
|
|
|
|
converter += " char *value = lua_tostring(L, -1);\n";
|
|
|
|
converter += " %s = shget(%s_map, value);\n" % (typename.lower(), typename.lower())
|
|
|
|
converter += " lua_pop(L, 1);\n";
|
|
|
|
|
|
|
|
converter += " return %s;\n}\n" % (typename.lower())
|
|
|
|
converters += [converter]
|
|
|
|
|
|
|
|
|
|
|
|
print('\n'.join(storages))
|
|
|
|
print("void bindgen_init(void) {\n" + '\n'.join(initializers) + "\n}\n")
|
|
|
|
print('\n'.join(converters))
|
|
|
|
print('\n'.join(bindings))
|
2024-12-23 17:59:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
loader = "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
|
|
|
|
modules = set(api["procedures"][procedure]["module"] for procedure in api["procedures"])
|
|
|
|
for module in modules:
|
2024-12-24 07:03:19 +00:00
|
|
|
loader += " bindgen_init();\n"
|
2025-01-11 23:44:41 +00:00
|
|
|
loader += " lua_createtable(L, 0, %i);\n" % len(api["procedures"])
|
2024-12-23 17:59:00 +00:00
|
|
|
for procedure, procedure_desc in api["procedures"].items():
|
|
|
|
if procedure_desc["module"] == module:
|
|
|
|
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
|
2025-01-09 18:47:08 +00:00
|
|
|
loader += " lua_setfield(L, -2, \"%s\");\n" % procedure_desc["symbol"]
|
2024-12-23 17:59:00 +00:00
|
|
|
loader += " lua_setglobal(L, \"%s\");\n" % module
|
|
|
|
|
2025-01-11 13:01:41 +00:00
|
|
|
loader += "}\n"
|
2024-12-23 17:59:00 +00:00
|
|
|
print(loader)
|
2024-12-24 07:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
unloader = "void bindgen_unload_%s(lua_State *L) {\n" % api["name"]
|
|
|
|
unloader += '\n'.join(deinitializers)
|
2025-01-11 13:01:41 +00:00
|
|
|
unloader += "}\n"
|
2024-12-24 07:03:19 +00:00
|
|
|
print(unloader)
|
2025-01-11 13:01:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
# exceptions for the base townengine api
|
|
|
|
# TODO: is there a way to generalize it? or rather, is there any need to do so?
|
|
|
|
if api["name"] == "twn":
|
|
|
|
contexter = "void bindgen_build_context(lua_State *L) {\n"
|
|
|
|
contexter += to_table(api["types"]["Context"], "ctx", 4)
|
|
|
|
contexter += "}"
|
|
|
|
print(contexter)
|