diff options
Diffstat (limited to 'src/nvim/viml/executor')
-rw-r--r-- | src/nvim/viml/executor/converter.c | 537 | ||||
-rw-r--r-- | src/nvim/viml/executor/converter.h | 12 | ||||
-rw-r--r-- | src/nvim/viml/executor/executor.c | 270 | ||||
-rw-r--r-- | src/nvim/viml/executor/executor.h | 23 | ||||
-rw-r--r-- | src/nvim/viml/executor/vim.lua | 2 |
5 files changed, 844 insertions, 0 deletions
diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c new file mode 100644 index 0000000000..2105beb08a --- /dev/null +++ b/src/nvim/viml/executor/converter.c @@ -0,0 +1,537 @@ +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> +#include <assert.h> + +#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" + +#include "nvim/viml/executor/converter.h" +#include "nvim/viml/executor/executor.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/executor/converter.c.generated.h" +#endif + +#define NLUA_PUSH_IDX(lstate, type, idx) \ + do { \ + STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ + "Number sizes do not match"); \ + const type src = idx; \ + lua_Number tgt; \ + memset(&tgt, 0, sizeof(tgt)); \ + memcpy(&tgt, &src, sizeof(src)); \ + lua_pushnumber(lstate, tgt); \ + } while (0) + +#define NLUA_POP_IDX(lstate, type, stack_idx, idx) \ + do { \ + STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \ + "Number sizes do not match"); \ + const lua_Number src = lua_tonumber(lstate, stack_idx); \ + type tgt; \ + memcpy(&tgt, &src, sizeof(tgt)); \ + idx = tgt; \ + } 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, true); +} + +/// Push value which is a locks index +/// +/// Used for containers tables. +static inline void nlua_push_locks_idx(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_pushboolean(lstate, false); +} + +/// 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_pushnumber(lstate, (lua_Number) 0); +} + +/// Push type +/// +/// Type is a value in vim.types table. +/// +/// @param[out] lstate Lua state. +/// @param[in] type Type to push (key in vim.types table). +static inline void nlua_push_type(lua_State *lstate, const char *const type) +{ + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "types"); + lua_remove(lstate, -2); + lua_getfield(lstate, -1, type); + lua_remove(lstate, -2); +} + +/// 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 char *const 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, "float"); + 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); +} + +static inline void nlua_add_locks_table(lua_State *lstate) +{ + nlua_push_locks_idx(lstate); + lua_newtable(lstate); + lua_rawset(lstate, -3); +} + +/// 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 +{ + nlua_create_typed_table(lstate, 0, 1 + dict.size, "dict"); + nlua_add_locks_table(lstate); + 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 +{ + nlua_create_typed_table(lstate, array.size, 1, "float"); + nlua_add_locks_table(lstate); + for (size_t i = 0; i < array.size; i++) { + nlua_push_Object(lstate, array.items[i]); + lua_rawseti(lstate, -3, (int) i + 1); + } +} + +#define GENERATE_INDEX_FUNCTION(type) \ +void nlua_push_##type(lua_State *lstate, const type item) \ + FUNC_ATTR_NONNULL_ALL \ +{ \ + NLUA_PUSH_IDX(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 +{ + String ret; + + ret.data = (char *) lua_tolstring(lstate, -1, &(ret.size)); + + if (ret.data == NULL) { + lua_pop(lstate, 1); + set_api_error("Expected lua string", err); + return (String) { .size = 0, .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 +{ + Integer ret = 0; + + if (!lua_isnumber(lstate, -1)) { + lua_pop(lstate, 1); + set_api_error("Expected lua integer", err); + return ret; + } + ret = (Integer) lua_tonumber(lstate, -1); + lua_pop(lstate, 1); + + return ret; +} + +/// 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 +{ + Boolean ret = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + return ret; +} + +static inline bool nlua_check_type(lua_State *lstate, Error *err, + const char *const type) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (lua_type(lstate, -1) != LUA_TTABLE) { + set_api_error("Expected lua table", err); + return true; + } + + nlua_push_type_idx(lstate); + lua_rawget(lstate, -2); + nlua_push_type(lstate, type); + if (!lua_rawequal(lstate, -2, -1)) { + lua_pop(lstate, 2); + set_api_error("Expected lua table with float type", err); + return true; + } + lua_pop(lstate, 2); + + return false; +} + +/// 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 +{ + Float ret = 0; + + if (nlua_check_type(lstate, err, "float")) { + lua_pop(lstate, 1); + return 0; + } + + nlua_push_val_idx(lstate); + lua_rawget(lstate, -2); + + if (!lua_isnumber(lstate, -1)) { + lua_pop(lstate, 2); + set_api_error("Value field should be lua number", err); + return ret; + } + ret = lua_tonumber(lstate, -1); + lua_pop(lstate, 2); + + 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 +{ + Array ret = { .size = 0, .items = NULL }; + + if (nlua_check_type(lstate, err, "list")) { + lua_pop(lstate, 1); + return ret; + } + + for (int i = 1; ; i++, ret.size++) { + lua_rawgeti(lstate, -1, i); + + if (lua_isnil(lstate, -1)) { + lua_pop(lstate, 1); + break; + } + lua_pop(lstate, 1); + } + + 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; + 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 dictionary +/// +/// Always pops one value from the stack. Does not check whether +/// `vim.is_dict(table[type_idx])` or whether topmost value on the stack is +/// a table. +Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Dictionary ret = { .size = 0, .items = NULL }; + + lua_pushnil(lstate); + + while (lua_next(lstate, -2)) { + if (lua_type(lstate, -2) == LUA_TSTRING) { + ret.size++; + } + lua_pop(lstate, 1); + } + + 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);) { + // 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 +{ + if (nlua_check_type(lstate, err, "dict")) { + lua_pop(lstate, 1); + return (Dictionary) { .size = 0, .items = NULL }; + } + + return nlua_pop_Dictionary_unchecked(lstate, err); +} + +/// Convert lua table to object +/// +/// Always pops one value from the stack. +Object nlua_pop_Object(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + Object ret = { .type = kObjectTypeNil }; + + switch (lua_type(lstate, -1)) { + case LUA_TNIL: { + ret.type = kObjectTypeNil; + lua_pop(lstate, 1); + break; + } + case LUA_TSTRING: { + ret.type = kObjectTypeString; + ret.data.string = nlua_pop_String(lstate, err); + break; + } + case LUA_TNUMBER: { + ret.type = kObjectTypeInteger; + ret.data.integer = nlua_pop_Integer(lstate, err); + break; + } + case LUA_TBOOLEAN: { + ret.type = kObjectTypeBoolean; + ret.data.boolean = nlua_pop_Boolean(lstate, err); + break; + } + case LUA_TTABLE: { + lua_getglobal(lstate, "vim"); + // stack: obj, vim +#define CHECK_TYPE(Type, key, vim_type) \ + lua_getfield(lstate, -1, "is_" #vim_type); \ + /* stack: obj, vim, checker */ \ + lua_pushvalue(lstate, -3); \ + /* stack: obj, vim, checker, obj */ \ + lua_call(lstate, 1, 1); \ + /* stack: obj, vim, result */ \ + if (lua_toboolean(lstate, -1)) { \ + lua_pop(lstate, 2); \ + /* stack: obj */ \ + ret.type = kObjectType##Type; \ + ret.data.key = nlua_pop_##Type(lstate, err); \ + /* stack: */ \ + break; \ + } \ + lua_pop(lstate, 1); \ + // stack: obj, vim + CHECK_TYPE(Float, floating, float) + CHECK_TYPE(Array, array, list) + CHECK_TYPE(Dictionary, dictionary, dict) +#undef CHECK_TYPE + lua_pop(lstate, 1); + // stack: obj + ret.type = kObjectTypeDictionary; + ret.data.dictionary = nlua_pop_Dictionary_unchecked(lstate, err); + break; + } + default: { + lua_pop(lstate, 1); + set_api_error("Cannot convert given lua type", err); + break; + } + } + if (err->set) { + ret.type = kObjectTypeNil; + } + + 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_IDX(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 diff --git a/src/nvim/viml/executor/converter.h b/src/nvim/viml/executor/converter.h new file mode 100644 index 0000000000..e11d0cef19 --- /dev/null +++ b/src/nvim/viml/executor/converter.h @@ -0,0 +1,12 @@ +#ifndef NVIM_VIML_EXECUTOR_CONVERTER_H +#define NVIM_VIML_EXECUTOR_CONVERTER_H + +#include <lua.h> +#include <stdbool.h> +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.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 new file mode 100644 index 0000000000..6f1e847649 --- /dev/null +++ b/src/nvim/viml/executor/executor.c @@ -0,0 +1,270 @@ +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#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/vim.h" +#include "nvim/vim.h" +#include "nvim/message.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 "<VimL compiled string>" + +/// 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 four 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) + +static void set_lua_error(lua_State *lstate, LuaError *lerr) + FUNC_ATTR_NONNULL_ALL +{ + const char *const str = lua_tolstring(lstate, -1, &lerr->lua_err_str.size); + lerr->lua_err_str.data = xmemdupz(str, lerr->lua_err_str.size); + lua_pop(lstate, 1); + + // FIXME? More specific error? + set_api_error("Error while executing lua code", &lerr->err); +} + +/// 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 *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 three values on the stack: string to evaluate, pointer to the +/// location where result is saved, pointer to the location where error is +/// saved. Always returns nothing (from the lua point of view). +static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +{ + String *str = (String *) lua_touserdata(lstate, 1); + Object *obj = (Object *) lua_touserdata(lstate, 2); + LuaError *lerr = (LuaError *) lua_touserdata(lstate, 3); + lua_pop(lstate, 3); + + if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { + set_lua_error(lstate, lerr); + return 0; + } + if (lua_pcall(lstate, 0, 1, 0)) { + set_lua_error(lstate, lerr); + return 0; + } + *obj = nlua_pop_Object(lstate, &lerr->err); + return 0; +} + +/// Initialize lua interpreter state +/// +/// Called by lua interpreter itself to initialize state. +static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL +{ + lua_pushcfunction(lstate, &nlua_stricmp); + lua_setglobal(lstate, "stricmp"); + if (luaL_dostring(lstate, (char *) &vim_module[0])) { + LuaError lerr; + set_lua_error(lstate, &lerr); + return 1; + } + nlua_add_api_functions(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 Object exec_lua_string(lua_State *lstate, String str, LuaError *lerr) + FUNC_ATTR_NONNULL_ALL +{ + Object ret = { kObjectTypeNil, { false } }; + NLUA_CALL_C_FUNCTION_3(lstate, nlua_exec_lua_string, 0, &str, &ret, lerr); + return ret; +} + +static lua_State *global_lstate = NULL; + +/// Execute lua string +/// +/// Used for :lua. +/// +/// @param[in] str String to execute. +/// @param[out] err Location where error will be saved. +/// @param[out] err_str Location where lua error string will be saved, if any. +/// +/// @return Result of the execution. +Object executor_exec_lua(String str, Error *err, String *err_str) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + LuaError lerr = { + .err = { .set = false }, + .lua_err_str = STRING_INIT, + }; + + Object ret = exec_lua_string(global_lstate, str, &lerr); + + *err = lerr.err; + *err_str = lerr.lua_err_str; + + return ret; +} + +/// 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. +/// 4. Pointer to location where error will be saved. +/// +/// @param[in,out] lstate Lua interpreter state. +static int nlua_eval_lua_string(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + String *str = (String *) lua_touserdata(lstate, 1); + Object *arg = (Object *) lua_touserdata(lstate, 2); + Object *ret = (Object *) lua_touserdata(lstate, 3); + LuaError *lerr = (LuaError *) lua_touserdata(lstate, 4); + + garray_T str_ga; + ga_init(&str_ga, 1, 80); +#define EVALHEADER "local _A=select(1,...) return " + ga_concat_len(&str_ga, EVALHEADER, sizeof(EVALHEADER) - 1); +#undef EVALHEADER + ga_concat_len(&str_ga, str->data, str->size); + if (luaL_loadbuffer(lstate, str_ga.ga_data, (size_t) str_ga.ga_len, + NLUA_EVAL_NAME)) { + set_lua_error(lstate, lerr); + return 0; + } + ga_clear(&str_ga); + + nlua_push_Object(lstate, *arg); + if (lua_pcall(lstate, 1, 1, 0)) { + set_lua_error(lstate, lerr); + return 0; + } + *ret = nlua_pop_Object(lstate, &lerr->err); + + return 0; +} + +static Object eval_lua_string(lua_State *lstate, String str, Object arg, + LuaError *lerr) + FUNC_ATTR_NONNULL_ALL +{ + Object ret = { kObjectTypeNil, { false } }; + NLUA_CALL_C_FUNCTION_4(lstate, nlua_eval_lua_string, 0, + &str, &arg, &ret, lerr); + return ret; +} + +/// Evaluate lua string +/// +/// Used for luaeval(). +/// +/// @param[in] str String to execute. +/// @param[out] err Location where error will be saved. +/// @param[out] err_str Location where lua error string will be saved, if any. +/// +/// @return Result of the execution. +Object executor_eval_lua(String str, Object arg, Error *err, String *err_str) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (global_lstate == NULL) { + global_lstate = init_lua(); + } + + LuaError lerr = { + .err = { .set = false }, + .lua_err_str = STRING_INIT, + }; + + Object ret = eval_lua_string(global_lstate, str, arg, &lerr); + + *err = lerr.err; + *err_str = lerr.lua_err_str; + + return ret; +} diff --git a/src/nvim/viml/executor/executor.h b/src/nvim/viml/executor/executor.h new file mode 100644 index 0000000000..85cb3550e7 --- /dev/null +++ b/src/nvim/viml/executor/executor.h @@ -0,0 +1,23 @@ +#ifndef NVIM_VIML_EXECUTOR_EXECUTOR_H +#define NVIM_VIML_EXECUTOR_EXECUTOR_H + +#include <lua.h> + +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.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 new file mode 100644 index 0000000000..8d1c5bdf4f --- /dev/null +++ b/src/nvim/viml/executor/vim.lua @@ -0,0 +1,2 @@ +-- TODO(ZyX-I): Create compatibility layer. +return {} |