diff options
Diffstat (limited to 'src/nvim/generators')
-rw-r--r-- | src/nvim/generators/c_grammar.lua | 2 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 49 | ||||
-rwxr-xr-x | src/nvim/generators/gen_declarations.lua | 2 | ||||
-rw-r--r-- | src/nvim/generators/gen_keysets.lua | 67 | ||||
-rw-r--r-- | src/nvim/generators/hashy.lua | 122 |
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 |