ZyX wrote:
> воскресенье, 26 августа 2012 г., 19:26:06 UTC+4 пользователь Luis Carvalho 
> написал:
> > I'm attaching two patches for if_lua (if_lua.c and docs in if_lua.txt). It
> > fixes (and simplifies) luaV_list_add and introduces a new feature: 
> > funcrefs. I
> > think that with this patch if_lua is now feature complete. :) Any feedback 
> > is,
> > of course, welcome!
> 
> Wondering whether it will support translating lua tables into vim
> dictionaries. And also something like "vim.list({"first", "second", ...})"
> to convert common lua replacement for arrays into vim List. Writing wrapper
> for every plugin wanting to return complex structure into VimL code does not
> look like a good idea.

Thanks for the feedback!

Since there's no one-to-one relation between Lua tables and Vim dictionaries,
I've avoided such conversions and left it to the user to come up with them as
needed (for instance, cyclic references can be tricky, some tables are meant
to be Vim lists, and so on.)

But you're right about using tables as initializers for lists (and dicts) -- I
guess I got lazy before and was waiting for someone to ask about these :) --
and so I'm attaching an updated patch. You can now do:

  :lua l = vim.list{math.pi, true, 'hello!'} -- l = [3.141569, 1, 'hello!']
  :lua d = vim.dict{list = l, msg = 'hi!', n = #l} -- d = {'n': 3.0, ...}

> 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.

I've also updated the documentation to mention that only numbers, strings,
booleans, and list, dict, and funcref userdata are converted to Vim equivalent
types. All other Lua values are converted to zero.

Cheers,
Luis

-- 
Computers are useless. They can only give you answers.
                -- Pablo Picasso

-- 
Luis Carvalho (Kozure)
lua -e 'print((("lexcarva...@no.gmail.spam.com"):gsub("(%u+%.)","")))'
-- 
Computers are useless. They can only give you answers.
                -- Pablo Picasso

-- 
Luis Carvalho (Kozure)
lua -e 'print((("lexcarva...@no.gmail.spam.com"):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-29 18:53:26.765151280 -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";
@@ -58,6 +64,7 @@
 
 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,6 +487,9 @@
 	case VAR_DICT:
 	    luaV_pushdict(L, tv->vval.v_dict);
 	    break;
+	case VAR_FUNC:
+	    luaV_pushfuncref(L, tv);
+	    break;
 	default:
 	    lua_pushnil(L);
     }
@@ -531,9 +541,18 @@
 		    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;
@@ -646,6 +665,7 @@
 	return 1; \
     }
 
+
 /* =======   List type   ======= */
 
     static luaV_List *
@@ -760,17 +780,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_totypval(L, 2, &v);
+    list_append_tv(l, &v);
     lua_settop(L, 1);
     return 1;
 }
@@ -881,8 +896,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;
 }
 
@@ -933,6 +954,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_totypval(L, i + 2, &v);
+	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 +1457,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_totypval(L, -1, &v);
+		    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_totypval(L, -1, &v); /* 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 +1610,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 +1641,7 @@
     {"line", luaV_line},
     {"list", luaV_list},
     {"dict", luaV_dict},
+    {"funcref", luaV_funcref},
     {"buffer", luaV_buffer},
     {"window", luaV_window},
     {"open", luaV_open},
@@ -1590,6 +1752,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-29 19:00:09.389134339 -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. All other Lua types, including
+userdata other than lists, dicts, and funcrefs, are converted to the number
+zero.
+
+Examples: >
 
 	:echo luaeval('math.pi')
 	:lua a = vim.list():add('newlist')

Raspunde prin e-mail lui