From c8beee6a344837e37110bc290398f8ea6b16d90c Mon Sep 17 00:00:00 2001 From: sanine Date: Mon, 20 Feb 2023 00:45:30 -0600 Subject: implement basic binding auto-generation --- util/bind.lua | 3 + util/generate-binding.lua | 147 ++++++++++++++++++++++++++++++++++------------ util/test.lua | 57 +++++++++++------- 3 files changed, 149 insertions(+), 58 deletions(-) create mode 100644 util/bind.lua (limited to 'util') 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) -- cgit v1.2.1