diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/gencharblob.lua | 48 | ||||
-rwxr-xr-x | scripts/gendeclarations.lua | 64 | ||||
-rw-r--r-- | scripts/genmsgpack.lua (renamed from scripts/gendispatch.lua) | 196 |
3 files changed, 278 insertions, 30 deletions
diff --git a/scripts/gencharblob.lua b/scripts/gencharblob.lua new file mode 100644 index 0000000000..d860375e26 --- /dev/null +++ b/scripts/gencharblob.lua @@ -0,0 +1,48 @@ +if arg[1] == '--help' then + print('Usage:') + print(' gencharblob.lua source target varname') + print('') + print('Generates C file with big uint8_t blob.') + print('Blob will be stored in a static const array named varname.') + os.exit() +end + +assert(#arg == 3) + +local source_file = arg[1] +local target_file = arg[2] +local varname = arg[3] + +source = io.open(source_file, 'r') +target = io.open(target_file, 'w') + +target:write('#include <stdint.h>\n\n') +target:write(('static const uint8_t %s[] = {\n'):format(varname)) + +num_bytes = 0 +MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line +target:write(' ') + +increase_num_bytes = function() + num_bytes = num_bytes + 1 + if num_bytes == MAX_NUM_BYTES then + num_bytes = 0 + target:write('\n ') + end +end + +for line in source:lines() do + for i = 1,string.len(line) do + byte = string.byte(line, i) + assert(byte ~= 0) + target:write(string.format(' %3u,', byte)) + increase_num_bytes() + end + target:write(string.format(' %3u,', string.byte('\n', 1))) + increase_num_bytes() +end + +target:write(' 0};\n') + +source:close() +target:close() diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index ff69b18ae4..e999e53e4a 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -69,17 +69,18 @@ local word = branch( right_word ) ) +local inline_comment = concat( + lit('/*'), + any_amount(concat( + neg_look_ahead(lit('*/')), + any_character + )), + lit('*/') +) 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('*/') - ), + inline_comment, concat( lit('//'), any_amount(concat( @@ -110,6 +111,7 @@ 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( + any_amount(branch(set(' ', '\t'), inline_comment)), typ_id, -- return type with function name spaces, lit('('), @@ -188,24 +190,44 @@ local footer = [[ local non_static = header local static = header -local filepattern = '^#%a* %d+ "[^"]-/?([^"/]+)"' +local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"' local curfile -init = 0 -curfile = nil -neededfile = fname:match('[^/]+$') +local init = 0 +local curfile = nil +local neededfile = fname:match('[^/]+$') +local declline = 0 +local declendpos = 0 +local curdir = nil +local is_needed_file = false while init ~= nil do - init = text:find('\n', init) + init = text:find('[\n;}]', init) if init == nil then break end + local init_is_nl = text:sub(init, init) == '\n' init = init + 1 - if text:sub(init, init) == '#' then - file = text:match(filepattern, init) + if init_is_nl and is_needed_file then + declline = declline + 1 + end + if init_is_nl and text:sub(init, init) == '#' then + local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file + is_needed_file = (curfile == neededfile) + declline = tonumber(line) - 1 + local curdir_start = dir:find('src/nvim/') + if curdir_start ~= nil then + curdir = dir:sub(curdir_start + #('src/nvim/')) + else + curdir = dir + end + else + declline = declline - 1 end - elseif curfile == neededfile then + elseif init < declendpos then + -- Skipping over declaration + elseif is_needed_file then s = init e = pattern:match(text, init) if e ~= nil then @@ -225,13 +247,17 @@ while init ~= nil do declaration = declaration:gsub(' ?(%*+) ?', ' %1') declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') - declaration = declaration .. ';\n' - if text:sub(s, s + 5) == 'static' then + declaration = declaration:gsub('^ ', '') + declaration = declaration .. ';' + declaration = declaration .. (' // %s/%s:%u'):format( + curdir, curfile, declline) + declaration = declaration .. '\n' + if declaration:sub(1, 6) == 'static' then static = static .. declaration else non_static = non_static .. declaration end - init = e + declendpos = e end end end diff --git a/scripts/gendispatch.lua b/scripts/genmsgpack.lua index c0291c55d3..86a051fb4c 100644 --- a/scripts/gendispatch.lua +++ b/scripts/genmsgpack.lua @@ -47,7 +47,16 @@ c_proto = Ct( grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) -- we need at least 4 arguments since the last two are output files -assert(#arg >= 3) +if arg[1] == '--help' then + print('Usage: genmsgpack.lua args') + print('Args: 1: source directory') + print(' 2: dispatch output file (dispatch_wrappers.generated.h)') + print(' 3: functions metadata output file (funcs_metadata.generated.h)') + print(' 4: API metadata output file (api_metadata.mpack)') + print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)') + print(' rest: C files where API functions are defined') +end +assert(#arg >= 4) functions = {} local nvimsrcdir = arg[1] @@ -58,17 +67,18 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path headers = {} -- output h file with generated dispatch functions -dispatch_outputf = arg[#arg-2] +dispatch_outputf = arg[2] -- output h file with packed metadata -funcs_metadata_outputf = arg[#arg-1] +funcs_metadata_outputf = arg[3] -- output metadata mpack file, for use by other build scripts -mpack_outputf = arg[#arg] +mpack_outputf = arg[4] +lua_c_bindings_outputf = arg[5] -- set of function names, used to detect duplicates function_names = {} -- read each input file, parse and append to the api metadata -for i = 2, #arg - 3 do +for i = 6, #arg do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do @@ -119,7 +129,9 @@ local deprecated_aliases = require("api.dispatch_deprecated") for i,f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, "nvim_") then - if f.since == nil then + if startswith(f.name, "nvim__") then + f.since = -1 + elseif f.since == nil then print("Function "..f.name.." lacks since field.\n") os.exit(1) end @@ -170,11 +182,13 @@ exported_attributes = {'name', 'parameters', 'return_type', 'method', 'since', 'deprecated_since'} exported_functions = {} for _,f in ipairs(functions) do - local f_exported = {} - for _,attr in ipairs(exported_attributes) do - f_exported[attr] = f[attr] + if not startswith(f.name, "nvim__") then + local f_exported = {} + for _,attr in ipairs(exported_attributes) do + f_exported[attr] = f[attr] + end + exported_functions[#exported_functions+1] = f_exported end - exported_functions[#exported_functions+1] = f_exported end @@ -212,6 +226,14 @@ local function real_type(type) return rv end +local function attr_name(rt) + if rt == 'Float' then + return 'floating' + else + return rt:lower() + end +end + -- start the handler functions. Visit each function metadata to build the -- handler function with code generated for validating arguments and calling to -- the real API. @@ -248,7 +270,7 @@ for i = 1, #functions do output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') else output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') - output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..attr_name(rt)..';') end if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages @@ -336,3 +358,155 @@ output:close() mpack_output = io.open(mpack_outputf, 'wb') mpack_output:write(mpack.pack(functions)) mpack_output:close() + +local function include_headers(output, headers) + for i = 1, #headers do + if headers[i]:sub(-12) ~= '.generated.h' then + output:write('\n#include "nvim/'..headers[i]..'"') + end + end +end + +local function write_shifted_output(output, str) + str = str:gsub('\n ', '\n') + str = str:gsub('^ ', '') + str = str:gsub(' +$', '') + output:write(str) +end + +-- start building lua output +output = io.open(lua_c_bindings_outputf, 'wb') + +output:write([[ +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/lua/converter.h" +]]) +include_headers(output, headers) +output:write('\n') + +lua_c_functions = {} + +local function process_function(fn) + lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name) + write_shifted_output(output, string.format([[ + + static int %s(lua_State *lstate) + { + Error err = ERROR_INIT; + if (lua_gettop(lstate) != %i) { + api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); + goto exit_0; + } + ]], lua_c_function_name, #fn.parameters, #fn.parameters, + (#fn.parameters == 1) and '' or 's')) + lua_c_functions[#lua_c_functions + 1] = { + binding=lua_c_function_name, + api=fn.name + } + local cparams = '' + local free_code = {} + for j = #fn.parameters,1,-1 do + param = fn.parameters[j] + cparam = string.format('arg%u', j) + param_type = real_type(param[1]) + lc_param_type = param_type:lower() + write_shifted_output(output, string.format([[ + const %s %s = nlua_pop_%s(lstate, &err); + + if (ERROR_SET(&err)) { + goto exit_%u; + } + ]], param[1], cparam, param_type, #fn.parameters - j)) + free_code[#free_code + 1] = ('api_free_%s(%s);'):format( + lc_param_type, cparam) + cparams = cparam .. ', ' .. cparams + end + if fn.receives_channel_id then + cparams = 'LUA_INTERNAL_CALL, ' .. cparams + end + if fn.can_fail then + cparams = cparams .. '&err' + else + cparams = cparams:gsub(', $', '') + end + local free_at_exit_code = '' + for i = 1, #free_code do + local rev_i = #free_code - i + 1 + local code = free_code[rev_i] + if i == 1 then + free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) + else + free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format( + rev_i, code) + end + end + local err_throw_code = [[ + + exit_0: + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, 2); + return lua_error(lstate); + } + ]] + if fn.return_type ~= 'void' then + if fn.return_type:match('^ArrayOf') then + return_type = 'Array' + else + return_type = fn.return_type + end + write_shifted_output(output, string.format([[ + const %s ret = %s(%s); + nlua_push_%s(lstate, ret); + api_free_%s(ret); + %s + %s + return 1; + ]], fn.return_type, fn.name, cparams, return_type, return_type:lower(), + free_at_exit_code, err_throw_code)) + else + write_shifted_output(output, string.format([[ + %s(%s); + %s + %s + return 0; + ]], fn.name, cparams, free_at_exit_code, err_throw_code)) + end + write_shifted_output(output, [[ + } + ]]) +end + +for _, fn in ipairs(functions) do + if not fn.noeval or fn.name:sub(1, 4) == '_vim' then + process_function(fn) + end +end + +output:write(string.format([[ +void nlua_add_api_functions(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, 0, %u); +]], #lua_c_functions)) +for _, func in ipairs(lua_c_functions) do + output:write(string.format([[ + + lua_pushcfunction(lstate, &%s); + lua_setfield(lstate, -2, "%s");]], func.binding, func.api)) +end +output:write([[ + + lua_setfield(lstate, -2, "api"); +} +]]) + +output:close() |