aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/viml/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/viml/executor')
-rw-r--r--src/nvim/viml/executor/converter.c537
-rw-r--r--src/nvim/viml/executor/converter.h12
-rw-r--r--src/nvim/viml/executor/executor.c270
-rw-r--r--src/nvim/viml/executor/executor.h23
-rw-r--r--src/nvim/viml/executor/vim.lua2
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 {}