aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/generators
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/generators')
-rw-r--r--src/nvim/generators/c_grammar.lua2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua49
-rwxr-xr-xsrc/nvim/generators/gen_declarations.lua2
-rw-r--r--src/nvim/generators/gen_keysets.lua67
-rw-r--r--src/nvim/generators/hashy.lua122
5 files changed, 231 insertions, 11 deletions
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index c9ab0cf709..a5f76e1c6a 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -17,7 +17,7 @@ local fill = ws ^ 0
local c_comment = P('//') * (not_nl ^ 0)
local c_preproc = P('#') * (not_nl ^ 0)
local typed_container =
- (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')')
+ (P('ArrayOf(') + P('DictionaryOf(') + P('Dict(')) * ((any - P(')')) ^ 1) * P(')')
local c_id = (
typed_container +
(letter * (alpha ^ 0))
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 99d80cdebc..6ed2e5dab8 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -152,6 +152,8 @@ for _,f in ipairs(functions) do
for i,param in ipairs(f.parameters) do
if param[1] == "DictionaryOf(LuaRef)" then
param = {"Dictionary", param[2]}
+ elseif startswith(param[1], "Dict(") then
+ param = {"Dictionary", param[2]}
end
f_exported.parameters[i] = param
end
@@ -173,7 +175,10 @@ local output = io.open(dispatch_outputf, 'wb')
local function real_type(type)
local rv = type
- if c_grammar.typed_container:match(rv) then
+ 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
@@ -209,8 +214,9 @@ for i = 1, #functions do
-- 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 '..param[1]..' '..converted..';')
+ output:write('\n '..rt..' '..converted..';')
end
output:write('\n')
output:write('\n if (args.size != '..#fn.parameters..') {')
@@ -225,7 +231,24 @@ for i = 1, #functions do
param = fn.parameters[j]
converted = 'arg_'..j
local rt = real_type(param[1])
- if rt ~= 'Object' then
+ 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 == kObjectTypeDictionary) {') --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.dictionary, 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['..
@@ -257,10 +280,7 @@ for i = 1, #functions do
"Wrong type for argument '..j..' when calling '..fn.name..', 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
@@ -423,13 +443,24 @@ local function process_function(fn)
if param[1] == "DictionaryOf(LuaRef)" then
extra = "true, "
end
+ local errshift = 0
+ if string.match(param_type, '^KeyDict_') then
+ write_shifted_output(output, string.format([[
+ %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, %s&err);]], param_type, cparam, cparam, param_type, extra))
+ cparam = '&'..cparam
+ errshift = 1 -- free incomplete dict on error
+ else
+ write_shifted_output(output, string.format([[
+ const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra))
+ end
+
write_shifted_output(output, string.format([[
- const %s %s = nlua_pop_%s(lstate, %s&err);
if (ERROR_SET(&err)) {
goto exit_%u;
}
- ]], param[1], cparam, param_type, extra, #fn.parameters - j))
+
+ ]], #fn.parameters - j + errshift))
free_code[#free_code + 1] = ('api_free_%s(%s);'):format(
lc_param_type, cparam)
cparams = cparam .. ', ' .. cparams
@@ -446,7 +477,7 @@ local function process_function(fn)
for i = 1, #free_code do
local rev_i = #free_code - i + 1
local code = free_code[rev_i]
- if i == 1 then
+ 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 .. ('\n exit_%u:\n %s'):format(
diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua
index 0782c8115d..97491679a4 100755
--- a/src/nvim/generators/gen_declarations.lua
+++ b/src/nvim/generators/gen_declarations.lua
@@ -60,7 +60,7 @@ local right_word = concat(
)
local word = branch(
concat(
- branch(lit('ArrayOf('), lit('DictionaryOf(')), -- typed container macro
+ branch(lit('ArrayOf('), lit('DictionaryOf('), lit('Dict(')), -- typed container macro
one_or_more(any_character - lit(')')),
lit(')')
),
diff --git a/src/nvim/generators/gen_keysets.lua b/src/nvim/generators/gen_keysets.lua
new file mode 100644
index 0000000000..63ef202fe1
--- /dev/null
+++ b/src/nvim/generators/gen_keysets.lua
@@ -0,0 +1,67 @@
+
+local nvimsrcdir = arg[1]
+local shared_file = arg[2]
+local funcs_file = arg[3]
+local defs_file = arg[4]
+
+_G.vim = loadfile(shared_file)()
+
+if nvimsrcdir == '--help' then
+ print([[
+Usage:
+ lua gen_keyset.lua TODOFIXUPDATETHIS
+
+Will generate build/src/nvim/auto/keyset.generated.h with definition of functions
+static const array.
+]])
+ os.exit(0)
+end
+
+
+package.path = nvimsrcdir .. '/?.lua;' .. package.path
+local hashy = require'generators.hashy'
+
+local funcspipe = io.open(funcs_file, 'wb')
+local defspipe = io.open(defs_file, 'wb')
+
+local keysets = require'api.keysets'
+
+for name, keys in pairs(keysets) do
+ local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
+ return name.."_table["..idx.."].str"
+ end)
+
+ defspipe:write("typedef struct {\n")
+ for _, key in ipairs(neworder) do
+ defspipe:write(" Object "..key..";\n")
+ end
+ defspipe:write("} KeyDict_"..name..";\n\n")
+
+ defspipe:write("extern KeySetLink "..name.."_table[];\n")
+
+ funcspipe:write("KeySetLink "..name.."_table[] = {\n")
+ for _, key in ipairs(neworder) do
+ funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..key..")},\n")
+ end
+ funcspipe:write(' {NULL, 0},\n')
+ funcspipe:write("};\n\n")
+
+ funcspipe:write(hashfun)
+
+ funcspipe:write([[
+Object *KeyDict_]]..name..[[_get_field(void *retval, const char *str, size_t len)
+{
+ int hash = ]]..name..[[_hash(str, len);
+ if (hash == -1) {
+ return NULL;
+ }
+
+ return (Object *)((char *)retval + ]]..name..[[_table[hash].ptr_off);
+}
+
+]])
+ defspipe:write("#define api_free_keydict_"..name.."(x) api_free_keydict(x, "..name.."_table)\n")
+end
+
+funcspipe:close()
+defspipe:close()
diff --git a/src/nvim/generators/hashy.lua b/src/nvim/generators/hashy.lua
new file mode 100644
index 0000000000..fac24c810a
--- /dev/null
+++ b/src/nvim/generators/hashy.lua
@@ -0,0 +1,122 @@
+-- HASHY McHASHFACE
+
+local M = {}
+_G.d = M
+
+
+local function setdefault(table, key)
+ local val = table[key]
+ if val == nil then
+ val = {}
+ table[key] = val
+ end
+ return val
+end
+
+function M.build_pos_hash(strings)
+ local len_buckets = {}
+ local maxlen = 0
+ for _,s in ipairs(strings) do
+ table.insert(setdefault(len_buckets, #s),s)
+ if #s > maxlen then maxlen = #s end
+ end
+
+ local len_pos_buckets = {}
+ local worst_buck_size = 0
+
+ for len = 1,maxlen do
+ local strs = len_buckets[len]
+ if strs then
+ -- the best position so far generates `best_bucket`
+ -- with `minsize` worst case collisions
+ local bestpos, minsize, best_bucket = nil, #strs*2, nil
+ for pos = 1,len do
+ local try_bucket = {}
+ for _,str in ipairs(strs) do
+ local poschar = string.sub(str, pos, pos)
+ table.insert(setdefault(try_bucket, poschar), str)
+ end
+ local maxsize = 1
+ for _,pos_strs in pairs(try_bucket) do
+ maxsize = math.max(maxsize, #pos_strs)
+ end
+ if maxsize < minsize then
+ bestpos = pos
+ minsize = maxsize
+ best_bucket = try_bucket
+ end
+ end
+ len_pos_buckets[len] = {bestpos, best_bucket}
+ worst_buck_size = math.max(worst_buck_size, minsize)
+ end
+ end
+ return len_pos_buckets, maxlen, worst_buck_size
+end
+
+function M.switcher(put, tab, maxlen, worst_buck_size)
+ local neworder = {}
+ put " switch (len) {\n"
+ local bucky = worst_buck_size > 1
+ for len = 1,maxlen do
+ local vals = tab[len]
+ if vals then
+ put(" case "..len..": ")
+ local pos, posbuck = unpack(vals)
+ local keys = vim.tbl_keys(posbuck)
+ if #keys > 1 then
+ table.sort(keys)
+ put("switch (str["..(pos-1).."]) {\n")
+ for _,c in ipairs(keys) do
+ local buck = posbuck[c]
+ local startidx = #neworder
+ vim.list_extend(neworder, buck)
+ local endidx = #neworder
+ put(" case '"..c.."': ")
+ put("low = "..startidx.."; ")
+ if bucky then put("high = "..endidx.."; ") end
+ put "break;\n"
+ end
+ put " default: break;\n"
+ put " }\n "
+ else
+ local startidx = #neworder
+ table.insert(neworder, posbuck[keys[1]][1])
+ local endidx = #neworder
+ put("low = "..startidx.."; ")
+ if bucky then put("high = "..endidx.."; ") end
+ end
+ put " break;\n"
+ end
+ end
+ put " default: break;\n"
+ put " }\n"
+ return neworder
+end
+
+function M.hashy_hash(name, strings, access)
+ local stats = {}
+ local put = function(str) table.insert(stats, str) end
+ local len_pos_buckets, maxlen, worst_buck_size = M.build_pos_hash(strings)
+ put("int "..name.."_hash(const char *str, size_t len)\n{\n")
+ if worst_buck_size > 1 then
+ put(" int low = 0, high = 0;\n")
+ else
+ put(" int low = -1;\n")
+ end
+ local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
+ if worst_buck_size > 1 then
+ error [[ not implemented yet ]] -- TODO(bfredl)
+ else
+ put [[
+ if (low < 0) {
+ return -1;
+ }
+ ]]
+ put("if(memcmp(str, "..access("low")..", len)) {\n return -1;\n }\n")
+ put " return low;\n"
+ put "}\n\n"
+ end
+ return neworder, table.concat(stats)
+end
+
+return M