summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-02-20 00:45:30 -0600
committersanine <sanine.not@pm.me>2023-02-20 00:45:30 -0600
commitc8beee6a344837e37110bc290398f8ea6b16d90c (patch)
tree266bb457eacde39b32729bb8023a976e7b04cf81 /util
parentb7946c0856155b0d25e47a6645fc853acb5f45a6 (diff)
implement basic binding auto-generation
Diffstat (limited to 'util')
-rw-r--r--util/bind.lua3
-rw-r--r--util/generate-binding.lua147
-rw-r--r--util/test.lua57
3 files changed, 149 insertions, 58 deletions
diff --git a/util/bind.lua b/util/bind.lua
new file mode 100644
index 0000000..1309e4e
--- /dev/null
+++ b/util/bind.lua
@@ -0,0 +1,3 @@
+local b = require 'generate-binding'
+
+print(b.bind(arg[1]))
diff --git a/util/generate-binding.lua b/util/generate-binding.lua
index 1c5359f..23aa057 100644
--- a/util/generate-binding.lua
+++ b/util/generate-binding.lua
@@ -1,61 +1,132 @@
local b = {}
-setmetatable(b, {__index=G})
+setmetatable(b, {__index=_G})
setfenv(1, b)
---===== enums =====--
-local Enum = {}
--- create a single enum element
-function Enum.new(name, id)
- local self = {
- name = string.upper(name),
- id = id,
- }
- setmetatable(self, Enum)
- return self
-end
--- create multiple enums
-function Enum.new_multi(tbl)
- local enums = {}
- for id, name in ipairs(tbl) do
- table.insert(enums, Enum.new(name, id))
- end
- return enums
+function ExtractFunctionName(signature)
+ return string.match(signature, "([%w_][%w_]*)%s-%(.*%)")
+end
+
+
+function ExtractFunctionType(signature)
+ return string.match(signature, "(.+)%s%s-[%w_]+%s-%(.*%)")
+end
+
+
+local function trimWhitespace(s)
+ s = string.gsub(s, "^%s*", "")
+ s = string.gsub(s, "%s+$", "")
+ return s
end
--- make enums read-only
-function Enum.__newindex(self)
- error("Attempted to set index on Enum")
+
+
+function ExtractFunctionArgs(signature)
+ local args = {}
+ local argStr = string.match(signature, "%((.*)%)")
+ for arg in string.gmatch(argStr, "([^,][^,]*),?") do
+ -- handle pointers (e.g. void *q)
+ arg = string.gsub(arg, "%*", " * ")
+
+ local type = string.match(arg, "(.+)%s%s-[%w_]+")
+ type = string.gsub(type, "%s%s+", " ")
+ type = string.gsub(type, "%* %*", "**")
+ type = trimWhitespace(type)
+
+ local name = string.match(arg, "([%w_]+)%s-,?$")
+ name = trimWhitespace(name)
+ table.insert(args, { type=type, name=name })
+ end
+ return args
end
--- make enums print nicely
-function Enum.__tostring(self)
- return self.name
+
+
+function GetPointerLevel(ctype)
+ local level = 0
+ for _ in string.gmatch(ctype, "%*") do
+ level = level + 1
+ end
+ return level
end
--- allow comparinge enums
-function Enum.__eq(self, other)
- return self.id == other.id
+
+
+function GetLuaType(ctype)
+ -- double (triple, etc) pointers
+ if GetPointerLevel(ctype) > 1 then return "unknown"
+ -- regular pointers
+ elseif GetPointerLevel(ctype) == 1 then
+ -- strings
+ if string.match(ctype, "char") then return "string"
+ else return "unknown" end
+ -- ordinary variables
+ else
+ -- numbers
+ if string.match(ctype, "float$") then return "number"
+ elseif string.match(ctype, "double$") then return "number"
+ -- integers
+ elseif string.match(ctype, "char$") then return "integer"
+ elseif string.match(ctype, "int$") then return "integer"
+ elseif string.match(ctype, "long$") then return "integer"
+ -- void
+ elseif string.match(ctype, "void$") then return "void"
+ -- unknown
+ else return "unknown" end
+ end
end
-local function check_match(string, pattern, rule)
- local match = string.match(string, '^' .. pattern)
- if match then
- return match, rule(match)
+function PullArg(arg, index)
+ local ltype = GetLuaType(arg.type)
+
+ local pull
+ if ltype == "unknown" then
+ pull = string.format("/* get: %s */", arg.type)
+ else
+ pull = string.format("luaL_check%s(L, %d);", ltype, index)
end
+
+ return string.format("%s %s = %s", arg.type, arg.name, pull)
end
-local function append_match(string, pattern, rule,
+function Call(ftype, fname, args)
+ local callArgs = "("
+ for index, arg in ipairs(args) do
+ callArgs = callArgs .. arg.name
+ if index ~= #args then
+ callArgs = callArgs .. ", "
+ end
+ end
+ callArgs = callArgs .. ")"
-local function lex(string)
- for _, typename in ipairs(c_int_types) do
- if (string.sub(string, 1, #typename) == typename) then
- return
+ local ltype = GetLuaType(ftype)
+ if ltype == "void" then
+ return string.format("%s%s;\n\treturn 0;", fname, callArgs)
+ elseif ltype == "unknown" then
+ return string.format(
+ "%s bind_result = %s%s;\n\t/* push result */\n\treturn /* count */;",
+ ftype, fname, callArgs
+ )
+ else
+ return string.format(
+ "%s bind_result = %s%s;\n\tlua_push%s(L, bind_result);\n\treturn 1;",
+ ftype, fname, callArgs, ltype
+ )
end
end
function bind(signature)
-
+ local ftype = ExtractFunctionType(signature)
+ local fname = ExtractFunctionName(signature)
+ local args = ExtractFunctionArgs(signature)
+
+ local result = string.format("int %s_bind(lua_State *L)\n{\n", fname)
+ for index, arg in ipairs(args) do
+ result = result .. "\t" .. PullArg(arg, index) .. "\n"
+ end
+
+ result = result .. "\t" .. Call(ftype, fname, args) .. "\n}"
+ return result
end
diff --git a/util/test.lua b/util/test.lua
index 9b9fc49..0b6a4b7 100644
--- a/util/test.lua
+++ b/util/test.lua
@@ -14,27 +14,44 @@ end
local b = require 'generate-binding'
-test("simplest possible binding", function()
- local binding = b.bind("void some_function();")
- assert(binding == [[
-int some_function_bind(lua_State *L)
-{
- some_function();
- return 0;
-}]])
+test("extract function name from signature", function()
+ local name = b.ExtractFunctionName("int some_name(void *qqq);")
+ assert(name == "some_name")
+ name = b.ExtractFunctionName("float quitGame(int a, int b, int c, int** m);")
+ assert(name == "quitGame")
+ name = b.ExtractFunctionName("void startGame ();")
+ assert(name == "startGame")
end)
-test("complicated binding", function()
- local binding = b.bind("const char * qqq(int a, float q, unsigned char m);")
- assert(binding == [[
-int qqq_bind(lua_State *L)
-{
- lua_Integer a = luaL_checkinteger(L, 1);
- lua_Number q = luaL_checknumber(L, 2);
- lua_Integer m = luaL_checkinteger(L, 3);
- const char *result = qqq(a, q, m);
- lua_pushstring(L, result);
- return 1;
-}]])
+test("extract function type from signature", function()
+ local ftype = b.ExtractFunctionType("int some_ftype(void *qqq);")
+ assert(ftype == "int")
+ ftype = b.ExtractFunctionType("float quitGame(int a, int b, int c, int** m);")
+ assert(ftype == "float")
+ ftype = b.ExtractFunctionType("void startGame ();")
+ assert(ftype == "void")
+end)
+
+
+test("extract arguments from signature", function()
+ local args = b.ExtractFunctionArgs("int some_args(void *qqq);")
+ assert(args ~= nil)
+ assert(#args == 1)
+ assert(args[1].type == "void *")
+ assert(args[1].name == "qqq")
+
+ args = b.ExtractFunctionArgs("float quitGame(int a, int b, int c, int** m);")
+ assert(#args == 4)
+ assert(args[1].type == "int")
+ assert(args[1].name == "a")
+ assert(args[2].type == "int")
+ assert(args[2].name == "b")
+ assert(args[3].type == "int")
+ assert(args[3].name == "c")
+ assert(args[4].type == "int **")
+ assert(args[4].name == "m")
+
+ args = b.ExtractFunctionArgs("void startGame ();")
+ assert(#args == 0)
end)