aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/generators/gen_api_dispatch.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/generators/gen_api_dispatch.lua')
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua990
1 files changed, 0 insertions, 990 deletions
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
deleted file mode 100644
index 378297d86a..0000000000
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ /dev/null
@@ -1,990 +0,0 @@
--- Example (manual) invocation:
---
--- make
--- cp build/nvim_version.lua src/nvim/
--- cd src/nvim
--- nvim -l generators/gen_api_dispatch.lua "../../build/src/nvim/auto/api/private/dispatch_wrappers.generated.h" "../../build/src/nvim/auto/api/private/api_metadata.generated.h" "../../build/funcs_metadata.mpack" "../../build/src/nvim/auto/lua_api_c_bindings.generated.h" "../../build/src/nvim/auto/keysets_defs.generated.h" "../../build/ui_metadata.mpack" "../../build/cmake.config/auto/versiondef_git.h" "./api/autocmd.h" "./api/buffer.h" "./api/command.h" "./api/deprecated.h" "./api/extmark.h" "./api/keysets_defs.h" "./api/options.h" "./api/tabpage.h" "./api/ui.h" "./api/vim.h" "./api/vimscript.h" "./api/win_config.h" "./api/window.h" "../../build/include/api/autocmd.h.generated.h" "../../build/include/api/buffer.h.generated.h" "../../build/include/api/command.h.generated.h" "../../build/include/api/deprecated.h.generated.h" "../../build/include/api/extmark.h.generated.h" "../../build/include/api/options.h.generated.h" "../../build/include/api/tabpage.h.generated.h" "../../build/include/api/ui.h.generated.h" "../../build/include/api/vim.h.generated.h" "../../build/include/api/vimscript.h.generated.h" "../../build/include/api/win_config.h.generated.h" "../../build/include/api/window.h.generated.h"
-
-local mpack = vim.mpack
-
-local hashy = require 'generators.hashy'
-
-local pre_args = 7
-assert(#arg >= pre_args)
--- output h file with generated dispatch functions (dispatch_wrappers.generated.h)
-local dispatch_outputf = arg[1]
--- output h file with packed metadata (api_metadata.generated.h)
-local api_metadata_outputf = arg[2]
--- output metadata mpack file, for use by other build scripts (funcs_metadata.mpack)
-local mpack_outputf = arg[3]
-local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c
-local keysets_outputf = arg[5] -- keysets_defs.generated.h
-local ui_metadata_inputf = arg[6] -- ui events metadata
-local git_version_inputf = arg[7] -- git version header
-
-local functions = {}
-
--- names of all headers relative to the source root (for inclusion in the
--- generated file)
-local headers = {}
-
--- set of function names, used to detect duplicates
-local function_names = {}
-
-local c_grammar = require('generators.c_grammar')
-
-local startswith = vim.startswith
-
-local function add_function(fn)
- local public = startswith(fn.name, 'nvim_') or fn.deprecated_since
- if public and not fn.noexport then
- functions[#functions + 1] = fn
- function_names[fn.name] = true
- if
- #fn.parameters >= 2
- and fn.parameters[2][1] == 'Array'
- and fn.parameters[2][2] == 'uidata'
- then
- -- function receives the "args" as a parameter
- fn.receives_array_args = true
- -- remove the args parameter
- table.remove(fn.parameters, 2)
- end
- 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
- if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then
- fn.has_lua_imp = true
- fn.parameters[#fn.parameters] = nil
- end
- if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then
- fn.receives_arena = true
- fn.parameters[#fn.parameters] = nil
- end
- end
-end
-
-local keysets = {}
-
-local function add_keyset(val)
- local keys = {}
- local types = {}
- local c_names = {}
- local is_set_name = 'is_set__' .. val.keyset_name .. '_'
- local has_optional = false
- for i, field in ipairs(val.fields) do
- local dict_key = field.dict_key or field.name
- if field.type ~= 'Object' then
- types[dict_key] = field.type
- end
- if field.name ~= is_set_name and field.type ~= 'OptionalKeys' then
- table.insert(keys, dict_key)
- if dict_key ~= field.name then
- c_names[dict_key] = field.name
- end
- else
- if i > 1 then
- error("'is_set__{type}_' must be first if present")
- elseif field.name ~= is_set_name then
- error(val.keyset_name .. ': name of first key should be ' .. is_set_name)
- elseif field.type ~= 'OptionalKeys' then
- error("'" .. is_set_name .. "' must have type 'OptionalKeys'")
- end
- has_optional = true
- end
- end
- table.insert(keysets, {
- name = val.keyset_name,
- keys = keys,
- c_names = c_names,
- types = types,
- has_optional = has_optional,
- })
-end
-
-local ui_options_text = nil
-
--- read each input file, parse and append to the api metadata
-for i = pre_args + 1, #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 = assert(io.open(full_path, 'rb'))
-
- local text = input:read('*all')
- local tmp = c_grammar.grammar:match(text)
- for j = 1, #tmp do
- local val = tmp[j]
- if val.keyset_name then
- add_keyset(val)
- elseif val.name then
- add_function(val)
- end
- end
-
- ui_options_text = ui_options_text or string.match(text, 'ui_ext_names%[][^{]+{([^}]+)}')
- 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
-
--- Export functions under older deprecated names.
--- These will be removed eventually.
-local deprecated_aliases = require('api.dispatch_deprecated')
-for _, f in ipairs(shallowcopy(functions)) do
- local ismethod = false
- if startswith(f.name, 'nvim_') then
- if startswith(f.name, 'nvim__') or f.name == 'nvim_error_event' 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
- f.remote = f.remote_only or not f.lua_only
- f.lua = f.lua_only or not f.remote_only
- f.eval = (not f.lua_only) and not f.remote_only
- else
- f.deprecated_since = tonumber(f.deprecated_since)
- assert(f.deprecated_since == 1)
- f.remote = true
- f.since = 0
- 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.lua = false
- newf.eval = false
- newf.since = 0
- newf.deprecated_since = 1
- functions[#functions + 1] = newf
- end
-end
-
--- don't expose internal attributes like "impl_name" in public metadata
-local exported_attributes = { 'name', 'return_type', 'method', 'since', 'deprecated_since' }
-local exported_functions = {}
-for _, f in ipairs(functions) do
- if not (startswith(f.name, 'nvim__') or f.name == 'nvim_error_event' or f.name == 'redraw') then
- local f_exported = {}
- for _, attr in ipairs(exported_attributes) do
- f_exported[attr] = f[attr]
- end
- f_exported.parameters = {}
- for i, param in ipairs(f.parameters) do
- if param[1] == 'DictOf(LuaRef)' then
- param = { 'Dict', param[2] }
- elseif startswith(param[1], 'Dict(') then
- param = { 'Dict', param[2] }
- end
- f_exported.parameters[i] = param
- end
- if startswith(f.return_type, 'Dict(') then
- f_exported.return_type = 'Dict'
- end
- exported_functions[#exported_functions + 1] = f_exported
- end
-end
-
-local ui_options = { 'rgb' }
-for x in string.gmatch(ui_options_text, '"([a-z][a-z_]+)"') do
- table.insert(ui_options, x)
-end
-
-local version = require 'nvim_version' -- `build/nvim_version.lua` file.
-local git_version = io.open(git_version_inputf):read '*a'
-local version_build = string.match(git_version, '#define NVIM_VERSION_BUILD "([^"]+)"') or vim.NIL
-
--- serialize the API metadata using msgpack and embed into the resulting
--- binary for easy querying by clients
-local api_metadata_output = assert(io.open(api_metadata_outputf, 'wb'))
-local pieces = {}
-
--- Naively using mpack.encode({foo=x, bar=y}) will make the build
--- "non-reproducible". Emit maps directly as FIXDICT(2) "foo" x "bar" y instead
-local function fixdict(num)
- if num > 15 then
- error 'implement more dict codes'
- end
- table.insert(pieces, string.char(128 + num))
-end
-local function put(item, item2)
- table.insert(pieces, mpack.encode(item))
- if item2 ~= nil then
- table.insert(pieces, mpack.encode(item2))
- end
-end
-
-fixdict(6)
-
-put('version')
-fixdict(1 + #version)
-for _, item in ipairs(version) do
- -- NB: all items are mandatory. But any error will be less confusing
- -- with placeholder vim.NIL (than invalid mpack data)
- local val = item[2] == nil and vim.NIL or item[2]
- put(item[1], val)
-end
-put('build', version_build)
-
-put('functions', exported_functions)
-put('ui_events')
-table.insert(pieces, io.open(ui_metadata_inputf, 'rb'):read('*all'))
-put('ui_options', ui_options)
-
-put('error_types')
-fixdict(2)
-put('Exception', { id = 0 })
-put('Validation', { id = 1 })
-
-put('types')
-local types =
- { { 'Buffer', 'nvim_buf_' }, { 'Window', 'nvim_win_' }, { 'Tabpage', 'nvim_tabpage_' } }
-fixdict(#types)
-for i, item in ipairs(types) do
- put(item[1])
- fixdict(2)
- put('id', i - 1)
- put('prefix', item[2])
-end
-
-local packed = table.concat(pieces)
-local dump_bin_array = require('generators.dump_bin_array')
-dump_bin_array(api_metadata_output, 'packed_api_metadata', packed)
-api_metadata_output:close()
-
--- start building the dispatch wrapper output
-local output = assert(io.open(dispatch_outputf, 'wb'))
-
-local keysets_defs = assert(io.open(keysets_outputf, 'wb'))
-
--- ===========================================================================
--- NEW API FILES MUST GO HERE.
---
--- When creating a new API file, you must include it here,
--- so that the dispatcher can find the C functions that you are creating!
--- ===========================================================================
-output:write([[
-#include "nvim/errors.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/globals.h"
-#include "nvim/log.h"
-#include "nvim/map_defs.h"
-
-#include "nvim/api/autocmd.h"
-#include "nvim/api/buffer.h"
-#include "nvim/api/command.h"
-#include "nvim/api/deprecated.h"
-#include "nvim/api/extmark.h"
-#include "nvim/api/options.h"
-#include "nvim/api/tabpage.h"
-#include "nvim/api/ui.h"
-#include "nvim/api/vim.h"
-#include "nvim/api/vimscript.h"
-#include "nvim/api/win_config.h"
-#include "nvim/api/window.h"
-#include "nvim/ui_client.h"
-
-]])
-
-keysets_defs:write('// IWYU pragma: private, include "nvim/api/private/dispatch.h"\n\n')
-
-for _, k in ipairs(keysets) do
- local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function(idx)
- return k.name .. '_table[' .. idx .. '].str'
- end)
-
- keysets_defs:write('extern KeySetLink ' .. k.name .. '_table[' .. (1 + #neworder) .. '];\n')
-
- local function typename(type)
- if type == 'HLGroupID' then
- return 'kObjectTypeInteger'
- elseif not type or vim.startswith(type, 'Union') then
- return 'kObjectTypeNil'
- elseif vim.startswith(type, 'LuaRefOf') then
- return 'kObjectTypeLuaRef'
- elseif type == 'StringArray' then
- return 'kUnpackTypeStringArray'
- elseif vim.startswith(type, 'ArrayOf') then
- return 'kObjectTypeArray'
- else
- return 'kObjectType' .. type
- end
- end
-
- output:write('KeySetLink ' .. k.name .. '_table[] = {\n')
- for i, key in ipairs(neworder) do
- local ind = -1
- if k.has_optional then
- ind = i
- keysets_defs:write('#define KEYSET_OPTIDX_' .. k.name .. '__' .. key .. ' ' .. ind .. '\n')
- end
- output:write(
- ' {"'
- .. key
- .. '", offsetof(KeyDict_'
- .. k.name
- .. ', '
- .. (k.c_names[key] or key)
- .. '), '
- .. typename(k.types[key])
- .. ', '
- .. ind
- .. ', '
- .. (k.types[key] == 'HLGroupID' and 'true' or 'false')
- .. '},\n'
- )
- end
- output:write(' {NULL, 0, kObjectTypeNil, -1, false},\n')
- output:write('};\n\n')
-
- output:write(hashfun)
-
- output:write([[
-KeySetLink *KeyDict_]] .. k.name .. [[_get_field(const char *str, size_t len)
-{
- int hash = ]] .. k.name .. [[_hash(str, len);
- if (hash == -1) {
- return NULL;
- }
- return &]] .. k.name .. [[_table[hash];
-}
-
-]])
-end
-
-local function real_type(type)
- local rv = type
- local rmatch = string.match(type, 'Dict%(([_%w]+)%)')
- if rmatch then
- return 'KeyDict_' .. rmatch
- elseif c_grammar.typed_container:match(rv) then
- if rv:match('Array') then
- rv = 'Array'
- else
- rv = 'Dict'
- 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 and fn.remote then
- local args = {}
-
- output:write(
- 'Object handle_' .. fn.name .. '(uint64_t channel_id, Array args, Arena* arena, Error *error)'
- )
- output:write('\n{')
- output:write('\n#ifdef NVIM_LOG_DEBUG')
- output:write('\n DLOG("RPC: ch %" PRIu64 ": invoke ' .. fn.name .. '", channel_id);')
- output:write('\n#endif')
- 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 rt = real_type(param[1])
- local converted = 'arg_' .. j
- output:write('\n ' .. rt .. ' ' .. converted .. ';')
- end
- output:write('\n')
- if not fn.receives_array_args then
- 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')
- end
-
- -- Validation/conversion for each argument
- for j = 1, #fn.parameters do
- local converted, param
- param = fn.parameters[j]
- converted = 'arg_' .. j
- local rt = real_type(param[1])
- if rt == 'Object' then
- output:write('\n ' .. converted .. ' = args.items[' .. (j - 1) .. '];\n')
- elseif rt:match('^KeyDict_') then
- converted = '&' .. converted
- output:write('\n if (args.items[' .. (j - 1) .. '].type == kObjectTypeDict) {') --luacheck: ignore 631
- output:write('\n memset(' .. converted .. ', 0, sizeof(*' .. converted .. '));') -- TODO: neeeee
- output:write(
- '\n if (!api_dict_to_keydict('
- .. converted
- .. ', '
- .. rt
- .. '_get_field, args.items['
- .. (j - 1)
- .. '].data.dict, error)) {'
- )
- output:write('\n goto cleanup;')
- output:write('\n }')
- output:write(
- '\n } else if (args.items['
- .. (j - 1)
- .. '].type == kObjectTypeArray && args.items['
- .. (j - 1)
- .. '].data.array.size == 0) {'
- ) --luacheck: ignore 631
- output:write('\n memset(' .. converted .. ', 0, sizeof(*' .. converted .. '));')
-
- output:write('\n } else {')
- output:write(
- '\n api_set_error(error, kErrorTypeException, \
- "Wrong type for argument '
- .. j
- .. ' when calling '
- .. fn.name
- .. ', expecting '
- .. param[1]
- .. '");'
- )
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- else
- 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
- if rt:match('^Float$') then
- -- accept integers for Floats
- output:write('\n } else if (args.items[' .. (j - 1) .. '].type == kObjectTypeInteger) {')
- output:write(
- '\n ' .. converted .. ' = (Float)args.items[' .. (j - 1) .. '].data.integer;'
- )
- end
- -- accept empty lua tables as empty dictionaries
- if rt:match('^Dict') then
- output:write(
- '\n } else if (args.items['
- .. (j - 1)
- .. '].type == kObjectTypeArray && args.items['
- .. (j - 1)
- .. '].data.array.size == 0) {'
- ) --luacheck: ignore 631
- output:write('\n ' .. converted .. ' = (Dict)ARRAY_DICT_INIT;')
- end
- output:write('\n } else {')
- output:write(
- '\n api_set_error(error, kErrorTypeException, \
- "Wrong type for argument '
- .. j
- .. ' when calling '
- .. fn.name
- .. ', expecting '
- .. param[1]
- .. '");'
- )
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- end
- args[#args + 1] = converted
- end
-
- if fn.textlock then
- output:write('\n if (text_locked()) {')
- output:write('\n api_set_error(error, kErrorTypeException, "%s", get_text_locked_msg());')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- elseif fn.textlock_allow_cmdwin then
- output:write('\n if (textlock != 0 || expr_map_locked()) {')
- output:write('\n api_set_error(error, kErrorTypeException, "%s", e_textlock);')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- end
-
- -- function call
- 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 .. '(')
-
- local call_args = {}
- if fn.receives_channel_id then
- table.insert(call_args, 'channel_id')
- end
-
- if fn.receives_array_args then
- table.insert(call_args, 'args')
- end
-
- for _, a in ipairs(args) do
- table.insert(call_args, a)
- end
-
- if fn.receives_arena then
- table.insert(call_args, 'arena')
- end
-
- if fn.has_lua_imp then
- table.insert(call_args, 'NULL')
- end
-
- if fn.can_fail then
- table.insert(call_args, 'error')
- end
-
- output:write(table.concat(call_args, ', '))
- output:write(');\n')
-
- if fn.can_fail then
- -- if the function can fail, also pass a pointer to the local error object
- -- and check for the error
- output:write('\n if (ERROR_SET(error)) {')
- output:write('\n goto cleanup;')
- output:write('\n }\n')
- end
-
- local ret_type = real_type(fn.return_type)
- if string.match(ret_type, '^KeyDict_') then
- local table = string.sub(ret_type, 9) .. '_table'
- output:write(
- '\n ret = DICT_OBJ(api_keydict_to_dict(&rv, '
- .. table
- .. ', ARRAY_SIZE('
- .. table
- .. '), arena));'
- )
- elseif ret_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
-
-local remote_fns = {}
-for _, fn in ipairs(functions) do
- if fn.remote then
- remote_fns[fn.name] = fn
- end
-end
-remote_fns.redraw = { impl_name = 'ui_client_redraw', fast = true }
-
-local names = vim.tbl_keys(remote_fns)
-table.sort(names)
-local hashorder, hashfun = hashy.hashy_hash('msgpack_rpc_get_handler_for', names, function(idx)
- return 'method_handlers[' .. idx .. '].name'
-end)
-
-output:write('const MsgpackRpcRequestHandler method_handlers[] = {\n')
-for n, name in ipairs(hashorder) do
- local fn = remote_fns[name]
- fn.handler_id = n - 1
- output:write(
- ' { .name = "'
- .. name
- .. '", .fn = handle_'
- .. (fn.impl_name or fn.name)
- .. ', .fast = '
- .. tostring(fn.fast)
- .. ', .ret_alloc = '
- .. tostring(not not fn.ret_alloc)
- .. '},\n'
- )
-end
-output:write('};\n\n')
-output:write(hashfun)
-
-output:close()
-
-functions.keysets = keysets
-local mpack_output = assert(io.open(mpack_outputf, 'wb'))
-mpack_output:write(mpack.encode(functions))
-mpack_output:close()
-
-local function include_headers(output_handle, headers_to_include)
- for i = 1, #headers_to_include do
- if headers_to_include[i]:sub(-12) ~= '.generated.h' then
- output_handle:write('\n#include "nvim/' .. headers_to_include[i] .. '"')
- end
- end
-end
-
-local function write_shifted_output(str, ...)
- str = str:gsub('\n ', '\n')
- str = str:gsub('^ ', '')
- str = str:gsub(' +$', '')
- output:write(string.format(str, ...))
-end
-
--- start building lua output
-output = assert(io.open(lua_c_bindings_outputf, 'wb'))
-
-include_headers(output, headers)
-output:write('\n')
-
-local lua_c_functions = {}
-
-local function process_function(fn)
- local lua_c_function_name = ('nlua_api_%s'):format(fn.name)
- write_shifted_output(
- [[
-
- static int %s(lua_State *lstate)
- {
- Error err = ERROR_INIT;
- Arena arena = ARENA_EMPTY;
- char *err_param = 0;
- 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,
- }
-
- if not fn.fast then
- write_shifted_output(
- [[
- if (!nlua_is_deferred_safe()) {
- return luaL_error(lstate, e_fast_api_disabled, "%s");
- }
- ]],
- fn.name
- )
- end
-
- if fn.textlock then
- write_shifted_output([[
- if (text_locked()) {
- api_set_error(&err, kErrorTypeException, "%%s", get_text_locked_msg());
- goto exit_0;
- }
- ]])
- elseif fn.textlock_allow_cmdwin then
- write_shifted_output([[
- if (textlock != 0 || expr_map_locked()) {
- api_set_error(&err, kErrorTypeException, "%%s", e_textlock);
- goto exit_0;
- }
- ]])
- end
-
- local cparams = ''
- local free_code = {}
- for j = #fn.parameters, 1, -1 do
- local param = fn.parameters[j]
- local cparam = string.format('arg%u', j)
- local param_type = real_type(param[1])
- local extra = param_type == 'Dict' and 'false, ' or ''
- local arg_free_code = ''
- if param[1] == 'Object' then
- extra = 'true, '
- arg_free_code = ' api_luarefs_free_object(' .. cparam .. ');'
- elseif param[1] == 'DictOf(LuaRef)' then
- extra = 'true, '
- arg_free_code = ' api_luarefs_free_dict(' .. cparam .. ');'
- elseif param[1] == 'LuaRef' then
- arg_free_code = ' api_free_luaref(' .. cparam .. ');'
- end
- local errshift = 0
- local seterr = ''
- if string.match(param_type, '^KeyDict_') then
- write_shifted_output(
- [[
- %s %s = KEYDICT_INIT;
- nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &arena, &err);
- ]],
- param_type,
- cparam,
- cparam,
- param_type
- )
- cparam = '&' .. cparam
- errshift = 1 -- free incomplete dict on error
- arg_free_code = ' api_luarefs_free_keydict('
- .. cparam
- .. ', '
- .. string.sub(param_type, 9)
- .. '_table);'
- else
- write_shifted_output(
- [[
- const %s %s = nlua_pop_%s(lstate, %s&arena, &err);]],
- param[1],
- cparam,
- param_type,
- extra
- )
- seterr = '\n err_param = "' .. param[2] .. '";'
- end
-
- write_shifted_output([[
-
- if (ERROR_SET(&err)) {]] .. seterr .. [[
-
- goto exit_%u;
- }
-
- ]], #fn.parameters - j + errshift)
- free_code[#free_code + 1] = arg_free_code
- cparams = cparam .. ', ' .. cparams
- end
- if fn.receives_channel_id then
- cparams = 'LUA_INTERNAL_CALL, ' .. cparams
- end
- if fn.receives_arena then
- cparams = cparams .. '&arena, '
- end
-
- if fn.has_lua_imp then
- cparams = cparams .. 'lstate, '
- 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 and not string.match(real_type(fn.parameters[1][1]), '^KeyDict_') then
- free_at_exit_code = free_at_exit_code .. ('\n%s'):format(code)
- else
- free_at_exit_code = free_at_exit_code .. ('\nexit_%u:\n%s'):format(rev_i, code)
- end
- end
- local err_throw_code = [[
-
-exit_0:
- arena_mem_free(arena_finish(&arena));
- if (ERROR_SET(&err)) {
- luaL_where(lstate, 1);
- if (err_param) {
- lua_pushstring(lstate, "Invalid '");
- lua_pushstring(lstate, err_param);
- lua_pushstring(lstate, "': ");
- }
- lua_pushstring(lstate, err.msg);
- api_clear_error(&err);
- lua_concat(lstate, err_param ? 5 : 2);
- return lua_error(lstate);
- }
-]]
- local return_type
- if fn.return_type ~= 'void' then
- if fn.return_type:match('^ArrayOf') then
- return_type = 'Array'
- else
- return_type = fn.return_type
- end
- local free_retval = ''
- if fn.ret_alloc then
- free_retval = ' api_free_' .. return_type:lower() .. '(ret);'
- end
- write_shifted_output(' %s ret = %s(%s);\n', fn.return_type, fn.name, cparams)
-
- local ret_type = real_type(fn.return_type)
- local ret_mode = (ret_type == 'Object') and '&' or ''
- if fn.has_lua_imp then
- -- only push onto the Lua stack if we haven't already
- write_shifted_output(
- [[
- if (lua_gettop(lstate) == 0) {
- nlua_push_%s(lstate, %sret, kNluaPushSpecial | kNluaPushFreeRefs);
- }
- ]],
- return_type,
- ret_mode
- )
- elseif string.match(ret_type, '^KeyDict_') then
- write_shifted_output(
- ' nlua_push_keydict(lstate, &ret, %s_table);\n',
- string.sub(ret_type, 9)
- )
- else
- local special = (fn.since ~= nil and fn.since < 11)
- write_shifted_output(
- ' nlua_push_%s(lstate, %sret, %s | kNluaPushFreeRefs);\n',
- return_type,
- ret_mode,
- special and 'kNluaPushSpecial' or '0'
- )
- end
-
- -- NOTE: we currently assume err_throw needs nothing from arena
- write_shifted_output(
- [[
- %s
- %s
- %s
- return 1;
- ]],
- free_retval,
- free_at_exit_code,
- err_throw_code
- )
- else
- write_shifted_output(
- [[
- %s(%s);
- %s
- %s
- return 0;
- ]],
- fn.name,
- cparams,
- free_at_exit_code,
- err_throw_code
- )
- end
- write_shifted_output([[
- }
- ]])
-end
-
-for _, fn in ipairs(functions) do
- if fn.lua 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)
-{
- 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()
-keysets_defs:close()