From f98a3d85ed2f34a62300097fd30b393a3b3be393 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 11 Apr 2017 01:09:36 +0300 Subject: lua: Move files from src/nvim/viml/executor to src/nvim/lua --- scripts/genmsgpack.lua | 2 +- src/nvim/CMakeLists.txt | 7 +- src/nvim/eval.c | 2 +- src/nvim/ex_docmd.c | 2 +- src/nvim/lua/converter.c | 1194 ++++++++++++++++++++++++++++++++++++ src/nvim/lua/converter.h | 15 + src/nvim/lua/executor.c | 576 +++++++++++++++++ src/nvim/lua/executor.h | 25 + src/nvim/lua/vim.lua | 2 + src/nvim/viml/executor/converter.c | 1194 ------------------------------------ src/nvim/viml/executor/converter.h | 15 - src/nvim/viml/executor/executor.c | 576 ----------------- src/nvim/viml/executor/executor.h | 25 - src/nvim/viml/executor/vim.lua | 2 - 14 files changed, 1818 insertions(+), 1819 deletions(-) create mode 100644 src/nvim/lua/converter.c create mode 100644 src/nvim/lua/converter.h create mode 100644 src/nvim/lua/executor.c create mode 100644 src/nvim/lua/executor.h create mode 100644 src/nvim/lua/vim.lua delete mode 100644 src/nvim/viml/executor/converter.c delete mode 100644 src/nvim/viml/executor/converter.h delete mode 100644 src/nvim/viml/executor/executor.c delete mode 100644 src/nvim/viml/executor/executor.h delete mode 100644 src/nvim/viml/executor/vim.lua diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index 2a7cb4bacf..c3b3f42618 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -377,7 +377,7 @@ output:write([[ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/viml/executor/converter.h" +#include "nvim/lua/converter.h" ]]) include_headers(output, headers) output:write('\n') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index cec9a09141..1401029cf5 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -33,8 +33,8 @@ set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) -set(VIM_MODULE_FILE ${GENERATED_DIR}/viml/executor/vim_module.generated.h) -set(VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/viml/executor/vim.lua) +set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) +set(VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua) set(CHAR_BLOB_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gencharblob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") @@ -72,8 +72,7 @@ foreach(subdir tui event eval - viml - viml/executor + lua ) if(${subdir} MATCHES "tui" AND NOT FEAT_TUI) continue() diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 75cd100061..e60cc4e218 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -94,7 +94,7 @@ #include "nvim/lib/kvec.h" #include "nvim/lib/khash.h" #include "nvim/lib/queue.h" -#include "nvim/viml/executor/executor.h" +#include "nvim/lua/executor.h" #include "nvim/eval/typval.h" #include "nvim/eval/executor.h" #include "nvim/eval/gc.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e44d759e04..b7bf6b4728 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -67,7 +67,7 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/shada.h" -#include "nvim/viml/executor/executor.h" +#include "nvim/lua/executor.h" #include "nvim/globals.h" static int quitmore = 0; diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c new file mode 100644 index 0000000000..348315124b --- /dev/null +++ b/src/nvim/lua/converter.c @@ -0,0 +1,1194 @@ +#include +#include +#include +#include +#include +#include + +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/func_attr.h" +#include "nvim/memory.h" +#include "nvim/assert.h" +// FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is +// redefined +#include "nvim/vim.h" +#include "nvim/globals.h" +#include "nvim/message.h" +#include "nvim/eval/typval.h" +#include "nvim/ascii.h" +#include "nvim/macros.h" + +#include "nvim/lib/kvec.h" +#include "nvim/eval/decode.h" + +#include "nvim/lua/converter.h" +#include "nvim/lua/executor.h" + +/// Determine, which keys lua table contains +typedef struct { + size_t maxidx; ///< Maximum positive integral value found. + size_t string_keys_num; ///< Number of string keys. + bool has_string_with_nul; ///< True if there is string key with NUL byte. + ObjectType type; ///< If has_type_key is true then attached value. Otherwise + ///< either kObjectTypeNil, kObjectTypeDictionary or + ///< kObjectTypeArray, depending on other properties. + lua_Number val; ///< If has_val_key and val_type == LUA_TNUMBER: value. + bool has_type_key; ///< True if type key is present. +} LuaTableProps; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/converter.c.generated.h" +#endif + +#define TYPE_IDX_VALUE true +#define VAL_IDX_VALUE false + +#define LUA_PUSH_STATIC_STRING(lstate, s) \ + lua_pushlstring(lstate, s, sizeof(s) - 1) + +static LuaTableProps nlua_traverse_table(lua_State *const lstate) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + size_t tsize = 0; // Total number of keys. + int val_type = 0; // If has_val_key: lua type of the value. + bool has_val_key = false; // True if val key was found, + // @see nlua_push_val_idx(). + size_t other_keys_num = 0; // Number of keys that are not string, integral + // or type keys. + LuaTableProps ret; + memset(&ret, 0, sizeof(ret)); + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2); + ret.type = kObjectTypeNil; + return ret; + } + lua_pushnil(lstate); + while (lua_next(lstate, -2)) { + switch (lua_type(lstate, -2)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + if (memchr(s, NUL, len) != NULL) { + ret.has_string_with_nul = true; + } + ret.string_keys_num++; + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -2); + if (n > (lua_Number)SIZE_MAX || n <= 0 + || ((lua_Number)((size_t)n)) != n) { + other_keys_num++; + } else { + const size_t idx = (size_t)n; + if (idx > ret.maxidx) { + ret.maxidx = idx; + } + } + break; + } + case LUA_TBOOLEAN: { + const bool b = lua_toboolean(lstate, -2); + if (b == TYPE_IDX_VALUE) { + if (lua_type(lstate, -1) == LUA_TNUMBER) { + lua_Number n = lua_tonumber(lstate, -1); + if (n == (lua_Number)kObjectTypeFloat + || n == (lua_Number)kObjectTypeArray + || n == (lua_Number)kObjectTypeDictionary) { + ret.has_type_key = true; + ret.type = (ObjectType)n; + } else { + other_keys_num++; + } + } else { + other_keys_num++; + } + } else { + has_val_key = true; + val_type = lua_type(lstate, -1); + if (val_type == LUA_TNUMBER) { + ret.val = lua_tonumber(lstate, -1); + } + } + break; + } + default: { + other_keys_num++; + break; + } + } + tsize++; + lua_pop(lstate, 1); + } + if (ret.has_type_key) { + if (ret.type == kObjectTypeFloat + && (!has_val_key || val_type != LUA_TNUMBER)) { + ret.type = kObjectTypeNil; + } else if (ret.type == kObjectTypeArray) { + // Determine what is the last number in a *sequence* of keys. + // This condition makes sure that Neovim will not crash when it gets table + // {[vim.type_idx]=vim.types.array, [SIZE_MAX]=1}: without it maxidx will + // be SIZE_MAX, with this condition it should be zero and [SIZE_MAX] key + // should be ignored. + if (ret.maxidx != 0 + && ret.maxidx != (tsize + - ret.has_type_key + - other_keys_num + - has_val_key + - ret.string_keys_num)) { + for (ret.maxidx = 0;; ret.maxidx++) { + lua_rawgeti(lstate, -1, (int)ret.maxidx + 1); + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 1); + break; + } + lua_pop(lstate, 1); + } + } + } + } else { + if (tsize == 0 + || (tsize == ret.maxidx + && other_keys_num == 0 + && ret.string_keys_num == 0)) { + ret.type = kObjectTypeArray; + } else if (ret.string_keys_num == tsize) { + ret.type = kObjectTypeDictionary; + } else { + ret.type = kObjectTypeNil; + } + } + return ret; +} + +/// Helper structure for nlua_pop_typval +typedef struct { + typval_T *tv; ///< Location where conversion result is saved. + bool container; ///< True if tv is a container. + bool special; ///< If true then tv is a _VAL part of special dictionary + ///< that represents mapping. + int idx; ///< Container index (used to detect self-referencing structures). +} TVPopStackItem; + +/// Convert lua object to VimL typval_T +/// +/// Should pop exactly one value from lua stack. +/// +/// @param lstate Lua state. +/// @param[out] ret_tv Where to put the result. +/// +/// @return `true` in case of success, `false` in case of failure. Error is +/// reported automatically. +bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) +{ + bool ret = true; + const int initial_size = lua_gettop(lstate); + kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); + while (ret && kv_size(stack)) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); + ret = false; + break; + } + TVPopStackItem cur = kv_pop(stack); + if (cur.container) { + if (cur.special || cur.tv->v_type == VAR_DICT) { + assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT)); + bool next_key_found = false; + while (lua_next(lstate, -2)) { + if (lua_type(lstate, -2) == LUA_TSTRING) { + next_key_found = true; + break; + } + lua_pop(lstate, 1); + } + if (next_key_found) { + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + if (cur.special) { + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(cur.tv->vval.v_list, kv_pair); + listitem_T *const key = tv_list_item_alloc(); + key->li_tv = decode_string(s, len, kTrue, false, false); + tv_list_append(kv_pair, key); + if (key->li_tv.v_type == VAR_UNKNOWN) { + ret = false; + tv_list_unref(kv_pair); + continue; + } + listitem_T *const val = tv_list_item_alloc(); + tv_list_append(kv_pair, val); + kv_push(stack, cur); + cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; + } else { + dictitem_T *const di = tv_dict_item_alloc_len(s, len); + if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { + assert(false); + } + kv_push(stack, cur); + cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; + } + } else { + lua_pop(lstate, 1); + continue; + } + } else { + assert(cur.tv->v_type == VAR_LIST); + lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1); + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 2); + continue; + } + listitem_T *const li = tv_list_item_alloc(); + tv_list_append(cur.tv->vval.v_list, li); + kv_push(stack, cur); + cur = (TVPopStackItem) { &li->li_tv, false, false, 0 }; + } + } + assert(!cur.container); + *cur.tv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = 0 }, + }; + switch (lua_type(lstate, -1)) { + case LUA_TNIL: { + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = kSpecialVarNull; + break; + } + case LUA_TBOOLEAN: { + cur.tv->v_type = VAR_SPECIAL; + cur.tv->vval.v_special = (lua_toboolean(lstate, -1) + ? kSpecialVarTrue + : kSpecialVarFalse); + break; + } + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -1, &len); + *cur.tv = decode_string(s, len, kNone, true, false); + if (cur.tv->v_type == VAR_UNKNOWN) { + ret = false; + } + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN + || ((lua_Number)((varnumber_T)n)) != n) { + cur.tv->v_type = VAR_FLOAT; + cur.tv->vval.v_float = (float_T)n; + } else { + cur.tv->v_type = VAR_NUMBER; + cur.tv->vval.v_number = (varnumber_T)n; + } + break; + } + case LUA_TTABLE: { + const LuaTableProps table_props = nlua_traverse_table(lstate); + + for (size_t i = 0; i < kv_size(stack); i++) { + const TVPopStackItem item = kv_A(stack, i); + if (item.container && lua_rawequal(lstate, -1, item.idx)) { + tv_copy(item.tv, cur.tv); + cur.container = false; + goto nlua_pop_typval_table_processing_end; + } + } + + switch (table_props.type) { + case kObjectTypeArray: { + cur.tv->v_type = VAR_LIST; + cur.tv->vval.v_list = tv_list_alloc(); + cur.tv->vval.v_list->lv_refcount++; + if (table_props.maxidx != 0) { + cur.container = true; + cur.idx = lua_gettop(lstate); + kv_push(stack, cur); + } + break; + } + case kObjectTypeDictionary: { + if (table_props.string_keys_num == 0) { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = tv_dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + } else { + cur.special = table_props.has_string_with_nul; + if (table_props.has_string_with_nul) { + decode_create_map_special_dict(cur.tv); + assert(cur.tv->v_type = VAR_DICT); + dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict, + S_LEN("_VAL")); + assert(val_di != NULL); + cur.tv = &val_di->di_tv; + assert(cur.tv->v_type == VAR_LIST); + } else { + cur.tv->v_type = VAR_DICT; + cur.tv->vval.v_dict = tv_dict_alloc(); + cur.tv->vval.v_dict->dv_refcount++; + } + cur.container = true; + cur.idx = lua_gettop(lstate); + kv_push(stack, cur); + lua_pushnil(lstate); + } + break; + } + case kObjectTypeFloat: { + cur.tv->v_type = VAR_FLOAT; + cur.tv->vval.v_float = (float_T)table_props.val; + break; + } + case kObjectTypeNil: { + EMSG(_("E5100: Cannot convert given lua table: table " + "should either have a sequence of positive integer keys " + "or contain only string keys")); + ret = false; + break; + } + default: { + assert(false); + } + } +nlua_pop_typval_table_processing_end: + break; + } + default: { + EMSG(_("E5101: Cannot convert given lua type")); + ret = false; + break; + } + } + if (!cur.container) { + lua_pop(lstate, 1); + } + } + kv_destroy(stack); + if (!ret) { + tv_clear(ret_tv); + *ret_tv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval = { .v_number = 0 }, + }; + lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); + } + assert(lua_gettop(lstate) == initial_size - 1); + return ret; +} + +#define TYPVAL_ENCODE_ALLOW_SPECIALS true + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + lua_pushnil(lstate) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + lua_pushboolean(lstate, (bool)(num)) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + lua_pushnumber(lstate, (lua_Number)(num)) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + TYPVAL_ENCODE_CONV_NUMBER(tv, flt) + +#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ + lua_pushlstring(lstate, (const char *)(str), (len)) + +#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ + TYPVAL_ENCODE_CONV_NIL() + +#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ + do { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + goto typval_encode_stop_converting_one_item; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_END(tv) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ + lua_createtable(lstate, 0, 0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary) + +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ + do { \ + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ + emsgf(_("E5102: Lua failed to grow stack to %i"), \ + lua_gettop(lstate) + 3); \ + return false; \ + } \ + lua_createtable(lstate, (int)(len), 0); \ + lua_pushnumber(lstate, 1); \ + } while (0) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ + do { \ + lua_Number idx = lua_tonumber(lstate, -2); \ + lua_rawset(lstate, -3); \ + lua_pushnumber(lstate, idx + 1); \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_END(tv) \ + lua_rawset(lstate, -3) + +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ + do { \ + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ + emsgf(_("E5102: Lua failed to grow stack to %i"), \ + lua_gettop(lstate) + 3); \ + return false; \ + } \ + lua_createtable(lstate, 0, (int)(len)); \ + } while (0) + +#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) + +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \ + lua_rawset(lstate, -3) + +#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ + do { \ + for (size_t backref = kv_size(*mpstack); backref; backref--) { \ + const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ + if (mpval.type == conv_type) { \ + if (conv_type == kMPConvDict \ + ? (void *)mpval.data.d.dict == (void *)(val) \ + : (void *)mpval.data.l.list == (void *)(val)) { \ + lua_pushvalue(lstate, \ + -((int)((kv_size(*mpstack) - backref + 1) * 2))); \ + break; \ + } \ + } \ + } \ + } while (0) + +#define TYPVAL_ENCODE_SCOPE static +#define TYPVAL_ENCODE_NAME lua +#define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const +#define TYPVAL_ENCODE_FIRST_ARG_NAME lstate +#include "nvim/eval/typval_encode.c.h" +#undef TYPVAL_ENCODE_SCOPE +#undef TYPVAL_ENCODE_NAME +#undef TYPVAL_ENCODE_FIRST_ARG_TYPE +#undef TYPVAL_ENCODE_FIRST_ARG_NAME + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC_START +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF +#undef TYPVAL_ENCODE_CONV_FUNC_END +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS + +/// Convert VimL typval_T to lua value +/// +/// Should leave single value in lua stack. May only fail if lua failed to grow +/// stack. +/// +/// @param lstate Lua interpreter state. +/// @param[in] tv typval_T to convert. +/// +/// @return true in case of success, false otherwise. +bool nlua_push_typval(lua_State *lstate, typval_T *const tv) +{ + const int initial_size = lua_gettop(lstate); + if (!lua_checkstack(lstate, initial_size + 2)) { + emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); + return false; + } + if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { + return false; + } + assert(lua_gettop(lstate) == initial_size + 1); + return true; +} + +#define NLUA_PUSH_HANDLE(lstate, type, idx) \ + do { \ + lua_pushnumber(lstate, (lua_Number)(idx)); \ + } while (0) + +#define NLUA_POP_HANDLE(lstate, type, stack_idx, idx) \ + do { \ + idx = (type)lua_tonumber(lstate, stack_idx); \ + } while (0) + +/// Push value which is a type index +/// +/// Used for all “typed” tables: i.e. for all tables which represent VimL +/// values. +static inline void nlua_push_type_idx(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, TYPE_IDX_VALUE); +} + +/// Push value which is a value index +/// +/// Used for tables which represent scalar values, like float value. +static inline void nlua_push_val_idx(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, VAL_IDX_VALUE); +} + +/// Push type +/// +/// Type is a value in vim.types table. +/// +/// @param[out] lstate Lua state. +/// @param[in] type Type to push. +static inline void nlua_push_type(lua_State *lstate, ObjectType type) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushnumber(lstate, (lua_Number)type); +} + +/// Create lua table which has an entry that determines its VimL type +/// +/// @param[out] lstate Lua state. +/// @param[in] narr Number of “array” entries to be populated later. +/// @param[in] nrec Number of “dictionary” entries to be populated later. +/// @param[in] type Type of the table. +static inline void nlua_create_typed_table(lua_State *lstate, + const size_t narr, + const size_t nrec, + const ObjectType type) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, (int)narr, (int)(1 + nrec)); + nlua_push_type_idx(lstate); + nlua_push_type(lstate, type); + lua_rawset(lstate, -3); +} + + +/// Convert given String to lua string +/// +/// Leaves converted string on top of the stack. +void nlua_push_String(lua_State *lstate, const String s) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushlstring(lstate, s.data, s.size); +} + +/// Convert given Integer to lua number +/// +/// Leaves converted number on top of the stack. +void nlua_push_Integer(lua_State *lstate, const Integer n) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushnumber(lstate, (lua_Number)n); +} + +/// Convert given Float to lua table +/// +/// Leaves converted table on top of the stack. +void nlua_push_Float(lua_State *lstate, const Float f) + FUNC_ATTR_NONNULL_ALL +{ + nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat); + nlua_push_val_idx(lstate); + lua_pushnumber(lstate, (lua_Number)f); + lua_rawset(lstate, -3); +} + +/// Convert given Float to lua boolean +/// +/// Leaves converted value on top of the stack. +void nlua_push_Boolean(lua_State *lstate, const Boolean b) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, b); +} + +/// Convert given Dictionary to lua table +/// +/// Leaves converted table on top of the stack. +void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict) + FUNC_ATTR_NONNULL_ALL +{ + if (dict.size == 0) { + nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); + } else { + lua_createtable(lstate, 0, (int)dict.size); + } + for (size_t i = 0; i < dict.size; i++) { + nlua_push_String(lstate, dict.items[i].key); + nlua_push_Object(lstate, dict.items[i].value); + lua_rawset(lstate, -3); + } +} + +/// Convert given Array to lua table +/// +/// Leaves converted table on top of the stack. +void nlua_push_Array(lua_State *lstate, const Array array) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, (int)array.size, 0); + for (size_t i = 0; i < array.size; i++) { + nlua_push_Object(lstate, array.items[i]); + lua_rawseti(lstate, -2, (int)i + 1); + } +} + +#define GENERATE_INDEX_FUNCTION(type) \ +void nlua_push_##type(lua_State *lstate, const type item) \ + FUNC_ATTR_NONNULL_ALL \ +{ \ + NLUA_PUSH_HANDLE(lstate, type, item); \ +} + +GENERATE_INDEX_FUNCTION(Buffer) +GENERATE_INDEX_FUNCTION(Window) +GENERATE_INDEX_FUNCTION(Tabpage) + +#undef GENERATE_INDEX_FUNCTION + +/// Convert given Object to lua value +/// +/// Leaves converted value on top of the stack. +void nlua_push_Object(lua_State *lstate, const Object obj) + FUNC_ATTR_NONNULL_ALL +{ + switch (obj.type) { + case kObjectTypeNil: { + lua_pushnil(lstate); + break; + } +#define ADD_TYPE(type, data_key) \ + case kObjectType##type: { \ + nlua_push_##type(lstate, obj.data.data_key); \ + break; \ + } + ADD_TYPE(Boolean, boolean) + ADD_TYPE(Integer, integer) + ADD_TYPE(Float, floating) + ADD_TYPE(String, string) + ADD_TYPE(Array, array) + ADD_TYPE(Dictionary, dictionary) +#undef ADD_TYPE +#define ADD_REMOTE_TYPE(type) \ + case kObjectType##type: { \ + nlua_push_##type(lstate, (type)obj.data.integer); \ + break; \ + } + ADD_REMOTE_TYPE(Buffer) + ADD_REMOTE_TYPE(Window) + ADD_REMOTE_TYPE(Tabpage) +#undef ADD_REMOTE_TYPE + } +} + + +/// Convert lua value to string +/// +/// Always pops one value from the stack. +String nlua_pop_String(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (lua_type(lstate, -1) != LUA_TSTRING) { + lua_pop(lstate, 1); + api_set_error(err, Validation, "Expected lua string"); + return (String) { .size = 0, .data = NULL }; + } + String ret; + + ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size)); + assert(ret.data != NULL); + ret.data = xmemdupz(ret.data, ret.size); + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua value to integer +/// +/// Always pops one value from the stack. +Integer nlua_pop_Integer(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (lua_type(lstate, -1) != LUA_TNUMBER) { + lua_pop(lstate, 1); + api_set_error(err, Validation, "Expected lua number"); + return 0; + } + const lua_Number n = lua_tonumber(lstate, -1); + lua_pop(lstate, 1); + if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN + || ((lua_Number)((Integer)n)) != n) { + api_set_error(err, Exception, "Number is not integral"); + return 0; + } + return (Integer)n; +} + +/// Convert lua value to boolean +/// +/// Always pops one value from the stack. +Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const Boolean ret = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + return ret; +} + +/// Check whether typed table on top of the stack has given type +/// +/// @param[in] lstate Lua state. +/// @param[out] err Location where error will be saved. May be NULL. +/// @param[in] type Type to check. +/// +/// @return @see nlua_traverse_table(). +static inline LuaTableProps nlua_check_type(lua_State *const lstate, + Error *const err, + const ObjectType type) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (lua_type(lstate, -1) != LUA_TTABLE) { + if (err) { + api_set_error(err, Validation, "Expected lua table"); + } + return (LuaTableProps) { .type = kObjectTypeNil }; + } + LuaTableProps table_props = nlua_traverse_table(lstate); + + if (type == kObjectTypeDictionary && table_props.type == kObjectTypeArray + && table_props.maxidx == 0 && !table_props.has_type_key) { + table_props.type = kObjectTypeDictionary; + } + + if (table_props.type != type) { + if (err) { + api_set_error(err, Validation, "Unexpected type"); + } + } + + return table_props; +} + +/// Convert lua table to float +/// +/// Always pops one value from the stack. +Float nlua_pop_Float(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (lua_type(lstate, -1) == LUA_TNUMBER) { + const Float ret = (Float)lua_tonumber(lstate, -1); + lua_pop(lstate, 1); + return ret; + } + + const LuaTableProps table_props = nlua_check_type(lstate, err, + kObjectTypeFloat); + lua_pop(lstate, 1); + if (table_props.type != kObjectTypeFloat) { + return 0; + } else { + return (Float)table_props.val; + } +} + +/// Convert lua table to array without determining whether it is array +/// +/// @param lstate Lua state. +/// @param[in] table_props nlua_traverse_table() output. +/// @param[out] err Location where error will be saved. +static Array nlua_pop_Array_unchecked(lua_State *const lstate, + const LuaTableProps table_props, + Error *const err) +{ + Array ret = { .size = table_props.maxidx, .items = NULL }; + + if (ret.size == 0) { + lua_pop(lstate, 1); + return ret; + } + + ret.items = xcalloc(ret.size, sizeof(*ret.items)); + for (size_t i = 1; i <= ret.size; i++) { + Object val; + + lua_rawgeti(lstate, -1, (int)i); + + val = nlua_pop_Object(lstate, err); + if (err->set) { + ret.size = i - 1; + lua_pop(lstate, 1); + api_free_array(ret); + return (Array) { .size = 0, .items = NULL }; + } + ret.items[i - 1] = val; + } + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua table to array +/// +/// Always pops one value from the stack. +Array nlua_pop_Array(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const LuaTableProps table_props = nlua_check_type(lstate, err, + kObjectTypeArray); + if (table_props.type != kObjectTypeArray) { + return (Array) { .size = 0, .items = NULL }; + } + return nlua_pop_Array_unchecked(lstate, table_props, err); +} + +/// Convert lua table to dictionary +/// +/// Always pops one value from the stack. Does not check whether whether topmost +/// value on the stack is a table. +/// +/// @param lstate Lua interpreter state. +/// @param[in] table_props nlua_traverse_table() output. +/// @param[out] err Location where error will be saved. +static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, + const LuaTableProps table_props, + Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Dictionary ret = { .size = table_props.string_keys_num, .items = NULL }; + + if (ret.size == 0) { + lua_pop(lstate, 1); + return ret; + } + ret.items = xcalloc(ret.size, sizeof(*ret.items)); + + lua_pushnil(lstate); + for (size_t i = 0; lua_next(lstate, -2) && i < ret.size;) { + // stack: dict, key, value + + if (lua_type(lstate, -2) == LUA_TSTRING) { + lua_pushvalue(lstate, -2); + // stack: dict, key, value, key + + ret.items[i].key = nlua_pop_String(lstate, err); + // stack: dict, key, value + + if (!err->set) { + ret.items[i].value = nlua_pop_Object(lstate, err); + // stack: dict, key + } else { + lua_pop(lstate, 1); + // stack: dict, key + } + + if (err->set) { + ret.size = i; + api_free_dictionary(ret); + lua_pop(lstate, 2); + // stack: + return (Dictionary) { .size = 0, .items = NULL }; + } + i++; + } else { + lua_pop(lstate, 1); + // stack: dict, key + } + } + lua_pop(lstate, 1); + + return ret; +} + +/// Convert lua table to dictionary +/// +/// Always pops one value from the stack. +Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const LuaTableProps table_props = nlua_check_type(lstate, err, + kObjectTypeDictionary); + if (table_props.type != kObjectTypeDictionary) { + lua_pop(lstate, 1); + return (Dictionary) { .size = 0, .items = NULL }; + } + + return nlua_pop_Dictionary_unchecked(lstate, table_props, err); +} + +/// Helper structure for nlua_pop_Object +typedef struct { + Object *obj; ///< Location where conversion result is saved. + bool container; ///< True if tv is a container. +} ObjPopStackItem; + +/// Convert lua table to object +/// +/// Always pops one value from the stack. +Object nlua_pop_Object(lua_State *const lstate, Error *const err) +{ + Object ret = NIL; + const int initial_size = lua_gettop(lstate); + kvec_t(ObjPopStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((ObjPopStackItem) { &ret, false })); + while (!err->set && kv_size(stack)) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + api_set_error(err, Exception, "Lua failed to grow stack"); + break; + } + ObjPopStackItem cur = kv_pop(stack); + if (cur.container) { + if (cur.obj->type == kObjectTypeDictionary) { + // stack: …, dict, key + if (cur.obj->data.dictionary.size + == cur.obj->data.dictionary.capacity) { + lua_pop(lstate, 2); + continue; + } + bool next_key_found = false; + while (lua_next(lstate, -2)) { + // stack: …, dict, new key, val + if (lua_type(lstate, -2) == LUA_TSTRING) { + next_key_found = true; + break; + } + lua_pop(lstate, 1); + // stack: …, dict, new key + } + if (next_key_found) { + // stack: …, dict, new key, val + size_t len; + const char *s = lua_tolstring(lstate, -2, &len); + const size_t idx = cur.obj->data.dictionary.size++; + cur.obj->data.dictionary.items[idx].key = (String) { + .data = xmemdupz(s, len), + .size = len, + }; + kv_push(stack, cur); + cur = (ObjPopStackItem) { + .obj = &cur.obj->data.dictionary.items[idx].value, + .container = false, + }; + } else { + // stack: …, dict + lua_pop(lstate, 1); + // stack: … + continue; + } + } else { + if (cur.obj->data.array.size == cur.obj->data.array.capacity) { + lua_pop(lstate, 1); + continue; + } + const size_t idx = cur.obj->data.array.size++; + lua_rawgeti(lstate, -1, (int)idx + 1); + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 2); + continue; + } + kv_push(stack, cur); + cur = (ObjPopStackItem) { + .obj = &cur.obj->data.array.items[idx], + .container = false, + }; + } + } + assert(!cur.container); + *cur.obj = NIL; + switch (lua_type(lstate, -1)) { + case LUA_TNIL: { + break; + } + case LUA_TBOOLEAN: { + *cur.obj = BOOLEAN_OBJ(lua_toboolean(lstate, -1)); + break; + } + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(lstate, -1, &len); + *cur.obj = STRING_OBJ(((String) { + .data = xmemdupz(s, len), + .size = len, + })); + break; + } + case LUA_TNUMBER: { + const lua_Number n = lua_tonumber(lstate, -1); + if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN + || ((lua_Number)((Integer)n)) != n) { + *cur.obj = FLOAT_OBJ((Float)n); + } else { + *cur.obj = INTEGER_OBJ((Integer)n); + } + break; + } + case LUA_TTABLE: { + const LuaTableProps table_props = nlua_traverse_table(lstate); + + switch (table_props.type) { + case kObjectTypeArray: { + *cur.obj = ARRAY_OBJ(((Array) { + .items = NULL, + .size = 0, + .capacity = 0, + })); + if (table_props.maxidx != 0) { + cur.obj->data.array.items = + xcalloc(table_props.maxidx, + sizeof(cur.obj->data.array.items[0])); + cur.obj->data.array.capacity = table_props.maxidx; + cur.container = true; + kv_push(stack, cur); + } + break; + } + case kObjectTypeDictionary: { + *cur.obj = DICTIONARY_OBJ(((Dictionary) { + .items = NULL, + .size = 0, + .capacity = 0, + })); + if (table_props.string_keys_num != 0) { + cur.obj->data.dictionary.items = + xcalloc(table_props.string_keys_num, + sizeof(cur.obj->data.dictionary.items[0])); + cur.obj->data.dictionary.capacity = table_props.string_keys_num; + cur.container = true; + kv_push(stack, cur); + lua_pushnil(lstate); + } + break; + } + case kObjectTypeFloat: { + *cur.obj = FLOAT_OBJ((Float)table_props.val); + break; + } + case kObjectTypeNil: { + api_set_error(err, Validation, "Cannot convert given lua table"); + break; + } + default: { + assert(false); + } + } + break; + } + default: { + api_set_error(err, Validation, "Cannot convert given lua type"); + break; + } + } + if (!cur.container) { + lua_pop(lstate, 1); + } + } + kv_destroy(stack); + if (err->set) { + api_free_object(ret); + ret = NIL; + lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); + } + assert(lua_gettop(lstate) == initial_size - 1); + return ret; +} + +#define GENERATE_INDEX_FUNCTION(type) \ +type nlua_pop_##type(lua_State *lstate, Error *err) \ + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + type ret; \ + NLUA_POP_HANDLE(lstate, type, -1, ret); \ + lua_pop(lstate, 1); \ + return ret; \ +} + +GENERATE_INDEX_FUNCTION(Buffer) +GENERATE_INDEX_FUNCTION(Window) +GENERATE_INDEX_FUNCTION(Tabpage) + +#undef GENERATE_INDEX_FUNCTION + +/// Record some auxilary values in vim module +/// +/// Assumes that module table is on top of the stack. +/// +/// Recorded values: +/// +/// `vim.type_idx`: @see nlua_push_type_idx() +/// `vim.val_idx`: @see nlua_push_val_idx() +/// `vim.types`: table mapping possible values of `vim.type_idx` to string +/// names (i.e. `array`, `float`, `dictionary`) and back. +void nlua_init_types(lua_State *const lstate) +{ + LUA_PUSH_STATIC_STRING(lstate, "type_idx"); + nlua_push_type_idx(lstate); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "val_idx"); + nlua_push_val_idx(lstate); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "types"); + lua_createtable(lstate, 0, 3); + + LUA_PUSH_STATIC_STRING(lstate, "float"); + lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat); + lua_rawset(lstate, -3); + lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat); + LUA_PUSH_STATIC_STRING(lstate, "float"); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "array"); + lua_pushnumber(lstate, (lua_Number)kObjectTypeArray); + lua_rawset(lstate, -3); + lua_pushnumber(lstate, (lua_Number)kObjectTypeArray); + LUA_PUSH_STATIC_STRING(lstate, "array"); + lua_rawset(lstate, -3); + + LUA_PUSH_STATIC_STRING(lstate, "dictionary"); + lua_pushnumber(lstate, (lua_Number)kObjectTypeDictionary); + lua_rawset(lstate, -3); + lua_pushnumber(lstate, (lua_Number)kObjectTypeDictionary); + LUA_PUSH_STATIC_STRING(lstate, "dictionary"); + lua_rawset(lstate, -3); + + lua_rawset(lstate, -3); +} diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h new file mode 100644 index 0000000000..49f6ac4016 --- /dev/null +++ b/src/nvim/lua/converter.h @@ -0,0 +1,15 @@ +#ifndef NVIM_VIML_EXECUTOR_CONVERTER_H +#define NVIM_VIML_EXECUTOR_CONVERTER_H + +#include +#include +#include + +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.h" +#include "nvim/eval.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/converter.h.generated.h" +#endif +#endif // NVIM_VIML_EXECUTOR_CONVERTER_H diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c new file mode 100644 index 0000000000..7cf326aef5 --- /dev/null +++ b/src/nvim/lua/executor.c @@ -0,0 +1,576 @@ +#include +#include +#include + +#include "nvim/misc1.h" +#include "nvim/getchar.h" +#include "nvim/garray.h" +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" +#include "nvim/vim.h" +#include "nvim/ex_getln.h" +#include "nvim/message.h" +#include "nvim/memline.h" +#include "nvim/buffer_defs.h" +#include "nvim/macros.h" +#include "nvim/screen.h" +#include "nvim/cursor.h" +#include "nvim/undo.h" +#include "nvim/ascii.h" + +#include "nvim/lua/executor.h" +#include "nvim/lua/converter.h" + +typedef struct { + Error err; + String lua_err_str; +} LuaError; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/vim_module.generated.h" +# include "lua/executor.c.generated.h" +#endif + +/// Name of the run code for use in messages +#define NLUA_EVAL_NAME "" + +/// Call C function which does not expect any arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +#define NLUA_CALL_C_FUNCTION_0(lstate, function, numret) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_call(lstate, 0, numret); \ + } while (0) +/// Call C function which expects one argument +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_call(lstate, 1, numret); \ + } while (0) +/// Call C function which expects two arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_pushlightuserdata(lstate, a2); \ + lua_call(lstate, 2, numret); \ + } while (0) +/// Call C function which expects three arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_3(lstate, function, numret, a1, a2, a3) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_pushlightuserdata(lstate, a2); \ + lua_pushlightuserdata(lstate, a3); \ + lua_call(lstate, 3, numret); \ + } while (0) +/// Call C function which expects five arguments +/// +/// @param function Called function +/// @param numret Number of returned arguments +/// @param a… Supplied argument (should be a void* pointer) +#define NLUA_CALL_C_FUNCTION_4(lstate, function, numret, a1, a2, a3, a4) \ + do { \ + lua_pushcfunction(lstate, &function); \ + lua_pushlightuserdata(lstate, a1); \ + lua_pushlightuserdata(lstate, a2); \ + lua_pushlightuserdata(lstate, a3); \ + lua_pushlightuserdata(lstate, a4); \ + lua_call(lstate, 4, numret); \ + } while (0) + +/// Convert lua error into a Vim error message +/// +/// @param lstate Lua interpreter state. +/// @param[in] msg Message base, must contain one `%s`. +static void nlua_error(lua_State *const lstate, const char *const msg) + FUNC_ATTR_NONNULL_ALL +{ + size_t len; + const char *const str = lua_tolstring(lstate, -1, &len); + + emsgf(msg, (int)len, str); + + lua_pop(lstate, 1); +} + +/// Compare two strings, ignoring case +/// +/// Expects two values on the stack: compared strings. Returns one of the +/// following numbers: 0, -1 or 1. +/// +/// Does no error handling: never call it with non-string or with some arguments +/// omitted. +static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const char *s1 = luaL_checklstring(lstate, 1, NULL); + const char *s2 = luaL_checklstring(lstate, 2, NULL); + const int ret = STRICMP(s1, s2); + lua_pop(lstate, 2); + lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); + return 1; +} + +/// Evaluate lua string +/// +/// Expects two values on the stack: string to evaluate, pointer to the +/// location where result is saved. Always returns nothing (from the lua point +/// of view). +static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const String *const str = (const String *)lua_touserdata(lstate, 1); + typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2); + lua_pop(lstate, 2); + + if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { + nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s")); + return 0; + } + if (lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s")); + return 0; + } + if (!nlua_pop_typval(lstate, ret_tv)) { + return 0; + } + return 0; +} + +/// Evaluate lua string for each line in range +/// +/// Expects two values on the stack: string to evaluate and pointer to integer +/// array with line range. Always returns nothing (from the lua point of view). +static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const String *const str = (const String *)lua_touserdata(lstate, 1); + const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2); + lua_pop(lstate, 2); + +#define DOSTART "return function(line, linenr) " +#define DOEND " end" + const size_t lcmd_len = (str->size + + (sizeof(DOSTART) - 1) + + (sizeof(DOEND) - 1)); + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len + 1); + } + memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1); + memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); + memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, DOEND, sizeof(DOEND) - 1); +#undef DOSTART +#undef DOEND + + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); + if (lcmd_len >= IOSIZE) { + xfree(lcmd); + } + return 0; + } + if (lcmd_len >= IOSIZE) { + xfree(lcmd); + } + if (lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); + return 0; + } + for (linenr_T l = range[0]; l <= range[1]; l++) { + if (l > curbuf->b_ml.ml_line_count) { + break; + } + lua_pushvalue(lstate, -1); + lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); + lua_pushnumber(lstate, (lua_Number)l); + if (lua_pcall(lstate, 2, 1, 0)) { + nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); + break; + } + if (lua_isstring(lstate, -1)) { + size_t new_line_len; + const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); + char *const new_line_transformed = xmemdupz(new_line, new_line_len); + for (size_t i = 0; i < new_line_len; i++) { + if (new_line_transformed[i] == NUL) { + new_line_transformed[i] = '\n'; + } + } + ml_replace(l, (char_u *)new_line_transformed, false); + changed_bytes(l, 0); + } + lua_pop(lstate, 1); + } + lua_pop(lstate, 1); + check_cursor(); + update_screen(NOT_VALID); + return 0; +} + +/// Evaluate lua file +/// +/// Expects one value on the stack: file to evaluate. Always returns nothing +/// (from the lua point of view). +static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + const char *const filename = (const char *)lua_touserdata(lstate, 1); + lua_pop(lstate, 1); + + if (luaL_loadfile(lstate, filename)) { + nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); + return 0; + } + if (lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); + return 0; + } + return 0; +} + +/// Initialize lua interpreter state +/// +/// Called by lua interpreter itself to initialize state. +static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + // stricmp + lua_pushcfunction(lstate, &nlua_stricmp); + lua_setglobal(lstate, "stricmp"); + + // print + lua_pushcfunction(lstate, &nlua_print); + lua_setglobal(lstate, "print"); + + // debug.debug + lua_getglobal(lstate, "debug"); + lua_pushcfunction(lstate, &nlua_debug); + lua_setfield(lstate, -2, "debug"); + lua_pop(lstate, 1); + + // vim + if (luaL_dostring(lstate, (char *)&vim_module[0])) { + nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); + return 1; + } + // vim.api + nlua_add_api_functions(lstate); + // vim.types, vim.type_idx, vim.val_idx + nlua_init_types(lstate); + lua_setglobal(lstate, "vim"); + return 0; +} + +/// Initialize lua interpreter +/// +/// Crashes NeoVim if initialization fails. Should be called once per lua +/// interpreter instance. +static lua_State *init_lua(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + lua_State *lstate = luaL_newstate(); + if (lstate == NULL) { + EMSG(_("E970: Failed to initialize lua interpreter")); + preserve_exit(); + } + luaL_openlibs(lstate); + NLUA_CALL_C_FUNCTION_0(lstate, nlua_state_init, 0); + return lstate; +} + +static lua_State *global_lstate = NULL; + +/// Execute lua string +/// +/// @param[in] str String to execute. +/// @param[out] ret_tv Location where result will be saved. +/// +/// @return Result of the execution. +void executor_exec_lua(const String str, typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, + (void *)&str, ret_tv); +} + +/// Evaluate lua string +/// +/// Used for luaeval(). Expects three values on the stack: +/// +/// 1. String to evaluate. +/// 2. _A value. +/// 3. Pointer to location where result is saved. +/// +/// @param[in,out] lstate Lua interpreter state. +static int nlua_eval_lua_string(lua_State *const lstate) + FUNC_ATTR_NONNULL_ALL +{ + const String *const str = (const String *)lua_touserdata(lstate, 1); + typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2); + typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3); + lua_pop(lstate, 3); + + garray_T str_ga; + ga_init(&str_ga, 1, 80); +#define EVALHEADER "local _A=select(1,...) return (" + const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1; + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len); + } + memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); + memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); + lcmd[lcmd_len - 1] = ')'; +#undef EVALHEADER + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, + _("E5107: Error while creating lua chunk for luaeval(): %.*s")); + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } + return 0; + } + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } + + if (arg == NULL || arg->v_type == VAR_UNKNOWN) { + lua_pushnil(lstate); + } else { + nlua_push_typval(lstate, arg); + } + if (lua_pcall(lstate, 1, 1, 0)) { + nlua_error(lstate, + _("E5108: Error while calling lua chunk for luaeval(): %.*s")); + return 0; + } + if (!nlua_pop_typval(lstate, ret_tv)) { + return 0; + } + + return 0; +} + +/// Print as a Vim message +/// +/// @param lstate Lua interpreter state. +static int nlua_print(lua_State *const lstate) + FUNC_ATTR_NONNULL_ALL +{ +#define PRINT_ERROR(msg) \ + do { \ + errmsg = msg; \ + errmsg_len = sizeof(msg) - 1; \ + goto nlua_print_error; \ + } while (0) + const int nargs = lua_gettop(lstate); + lua_getglobal(lstate, "tostring"); + const char *errmsg = NULL; + size_t errmsg_len = 0; + garray_T msg_ga; + ga_init(&msg_ga, 1, 80); + int curargidx = 1; + for (; curargidx <= nargs; curargidx++) { + lua_pushvalue(lstate, -1); // tostring + lua_pushvalue(lstate, curargidx); // arg + if (lua_pcall(lstate, 1, 1, 0)) { + errmsg = lua_tolstring(lstate, -1, &errmsg_len); + goto nlua_print_error; + } + size_t len; + const char *const s = lua_tolstring(lstate, -1, &len); + if (s == NULL) { + PRINT_ERROR( + ""); + } + ga_concat_len(&msg_ga, s, len); + if (curargidx < nargs) { + ga_append(&msg_ga, ' '); + } + lua_pop(lstate, 1); + } +#undef PRINT_ERROR + lua_pop(lstate, nargs + 1); + ga_append(&msg_ga, NUL); + { + const size_t len = (size_t)msg_ga.ga_len - 1; + char *const str = (char *)msg_ga.ga_data; + + for (size_t i = 0; i < len;) { + const size_t start = i; + while (i < len) { + switch (str[i]) { + case NUL: { + str[i] = NL; + i++; + continue; + } + case NL: { + str[i] = NUL; + i++; + break; + } + default: { + i++; + continue; + } + } + break; + } + msg((char_u *)str + start); + } + if (str[len - 1] == NUL) { // Last was newline + msg((char_u *)""); + } + } + ga_clear(&msg_ga); + return 0; +nlua_print_error: + emsgf(_("E5114: Error while converting print argument #%i: %.*s"), + curargidx, errmsg_len, errmsg); + ga_clear(&msg_ga); + lua_pop(lstate, lua_gettop(lstate)); + return 0; +} + +/// debug.debug implementation: interaction with user while debugging +/// +/// @param lstate Lua interpreter state. +int nlua_debug(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + const typval_T input_args[] = { + { + .v_lock = VAR_FIXED, + .v_type = VAR_STRING, + .vval.v_string = (char_u *)"lua_debug> ", + }, + { + .v_type = VAR_UNKNOWN, + }, + }; + for (;;) { + lua_settop(lstate, 0); + typval_T input; + get_user_input(input_args, &input, false); + msg_putchar('\n'); // Avoid outputting on input line. + if (input.v_type != VAR_STRING + || input.vval.v_string == NULL + || *input.vval.v_string == NUL + || STRCMP(input.vval.v_string, "cont") == 0) { + tv_clear(&input); + return 0; + } + if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string, + STRLEN(input.vval.v_string), "=(debug command)")) { + nlua_error(lstate, _("E5115: Error while loading debug string: %.*s")); + } + tv_clear(&input); + if (lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5116: Error while calling debug string: %.*s")); + } + } + return 0; +} + +/// Evaluate lua string +/// +/// Used for luaeval(). +/// +/// @param[in] str String to execute. +/// @param[in] arg Second argument to `luaeval()`. +/// @param[out] ret_tv Location where result will be saved. +/// +/// @return Result of the execution. +void executor_eval_lua(const String str, typval_T *const arg, + typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0, + (void *)&str, arg, ret_tv); +} + +/// Run lua string +/// +/// Used for :lua. +/// +/// @param eap VimL command being run. +void ex_lua(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + size_t len; + char *const code = script_get(eap, &len); + if (eap->skip) { + xfree(code); + return; + } + typval_T tv = { .v_type = VAR_UNKNOWN }; + executor_exec_lua((String) { .data = code, .size = len }, &tv); + tv_clear(&tv); + xfree(code); +} + +/// Run lua string for each line in range +/// +/// Used for :luado. +/// +/// @param eap VimL command being run. +void ex_luado(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { + EMSG(_("cannot save undo information")); + return; + } + const String cmd = { + .size = STRLEN(eap->arg), + .data = (char *)eap->arg, + }; + const linenr_T range[] = { eap->line1, eap->line2 }; + NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0, + (void *)&cmd, (void *)range); +} + +/// Run lua file +/// +/// Used for :luafile. +/// +/// @param eap VimL command being run. +void ex_luafile(exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + NLUA_CALL_C_FUNCTION_1(global_lstate, nlua_exec_lua_file, 0, + (void *)eap->arg); +} diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h new file mode 100644 index 0000000000..22e55a56d3 --- /dev/null +++ b/src/nvim/lua/executor.h @@ -0,0 +1,25 @@ +#ifndef NVIM_VIML_EXECUTOR_EXECUTOR_H +#define NVIM_VIML_EXECUTOR_EXECUTOR_H + +#include + +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.h" +#include "nvim/eval/typval.h" +#include "nvim/ex_cmds_defs.h" + +// Generated by msgpack-gen.lua +void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; + +#define set_api_error(s, err) \ + do { \ + Error *err_ = (err); \ + err_->type = kErrorTypeException; \ + err_->set = true; \ + memcpy(&err_->msg[0], s, sizeof(s)); \ + } while (0) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/executor.h.generated.h" +#endif +#endif // NVIM_VIML_EXECUTOR_EXECUTOR_H diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua new file mode 100644 index 0000000000..8d1c5bdf4f --- /dev/null +++ b/src/nvim/lua/vim.lua @@ -0,0 +1,2 @@ +-- TODO(ZyX-I): Create compatibility layer. +return {} diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c deleted file mode 100644 index 425a38538d..0000000000 --- a/src/nvim/viml/executor/converter.c +++ /dev/null @@ -1,1194 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "nvim/api/private/defs.h" -#include "nvim/api/private/helpers.h" -#include "nvim/func_attr.h" -#include "nvim/memory.h" -#include "nvim/assert.h" -// FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is -// redefined -#include "nvim/vim.h" -#include "nvim/globals.h" -#include "nvim/message.h" -#include "nvim/eval/typval.h" -#include "nvim/ascii.h" -#include "nvim/macros.h" - -#include "nvim/lib/kvec.h" -#include "nvim/eval/decode.h" - -#include "nvim/viml/executor/converter.h" -#include "nvim/viml/executor/executor.h" - -/// Determine, which keys lua table contains -typedef struct { - size_t maxidx; ///< Maximum positive integral value found. - size_t string_keys_num; ///< Number of string keys. - bool has_string_with_nul; ///< True if there is string key with NUL byte. - ObjectType type; ///< If has_type_key is true then attached value. Otherwise - ///< either kObjectTypeNil, kObjectTypeDictionary or - ///< kObjectTypeArray, depending on other properties. - lua_Number val; ///< If has_val_key and val_type == LUA_TNUMBER: value. - bool has_type_key; ///< True if type key is present. -} LuaTableProps; - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "viml/executor/converter.c.generated.h" -#endif - -#define TYPE_IDX_VALUE true -#define VAL_IDX_VALUE false - -#define LUA_PUSH_STATIC_STRING(lstate, s) \ - lua_pushlstring(lstate, s, sizeof(s) - 1) - -static LuaTableProps nlua_traverse_table(lua_State *const lstate) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - size_t tsize = 0; // Total number of keys. - int val_type = 0; // If has_val_key: lua type of the value. - bool has_val_key = false; // True if val key was found, - // @see nlua_push_val_idx(). - size_t other_keys_num = 0; // Number of keys that are not string, integral - // or type keys. - LuaTableProps ret; - memset(&ret, 0, sizeof(ret)); - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { - emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2); - ret.type = kObjectTypeNil; - return ret; - } - lua_pushnil(lstate); - while (lua_next(lstate, -2)) { - switch (lua_type(lstate, -2)) { - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -2, &len); - if (memchr(s, NUL, len) != NULL) { - ret.has_string_with_nul = true; - } - ret.string_keys_num++; - break; - } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -2); - if (n > (lua_Number)SIZE_MAX || n <= 0 - || ((lua_Number)((size_t)n)) != n) { - other_keys_num++; - } else { - const size_t idx = (size_t)n; - if (idx > ret.maxidx) { - ret.maxidx = idx; - } - } - break; - } - case LUA_TBOOLEAN: { - const bool b = lua_toboolean(lstate, -2); - if (b == TYPE_IDX_VALUE) { - if (lua_type(lstate, -1) == LUA_TNUMBER) { - lua_Number n = lua_tonumber(lstate, -1); - if (n == (lua_Number)kObjectTypeFloat - || n == (lua_Number)kObjectTypeArray - || n == (lua_Number)kObjectTypeDictionary) { - ret.has_type_key = true; - ret.type = (ObjectType)n; - } else { - other_keys_num++; - } - } else { - other_keys_num++; - } - } else { - has_val_key = true; - val_type = lua_type(lstate, -1); - if (val_type == LUA_TNUMBER) { - ret.val = lua_tonumber(lstate, -1); - } - } - break; - } - default: { - other_keys_num++; - break; - } - } - tsize++; - lua_pop(lstate, 1); - } - if (ret.has_type_key) { - if (ret.type == kObjectTypeFloat - && (!has_val_key || val_type != LUA_TNUMBER)) { - ret.type = kObjectTypeNil; - } else if (ret.type == kObjectTypeArray) { - // Determine what is the last number in a *sequence* of keys. - // This condition makes sure that Neovim will not crash when it gets table - // {[vim.type_idx]=vim.types.array, [SIZE_MAX]=1}: without it maxidx will - // be SIZE_MAX, with this condition it should be zero and [SIZE_MAX] key - // should be ignored. - if (ret.maxidx != 0 - && ret.maxidx != (tsize - - ret.has_type_key - - other_keys_num - - has_val_key - - ret.string_keys_num)) { - for (ret.maxidx = 0;; ret.maxidx++) { - lua_rawgeti(lstate, -1, (int)ret.maxidx + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 1); - break; - } - lua_pop(lstate, 1); - } - } - } - } else { - if (tsize == 0 - || (tsize == ret.maxidx - && other_keys_num == 0 - && ret.string_keys_num == 0)) { - ret.type = kObjectTypeArray; - } else if (ret.string_keys_num == tsize) { - ret.type = kObjectTypeDictionary; - } else { - ret.type = kObjectTypeNil; - } - } - return ret; -} - -/// Helper structure for nlua_pop_typval -typedef struct { - typval_T *tv; ///< Location where conversion result is saved. - bool container; ///< True if tv is a container. - bool special; ///< If true then tv is a _VAL part of special dictionary - ///< that represents mapping. - int idx; ///< Container index (used to detect self-referencing structures). -} TVPopStackItem; - -/// Convert lua object to VimL typval_T -/// -/// Should pop exactly one value from lua stack. -/// -/// @param lstate Lua state. -/// @param[out] ret_tv Where to put the result. -/// -/// @return `true` in case of success, `false` in case of failure. Error is -/// reported automatically. -bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) -{ - bool ret = true; - const int initial_size = lua_gettop(lstate); - kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); - while (ret && kv_size(stack)) { - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { - emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); - ret = false; - break; - } - TVPopStackItem cur = kv_pop(stack); - if (cur.container) { - if (cur.special || cur.tv->v_type == VAR_DICT) { - assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT)); - bool next_key_found = false; - while (lua_next(lstate, -2)) { - if (lua_type(lstate, -2) == LUA_TSTRING) { - next_key_found = true; - break; - } - lua_pop(lstate, 1); - } - if (next_key_found) { - size_t len; - const char *s = lua_tolstring(lstate, -2, &len); - if (cur.special) { - list_T *const kv_pair = tv_list_alloc(); - tv_list_append_list(cur.tv->vval.v_list, kv_pair); - listitem_T *const key = tv_list_item_alloc(); - key->li_tv = decode_string(s, len, kTrue, false, false); - tv_list_append(kv_pair, key); - if (key->li_tv.v_type == VAR_UNKNOWN) { - ret = false; - tv_list_unref(kv_pair); - continue; - } - listitem_T *const val = tv_list_item_alloc(); - tv_list_append(kv_pair, val); - kv_push(stack, cur); - cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; - } else { - dictitem_T *const di = tv_dict_item_alloc_len(s, len); - if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { - assert(false); - } - kv_push(stack, cur); - cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; - } - } else { - lua_pop(lstate, 1); - continue; - } - } else { - assert(cur.tv->v_type == VAR_LIST); - lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 2); - continue; - } - listitem_T *const li = tv_list_item_alloc(); - tv_list_append(cur.tv->vval.v_list, li); - kv_push(stack, cur); - cur = (TVPopStackItem) { &li->li_tv, false, false, 0 }; - } - } - assert(!cur.container); - *cur.tv = (typval_T) { - .v_type = VAR_NUMBER, - .v_lock = VAR_UNLOCKED, - .vval = { .v_number = 0 }, - }; - switch (lua_type(lstate, -1)) { - case LUA_TNIL: { - cur.tv->v_type = VAR_SPECIAL; - cur.tv->vval.v_special = kSpecialVarNull; - break; - } - case LUA_TBOOLEAN: { - cur.tv->v_type = VAR_SPECIAL; - cur.tv->vval.v_special = (lua_toboolean(lstate, -1) - ? kSpecialVarTrue - : kSpecialVarFalse); - break; - } - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -1, &len); - *cur.tv = decode_string(s, len, kNone, true, false); - if (cur.tv->v_type == VAR_UNKNOWN) { - ret = false; - } - break; - } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -1); - if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN - || ((lua_Number)((varnumber_T)n)) != n) { - cur.tv->v_type = VAR_FLOAT; - cur.tv->vval.v_float = (float_T)n; - } else { - cur.tv->v_type = VAR_NUMBER; - cur.tv->vval.v_number = (varnumber_T)n; - } - break; - } - case LUA_TTABLE: { - const LuaTableProps table_props = nlua_traverse_table(lstate); - - for (size_t i = 0; i < kv_size(stack); i++) { - const TVPopStackItem item = kv_A(stack, i); - if (item.container && lua_rawequal(lstate, -1, item.idx)) { - tv_copy(item.tv, cur.tv); - cur.container = false; - goto nlua_pop_typval_table_processing_end; - } - } - - switch (table_props.type) { - case kObjectTypeArray: { - cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = tv_list_alloc(); - cur.tv->vval.v_list->lv_refcount++; - if (table_props.maxidx != 0) { - cur.container = true; - cur.idx = lua_gettop(lstate); - kv_push(stack, cur); - } - break; - } - case kObjectTypeDictionary: { - if (table_props.string_keys_num == 0) { - cur.tv->v_type = VAR_DICT; - cur.tv->vval.v_dict = tv_dict_alloc(); - cur.tv->vval.v_dict->dv_refcount++; - } else { - cur.special = table_props.has_string_with_nul; - if (table_props.has_string_with_nul) { - decode_create_map_special_dict(cur.tv); - assert(cur.tv->v_type = VAR_DICT); - dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict, - S_LEN("_VAL")); - assert(val_di != NULL); - cur.tv = &val_di->di_tv; - assert(cur.tv->v_type == VAR_LIST); - } else { - cur.tv->v_type = VAR_DICT; - cur.tv->vval.v_dict = tv_dict_alloc(); - cur.tv->vval.v_dict->dv_refcount++; - } - cur.container = true; - cur.idx = lua_gettop(lstate); - kv_push(stack, cur); - lua_pushnil(lstate); - } - break; - } - case kObjectTypeFloat: { - cur.tv->v_type = VAR_FLOAT; - cur.tv->vval.v_float = (float_T)table_props.val; - break; - } - case kObjectTypeNil: { - EMSG(_("E5100: Cannot convert given lua table: table " - "should either have a sequence of positive integer keys " - "or contain only string keys")); - ret = false; - break; - } - default: { - assert(false); - } - } -nlua_pop_typval_table_processing_end: - break; - } - default: { - EMSG(_("E5101: Cannot convert given lua type")); - ret = false; - break; - } - } - if (!cur.container) { - lua_pop(lstate, 1); - } - } - kv_destroy(stack); - if (!ret) { - tv_clear(ret_tv); - *ret_tv = (typval_T) { - .v_type = VAR_NUMBER, - .v_lock = VAR_UNLOCKED, - .vval = { .v_number = 0 }, - }; - lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); - } - assert(lua_gettop(lstate) == initial_size - 1); - return ret; -} - -#define TYPVAL_ENCODE_ALLOW_SPECIALS true - -#define TYPVAL_ENCODE_CONV_NIL(tv) \ - lua_pushnil(lstate) - -#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - lua_pushboolean(lstate, (bool)(num)) - -#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - lua_pushnumber(lstate, (lua_Number)(num)) - -#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER - -#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - TYPVAL_ENCODE_CONV_NUMBER(tv, flt) - -#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ - lua_pushlstring(lstate, (const char *)(str), (len)) - -#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING - -#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ - TYPVAL_ENCODE_CONV_NIL() - -#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ - goto typval_encode_stop_converting_one_item; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) -#define TYPVAL_ENCODE_CONV_FUNC_END(tv) - -#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - lua_createtable(lstate, 0, 0) - -#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary) - -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ - do { \ - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ - emsgf(_("E5102: Lua failed to grow stack to %i"), \ - lua_gettop(lstate) + 3); \ - return false; \ - } \ - lua_createtable(lstate, (int)(len), 0); \ - lua_pushnumber(lstate, 1); \ - } while (0) - -#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) - -#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ - do { \ - lua_Number idx = lua_tonumber(lstate, -2); \ - lua_rawset(lstate, -3); \ - lua_pushnumber(lstate, idx + 1); \ - } while (0) - -#define TYPVAL_ENCODE_CONV_LIST_END(tv) \ - lua_rawset(lstate, -3) - -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ - do { \ - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \ - emsgf(_("E5102: Lua failed to grow stack to %i"), \ - lua_gettop(lstate) + 3); \ - return false; \ - } \ - lua_createtable(lstate, 0, (int)(len)); \ - } while (0) - -#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair) - -#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) - -#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) - -#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \ - lua_rawset(lstate, -3) - -#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ - TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) - -#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ - do { \ - for (size_t backref = kv_size(*mpstack); backref; backref--) { \ - const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ - if (mpval.type == conv_type) { \ - if (conv_type == kMPConvDict \ - ? (void *)mpval.data.d.dict == (void *)(val) \ - : (void *)mpval.data.l.list == (void *)(val)) { \ - lua_pushvalue(lstate, \ - -((int)((kv_size(*mpstack) - backref + 1) * 2))); \ - break; \ - } \ - } \ - } \ - } while (0) - -#define TYPVAL_ENCODE_SCOPE static -#define TYPVAL_ENCODE_NAME lua -#define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const -#define TYPVAL_ENCODE_FIRST_ARG_NAME lstate -#include "nvim/eval/typval_encode.c.h" -#undef TYPVAL_ENCODE_SCOPE -#undef TYPVAL_ENCODE_NAME -#undef TYPVAL_ENCODE_FIRST_ARG_TYPE -#undef TYPVAL_ENCODE_FIRST_ARG_NAME - -#undef TYPVAL_ENCODE_CONV_STRING -#undef TYPVAL_ENCODE_CONV_STR_STRING -#undef TYPVAL_ENCODE_CONV_EXT_STRING -#undef TYPVAL_ENCODE_CONV_NUMBER -#undef TYPVAL_ENCODE_CONV_FLOAT -#undef TYPVAL_ENCODE_CONV_FUNC_START -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF -#undef TYPVAL_ENCODE_CONV_FUNC_END -#undef TYPVAL_ENCODE_CONV_EMPTY_LIST -#undef TYPVAL_ENCODE_CONV_LIST_START -#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START -#undef TYPVAL_ENCODE_CONV_EMPTY_DICT -#undef TYPVAL_ENCODE_CONV_NIL -#undef TYPVAL_ENCODE_CONV_BOOL -#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER -#undef TYPVAL_ENCODE_CONV_DICT_START -#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START -#undef TYPVAL_ENCODE_CONV_DICT_END -#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY -#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK -#undef TYPVAL_ENCODE_CONV_LIST_END -#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_RECURSE -#undef TYPVAL_ENCODE_ALLOW_SPECIALS - -/// Convert VimL typval_T to lua value -/// -/// Should leave single value in lua stack. May only fail if lua failed to grow -/// stack. -/// -/// @param lstate Lua interpreter state. -/// @param[in] tv typval_T to convert. -/// -/// @return true in case of success, false otherwise. -bool nlua_push_typval(lua_State *lstate, typval_T *const tv) -{ - const int initial_size = lua_gettop(lstate); - if (!lua_checkstack(lstate, initial_size + 2)) { - emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); - return false; - } - if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { - return false; - } - assert(lua_gettop(lstate) == initial_size + 1); - return true; -} - -#define NLUA_PUSH_HANDLE(lstate, type, idx) \ - do { \ - lua_pushnumber(lstate, (lua_Number)(idx)); \ - } while (0) - -#define NLUA_POP_HANDLE(lstate, type, stack_idx, idx) \ - do { \ - idx = (type)lua_tonumber(lstate, stack_idx); \ - } while (0) - -/// Push value which is a type index -/// -/// Used for all “typed” tables: i.e. for all tables which represent VimL -/// values. -static inline void nlua_push_type_idx(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushboolean(lstate, TYPE_IDX_VALUE); -} - -/// Push value which is a value index -/// -/// Used for tables which represent scalar values, like float value. -static inline void nlua_push_val_idx(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushboolean(lstate, VAL_IDX_VALUE); -} - -/// Push type -/// -/// Type is a value in vim.types table. -/// -/// @param[out] lstate Lua state. -/// @param[in] type Type to push. -static inline void nlua_push_type(lua_State *lstate, ObjectType type) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushnumber(lstate, (lua_Number)type); -} - -/// Create lua table which has an entry that determines its VimL type -/// -/// @param[out] lstate Lua state. -/// @param[in] narr Number of “array” entries to be populated later. -/// @param[in] nrec Number of “dictionary” entries to be populated later. -/// @param[in] type Type of the table. -static inline void nlua_create_typed_table(lua_State *lstate, - const size_t narr, - const size_t nrec, - const ObjectType type) - FUNC_ATTR_NONNULL_ALL -{ - lua_createtable(lstate, (int)narr, (int)(1 + nrec)); - nlua_push_type_idx(lstate); - nlua_push_type(lstate, type); - lua_rawset(lstate, -3); -} - - -/// Convert given String to lua string -/// -/// Leaves converted string on top of the stack. -void nlua_push_String(lua_State *lstate, const String s) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushlstring(lstate, s.data, s.size); -} - -/// Convert given Integer to lua number -/// -/// Leaves converted number on top of the stack. -void nlua_push_Integer(lua_State *lstate, const Integer n) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushnumber(lstate, (lua_Number)n); -} - -/// Convert given Float to lua table -/// -/// Leaves converted table on top of the stack. -void nlua_push_Float(lua_State *lstate, const Float f) - FUNC_ATTR_NONNULL_ALL -{ - nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat); - nlua_push_val_idx(lstate); - lua_pushnumber(lstate, (lua_Number)f); - lua_rawset(lstate, -3); -} - -/// Convert given Float to lua boolean -/// -/// Leaves converted value on top of the stack. -void nlua_push_Boolean(lua_State *lstate, const Boolean b) - FUNC_ATTR_NONNULL_ALL -{ - lua_pushboolean(lstate, b); -} - -/// Convert given Dictionary to lua table -/// -/// Leaves converted table on top of the stack. -void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict) - FUNC_ATTR_NONNULL_ALL -{ - if (dict.size == 0) { - nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); - } else { - lua_createtable(lstate, 0, (int)dict.size); - } - for (size_t i = 0; i < dict.size; i++) { - nlua_push_String(lstate, dict.items[i].key); - nlua_push_Object(lstate, dict.items[i].value); - lua_rawset(lstate, -3); - } -} - -/// Convert given Array to lua table -/// -/// Leaves converted table on top of the stack. -void nlua_push_Array(lua_State *lstate, const Array array) - FUNC_ATTR_NONNULL_ALL -{ - lua_createtable(lstate, (int)array.size, 0); - for (size_t i = 0; i < array.size; i++) { - nlua_push_Object(lstate, array.items[i]); - lua_rawseti(lstate, -2, (int)i + 1); - } -} - -#define GENERATE_INDEX_FUNCTION(type) \ -void nlua_push_##type(lua_State *lstate, const type item) \ - FUNC_ATTR_NONNULL_ALL \ -{ \ - NLUA_PUSH_HANDLE(lstate, type, item); \ -} - -GENERATE_INDEX_FUNCTION(Buffer) -GENERATE_INDEX_FUNCTION(Window) -GENERATE_INDEX_FUNCTION(Tabpage) - -#undef GENERATE_INDEX_FUNCTION - -/// Convert given Object to lua value -/// -/// Leaves converted value on top of the stack. -void nlua_push_Object(lua_State *lstate, const Object obj) - FUNC_ATTR_NONNULL_ALL -{ - switch (obj.type) { - case kObjectTypeNil: { - lua_pushnil(lstate); - break; - } -#define ADD_TYPE(type, data_key) \ - case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key); \ - break; \ - } - ADD_TYPE(Boolean, boolean) - ADD_TYPE(Integer, integer) - ADD_TYPE(Float, floating) - ADD_TYPE(String, string) - ADD_TYPE(Array, array) - ADD_TYPE(Dictionary, dictionary) -#undef ADD_TYPE -#define ADD_REMOTE_TYPE(type) \ - case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer); \ - break; \ - } - ADD_REMOTE_TYPE(Buffer) - ADD_REMOTE_TYPE(Window) - ADD_REMOTE_TYPE(Tabpage) -#undef ADD_REMOTE_TYPE - } -} - - -/// Convert lua value to string -/// -/// Always pops one value from the stack. -String nlua_pop_String(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (lua_type(lstate, -1) != LUA_TSTRING) { - lua_pop(lstate, 1); - api_set_error(err, Validation, "Expected lua string"); - return (String) { .size = 0, .data = NULL }; - } - String ret; - - ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size)); - assert(ret.data != NULL); - ret.data = xmemdupz(ret.data, ret.size); - lua_pop(lstate, 1); - - return ret; -} - -/// Convert lua value to integer -/// -/// Always pops one value from the stack. -Integer nlua_pop_Integer(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (lua_type(lstate, -1) != LUA_TNUMBER) { - lua_pop(lstate, 1); - api_set_error(err, Validation, "Expected lua number"); - return 0; - } - const lua_Number n = lua_tonumber(lstate, -1); - lua_pop(lstate, 1); - if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN - || ((lua_Number)((Integer)n)) != n) { - api_set_error(err, Exception, "Number is not integral"); - return 0; - } - return (Integer)n; -} - -/// Convert lua value to boolean -/// -/// Always pops one value from the stack. -Boolean nlua_pop_Boolean(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - const Boolean ret = lua_toboolean(lstate, -1); - lua_pop(lstate, 1); - return ret; -} - -/// Check whether typed table on top of the stack has given type -/// -/// @param[in] lstate Lua state. -/// @param[out] err Location where error will be saved. May be NULL. -/// @param[in] type Type to check. -/// -/// @return @see nlua_traverse_table(). -static inline LuaTableProps nlua_check_type(lua_State *const lstate, - Error *const err, - const ObjectType type) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (lua_type(lstate, -1) != LUA_TTABLE) { - if (err) { - api_set_error(err, Validation, "Expected lua table"); - } - return (LuaTableProps) { .type = kObjectTypeNil }; - } - LuaTableProps table_props = nlua_traverse_table(lstate); - - if (type == kObjectTypeDictionary && table_props.type == kObjectTypeArray - && table_props.maxidx == 0 && !table_props.has_type_key) { - table_props.type = kObjectTypeDictionary; - } - - if (table_props.type != type) { - if (err) { - api_set_error(err, Validation, "Unexpected type"); - } - } - - return table_props; -} - -/// Convert lua table to float -/// -/// Always pops one value from the stack. -Float nlua_pop_Float(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (lua_type(lstate, -1) == LUA_TNUMBER) { - const Float ret = (Float)lua_tonumber(lstate, -1); - lua_pop(lstate, 1); - return ret; - } - - const LuaTableProps table_props = nlua_check_type(lstate, err, - kObjectTypeFloat); - lua_pop(lstate, 1); - if (table_props.type != kObjectTypeFloat) { - return 0; - } else { - return (Float)table_props.val; - } -} - -/// Convert lua table to array without determining whether it is array -/// -/// @param lstate Lua state. -/// @param[in] table_props nlua_traverse_table() output. -/// @param[out] err Location where error will be saved. -static Array nlua_pop_Array_unchecked(lua_State *const lstate, - const LuaTableProps table_props, - Error *const err) -{ - Array ret = { .size = table_props.maxidx, .items = NULL }; - - if (ret.size == 0) { - lua_pop(lstate, 1); - return ret; - } - - ret.items = xcalloc(ret.size, sizeof(*ret.items)); - for (size_t i = 1; i <= ret.size; i++) { - Object val; - - lua_rawgeti(lstate, -1, (int)i); - - val = nlua_pop_Object(lstate, err); - if (err->set) { - ret.size = i - 1; - lua_pop(lstate, 1); - api_free_array(ret); - return (Array) { .size = 0, .items = NULL }; - } - ret.items[i - 1] = val; - } - lua_pop(lstate, 1); - - return ret; -} - -/// Convert lua table to array -/// -/// Always pops one value from the stack. -Array nlua_pop_Array(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - const LuaTableProps table_props = nlua_check_type(lstate, err, - kObjectTypeArray); - if (table_props.type != kObjectTypeArray) { - return (Array) { .size = 0, .items = NULL }; - } - return nlua_pop_Array_unchecked(lstate, table_props, err); -} - -/// Convert lua table to dictionary -/// -/// Always pops one value from the stack. Does not check whether whether topmost -/// value on the stack is a table. -/// -/// @param lstate Lua interpreter state. -/// @param[in] table_props nlua_traverse_table() output. -/// @param[out] err Location where error will be saved. -static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, - const LuaTableProps table_props, - Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - Dictionary ret = { .size = table_props.string_keys_num, .items = NULL }; - - if (ret.size == 0) { - lua_pop(lstate, 1); - return ret; - } - ret.items = xcalloc(ret.size, sizeof(*ret.items)); - - lua_pushnil(lstate); - for (size_t i = 0; lua_next(lstate, -2) && i < ret.size;) { - // stack: dict, key, value - - if (lua_type(lstate, -2) == LUA_TSTRING) { - lua_pushvalue(lstate, -2); - // stack: dict, key, value, key - - ret.items[i].key = nlua_pop_String(lstate, err); - // stack: dict, key, value - - if (!err->set) { - ret.items[i].value = nlua_pop_Object(lstate, err); - // stack: dict, key - } else { - lua_pop(lstate, 1); - // stack: dict, key - } - - if (err->set) { - ret.size = i; - api_free_dictionary(ret); - lua_pop(lstate, 2); - // stack: - return (Dictionary) { .size = 0, .items = NULL }; - } - i++; - } else { - lua_pop(lstate, 1); - // stack: dict, key - } - } - lua_pop(lstate, 1); - - return ret; -} - -/// Convert lua table to dictionary -/// -/// Always pops one value from the stack. -Dictionary nlua_pop_Dictionary(lua_State *lstate, Error *err) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - const LuaTableProps table_props = nlua_check_type(lstate, err, - kObjectTypeDictionary); - if (table_props.type != kObjectTypeDictionary) { - lua_pop(lstate, 1); - return (Dictionary) { .size = 0, .items = NULL }; - } - - return nlua_pop_Dictionary_unchecked(lstate, table_props, err); -} - -/// Helper structure for nlua_pop_Object -typedef struct { - Object *obj; ///< Location where conversion result is saved. - bool container; ///< True if tv is a container. -} ObjPopStackItem; - -/// Convert lua table to object -/// -/// Always pops one value from the stack. -Object nlua_pop_Object(lua_State *const lstate, Error *const err) -{ - Object ret = NIL; - const int initial_size = lua_gettop(lstate); - kvec_t(ObjPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((ObjPopStackItem) { &ret, false })); - while (!err->set && kv_size(stack)) { - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { - api_set_error(err, Exception, "Lua failed to grow stack"); - break; - } - ObjPopStackItem cur = kv_pop(stack); - if (cur.container) { - if (cur.obj->type == kObjectTypeDictionary) { - // stack: …, dict, key - if (cur.obj->data.dictionary.size - == cur.obj->data.dictionary.capacity) { - lua_pop(lstate, 2); - continue; - } - bool next_key_found = false; - while (lua_next(lstate, -2)) { - // stack: …, dict, new key, val - if (lua_type(lstate, -2) == LUA_TSTRING) { - next_key_found = true; - break; - } - lua_pop(lstate, 1); - // stack: …, dict, new key - } - if (next_key_found) { - // stack: …, dict, new key, val - size_t len; - const char *s = lua_tolstring(lstate, -2, &len); - const size_t idx = cur.obj->data.dictionary.size++; - cur.obj->data.dictionary.items[idx].key = (String) { - .data = xmemdupz(s, len), - .size = len, - }; - kv_push(stack, cur); - cur = (ObjPopStackItem) { - .obj = &cur.obj->data.dictionary.items[idx].value, - .container = false, - }; - } else { - // stack: …, dict - lua_pop(lstate, 1); - // stack: … - continue; - } - } else { - if (cur.obj->data.array.size == cur.obj->data.array.capacity) { - lua_pop(lstate, 1); - continue; - } - const size_t idx = cur.obj->data.array.size++; - lua_rawgeti(lstate, -1, (int)idx + 1); - if (lua_isnil(lstate, -1)) { - lua_pop(lstate, 2); - continue; - } - kv_push(stack, cur); - cur = (ObjPopStackItem) { - .obj = &cur.obj->data.array.items[idx], - .container = false, - }; - } - } - assert(!cur.container); - *cur.obj = NIL; - switch (lua_type(lstate, -1)) { - case LUA_TNIL: { - break; - } - case LUA_TBOOLEAN: { - *cur.obj = BOOLEAN_OBJ(lua_toboolean(lstate, -1)); - break; - } - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(lstate, -1, &len); - *cur.obj = STRING_OBJ(((String) { - .data = xmemdupz(s, len), - .size = len, - })); - break; - } - case LUA_TNUMBER: { - const lua_Number n = lua_tonumber(lstate, -1); - if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN - || ((lua_Number)((Integer)n)) != n) { - *cur.obj = FLOAT_OBJ((Float)n); - } else { - *cur.obj = INTEGER_OBJ((Integer)n); - } - break; - } - case LUA_TTABLE: { - const LuaTableProps table_props = nlua_traverse_table(lstate); - - switch (table_props.type) { - case kObjectTypeArray: { - *cur.obj = ARRAY_OBJ(((Array) { - .items = NULL, - .size = 0, - .capacity = 0, - })); - if (table_props.maxidx != 0) { - cur.obj->data.array.items = - xcalloc(table_props.maxidx, - sizeof(cur.obj->data.array.items[0])); - cur.obj->data.array.capacity = table_props.maxidx; - cur.container = true; - kv_push(stack, cur); - } - break; - } - case kObjectTypeDictionary: { - *cur.obj = DICTIONARY_OBJ(((Dictionary) { - .items = NULL, - .size = 0, - .capacity = 0, - })); - if (table_props.string_keys_num != 0) { - cur.obj->data.dictionary.items = - xcalloc(table_props.string_keys_num, - sizeof(cur.obj->data.dictionary.items[0])); - cur.obj->data.dictionary.capacity = table_props.string_keys_num; - cur.container = true; - kv_push(stack, cur); - lua_pushnil(lstate); - } - break; - } - case kObjectTypeFloat: { - *cur.obj = FLOAT_OBJ((Float)table_props.val); - break; - } - case kObjectTypeNil: { - api_set_error(err, Validation, "Cannot convert given lua table"); - break; - } - default: { - assert(false); - } - } - break; - } - default: { - api_set_error(err, Validation, "Cannot convert given lua type"); - break; - } - } - if (!cur.container) { - lua_pop(lstate, 1); - } - } - kv_destroy(stack); - if (err->set) { - api_free_object(ret); - ret = NIL; - lua_pop(lstate, lua_gettop(lstate) - initial_size + 1); - } - assert(lua_gettop(lstate) == initial_size - 1); - return ret; -} - -#define GENERATE_INDEX_FUNCTION(type) \ -type nlua_pop_##type(lua_State *lstate, Error *err) \ - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - type ret; \ - NLUA_POP_HANDLE(lstate, type, -1, ret); \ - lua_pop(lstate, 1); \ - return ret; \ -} - -GENERATE_INDEX_FUNCTION(Buffer) -GENERATE_INDEX_FUNCTION(Window) -GENERATE_INDEX_FUNCTION(Tabpage) - -#undef GENERATE_INDEX_FUNCTION - -/// Record some auxilary values in vim module -/// -/// Assumes that module table is on top of the stack. -/// -/// Recorded values: -/// -/// `vim.type_idx`: @see nlua_push_type_idx() -/// `vim.val_idx`: @see nlua_push_val_idx() -/// `vim.types`: table mapping possible values of `vim.type_idx` to string -/// names (i.e. `array`, `float`, `dictionary`) and back. -void nlua_init_types(lua_State *const lstate) -{ - LUA_PUSH_STATIC_STRING(lstate, "type_idx"); - nlua_push_type_idx(lstate); - lua_rawset(lstate, -3); - - LUA_PUSH_STATIC_STRING(lstate, "val_idx"); - nlua_push_val_idx(lstate); - lua_rawset(lstate, -3); - - LUA_PUSH_STATIC_STRING(lstate, "types"); - lua_createtable(lstate, 0, 3); - - LUA_PUSH_STATIC_STRING(lstate, "float"); - lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat); - lua_rawset(lstate, -3); - lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat); - LUA_PUSH_STATIC_STRING(lstate, "float"); - lua_rawset(lstate, -3); - - LUA_PUSH_STATIC_STRING(lstate, "array"); - lua_pushnumber(lstate, (lua_Number)kObjectTypeArray); - lua_rawset(lstate, -3); - lua_pushnumber(lstate, (lua_Number)kObjectTypeArray); - LUA_PUSH_STATIC_STRING(lstate, "array"); - lua_rawset(lstate, -3); - - LUA_PUSH_STATIC_STRING(lstate, "dictionary"); - lua_pushnumber(lstate, (lua_Number)kObjectTypeDictionary); - lua_rawset(lstate, -3); - lua_pushnumber(lstate, (lua_Number)kObjectTypeDictionary); - LUA_PUSH_STATIC_STRING(lstate, "dictionary"); - lua_rawset(lstate, -3); - - lua_rawset(lstate, -3); -} diff --git a/src/nvim/viml/executor/converter.h b/src/nvim/viml/executor/converter.h deleted file mode 100644 index dbbaaebf6b..0000000000 --- a/src/nvim/viml/executor/converter.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef NVIM_VIML_EXECUTOR_CONVERTER_H -#define NVIM_VIML_EXECUTOR_CONVERTER_H - -#include -#include -#include - -#include "nvim/api/private/defs.h" -#include "nvim/func_attr.h" -#include "nvim/eval.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "viml/executor/converter.h.generated.h" -#endif -#endif // NVIM_VIML_EXECUTOR_CONVERTER_H diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c deleted file mode 100644 index 826460772e..0000000000 --- a/src/nvim/viml/executor/executor.c +++ /dev/null @@ -1,576 +0,0 @@ -#include -#include -#include - -#include "nvim/misc1.h" -#include "nvim/getchar.h" -#include "nvim/garray.h" -#include "nvim/func_attr.h" -#include "nvim/api/private/defs.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" -#include "nvim/vim.h" -#include "nvim/ex_getln.h" -#include "nvim/message.h" -#include "nvim/memline.h" -#include "nvim/buffer_defs.h" -#include "nvim/macros.h" -#include "nvim/screen.h" -#include "nvim/cursor.h" -#include "nvim/undo.h" -#include "nvim/ascii.h" - -#include "nvim/viml/executor/executor.h" -#include "nvim/viml/executor/converter.h" - -typedef struct { - Error err; - String lua_err_str; -} LuaError; - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "viml/executor/vim_module.generated.h" -# include "viml/executor/executor.c.generated.h" -#endif - -/// Name of the run code for use in messages -#define NLUA_EVAL_NAME "" - -/// Call C function which does not expect any arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -#define NLUA_CALL_C_FUNCTION_0(lstate, function, numret) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_call(lstate, 0, numret); \ - } while (0) -/// Call C function which expects one argument -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_call(lstate, 1, numret); \ - } while (0) -/// Call C function which expects two arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_pushlightuserdata(lstate, a2); \ - lua_call(lstate, 2, numret); \ - } while (0) -/// Call C function which expects three arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_3(lstate, function, numret, a1, a2, a3) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_pushlightuserdata(lstate, a2); \ - lua_pushlightuserdata(lstate, a3); \ - lua_call(lstate, 3, numret); \ - } while (0) -/// Call C function which expects five arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_4(lstate, function, numret, a1, a2, a3, a4) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_pushlightuserdata(lstate, a2); \ - lua_pushlightuserdata(lstate, a3); \ - lua_pushlightuserdata(lstate, a4); \ - lua_call(lstate, 4, numret); \ - } while (0) - -/// Convert lua error into a Vim error message -/// -/// @param lstate Lua interpreter state. -/// @param[in] msg Message base, must contain one `%s`. -static void nlua_error(lua_State *const lstate, const char *const msg) - FUNC_ATTR_NONNULL_ALL -{ - size_t len; - const char *const str = lua_tolstring(lstate, -1, &len); - - emsgf(msg, (int)len, str); - - lua_pop(lstate, 1); -} - -/// Compare two strings, ignoring case -/// -/// Expects two values on the stack: compared strings. Returns one of the -/// following numbers: 0, -1 or 1. -/// -/// Does no error handling: never call it with non-string or with some arguments -/// omitted. -static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const char *s1 = luaL_checklstring(lstate, 1, NULL); - const char *s2 = luaL_checklstring(lstate, 2, NULL); - const int ret = STRICMP(s1, s2); - lua_pop(lstate, 2); - lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); - return 1; -} - -/// Evaluate lua string -/// -/// Expects two values on the stack: string to evaluate, pointer to the -/// location where result is saved. Always returns nothing (from the lua point -/// of view). -static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const String *const str = (const String *)lua_touserdata(lstate, 1); - typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2); - lua_pop(lstate, 2); - - if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s")); - return 0; - } - if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s")); - return 0; - } - if (!nlua_pop_typval(lstate, ret_tv)) { - return 0; - } - return 0; -} - -/// Evaluate lua string for each line in range -/// -/// Expects two values on the stack: string to evaluate and pointer to integer -/// array with line range. Always returns nothing (from the lua point of view). -static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const String *const str = (const String *)lua_touserdata(lstate, 1); - const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2); - lua_pop(lstate, 2); - -#define DOSTART "return function(line, linenr) " -#define DOEND " end" - const size_t lcmd_len = (str->size - + (sizeof(DOSTART) - 1) - + (sizeof(DOEND) - 1)); - char *lcmd; - if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; - } else { - lcmd = xmalloc(lcmd_len + 1); - } - memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1); - memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); - memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, DOEND, sizeof(DOEND) - 1); -#undef DOSTART -#undef DOEND - - if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); - if (lcmd_len >= IOSIZE) { - xfree(lcmd); - } - return 0; - } - if (lcmd_len >= IOSIZE) { - xfree(lcmd); - } - if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); - return 0; - } - for (linenr_T l = range[0]; l <= range[1]; l++) { - if (l > curbuf->b_ml.ml_line_count) { - break; - } - lua_pushvalue(lstate, -1); - lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); - lua_pushnumber(lstate, (lua_Number)l); - if (lua_pcall(lstate, 2, 1, 0)) { - nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); - break; - } - if (lua_isstring(lstate, -1)) { - size_t new_line_len; - const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); - char *const new_line_transformed = xmemdupz(new_line, new_line_len); - for (size_t i = 0; i < new_line_len; i++) { - if (new_line_transformed[i] == NUL) { - new_line_transformed[i] = '\n'; - } - } - ml_replace(l, (char_u *)new_line_transformed, false); - changed_bytes(l, 0); - } - lua_pop(lstate, 1); - } - lua_pop(lstate, 1); - check_cursor(); - update_screen(NOT_VALID); - return 0; -} - -/// Evaluate lua file -/// -/// Expects one value on the stack: file to evaluate. Always returns nothing -/// (from the lua point of view). -static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const char *const filename = (const char *)lua_touserdata(lstate, 1); - lua_pop(lstate, 1); - - if (luaL_loadfile(lstate, filename)) { - nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); - return 0; - } - if (lua_pcall(lstate, 0, 0, 0)) { - nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); - return 0; - } - return 0; -} - -/// Initialize lua interpreter state -/// -/// Called by lua interpreter itself to initialize state. -static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - // stricmp - lua_pushcfunction(lstate, &nlua_stricmp); - lua_setglobal(lstate, "stricmp"); - - // print - lua_pushcfunction(lstate, &nlua_print); - lua_setglobal(lstate, "print"); - - // debug.debug - lua_getglobal(lstate, "debug"); - lua_pushcfunction(lstate, &nlua_debug); - lua_setfield(lstate, -2, "debug"); - lua_pop(lstate, 1); - - // vim - if (luaL_dostring(lstate, (char *)&vim_module[0])) { - nlua_error(lstate, _("E5106: Error while creating vim module: %.*s")); - return 1; - } - // vim.api - nlua_add_api_functions(lstate); - // vim.types, vim.type_idx, vim.val_idx - nlua_init_types(lstate); - lua_setglobal(lstate, "vim"); - return 0; -} - -/// Initialize lua interpreter -/// -/// Crashes NeoVim if initialization fails. Should be called once per lua -/// interpreter instance. -static lua_State *init_lua(void) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - lua_State *lstate = luaL_newstate(); - if (lstate == NULL) { - EMSG(_("E970: Failed to initialize lua interpreter")); - preserve_exit(); - } - luaL_openlibs(lstate); - NLUA_CALL_C_FUNCTION_0(lstate, nlua_state_init, 0); - return lstate; -} - -static lua_State *global_lstate = NULL; - -/// Execute lua string -/// -/// @param[in] str String to execute. -/// @param[out] ret_tv Location where result will be saved. -/// -/// @return Result of the execution. -void executor_exec_lua(const String str, typval_T *const ret_tv) - FUNC_ATTR_NONNULL_ALL -{ - if (global_lstate == NULL) { - global_lstate = init_lua(); - } - - NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, - (void *)&str, ret_tv); -} - -/// Evaluate lua string -/// -/// Used for luaeval(). Expects three values on the stack: -/// -/// 1. String to evaluate. -/// 2. _A value. -/// 3. Pointer to location where result is saved. -/// -/// @param[in,out] lstate Lua interpreter state. -static int nlua_eval_lua_string(lua_State *const lstate) - FUNC_ATTR_NONNULL_ALL -{ - const String *const str = (const String *)lua_touserdata(lstate, 1); - typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2); - typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3); - lua_pop(lstate, 3); - - garray_T str_ga; - ga_init(&str_ga, 1, 80); -#define EVALHEADER "local _A=select(1,...) return (" - const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1; - char *lcmd; - if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; - } else { - lcmd = xmalloc(lcmd_len); - } - memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); - memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); - lcmd[lcmd_len - 1] = ')'; -#undef EVALHEADER - if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { - nlua_error(lstate, - _("E5107: Error while creating lua chunk for luaeval(): %.*s")); - if (lcmd != (char *)IObuff) { - xfree(lcmd); - } - return 0; - } - if (lcmd != (char *)IObuff) { - xfree(lcmd); - } - - if (arg == NULL || arg->v_type == VAR_UNKNOWN) { - lua_pushnil(lstate); - } else { - nlua_push_typval(lstate, arg); - } - if (lua_pcall(lstate, 1, 1, 0)) { - nlua_error(lstate, - _("E5108: Error while calling lua chunk for luaeval(): %.*s")); - return 0; - } - if (!nlua_pop_typval(lstate, ret_tv)) { - return 0; - } - - return 0; -} - -/// Print as a Vim message -/// -/// @param lstate Lua interpreter state. -static int nlua_print(lua_State *const lstate) - FUNC_ATTR_NONNULL_ALL -{ -#define PRINT_ERROR(msg) \ - do { \ - errmsg = msg; \ - errmsg_len = sizeof(msg) - 1; \ - goto nlua_print_error; \ - } while (0) - const int nargs = lua_gettop(lstate); - lua_getglobal(lstate, "tostring"); - const char *errmsg = NULL; - size_t errmsg_len = 0; - garray_T msg_ga; - ga_init(&msg_ga, 1, 80); - int curargidx = 1; - for (; curargidx <= nargs; curargidx++) { - lua_pushvalue(lstate, -1); // tostring - lua_pushvalue(lstate, curargidx); // arg - if (lua_pcall(lstate, 1, 1, 0)) { - errmsg = lua_tolstring(lstate, -1, &errmsg_len); - goto nlua_print_error; - } - size_t len; - const char *const s = lua_tolstring(lstate, -1, &len); - if (s == NULL) { - PRINT_ERROR( - ""); - } - ga_concat_len(&msg_ga, s, len); - if (curargidx < nargs) { - ga_append(&msg_ga, ' '); - } - lua_pop(lstate, 1); - } -#undef PRINT_ERROR - lua_pop(lstate, nargs + 1); - ga_append(&msg_ga, NUL); - { - const size_t len = (size_t)msg_ga.ga_len - 1; - char *const str = (char *)msg_ga.ga_data; - - for (size_t i = 0; i < len;) { - const size_t start = i; - while (i < len) { - switch (str[i]) { - case NUL: { - str[i] = NL; - i++; - continue; - } - case NL: { - str[i] = NUL; - i++; - break; - } - default: { - i++; - continue; - } - } - break; - } - msg((char_u *)str + start); - } - if (str[len - 1] == NUL) { // Last was newline - msg((char_u *)""); - } - } - ga_clear(&msg_ga); - return 0; -nlua_print_error: - emsgf(_("E5114: Error while converting print argument #%i: %.*s"), - curargidx, errmsg_len, errmsg); - ga_clear(&msg_ga); - lua_pop(lstate, lua_gettop(lstate)); - return 0; -} - -/// debug.debug implementation: interaction with user while debugging -/// -/// @param lstate Lua interpreter state. -int nlua_debug(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL -{ - const typval_T input_args[] = { - { - .v_lock = VAR_FIXED, - .v_type = VAR_STRING, - .vval.v_string = (char_u *)"lua_debug> ", - }, - { - .v_type = VAR_UNKNOWN, - }, - }; - for (;;) { - lua_settop(lstate, 0); - typval_T input; - get_user_input(input_args, &input, false); - msg_putchar('\n'); // Avoid outputting on input line. - if (input.v_type != VAR_STRING - || input.vval.v_string == NULL - || *input.vval.v_string == NUL - || STRCMP(input.vval.v_string, "cont") == 0) { - tv_clear(&input); - return 0; - } - if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string, - STRLEN(input.vval.v_string), "=(debug command)")) { - nlua_error(lstate, _("E5115: Error while loading debug string: %.*s")); - } - tv_clear(&input); - if (lua_pcall(lstate, 0, 0, 0)) { - nlua_error(lstate, _("E5116: Error while calling debug string: %.*s")); - } - } - return 0; -} - -/// Evaluate lua string -/// -/// Used for luaeval(). -/// -/// @param[in] str String to execute. -/// @param[in] arg Second argument to `luaeval()`. -/// @param[out] ret_tv Location where result will be saved. -/// -/// @return Result of the execution. -void executor_eval_lua(const String str, typval_T *const arg, - typval_T *const ret_tv) - FUNC_ATTR_NONNULL_ALL -{ - if (global_lstate == NULL) { - global_lstate = init_lua(); - } - - NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0, - (void *)&str, arg, ret_tv); -} - -/// Run lua string -/// -/// Used for :lua. -/// -/// @param eap VimL command being run. -void ex_lua(exarg_T *const eap) - FUNC_ATTR_NONNULL_ALL -{ - size_t len; - char *const code = script_get(eap, &len); - if (eap->skip) { - xfree(code); - return; - } - typval_T tv = { .v_type = VAR_UNKNOWN }; - executor_exec_lua((String) { .data = code, .size = len }, &tv); - tv_clear(&tv); - xfree(code); -} - -/// Run lua string for each line in range -/// -/// Used for :luado. -/// -/// @param eap VimL command being run. -void ex_luado(exarg_T *const eap) - FUNC_ATTR_NONNULL_ALL -{ - if (global_lstate == NULL) { - global_lstate = init_lua(); - } - if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { - EMSG(_("cannot save undo information")); - return; - } - const String cmd = { - .size = STRLEN(eap->arg), - .data = (char *)eap->arg, - }; - const linenr_T range[] = { eap->line1, eap->line2 }; - NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0, - (void *)&cmd, (void *)range); -} - -/// Run lua file -/// -/// Used for :luafile. -/// -/// @param eap VimL command being run. -void ex_luafile(exarg_T *const eap) - FUNC_ATTR_NONNULL_ALL -{ - if (global_lstate == NULL) { - global_lstate = init_lua(); - } - NLUA_CALL_C_FUNCTION_1(global_lstate, nlua_exec_lua_file, 0, - (void *)eap->arg); -} diff --git a/src/nvim/viml/executor/executor.h b/src/nvim/viml/executor/executor.h deleted file mode 100644 index 648bb73785..0000000000 --- a/src/nvim/viml/executor/executor.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef NVIM_VIML_EXECUTOR_EXECUTOR_H -#define NVIM_VIML_EXECUTOR_EXECUTOR_H - -#include - -#include "nvim/api/private/defs.h" -#include "nvim/func_attr.h" -#include "nvim/eval/typval.h" -#include "nvim/ex_cmds_defs.h" - -// Generated by msgpack-gen.lua -void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; - -#define set_api_error(s, err) \ - do { \ - Error *err_ = (err); \ - err_->type = kErrorTypeException; \ - err_->set = true; \ - memcpy(&err_->msg[0], s, sizeof(s)); \ - } while (0) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "viml/executor/executor.h.generated.h" -#endif -#endif // NVIM_VIML_EXECUTOR_EXECUTOR_H diff --git a/src/nvim/viml/executor/vim.lua b/src/nvim/viml/executor/vim.lua deleted file mode 100644 index 8d1c5bdf4f..0000000000 --- a/src/nvim/viml/executor/vim.lua +++ /dev/null @@ -1,2 +0,0 @@ --- TODO(ZyX-I): Create compatibility layer. -return {} -- cgit