diff options
Diffstat (limited to 'scripts/gendeclarations.lua')
-rwxr-xr-x | scripts/gendeclarations.lua | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua new file mode 100755 index 0000000000..311ed8b527 --- /dev/null +++ b/scripts/gendeclarations.lua @@ -0,0 +1,232 @@ +#!/usr/bin/lua + +local fname = arg[1] +local static_fname = arg[2] +local non_static_fname = arg[3] +local cpp = arg[4] + +cpp = cpp:gsub(' %-DINCLUDE_GENERATED_DECLARATIONS ', ' ') + +local lpeg = require('lpeg') + +local fold = function (func, ...) + local result = nil + for i, v in ipairs({...}) do + if result == nil then + result = v + else + result = func(result, v) + end + end + return result +end + +local folder = function (func) + return function (...) + return fold(func, ...) + end +end + +local lit = lpeg.P +local set = function(...) + return lpeg.S(fold(function (a, b) return a .. b end, ...)) +end +local any_character = lpeg.P(1) +local rng = function(s, e) return lpeg.R(s .. e) end +local concat = folder(function (a, b) return a * b end) +local branch = folder(function (a, b) return a + b end) +local one_or_more = function(v) return v ^ 1 end +local two_or_more = function(v) return v ^ 2 end +local any_amount = function(v) return v ^ 0 end +local one_or_no = function(v) return v ^ -1 end +local look_behind = lpeg.B +local look_ahead = function(v) return #v end +local neg_look_ahead = function(v) return -v end +local neg_look_behind = function(v) return -look_behind(v) end + +local w = branch( + rng('a', 'z'), + rng('A', 'Z'), + lit('_') +) +local aw = branch( + w, + rng('0', '9') +) +local s = set(' ', '\n', '\t') +local raw_word = concat(w, any_amount(aw)) +local right_word = concat( + raw_word, + neg_look_ahead(aw) +) +local word = concat( + neg_look_behind(aw), + right_word +) +local spaces = any_amount(branch( + s, + -- Comments are really handled by preprocessor, so the following is not needed + concat( + lit('/*'), + any_amount(concat( + neg_look_ahead(lit('*/')), + any_character + )), + lit('*/') + ), + concat( + lit('//'), + any_amount(concat( + neg_look_ahead(lit('\n')), + any_character + )), + lit('\n') + ) +)) +local typ_part = concat( + word, + any_amount(concat( + spaces, + lit('*') + )), + spaces +) +local typ = one_or_more(typ_part) +local typ_id = two_or_more(typ_part) +local arg = typ_id -- argument name is swallowed by typ +local pattern = concat( + typ_id, -- return type with function name + spaces, + lit('('), + spaces, + one_or_no(branch( -- function arguments + concat( + arg, -- first argument, does not require comma + any_amount(concat( -- following arguments, start with a comma + spaces, + lit(','), + spaces, + arg, + any_amount(concat( + lit('['), + spaces, + any_amount(aw), + spaces, + lit(']') + )) + )), + one_or_no(concat( + spaces, + lit(','), + spaces, + lit('...') + )) + ), + lit('void') -- also accepts just void + )), + spaces, + lit(')'), + any_amount(concat( -- optional attributes + spaces, + lit('FUNC_ATTR_'), + any_amount(aw), + one_or_no(concat( -- attribute argument + spaces, + lit('('), + any_amount(concat( + neg_look_ahead(lit(')')), + any_character + )), + lit(')') + )) + )), + look_ahead(concat( -- definition must be followed by "{" + spaces, + lit('{') + )) +) + +if fname == '--help' then + print'Usage:' + print() + print' gendeclarations.lua definitions.c static.h non-static.h "cc -E …"' + os.exit() +end + +local pipe = io.popen(cpp .. ' -DDO_NOT_DEFINE_EMPTY_ATTRIBUTES ' .. fname, 'r') +local text = pipe:read('*a') +if not pipe:close() then + os.exit(2) +end + +local header = [[ +#ifndef DEFINE_FUNC_ATTRIBUTES +# define DEFINE_FUNC_ATTRIBUTES +#endif +#include "nvim/func_attr.h" +#undef DEFINE_FUNC_ATTRIBUTES +]] + +local footer = [[ +#include "nvim/func_attr.h" +]] + +local non_static = header +local static = header + +local filepattern = '^# %d+ "[^"]-/?([^"/]+)"' +local curfile + +init = 0 +curfile = nil +neededfile = fname:match('[^/]+$') +while init ~= nil do + init = text:find('\n', init) + if init == nil then + break + end + init = init + 1 + if text:sub(init, init) == '#' then + file = text:match(filepattern, init) + if file ~= nil then + curfile = file + end + elseif curfile == neededfile then + s = init + e = pattern:match(text, init) + if e ~= nil then + local declaration = text:sub(s, e - 1) + -- Comments are really handled by preprocessor, so the following is not + -- needed + declaration = declaration:gsub('/%*.-%*/', '') + declaration = declaration:gsub('//.-\n', '\n') + + declaration = declaration:gsub('\n', ' ') + declaration = declaration:gsub('%s+', ' ') + declaration = declaration:gsub(' ?%( ?', '(') + declaration = declaration:gsub(' ?%) ?', ')') + declaration = declaration:gsub(' ?, ?', ', ') + declaration = declaration:gsub(' ?(%*+) ?', ' %1') + declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') + declaration = declaration:gsub(' $', '') + declaration = declaration .. ';\n' + if text:sub(s, s + 5) == 'static' then + static = static .. declaration + else + non_static = non_static .. declaration + end + end + end +end + +non_static = non_static .. footer +static = static .. footer + +local F +F = io.open(static_fname, 'w') +F:write(static) +F:close() + +F = io.open(non_static_fname, 'w') +F:write(non_static) +F:close() |