mirror of
https://github.com/neovim/neovim.git
synced 2025-09-13 23:08:16 +00:00
eval: Add luaeval function
No tests yet, no documentation update, no :lua* stuff, no vim module. converter.c should also work with typval_T, not Object. Known problem: luaeval("1", {}) results in PANIC: unprotected error in call to Lua API (attempt to index a nil value) Ref #3823
This commit is contained in:
@@ -310,6 +310,9 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS})
|
||||
find_package(Msgpack 1.0.0 REQUIRED)
|
||||
include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
|
||||
|
||||
find_package(LuaJit REQUIRED)
|
||||
include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS})
|
||||
|
||||
if(UNIX)
|
||||
option(FEAT_TUI "Enable the Terminal UI" ON)
|
||||
else()
|
||||
|
38
scripts/generate_vim_module.lua
Normal file
38
scripts/generate_vim_module.lua
Normal file
@@ -0,0 +1,38 @@
|
||||
assert(#arg == 2)
|
||||
|
||||
module_file = arg[1]
|
||||
target_file = arg[2]
|
||||
|
||||
module = io.open(module_file, 'r')
|
||||
target = io.open(target_file, 'w')
|
||||
|
||||
target:write('#include <stdint.h>\n\n')
|
||||
target:write('static const uint8_t vim_module[] = {\n')
|
||||
|
||||
num_bytes = 0
|
||||
MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line
|
||||
target:write(' ')
|
||||
|
||||
increase_num_bytes = function()
|
||||
num_bytes = num_bytes + 1
|
||||
if num_bytes == MAX_NUM_BYTES then
|
||||
num_bytes = 0
|
||||
target:write('\n ')
|
||||
end
|
||||
end
|
||||
|
||||
for line in module:lines() do
|
||||
for i = 1,string.len(line) do
|
||||
byte = string.byte(line, i)
|
||||
assert(byte ~= 0)
|
||||
target:write(string.format(' %3u,', byte))
|
||||
increase_num_bytes()
|
||||
end
|
||||
target:write(string.format(' %3u,', string.byte('\n', 1)))
|
||||
increase_num_bytes()
|
||||
end
|
||||
|
||||
target:write(' 0};\n')
|
||||
|
||||
module:close()
|
||||
target:close()
|
@@ -45,7 +45,16 @@ c_proto = Ct(
|
||||
grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
|
||||
|
||||
-- we need at least 4 arguments since the last two are output files
|
||||
assert(#arg >= 3)
|
||||
if arg[1] == '--help' then
|
||||
print('Usage: genmsgpack.lua args')
|
||||
print('Args: 1: source directory')
|
||||
print(' 2: dispatch output file (dispatch_wrappers.generated.h)')
|
||||
print(' 3: functions metadata output file (funcs_metadata.generated.h)')
|
||||
print(' 4: API metadata output file (api_metadata.mpack)')
|
||||
print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)')
|
||||
print(' rest: C files where API functions are defined')
|
||||
end
|
||||
assert(#arg >= 4)
|
||||
functions = {}
|
||||
|
||||
local nvimsrcdir = arg[1]
|
||||
@@ -56,17 +65,18 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path
|
||||
headers = {}
|
||||
|
||||
-- output h file with generated dispatch functions
|
||||
dispatch_outputf = arg[#arg-2]
|
||||
dispatch_outputf = arg[2]
|
||||
-- output h file with packed metadata
|
||||
funcs_metadata_outputf = arg[#arg-1]
|
||||
funcs_metadata_outputf = arg[3]
|
||||
-- output metadata mpack file, for use by other build scripts
|
||||
mpack_outputf = arg[#arg]
|
||||
mpack_outputf = arg[4]
|
||||
lua_c_bindings_outputf = arg[5]
|
||||
|
||||
-- set of function names, used to detect duplicates
|
||||
function_names = {}
|
||||
|
||||
-- read each input file, parse and append to the api metadata
|
||||
for i = 2, #arg - 3 do
|
||||
for i = 6, #arg do
|
||||
local full_path = arg[i]
|
||||
local parts = {}
|
||||
for part in string.gmatch(full_path, '[^/]+') do
|
||||
@@ -332,3 +342,128 @@ output:close()
|
||||
mpack_output = io.open(mpack_outputf, 'wb')
|
||||
mpack_output:write(mpack.pack(functions))
|
||||
mpack_output:close()
|
||||
|
||||
local function include_headers(output, headers)
|
||||
for i = 1, #headers do
|
||||
if headers[i]:sub(-12) ~= '.generated.h' then
|
||||
output:write('\n#include "nvim/'..headers[i]..'"')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function write_shifted_output(output, str)
|
||||
str = str:gsub('\n ', '\n')
|
||||
str = str:gsub('^ ', '')
|
||||
str = str:gsub(' +$', '')
|
||||
output:write(str)
|
||||
end
|
||||
|
||||
-- start building lua output
|
||||
output = io.open(lua_c_bindings_outputf, 'wb')
|
||||
|
||||
output:write([[
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "nvim/func_attr.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/viml/executor/converter.h"
|
||||
]])
|
||||
include_headers(output, headers)
|
||||
output:write('\n')
|
||||
|
||||
lua_c_functions = {}
|
||||
|
||||
local function process_function(fn)
|
||||
lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name)
|
||||
write_shifted_output(output, string.format([[
|
||||
|
||||
static int %s(lua_State *lstate)
|
||||
{
|
||||
Error err = {.set = false};
|
||||
]], lua_c_function_name))
|
||||
lua_c_functions[#lua_c_functions + 1] = {
|
||||
binding=lua_c_function_name,
|
||||
api=fn.name
|
||||
}
|
||||
cparams = ''
|
||||
for j, param in ipairs(fn.parameters) do
|
||||
cparam = string.format('arg%u', j)
|
||||
if param[1]:match('^ArrayOf') then
|
||||
param_type = 'Array'
|
||||
else
|
||||
param_type = param[1]
|
||||
end
|
||||
write_shifted_output(output, string.format([[
|
||||
%s %s = nlua_pop_%s(lstate, &err);
|
||||
if (err.set) {
|
||||
lua_pushstring(lstate, err.msg);
|
||||
return lua_error(lstate);
|
||||
}
|
||||
]], param[1], cparam, param_type))
|
||||
cparams = cparams .. cparam .. ', '
|
||||
end
|
||||
if fn.receives_channel_id then
|
||||
cparams = 'INTERNAL_CALL, ' .. cparams
|
||||
end
|
||||
if fn.can_fail then
|
||||
cparams = cparams .. '&err'
|
||||
else
|
||||
cparams = cparams:gsub(', $', '')
|
||||
end
|
||||
local name = fn.impl_name or fn.name
|
||||
if fn.return_type ~= 'void' then
|
||||
if fn.return_type:match('^ArrayOf') then
|
||||
return_type = 'Array'
|
||||
else
|
||||
return_type = fn.return_type
|
||||
end
|
||||
write_shifted_output(output, string.format([[
|
||||
%s ret = %s(%s);
|
||||
if (err.set) {
|
||||
lua_pushstring(lstate, err.msg);
|
||||
return lua_error(lstate);
|
||||
}
|
||||
nlua_push_%s(lstate, ret);
|
||||
return 1;
|
||||
]], fn.return_type, name, cparams, return_type))
|
||||
else
|
||||
write_shifted_output(output, string.format([[
|
||||
%s(%s);
|
||||
if (err.set) {
|
||||
lua_pushstring(lstate, err.msg);
|
||||
return lua_error(lstate);
|
||||
}
|
||||
return 0;
|
||||
]], name, cparams))
|
||||
end
|
||||
write_shifted_output(output, [[
|
||||
}
|
||||
]])
|
||||
end
|
||||
|
||||
for _, fn in ipairs(functions) do
|
||||
if not fn.noeval then
|
||||
process_function(fn)
|
||||
end
|
||||
end
|
||||
|
||||
output:write(string.format([[
|
||||
void nlua_add_api_functions(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
lua_createtable(lstate, 0, %u);
|
||||
]], #lua_c_functions))
|
||||
for _, func in ipairs(lua_c_functions) do
|
||||
output:write(string.format([[
|
||||
lua_pushcfunction(lstate, &%s);
|
||||
lua_setfield(lstate, -2, "%s");
|
||||
]], func.binding, func.api))
|
||||
end
|
||||
output:write([[
|
||||
lua_setfield(lstate, -2, "api");
|
||||
}
|
||||
]])
|
||||
|
||||
output:close()
|
@@ -11,11 +11,10 @@ endif()
|
||||
endif()
|
||||
|
||||
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
|
||||
set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua)
|
||||
file(GLOB API_HEADERS api/*.h)
|
||||
file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
|
||||
set(MSGPACK_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genmsgpack.lua)
|
||||
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
|
||||
set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack)
|
||||
set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c)
|
||||
set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua)
|
||||
set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
|
||||
set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h)
|
||||
@@ -37,8 +36,14 @@ set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua)
|
||||
set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua)
|
||||
set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua)
|
||||
set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode)
|
||||
file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt)
|
||||
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_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/generate_vim_module.lua)
|
||||
|
||||
file(GLOB API_HEADERS api/*.h)
|
||||
file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
|
||||
file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt)
|
||||
|
||||
include_directories(${GENERATED_DIR})
|
||||
include_directories(${CACHED_GENERATED_DIR})
|
||||
@@ -57,6 +62,8 @@ foreach(subdir
|
||||
tui
|
||||
event
|
||||
eval
|
||||
viml
|
||||
viml/executor
|
||||
)
|
||||
if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
|
||||
continue()
|
||||
@@ -198,18 +205,30 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
|
||||
${UNICODE_FILES}
|
||||
)
|
||||
|
||||
add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA}
|
||||
${API_METADATA}
|
||||
COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
|
||||
${API_HEADERS} ${GENERATED_API_DISPATCH}
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA}
|
||||
${API_METADATA} ${MSGPACK_LUA_C_BINDINGS}
|
||||
COMMAND ${LUA_PRG} ${MSGPACK_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
|
||||
${GENERATED_API_DISPATCH}
|
||||
${GENERATED_FUNCS_METADATA} ${API_METADATA}
|
||||
${MSGPACK_LUA_C_BINDINGS}
|
||||
${API_HEADERS}
|
||||
DEPENDS
|
||||
${API_HEADERS}
|
||||
${MSGPACK_RPC_HEADERS}
|
||||
${DISPATCH_GENERATOR}
|
||||
${MSGPACK_GENERATOR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${VIM_MODULE_FILE}
|
||||
COMMAND ${LUA_PRG} ${VIM_MODULE_GENERATOR} ${VIM_MODULE_SOURCE}
|
||||
${VIM_MODULE_FILE}
|
||||
DEPENDS
|
||||
${VIM_MODULE_GENERATOR}
|
||||
${VIM_MODULE_SOURCE}
|
||||
)
|
||||
|
||||
list(APPEND NEOVIM_GENERATED_SOURCES
|
||||
"${PROJECT_BINARY_DIR}/config/auto/pathdef.c"
|
||||
"${GENERATED_API_DISPATCH}"
|
||||
@@ -219,6 +238,8 @@ list(APPEND NEOVIM_GENERATED_SOURCES
|
||||
"${GENERATED_EVENTS_NAMES_MAP}"
|
||||
"${GENERATED_OPTIONS}"
|
||||
"${GENERATED_UNICODE_TABLES}"
|
||||
"${MSGPACK_LUA_C_BINDINGS}"
|
||||
"${VIM_MODULE_FILE}"
|
||||
)
|
||||
|
||||
add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
|
||||
@@ -270,6 +291,7 @@ list(APPEND NVIM_LINK_LIBRARIES
|
||||
${LIBTERMKEY_LIBRARIES}
|
||||
${UNIBILIUM_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${LUAJIT_LIBRARIES}
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
|
@@ -95,6 +95,7 @@
|
||||
#include "nvim/lib/khash.h"
|
||||
#include "nvim/lib/queue.h"
|
||||
#include "nvim/eval/typval_encode.h"
|
||||
#include "nvim/viml/executor/executor.h"
|
||||
|
||||
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
|
||||
|
||||
@@ -13376,6 +13377,48 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
|
||||
}
|
||||
}
|
||||
|
||||
/// luaeval() function implementation
|
||||
static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
char *const str = (char *) get_tv_string(&argvars[0]);
|
||||
if (str == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object arg;
|
||||
if (argvars[1].v_type == VAR_UNKNOWN) {
|
||||
arg = NIL;
|
||||
} else {
|
||||
arg = vim_to_object(&argvars[1]);
|
||||
}
|
||||
|
||||
// TODO(ZyX-I): Create function which converts lua objects directly to VimL
|
||||
// objects, not to API objects.
|
||||
Error err;
|
||||
String err_str;
|
||||
Object ret = executor_eval_lua(cstr_as_string(str), arg, &err, &err_str);
|
||||
if (err.set) {
|
||||
if (err_str.size) {
|
||||
EMSG3(_("E971: Failed to eval lua string: %s (%s)"), err.msg,
|
||||
err_str.data);
|
||||
} else {
|
||||
EMSG2(_("E971: Failed to eval lua string: %s"), err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
api_free_string(err_str);
|
||||
|
||||
if (!err.set) {
|
||||
if (!object_to_vim(ret, rettv, &err)) {
|
||||
EMSG2(_("E972: Failed to convert resulting API object to VimL: %s"),
|
||||
err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
api_free_object(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* "map()" function
|
||||
*/
|
||||
|
@@ -193,6 +193,7 @@ return {
|
||||
localtime={},
|
||||
log={args=1, func="float_op_wrapper", data="&log"},
|
||||
log10={args=1, func="float_op_wrapper", data="&log10"},
|
||||
luaeval={args={1, 2}},
|
||||
map={args=2},
|
||||
maparg={args={1, 4}},
|
||||
mapcheck={args={1, 3}},
|
||||
|
537
src/nvim/viml/executor/converter.c
Normal file
537
src/nvim/viml/executor/converter.c
Normal file
@@ -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
|
12
src/nvim/viml/executor/converter.h
Normal file
12
src/nvim/viml/executor/converter.h
Normal file
@@ -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
|
270
src/nvim/viml/executor/executor.c
Normal file
270
src/nvim/viml/executor/executor.c
Normal file
@@ -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;
|
||||
}
|
23
src/nvim/viml/executor/executor.h
Normal file
23
src/nvim/viml/executor/executor.h
Normal file
@@ -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
|
2
src/nvim/viml/executor/vim.lua
Normal file
2
src/nvim/viml/executor/vim.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
-- TODO(ZyX-I): Create compatibility layer.
|
||||
return {}
|
Reference in New Issue
Block a user