ZyX wrote:
> > > By the way, why "luaeval('{}')" returns "0" instead of throwing an error?
> >
> > Two reasons: luaeval depends on a conversion routine and I don't want to
> > throw
> > an error whenever a Lua value cannot be converted to a Vim type; and I
> > usually
> > avoid throwing errors if they can be thrown by the user based on a returned
> > value, as it is the case here.
> :echo luaeval('{}') is luaeval('false')
> 1
> . Without +float it is worse. Depending on the plugin it may be an issue, and
> it definitely prevents anybody from writing a wrapper for luaeval that will
> throw an error in case of failed conversion.
>
> I also don’t like the idea of such implicit conversion that gives not a
> single clue about the source of invalid value (if it were stringified like
> mzeval() claims to do it would be easier to guess where invalid input is
> received. Explicit error like in my py*eval() is better because you
> immediately see error line number) and with vim ability to convert numbers to
> strings or floats shown error may be actually far away from the real one, if
> there will be any (and not silent unexpected behavior).
OK, point taken. I'm now throwing an error whenever an "illegal" conversion is
attempted; please check the updated patches (if_lua.c and if_lua.txt.) Thanks
again for the feedback!
Cheers,
Luis
--
Computers are useless. They can only give you answers.
-- Pablo Picasso
--
Luis Carvalho (Kozure)
lua -e 'print((("[email protected]"):gsub("(%u+%.)","")))'
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
--- if_lua.c.orig 2012-08-25 21:09:48.013216503 -0400
+++ if_lua.c 2012-08-30 18:49:02.025139464 -0400
@@ -28,10 +28,16 @@
typedef win_T *luaV_Window;
typedef dict_T *luaV_Dict;
typedef list_T *luaV_List;
+typedef struct {
+ typval_T tv; /* funcref */
+ typval_T args;
+ dict_T *self; /* selfdict */
+} luaV_Funcref;
typedef void (*msgfunc_T)(char_u *);
static const char LUAVIM_DICT[] = "dict";
static const char LUAVIM_LIST[] = "list";
+static const char LUAVIM_FUNCREF[] = "funcref";
static const char LUAVIM_BUFFER[] = "buffer";
static const char LUAVIM_WINDOW[] = "window";
static const char LUAVIM_FREE[] = "luaV_free";
@@ -55,9 +61,15 @@
if (sandbox) luaL_error((L), "not allowed in sandbox")
#define luaV_msg(L) luaV_msgfunc((L), (msgfunc_T) msg)
#define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg)
+#define luaV_checktypval(L,a,v,msg) \
+ do { \
+ if (luaV_totypval(L, a, v) == FAIL) \
+ luaL_error(L, msg ": cannot convert value"); \
+ } while (0)
static luaV_List *luaV_pushlist (lua_State *L, list_T *lis);
static luaV_Dict *luaV_pushdict (lua_State *L, dict_T *dic);
+static luaV_Funcref *luaV_pushfuncref (lua_State *L, typval_T *tv);
#if LUA_VERSION_NUM <= 501
#define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
@@ -480,15 +492,19 @@
case VAR_DICT:
luaV_pushdict(L, tv->vval.v_dict);
break;
+ case VAR_FUNC:
+ luaV_pushfuncref(L, tv);
+ break;
default:
lua_pushnil(L);
}
}
-/* converts lua value at 'pos' to typval 'tv' */
- static void
+/* converts lua value at 'pos' to typval 'tv'; returns OK or FAIL */
+ static int
luaV_totypval (lua_State *L, int pos, typval_T *tv)
{
+ int status = OK;
switch(lua_type(L, pos)) {
case LUA_TBOOLEAN:
tv->v_type = VAR_NUMBER;
@@ -531,14 +547,25 @@
lua_pop(L, 3); /* MTs */
return;
}
- lua_pop(L, 3); /* MTs */
+ /* check funcref */
+ luaV_getfield(L, LUAVIM_FUNCREF);
+ if (lua_rawequal(L, -1, -4))
+ {
+ luaV_Funcref *f = (luaV_Funcref *) p;
+ copy_tv(&f->tv, tv);
+ lua_pop(L, 4); /* MTs */
+ return;
+ }
+ lua_pop(L, 4); /* MTs */
}
- break;
+ /* unknown userdata: fall through */
}
default:
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
+ status = FAIL;
}
+ return status;
}
/* similar to luaL_addlstring, but replaces \0 with \n if toline and
@@ -646,6 +673,7 @@
return 1; \
}
+
/* ======= List type ======= */
static luaV_List *
@@ -748,7 +776,7 @@
else
{
typval_T v;
- luaV_totypval(L, 3, &v);
+ luaV_checktypval(L, 3, &v, "setting list item");
clear_tv(&li->li_tv);
copy_tv(&v, &li->li_tv);
}
@@ -760,17 +788,12 @@
{
luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST);
list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis);
- listitem_T *li;
+ typval_T v;
if (l->lv_lock)
luaL_error(L, "list is locked");
- li = listitem_alloc();
- if (li != NULL)
- {
- typval_T v;
- lua_settop(L, 2);
- luaV_totypval(L, 2, &v);
- list_append_tv(l, &v);
- }
+ lua_settop(L, 2);
+ luaV_checktypval(L, 2, &v, "adding list item");
+ list_append_tv(l, &v);
lua_settop(L, 1);
return 1;
}
@@ -792,7 +815,7 @@
luaL_error(L, "invalid position");
}
lua_settop(L, 2);
- luaV_totypval(L, 2, &v);
+ luaV_checktypval(L, 2, &v, "inserting list item");
list_insert_tv(l, &v, li);
lua_settop(L, 1);
return 1;
@@ -881,8 +904,14 @@
dictitem_T *di = dict_find(d, key, -1);
if (di == NULL)
lua_pushnil(L);
- else
+ else {
luaV_pushtypval(L, &di->di_tv);
+ if (di->di_tv.v_type == VAR_FUNC) { /* funcref? */
+ luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, -1);
+ f->self = d; /* keep "self" reference */
+ d->dv_refcount++;
+ }
+ }
return 1;
}
@@ -916,7 +945,7 @@
}
else {
typval_T v;
- luaV_totypval(L, 3, &v);
+ luaV_checktypval(L, 3, &v, "setting dict item");
copy_tv(&v, &di->di_tv);
}
return 0;
@@ -933,6 +962,88 @@
};
+/* ======= Funcref type ======= */
+
+ static luaV_Funcref *
+luaV_newfuncref (lua_State *L, char_u *name)
+{
+ luaV_Funcref *f = (luaV_Funcref *) lua_newuserdata(L,
+ sizeof(luaV_Funcref));
+ if (name != NULL) {
+ func_ref(name); /* as in copy_tv */
+ f->tv.vval.v_string = vim_strsave(name);
+ }
+ f->tv.v_type = VAR_FUNC;
+ f->args.v_type = VAR_LIST;
+ f->self = NULL;
+ luaV_getfield(L, LUAVIM_FUNCREF);
+ lua_setmetatable(L, -2);
+ return f;
+}
+
+ static luaV_Funcref *
+luaV_pushfuncref (lua_State *L, typval_T *tv)
+{
+ luaV_Funcref *f = luaV_newfuncref(L, NULL);
+ copy_tv(tv, &f->tv);
+ return f;
+}
+
+
+luaV_type_tostring(funcref, LUAVIM_FUNCREF)
+
+ static int
+luaV_funcref_gc (lua_State *L)
+{
+ luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
+ func_unref(f->tv.vval.v_string);
+ vim_free(f->tv.vval.v_string);
+ dict_unref(f->self);
+ return 0;
+}
+
+/* equivalent to string(funcref) */
+ static int
+luaV_funcref_len (lua_State *L)
+{
+ luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
+ lua_pushstring(L, (const char *) f->tv.vval.v_string);
+ return 1;
+}
+
+ static int
+luaV_funcref_call (lua_State *L)
+{
+ luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
+ int i, n = lua_gettop(L) - 1; /* #args */
+ int status;
+ typval_T v, rettv;
+ f->args.vval.v_list = list_alloc();
+ rettv.v_type = VAR_UNKNOWN; /* as in clear_tv */
+ for (i = 0; i < n; i++) {
+ luaV_checktypval(L, i + 2, &v, "calling funcref");
+ list_append_tv(f->args.vval.v_list, &v);
+ }
+ status = func_call(f->tv.vval.v_string, &f->args, f->self, &rettv);
+ if (status == OK)
+ luaV_pushtypval(L, &rettv);
+ clear_tv(&f->args);
+ clear_tv(&rettv);
+ if (status != OK)
+ luaL_error(L, "cannot call funcref");
+ return 1;
+}
+
+static const luaL_Reg luaV_Funcref_mt[] = {
+ {"__tostring", luaV_funcref_tostring},
+ {"__gc", luaV_funcref_gc},
+ {"__len", luaV_funcref_len},
+ {"__call", luaV_funcref_call},
+ {NULL, NULL}
+};
+
+
+
/* ======= Buffer type ======= */
luaV_newtype(buf_T, buffer, luaV_Buffer, LUAVIM_BUFFER)
@@ -1354,22 +1465,74 @@
static int
luaV_list(lua_State *L)
{
- list_T *l = list_alloc();
+ list_T *l;
+ int initarg = !lua_isnoneornil(L, 1);
+ if (initarg && lua_type(L, 1) != LUA_TTABLE)
+ luaL_error(L, "table expected, got %s", luaL_typename(L, 1));
+ l = list_alloc();
if (l == NULL)
lua_pushnil(L);
- else
+ else {
luaV_newlist(L, l);
+ if (initarg) { /* traverse table to init dict */
+ int notnil, i = 0;
+ typval_T v;
+ do {
+ lua_rawgeti(L, 1, ++i);
+ notnil = !lua_isnil(L, -1);
+ if (notnil) {
+ luaV_checktypval(L, -1, &v, "vim.list");
+ list_append_tv(l, &v);
+ }
+ lua_pop(L, 1); /* value */
+ } while (notnil);
+ }
+ }
return 1;
}
static int
luaV_dict(lua_State *L)
{
- dict_T *d = dict_alloc();
+ dict_T *d;
+ int initarg = !lua_isnoneornil(L, 1);
+ if (initarg && lua_type(L, 1) != LUA_TTABLE)
+ luaL_error(L, "table expected, got %s", luaL_typename(L, 1));
+ d = dict_alloc();
if (d == NULL)
lua_pushnil(L);
- else
+ else {
luaV_newdict(L, d);
+ if (initarg) { /* traverse table to init dict */
+ lua_pushnil(L);
+ while (lua_next(L, 1)) {
+ if (lua_type(L, -2) == LUA_TSTRING) {
+ char_u *key = (char_u *) lua_tostring(L, -2);
+ dictitem_T *di = dictitem_alloc(key);
+ typval_T v;
+ if (di == NULL || dict_add(d, di) == FAIL) {
+ vim_free(di);
+ lua_pushnil(L);
+ return 1;
+ }
+ luaV_checktypval(L, -1, &v, "vim.dict"); /* value */
+ copy_tv(&v, &di->di_tv);
+ }
+ lua_pop(L, 1); /* value */
+ }
+ }
+ }
+ return 1;
+}
+
+ static int
+luaV_funcref(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ /* note: not checking if function exists (needs function_exists) */
+ if (name == NULL || *name == NUL || VIM_ISDIGIT(*name))
+ luaL_error(L, "invalid function name: %s", name);
+ luaV_newfuncref(L, (char_u *) name);
return 1;
}
@@ -1455,6 +1618,12 @@
lua_pushstring(L, "dict");
return 1;
}
+ luaV_getfield(L, LUAVIM_FUNCREF);
+ if (lua_rawequal(L, -1, 2))
+ {
+ lua_pushstring(L, "funcref");
+ return 1;
+ }
luaV_getfield(L, LUAVIM_BUFFER);
if (lua_rawequal(L, -1, 2))
{
@@ -1480,6 +1649,7 @@
{"line", luaV_line},
{"list", luaV_list},
{"dict", luaV_dict},
+ {"funcref", luaV_funcref},
{"buffer", luaV_buffer},
{"window", luaV_window},
{"open", luaV_open},
@@ -1520,7 +1690,8 @@
luaV_emsg(L);
return 0;
}
- luaV_totypval(L, -1, rettv);
+ if (luaV_totypval(L, -1, rettv) == FAIL)
+ emsg("luaeval: cannot convert value");
return 0;
}
@@ -1590,6 +1761,9 @@
luaV_newmetatable(L, LUAVIM_DICT);
lua_pushvalue(L, 1);
luaV_openlib(L, luaV_Dict_mt, 1);
+ luaV_newmetatable(L, LUAVIM_FUNCREF);
+ lua_pushvalue(L, 1);
+ luaV_openlib(L, luaV_Funcref_mt, 1);
luaV_newmetatable(L, LUAVIM_BUFFER);
lua_pushvalue(L, 1); /* cache table */
luaV_openlib(L, luaV_Buffer_mt, 1);
--- if_lua.txt.orig 2012-08-25 23:59:42.648787775 -0400
+++ if_lua.txt 2012-08-30 18:54:04.133126761 -0400
@@ -10,9 +10,10 @@
2. The vim module |lua-vim|
3. List userdata |lua-list|
4. Dict userdata |lua-dict|
-5. Buffer userdata |lua-buffer|
-6. Window userdata |lua-window|
-7. The luaeval function |lua-luaeval|
+5. Funcref userdata |lua-funcref|
+6. Buffer userdata |lua-buffer|
+7. Window userdata |lua-window|
+8. The luaeval function |lua-luaeval|
{Vi does not have any of these commands}
@@ -110,9 +111,31 @@
module also includes routines for buffer, window, and current line queries,
Vim evaluation and command execution, and others.
- vim.list() Returns an empty list (see |List|).
-
- vim.dict() Returns an empty dictionary (see |Dictionary|).
+ vim.list([arg]) Returns an empty list or, if "arg" is a Lua
+ table with numeric keys 1, ..., n (a
+ "sequence"), returns a list l such that l[i] =
+ arg[i] for i = 1, ..., n (see |List|).
+ Non-numeric keys are not used to initialize
+ the list. See also |lua-eval| for conversion
+ rules. Example: >
+ :lua t = {math.pi, false, say = 'hi'}
+ :echo luaeval('vim.list(t)')
+ :" [3.141593, 0], 'say' is ignored
+<
+ vim.dict([arg]) Returns an empty dictionary or, if "arg" is a
+ Lua table, returns a dict d such that d[k] =
+ arg[k] for all string keys k in "arg" (see
+ |Dictionary|). Keys that are not strings are
+ not used to initialize the dictionary. See
+ also |lua-eval| for conversion rules.
+ Example: >
+ :lua t = {math.pi, false, say = 'hi'}
+ :echo luaeval('vim.dict(t)')
+ :" {'say': 'hi'}, numeric keys ignored
+<
+ vim.funcref({name}) Returns a Funcref to function {name} (see
+ |Funcref|). It is equivalent to Vim's
+ "function".
vim.buffer([arg]) If "arg" is a number, returns buffer with
number "arg" in the buffer list or, if "arg"
@@ -131,9 +154,9 @@
vim.type({arg}) Returns the type of {arg}. It is equivalent to
Lua's "type" function, but returns "list",
- "dict", "buffer", or "window" if {arg} is a
- list, dictionary, buffer, or window,
- respectively. Examples: >
+ "dict", "funcref", "buffer", or "window" if
+ {arg} is a list, dictionary, funcref, buffer,
+ or window, respectively. Examples: >
:lua l = vim.list()
:lua print(type(l), vim.type(l))
:" userdata list
@@ -229,7 +252,40 @@
<
==============================================================================
-5. Buffer userdata *lua-buffer*
+5. Funcref userdata *lua-funcref*
+
+Funcref userdata represent funcref variables in Vim. Funcrefs that were
+defined with a "dict" attribute need to be obtained as a dictionary key
+in order to have "self" properly assigned to the dictionary (see examples
+below.) A funcref "f" has the following properties:
+
+Properties
+----------
+ o "#f" is the name of the function referenced by "f"
+ o "f(...)" calls the function referenced by "f" (with arguments)
+
+Examples:
+>
+ :function I(x)
+ : return a:x
+ : endfunction
+ :let R = function('I')
+ :lua i1 = vim.funcref('I')
+ :lua i2 = vim.eval('R')
+ :lua print(#i1, #i2) -- both 'I'
+ :lua print(i1, i2, #i2(i1) == #i1(i2))
+ :function Mylen() dict
+ : return len(self.data)
+ : endfunction
+ :let mydict = {'data': [0, 1, 2, 3]}
+ :lua d = vim.eval('mydict'); d.len = vim.funcref('Mylen')
+ :echo mydict.len()
+ :lua l = d.len -- assign d as 'self'
+ :lua print(l())
+<
+
+==============================================================================
+6. Buffer userdata *lua-buffer*
Buffer userdata represent vim buffers. A buffer userdata "b" has the following
properties and methods:
@@ -281,7 +337,7 @@
<
==============================================================================
-6. Window userdata *lua-window*
+7. Window userdata *lua-window*
Window objects represent vim windows. A window userdata "w" has the following
properties and methods:
@@ -313,7 +369,7 @@
<
==============================================================================
-7. The luaeval function *lua-luaeval* *lua-eval*
+8. The luaeval function *lua-luaeval* *lua-eval*
The (dual) equivalent of "vim.eval" for passing Lua values to Vim is
"luaeval". "luaeval" takes an expression string and an optional argument and
@@ -325,7 +381,13 @@
return chunk(arg) -- return typval
end
<
-Note that "_A" receives the argument to "luaeval". Examples: >
+Note that "_A" receives the argument to "luaeval". Lua numbers, strings, and
+list, dict, and funcref userdata are converted to their Vim respective types,
+while Lua booleans are converted to numbers. An error is thrown if conversion
+of any of the remaining Lua types, including userdata other than lists, dicts,
+and funcrefs, is attempted.
+
+Examples: >
:echo luaeval('math.pi')
:lua a = vim.list():add('newlist')