# HG changeset patch # User jbe # Date 1408828397 -7200 # Node ID 367fc70acc15cbf0aed04abf25595aab5f7c1a30 # Parent 2c4d9e44f704054284153b920f562e646098a64b Respect the __ipairs and __len metamethods diff -r 2c4d9e44f704 -r 367fc70acc15 README --- a/README Fri Aug 22 12:27:25 2014 +0200 +++ b/README Sat Aug 23 23:13:17 2014 +0200 @@ -3,31 +3,23 @@ This is an experimental package to extend Lua in the following manner: -* allow ``ipairs(seq)`` to accept tables as well as functions, +* allow ``ipairs(seq)`` to accept tables as well as functions (i.e function + iterators), * add a new function ``string.concat(separator, seq)`` to concat either table entries or function return values, * provide auxiliary C functions and macros to simplify iterating over both tables and iterator functions with the same statement. -In other words: -When calling ``ipairs(seq)`` or ``string.concat(separator, seq)``, -then ``seq`` may either be a (table) sequence or a (function) iterator. -Auxiliary C functions and macros are provided to simplify extending your own -C functions in the same manner. - -This library completely ignores the ``__ipairs`` metamethod (as it is -deprecated since Lua 5.3.0-alpha). It respects, however, any ``__index`` and -``__call`` metamethods (this may cause unexpected behavior when passing -callable tables to ``ipairs``). The ``__call`` metamethod takes precedence over -an existing ``__index`` metamethod. - +In all cases, existing ``__index``, ``__len``, or ``__ipairs`` metamethods are +respected. The ``__ipairs`` metamethod takes precedence. Lua part of the library ----------------------- -The modified ``ipairs(seq)`` function and the new ``string.concat`` function -work as demonstrated in the following examples: +The modified ``ipairs(seq)`` and the new ``string.concat(sep, seq)`` functions +accept either a table or a function as ``seq``. This is demonstrated in the +following examples: require "seqlua" diff -r 2c4d9e44f704 -r 367fc70acc15 seqlua.c --- a/seqlua.c Fri Aug 22 12:27:25 2014 +0200 +++ b/seqlua.c Sat Aug 23 23:13:17 2014 +0200 @@ -2,28 +2,18 @@ #include #include "seqlualib.h" -static int seqlua_ipairsaux_raw(lua_State *L) { - lua_Integer i; - luaL_checktype(L, 1, LUA_TTABLE); - i = luaL_checkinteger(L, 2) + 1; - lua_pushinteger(L, i); - lua_rawgeti(L, 1, i); // TODO: Lua 5.3 returns type - return lua_isnil(L, -1) ? 1 : 2; -} - -static int seqlua_ipairsaux_meta(lua_State *L) { - lua_Integer i; - i = luaL_checkinteger(L, 2) + 1; +static int seqlua_ipairsaux_index(lua_State *L) { + lua_Integer i = luaL_checkinteger(L, 2) + 1; lua_pushinteger(L, i); lua_pushinteger(L, i); lua_gettable(L, 1); // TODO: Lua 5.3 returns type - return lua_isnil(L, -1) ? 1 : 2; + return lua_isnil(L, -1) && i > luaL_len(L, 1) ? 1 : 2; } -static int seqlua_ipairsaux_func(lua_State *L) { +static int seqlua_ipairsaux_call(lua_State *L) { lua_pushinteger(L, luaL_checkinteger(L, 2) + 1); - lua_insert(L, 1); - lua_settop(L, 2); + lua_insert(L, 1); // integer on stack index 1 + lua_settop(L, 2); // function on stack index 2 lua_call(L, 0, LUA_MULTRET); if (lua_isnoneornil(L, 2)) { lua_settop(L, 0); @@ -35,20 +25,22 @@ } static int seqlua_ipairs(lua_State *L) { - luaL_checkany(L, 1); // provides better error message - if ( - lua_type(L, 1) == LUA_TFUNCTION || - (luaL_getmetafield(L, 1, "__call") && (lua_pop(L, 1), 1)) - ) { - lua_pushcfunction(L, seqlua_ipairsaux_func); - } else if (luaL_getmetafield(L, 1, "__index")) { - lua_pushcfunction(L, seqlua_ipairsaux_meta); + if (luaL_getmetafield(L, 1, "__ipairs")) { + lua_pushvalue(L, 1); + lua_call(L, 1, 3); } else { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushcfunction(L, seqlua_ipairsaux_raw); + int t = lua_type(L, 1); + if (t == LUA_TFUNCTION) { + lua_pushcfunction(L, seqlua_ipairsaux_call); + } else { + if (t != LUA_TTABLE && !luaL_getmetafield(L, 1, "__index")) { + luaL_checktype(L, 1, LUA_TTABLE); + } + lua_pushcfunction(L, seqlua_ipairsaux_index); + } + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); } - lua_pushvalue(L, 1); - lua_pushinteger(L, 0); return 3; } diff -r 2c4d9e44f704 -r 367fc70acc15 seqlualib.c --- a/seqlualib.c Fri Aug 22 12:27:25 2014 +0200 +++ b/seqlualib.c Sat Aug 23 23:13:17 2014 +0200 @@ -2,25 +2,37 @@ #include #include "seqlualib.h" -#define SEQLUA_ITERTYPE_FUNC 1 -#define SEQLUA_ITERTYPE_META 2 -#define SEQLUA_ITERTYPE_RAW 3 +#define SEQLUA_ITERTYPE_IPAIRS 1 +#define SEQLUA_ITERTYPE_CALL 2 +#define SEQLUA_ITERTYPE_INDEX 3 +#define SEQLUA_ITERTYPE_RAW 4 void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx) { - luaL_checkany(L, idx); // provides better error message + if (luaL_getmetafield(L, idx, "__ipairs")) { + lua_pushvalue(L, idx); + lua_call(L, 1, 3); + iter->itertype = SEQLUA_ITERTYPE_IPAIRS; + } else { + if (lua_type(L, idx) == LUA_TFUNCTION) { + iter->itertype = SEQLUA_ITERTYPE_CALL; + } else if ( + luaL_getmetafield(L, idx, "__index") || + luaL_getmetafield(L, idx, "__len") + ) { + lua_pop(L, 1); + iter->itertype = SEQLUA_ITERTYPE_INDEX; + iter->len = luaL_len(L, idx); + } else { + luaL_checktype(L, idx, LUA_TTABLE); + iter->itertype = SEQLUA_ITERTYPE_RAW; + } + // always occupy 3 stack indicies + lua_pushnil(L); + lua_pushnil(L); + lua_pushnil(L); + iter->idx = idx; + } iter->L = L; - iter->idx = idx; - if ( - lua_type(L, idx) == LUA_TFUNCTION || - (luaL_getmetafield(L, idx, "__call") && (lua_pop(L, 1), 1)) - ) { - iter->itertype = SEQLUA_ITERTYPE_FUNC; - } else if (luaL_getmetafield(L, idx, "__index") && (lua_pop(L, 1), 1)) { - iter->itertype = SEQLUA_ITERTYPE_META; - } else { - luaL_checktype(L, idx, LUA_TTABLE); - iter->itertype = SEQLUA_ITERTYPE_RAW; - } iter->i = 0; } @@ -28,20 +40,35 @@ lua_State *L = iter->L; lua_Integer i = ++iter->i; switch (iter->itertype) { - case SEQLUA_ITERTYPE_FUNC: + case SEQLUA_ITERTYPE_IPAIRS: + lua_pushvalue(L, -3); + lua_pushvalue(L, -3); + lua_pushvalue(L, -3); + lua_call(L, 2, 2); + if (lua_isnil(L, -2)) { + lua_pop(L, 5); + return 0; + } + lua_remove(L, -3); + return 1; + case SEQLUA_ITERTYPE_CALL: lua_pushvalue(L, iter->idx); lua_call(L, 0, 1); break; - case SEQLUA_ITERTYPE_META: + case SEQLUA_ITERTYPE_INDEX: + if (i > iter->len) { + lua_pop(L, 3); + return 0; + } lua_pushinteger(L, i); lua_gettable(L, iter->idx); - break; + return 1; case SEQLUA_ITERTYPE_RAW: lua_rawgeti(L, iter->idx, i); break; } if (lua_isnil(L, -1)) { - lua_pop(L, 1); + lua_pop(L, 4); return 0; } return 1; diff -r 2c4d9e44f704 -r 367fc70acc15 seqlualib.h --- a/seqlualib.h Fri Aug 22 12:27:25 2014 +0200 +++ b/seqlualib.h Sat Aug 23 23:13:17 2014 +0200 @@ -6,6 +6,7 @@ int idx; int itertype; lua_Integer i; + lua_Integer len; } seqlua_Iterator; extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx);