seqlua
changeset 30:367fc70acc15
Respect the __ipairs and __len metamethods
author | jbe |
---|---|
date | Sat Aug 23 23:13:17 2014 +0200 (2014-08-23) |
parents | 2c4d9e44f704 |
children | 4fc9090ada1d |
files | README seqlua.c seqlualib.c seqlualib.h |
line diff
1.1 --- a/README Fri Aug 22 12:27:25 2014 +0200 1.2 +++ b/README Sat Aug 23 23:13:17 2014 +0200 1.3 @@ -3,31 +3,23 @@ 1.4 1.5 This is an experimental package to extend Lua in the following manner: 1.6 1.7 -* allow ``ipairs(seq)`` to accept tables as well as functions, 1.8 +* allow ``ipairs(seq)`` to accept tables as well as functions (i.e function 1.9 + iterators), 1.10 * add a new function ``string.concat(separator, seq)`` to concat either table 1.11 entries or function return values, 1.12 * provide auxiliary C functions and macros to simplify iterating over both 1.13 tables and iterator functions with the same statement. 1.14 1.15 -In other words: 1.16 -When calling ``ipairs(seq)`` or ``string.concat(separator, seq)``, 1.17 -then ``seq`` may either be a (table) sequence or a (function) iterator. 1.18 -Auxiliary C functions and macros are provided to simplify extending your own 1.19 -C functions in the same manner. 1.20 - 1.21 -This library completely ignores the ``__ipairs`` metamethod (as it is 1.22 -deprecated since Lua 5.3.0-alpha). It respects, however, any ``__index`` and 1.23 -``__call`` metamethods (this may cause unexpected behavior when passing 1.24 -callable tables to ``ipairs``). The ``__call`` metamethod takes precedence over 1.25 -an existing ``__index`` metamethod. 1.26 - 1.27 +In all cases, existing ``__index``, ``__len``, or ``__ipairs`` metamethods are 1.28 +respected. The ``__ipairs`` metamethod takes precedence. 1.29 1.30 1.31 Lua part of the library 1.32 ----------------------- 1.33 1.34 -The modified ``ipairs(seq)`` function and the new ``string.concat`` function 1.35 -work as demonstrated in the following examples: 1.36 +The modified ``ipairs(seq)`` and the new ``string.concat(sep, seq)`` functions 1.37 +accept either a table or a function as ``seq``. This is demonstrated in the 1.38 +following examples: 1.39 1.40 require "seqlua" 1.41
2.1 --- a/seqlua.c Fri Aug 22 12:27:25 2014 +0200 2.2 +++ b/seqlua.c Sat Aug 23 23:13:17 2014 +0200 2.3 @@ -2,28 +2,18 @@ 2.4 #include <lauxlib.h> 2.5 #include "seqlualib.h" 2.6 2.7 -static int seqlua_ipairsaux_raw(lua_State *L) { 2.8 - lua_Integer i; 2.9 - luaL_checktype(L, 1, LUA_TTABLE); 2.10 - i = luaL_checkinteger(L, 2) + 1; 2.11 - lua_pushinteger(L, i); 2.12 - lua_rawgeti(L, 1, i); // TODO: Lua 5.3 returns type 2.13 - return lua_isnil(L, -1) ? 1 : 2; 2.14 -} 2.15 - 2.16 -static int seqlua_ipairsaux_meta(lua_State *L) { 2.17 - lua_Integer i; 2.18 - i = luaL_checkinteger(L, 2) + 1; 2.19 +static int seqlua_ipairsaux_index(lua_State *L) { 2.20 + lua_Integer i = luaL_checkinteger(L, 2) + 1; 2.21 lua_pushinteger(L, i); 2.22 lua_pushinteger(L, i); 2.23 lua_gettable(L, 1); // TODO: Lua 5.3 returns type 2.24 - return lua_isnil(L, -1) ? 1 : 2; 2.25 + return lua_isnil(L, -1) && i > luaL_len(L, 1) ? 1 : 2; 2.26 } 2.27 2.28 -static int seqlua_ipairsaux_func(lua_State *L) { 2.29 +static int seqlua_ipairsaux_call(lua_State *L) { 2.30 lua_pushinteger(L, luaL_checkinteger(L, 2) + 1); 2.31 - lua_insert(L, 1); 2.32 - lua_settop(L, 2); 2.33 + lua_insert(L, 1); // integer on stack index 1 2.34 + lua_settop(L, 2); // function on stack index 2 2.35 lua_call(L, 0, LUA_MULTRET); 2.36 if (lua_isnoneornil(L, 2)) { 2.37 lua_settop(L, 0); 2.38 @@ -35,20 +25,22 @@ 2.39 } 2.40 2.41 static int seqlua_ipairs(lua_State *L) { 2.42 - luaL_checkany(L, 1); // provides better error message 2.43 - if ( 2.44 - lua_type(L, 1) == LUA_TFUNCTION || 2.45 - (luaL_getmetafield(L, 1, "__call") && (lua_pop(L, 1), 1)) 2.46 - ) { 2.47 - lua_pushcfunction(L, seqlua_ipairsaux_func); 2.48 - } else if (luaL_getmetafield(L, 1, "__index")) { 2.49 - lua_pushcfunction(L, seqlua_ipairsaux_meta); 2.50 + if (luaL_getmetafield(L, 1, "__ipairs")) { 2.51 + lua_pushvalue(L, 1); 2.52 + lua_call(L, 1, 3); 2.53 } else { 2.54 - luaL_checktype(L, 1, LUA_TTABLE); 2.55 - lua_pushcfunction(L, seqlua_ipairsaux_raw); 2.56 + int t = lua_type(L, 1); 2.57 + if (t == LUA_TFUNCTION) { 2.58 + lua_pushcfunction(L, seqlua_ipairsaux_call); 2.59 + } else { 2.60 + if (t != LUA_TTABLE && !luaL_getmetafield(L, 1, "__index")) { 2.61 + luaL_checktype(L, 1, LUA_TTABLE); 2.62 + } 2.63 + lua_pushcfunction(L, seqlua_ipairsaux_index); 2.64 + } 2.65 + lua_pushvalue(L, 1); 2.66 + lua_pushinteger(L, 0); 2.67 } 2.68 - lua_pushvalue(L, 1); 2.69 - lua_pushinteger(L, 0); 2.70 return 3; 2.71 } 2.72
3.1 --- a/seqlualib.c Fri Aug 22 12:27:25 2014 +0200 3.2 +++ b/seqlualib.c Sat Aug 23 23:13:17 2014 +0200 3.3 @@ -2,25 +2,37 @@ 3.4 #include <lauxlib.h> 3.5 #include "seqlualib.h" 3.6 3.7 -#define SEQLUA_ITERTYPE_FUNC 1 3.8 -#define SEQLUA_ITERTYPE_META 2 3.9 -#define SEQLUA_ITERTYPE_RAW 3 3.10 +#define SEQLUA_ITERTYPE_IPAIRS 1 3.11 +#define SEQLUA_ITERTYPE_CALL 2 3.12 +#define SEQLUA_ITERTYPE_INDEX 3 3.13 +#define SEQLUA_ITERTYPE_RAW 4 3.14 3.15 void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx) { 3.16 - luaL_checkany(L, idx); // provides better error message 3.17 + if (luaL_getmetafield(L, idx, "__ipairs")) { 3.18 + lua_pushvalue(L, idx); 3.19 + lua_call(L, 1, 3); 3.20 + iter->itertype = SEQLUA_ITERTYPE_IPAIRS; 3.21 + } else { 3.22 + if (lua_type(L, idx) == LUA_TFUNCTION) { 3.23 + iter->itertype = SEQLUA_ITERTYPE_CALL; 3.24 + } else if ( 3.25 + luaL_getmetafield(L, idx, "__index") || 3.26 + luaL_getmetafield(L, idx, "__len") 3.27 + ) { 3.28 + lua_pop(L, 1); 3.29 + iter->itertype = SEQLUA_ITERTYPE_INDEX; 3.30 + iter->len = luaL_len(L, idx); 3.31 + } else { 3.32 + luaL_checktype(L, idx, LUA_TTABLE); 3.33 + iter->itertype = SEQLUA_ITERTYPE_RAW; 3.34 + } 3.35 + // always occupy 3 stack indicies 3.36 + lua_pushnil(L); 3.37 + lua_pushnil(L); 3.38 + lua_pushnil(L); 3.39 + iter->idx = idx; 3.40 + } 3.41 iter->L = L; 3.42 - iter->idx = idx; 3.43 - if ( 3.44 - lua_type(L, idx) == LUA_TFUNCTION || 3.45 - (luaL_getmetafield(L, idx, "__call") && (lua_pop(L, 1), 1)) 3.46 - ) { 3.47 - iter->itertype = SEQLUA_ITERTYPE_FUNC; 3.48 - } else if (luaL_getmetafield(L, idx, "__index") && (lua_pop(L, 1), 1)) { 3.49 - iter->itertype = SEQLUA_ITERTYPE_META; 3.50 - } else { 3.51 - luaL_checktype(L, idx, LUA_TTABLE); 3.52 - iter->itertype = SEQLUA_ITERTYPE_RAW; 3.53 - } 3.54 iter->i = 0; 3.55 } 3.56 3.57 @@ -28,20 +40,35 @@ 3.58 lua_State *L = iter->L; 3.59 lua_Integer i = ++iter->i; 3.60 switch (iter->itertype) { 3.61 - case SEQLUA_ITERTYPE_FUNC: 3.62 + case SEQLUA_ITERTYPE_IPAIRS: 3.63 + lua_pushvalue(L, -3); 3.64 + lua_pushvalue(L, -3); 3.65 + lua_pushvalue(L, -3); 3.66 + lua_call(L, 2, 2); 3.67 + if (lua_isnil(L, -2)) { 3.68 + lua_pop(L, 5); 3.69 + return 0; 3.70 + } 3.71 + lua_remove(L, -3); 3.72 + return 1; 3.73 + case SEQLUA_ITERTYPE_CALL: 3.74 lua_pushvalue(L, iter->idx); 3.75 lua_call(L, 0, 1); 3.76 break; 3.77 - case SEQLUA_ITERTYPE_META: 3.78 + case SEQLUA_ITERTYPE_INDEX: 3.79 + if (i > iter->len) { 3.80 + lua_pop(L, 3); 3.81 + return 0; 3.82 + } 3.83 lua_pushinteger(L, i); 3.84 lua_gettable(L, iter->idx); 3.85 - break; 3.86 + return 1; 3.87 case SEQLUA_ITERTYPE_RAW: 3.88 lua_rawgeti(L, iter->idx, i); 3.89 break; 3.90 } 3.91 if (lua_isnil(L, -1)) { 3.92 - lua_pop(L, 1); 3.93 + lua_pop(L, 4); 3.94 return 0; 3.95 } 3.96 return 1;
4.1 --- a/seqlualib.h Fri Aug 22 12:27:25 2014 +0200 4.2 +++ b/seqlualib.h Sat Aug 23 23:13:17 2014 +0200 4.3 @@ -6,6 +6,7 @@ 4.4 int idx; 4.5 int itertype; 4.6 lua_Integer i; 4.7 + lua_Integer len; 4.8 } seqlua_Iterator; 4.9 4.10 extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx);