aboutsummaryrefslogtreecommitdiff
path: root/scripts/genmsgpack.lua
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/genmsgpack.lua')
-rw-r--r--scripts/genmsgpack.lua513
1 files changed, 0 insertions, 513 deletions
diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua
deleted file mode 100644
index c16a844586..0000000000
--- a/scripts/genmsgpack.lua
+++ /dev/null
@@ -1,513 +0,0 @@
-lpeg = require('lpeg')
-mpack = require('mpack')
-
--- lpeg grammar for building api metadata from a set of header files. It
--- ignores comments and preprocessor commands and parses a very small subset
--- of C prototypes with a limited set of types
-P, R, S = lpeg.P, lpeg.R, lpeg.S
-C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg
-
-any = P(1) -- (consume one character)
-letter = R('az', 'AZ') + S('_$')
-num = R('09')
-alpha = letter + num
-nl = P('\r\n') + P('\n')
-not_nl = any - nl
-ws = S(' \t') + nl
-fill = ws ^ 0
-c_comment = P('//') * (not_nl ^ 0)
-c_preproc = P('#') * (not_nl ^ 0)
-typed_container =
- (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')')
-c_id = (
- typed_container +
- (letter * (alpha ^ 0))
-)
-c_void = P('void')
-c_param_type = (
- ((P('Error') * fill * P('*') * fill) * Cc('error')) +
- (C(c_id) * (ws ^ 1))
- )
-c_type = (C(c_void) * (ws ^ 1)) + c_param_type
-c_param = Ct(c_param_type * C(c_id))
-c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0)
-c_params = Ct(c_void + c_param_list)
-c_proto = Ct(
- Cg(c_type, 'return_type') * Cg(c_id, 'name') *
- fill * P('(') * fill * Cg(c_params, 'parameters') * fill * P(')') *
- Cg(Cc(false), 'async') *
- (fill * Cg((P('FUNC_API_SINCE(') * C(num ^ 1)) * P(')'), 'since') ^ -1) *
- (fill * Cg((P('FUNC_API_DEPRECATED_SINCE(') * C(num ^ 1)) * P(')'),
- 'deprecated_since') ^ -1) *
- (fill * Cg((P('FUNC_API_ASYNC') * Cc(true)), 'async') ^ -1) *
- (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) *
- (fill * Cg((P('FUNC_API_NOEVAL') * Cc(true)), 'noeval') ^ -1) *
- fill * P(';')
- )
-grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
-
--- we need at least 4 arguments since the last two are output files
-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]
-package.path = nvimsrcdir .. '/?.lua;' .. package.path
-
--- names of all headers relative to the source root (for inclusion in the
--- generated file)
-headers = {}
-
--- output h file with generated dispatch functions
-dispatch_outputf = arg[2]
--- output h file with packed metadata
-funcs_metadata_outputf = arg[3]
--- output metadata mpack file, for use by other build scripts
-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 = 6, #arg do
- local full_path = arg[i]
- local parts = {}
- for part in string.gmatch(full_path, '[^/]+') do
- parts[#parts + 1] = part
- end
- headers[#headers + 1] = parts[#parts - 1]..'/'..parts[#parts]
-
- local input = io.open(full_path, 'rb')
-
- local tmp = grammar:match(input:read('*all'))
- for i = 1, #tmp do
- local fn = tmp[i]
- if not fn.noexport then
- functions[#functions + 1] = tmp[i]
- function_names[fn.name] = true
- if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then
- -- this function should receive the channel id
- fn.receives_channel_id = true
- -- remove the parameter since it won't be passed by the api client
- table.remove(fn.parameters, 1)
- end
- if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'error' then
- -- function can fail if the last parameter type is 'Error'
- fn.can_fail = true
- -- remove the error parameter, msgpack has it's own special field
- -- for specifying errors
- fn.parameters[#fn.parameters] = nil
- end
- end
- end
- input:close()
-end
-
-local function shallowcopy(orig)
- local copy = {}
- for orig_key, orig_value in pairs(orig) do
- copy[orig_key] = orig_value
- end
- return copy
-end
-
-local function startswith(String,Start)
- return string.sub(String,1,string.len(Start))==Start
-end
-
--- Export functions under older deprecated names.
--- These will be removed eventually.
-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 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
- f.since = tonumber(f.since)
- if f.deprecated_since ~= nil then
- f.deprecated_since = tonumber(f.deprecated_since)
- end
-
- if startswith(f.name, "nvim_buf_") then
- ismethod = true
- elseif startswith(f.name, "nvim_win_") then
- ismethod = true
- elseif startswith(f.name, "nvim_tabpage_") then
- ismethod = true
- end
- else
- f.noeval = true
- f.since = 0
- f.deprecated_since = 1
- end
- f.method = ismethod
- local newname = deprecated_aliases[f.name]
- if newname ~= nil then
- if function_names[newname] then
- -- duplicate
- print("Function "..f.name.." has deprecated alias\n"
- ..newname.." which has a separate implementation.\n"..
- "Please remove it from src/nvim/api/dispatch_deprecated.lua")
- os.exit(1)
- end
- local newf = shallowcopy(f)
- newf.name = newname
- if newname == "ui_try_resize" then
- -- The return type was incorrectly set to Object in 0.1.5.
- -- Keep it that way for clients that rely on this.
- newf.return_type = "Object"
- end
- newf.impl_name = f.name
- newf.noeval = true
- newf.since = 0
- newf.deprecated_since = 1
- functions[#functions+1] = newf
- end
-end
-
--- don't expose internal attributes like "impl_name" in public metadata
-exported_attributes = {'name', 'parameters', 'return_type', 'method',
- 'since', 'deprecated_since'}
-exported_functions = {}
-for _,f in ipairs(functions) do
- 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
-end
-
-
-funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb')
-funcs_metadata_output:write([[
-static const uint8_t funcs_metadata[] = {
-]])
-
--- serialize the API metadata using msgpack and embed into the resulting
--- binary for easy querying by clients
-packed_exported_functions = mpack.pack(exported_functions)
-for i = 1, #packed_exported_functions do
- funcs_metadata_output:write(string.byte(packed_exported_functions, i)..', ')
- if i % 10 == 0 then
- funcs_metadata_output:write('\n ')
- end
-end
-funcs_metadata_output:write([[
-};
-]])
-funcs_metadata_output:close()
-
--- start building the dispatch wrapper output
-output = io.open(dispatch_outputf, 'wb')
-
-local function real_type(type)
- local rv = type
- if typed_container:match(rv) then
- if rv:match('Array') then
- rv = 'Array'
- else
- rv = 'Dictionary'
- end
- end
- 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.
-for i = 1, #functions do
- local fn = functions[i]
- if fn.impl_name == nil then
- local args = {}
-
- output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
- output:write('\n{')
- output:write('\n Object ret = NIL;')
- -- Declare/initialize variables that will hold converted arguments
- for j = 1, #fn.parameters do
- local param = fn.parameters[j]
- local converted = 'arg_'..j
- output:write('\n '..param[1]..' '..converted..';')
- end
- output:write('\n')
- output:write('\n if (args.size != '..#fn.parameters..') {')
- output:write('\n api_set_error(error, kErrorTypeException, "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
-
- -- Validation/conversion for each argument
- for j = 1, #fn.parameters do
- local converted, convert_arg, param, arg
- param = fn.parameters[j]
- converted = 'arg_'..j
- local rt = real_type(param[1])
- if rt ~= 'Object' then
- if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') then
- -- Buffer, Window, and Tabpage have a specific type, but are stored in integer
- output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..' && args.items['..(j - 1)..'].data.integer >= 0) {')
- 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.'..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
- output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {')
- output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;')
- end
- output:write('\n } else {')
- output:write('\n api_set_error(error, kErrorTypeException, "Wrong type for argument '..j..', expecting '..param[1]..'");')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- else
- output:write('\n '..converted..' = args.items['..(j - 1)..'];\n')
- end
-
- args[#args + 1] = converted
- end
-
- -- function call
- local call_args = table.concat(args, ', ')
- output:write('\n ')
- if fn.return_type ~= 'void' then
- -- has a return value, prefix the call with a declaration
- output:write(fn.return_type..' rv = ')
- end
-
- -- write the function name and the opening parenthesis
- output:write(fn.name..'(')
-
- if fn.receives_channel_id then
- -- if the function receives the channel id, pass it as first argument
- if #args > 0 or fn.can_fail then
- output:write('channel_id, '..call_args)
- else
- output:write('channel_id')
- end
- else
- output:write(call_args)
- end
-
- if fn.can_fail then
- -- if the function can fail, also pass a pointer to the local error object
- if #args > 0 then
- output:write(', error);\n')
- else
- output:write('error);\n')
- end
- -- and check for the error
- output:write('\n if (ERROR_SET(error)) {')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- else
- output:write(');\n')
- end
-
- if fn.return_type ~= 'void' then
- output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);')
- end
- output:write('\n\ncleanup:');
-
- output:write('\n return ret;\n}\n\n');
- end
-end
-
--- Generate a function that initializes method names with handler functions
-output:write([[
-void msgpack_rpc_init_method_table(void)
-{
- methods = map_new(String, MsgpackRpcRequestHandler)();
-
-]])
-
-for i = 1, #functions do
- local fn = functions[i]
- output:write(' msgpack_rpc_add_method_handler('..
- '(String) {.data = "'..fn.name..'", '..
- '.size = sizeof("'..fn.name..'") - 1}, '..
- '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name)..
- ', .async = '..tostring(fn.async)..'});\n')
-
-end
-
-output:write('\n}\n\n')
-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()