diff options
author | fesily <fesil@foxmail.com> | 2023-05-12 15:19:09 +0800 |
---|---|---|
committer | fesily <fesil@foxmail.com> | 2023-05-12 15:19:09 +0800 |
commit | 86dcb1a7e5eed8cf2e1b7109b4c0debf6967c485 (patch) | |
tree | 94b0eda62fe218313028498458f3951e0e6a5b45 /script/plugins/ffi/c-parser/cdefines.lua | |
parent | 7fa6ee16cd746b70b070331ae4e48dacc2384ca5 (diff) | |
download | lua-language-server-86dcb1a7e5eed8cf2e1b7109b4c0debf6967c485.zip |
link server by plugin
Diffstat (limited to 'script/plugins/ffi/c-parser/cdefines.lua')
-rw-r--r-- | script/plugins/ffi/c-parser/cdefines.lua | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/script/plugins/ffi/c-parser/cdefines.lua b/script/plugins/ffi/c-parser/cdefines.lua new file mode 100644 index 00000000..55065f2d --- /dev/null +++ b/script/plugins/ffi/c-parser/cdefines.lua @@ -0,0 +1,152 @@ + +local cdefines = {} + +local c99 = require("plugins.ffi.c-parser.c99") +local cpp = require("plugins.ffi.c-parser.cpp") +local typed = require("plugins.ffi.c-parser.typed") + +local function add_type(lst, name, typ) + lst[name] = typ + table.insert(lst, { name = name, type = typ }) +end + +local base_c_types = { + CONST_CHAR_PTR = { "const", "char", "*" }, + CONST_CHAR = { "const", "char" }, + LONG_LONG = { "long", "long" }, + LONG = { "long" }, + DOUBLE = { "double" }, + INT = { "int" }, +} + +local function get_binop_type(e1, e2) + if e1[1] == "double" or e2[1] == "double" then + return base_c_types.DOUBLE + end + if e1[2] == "long" or e2[2] == "long" then + return base_c_types.LONG_LONG + end + if e1[1] == "long" or e2[1] == "long" then + return base_c_types.LONG + end + return base_c_types.INT +end + +local binop_set = { + ["+"] = true, + ["-"] = true, + ["*"] = true, + ["/"] = true, + ["%"] = true, +} + +local relop_set = { + ["<"] = true, + [">"] = true, + [">="] = true, + ["<="] = true, + ["=="] = true, + ["!="] = true, +} + +local bitop_set = { + ["<<"] = true, + [">>"] = true, + ["&"] = true, + ["^"] = true, + ["|"] = true, +} + +-- Best-effort assessment of the type of a #define +local get_type_of_exp +get_type_of_exp = typed("Exp, TypeList -> {string}?", function(exp, lst) + if type(exp[1]) == "string" and exp[2] == nil then + local val = exp[1] + if val:sub(1,1) == '"' or val:sub(1,2) == 'L"' then + return base_c_types.CONST_CHAR_PTR + elseif val:sub(1,1) == "'" or val:sub(1,2) == "L'" then + return base_c_types.CONST_CHAR + elseif val:match("^[0-9]*LL$") then + return base_c_types.LONG_LONG + elseif val:match("^[0-9]*L$") then + return base_c_types.LONG + elseif val:match("%.") then + return base_c_types.DOUBLE + else + return base_c_types.INT + end + end + + if type(exp[1]) == "string" and exp[2] and exp[2].args then + local fn = lst[exp[1]] + if not fn or not fn.ret then + return nil -- unknown function, or not a function + end + local r = fn.ret.type + return table.move(r, 1, #r, 1, {}) -- shallow_copy(r) + end + + if exp.unop == "*" then + local etype = get_type_of_exp(exp[1], lst) + if not etype then + return nil + end + local rem = table.remove(etype) + assert(rem == "*") + return etype + elseif exp.unop == "-" then + return get_type_of_exp(exp[1], lst) + elseif exp.op == "?" then + return get_type_of_exp(exp[2], lst) + elseif exp.op == "," then + return get_type_of_exp(exp[#exp], lst) + elseif binop_set[exp.op] then + local e1 = get_type_of_exp(exp[1], lst) + if not e1 then + return nil + end + -- some binops are also unops (e.g. - and *) + if exp[2] then + local e2 = get_type_of_exp(exp[2], lst) + if not e2 then + return nil + end + return get_binop_type(e1, e2) + else + return e1 + end + elseif relop_set[exp.op] then + return base_c_types.INT + elseif bitop_set[exp.op] then + return get_type_of_exp(exp[1], lst) -- ...or should it be int? + elseif exp.op then + print("FIXME unsupported op", exp.op) + end + return nil +end) + +function cdefines.register_define(lst, name, text, define_set) + local exp, err, line, col = c99.match_language_expression_grammar(text .. " ") + if not exp then + -- failed parsing expression + -- print(("failed parsing: %d:%d: %s\n"):format(line, col, text)) + return + end + local typ = get_type_of_exp(exp, lst) + if typ then + add_type(lst, name, { type = typ }) + end +end + +function cdefines.register_defines(lst, define_set) + for name, def in pairs(define_set) do + if #def == 0 then + goto continue + end + local text = cpp.expand_macro(name, define_set) + cdefines.register_define(lst, name, text, define_set) + ::continue:: + end +end + +return cdefines |