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 +static int seqlua_ipairsaux_index(lua_State *L) {
    2.12 +  lua_Integer i = luaL_checkinteger(L, 2) + 1;
    2.13    lua_pushinteger(L, i);
    2.14 -  lua_rawgeti(L, 1, i);  // TODO: Lua 5.3 returns type
    2.15 -  return lua_isnil(L, -1) ? 1 : 2;
    2.16 +  lua_pushinteger(L, i);
    2.17 +  lua_gettable(L, 1);  // TODO: Lua 5.3 returns type
    2.18 +  return lua_isnil(L, -1) && i > luaL_len(L, 1) ? 1 : 2;
    2.19  }
    2.20  
    2.21 -static int seqlua_ipairsaux_meta(lua_State *L) {
    2.22 -  lua_Integer i;
    2.23 -  i = luaL_checkinteger(L, 2) + 1;
    2.24 -  lua_pushinteger(L, i);
    2.25 -  lua_pushinteger(L, i);
    2.26 -  lua_gettable(L, 1);  // TODO: Lua 5.3 returns type
    2.27 -  return lua_isnil(L, -1) ? 1 : 2;
    2.28 -}
    2.29 -
    2.30 -static int seqlua_ipairsaux_func(lua_State *L) {
    2.31 +static int seqlua_ipairsaux_call(lua_State *L) {
    2.32    lua_pushinteger(L, luaL_checkinteger(L, 2) + 1);
    2.33 -  lua_insert(L, 1);
    2.34 -  lua_settop(L, 2);
    2.35 +  lua_insert(L, 1);  // integer on stack index 1
    2.36 +  lua_settop(L, 2);  // function on stack index 2
    2.37    lua_call(L, 0, LUA_MULTRET);
    2.38    if (lua_isnoneornil(L, 2)) {
    2.39      lua_settop(L, 0);
    2.40 @@ -35,20 +25,22 @@
    2.41  }
    2.42  
    2.43  static int seqlua_ipairs(lua_State *L) {
    2.44 -  luaL_checkany(L, 1);  // provides better error message
    2.45 -  if (
    2.46 -    lua_type(L, 1) == LUA_TFUNCTION ||
    2.47 -    (luaL_getmetafield(L, 1, "__call") && (lua_pop(L, 1), 1))
    2.48 -  ) {
    2.49 -    lua_pushcfunction(L, seqlua_ipairsaux_func);
    2.50 -  } else if (luaL_getmetafield(L, 1, "__index")) {
    2.51 -    lua_pushcfunction(L, seqlua_ipairsaux_meta);
    2.52 +  if (luaL_getmetafield(L, 1, "__ipairs")) {
    2.53 +    lua_pushvalue(L, 1);
    2.54 +    lua_call(L, 1, 3);
    2.55    } else {
    2.56 -    luaL_checktype(L, 1, LUA_TTABLE);
    2.57 -    lua_pushcfunction(L, seqlua_ipairsaux_raw);
    2.58 +    int t = lua_type(L, 1);
    2.59 +    if (t == LUA_TFUNCTION) {
    2.60 +      lua_pushcfunction(L, seqlua_ipairsaux_call);
    2.61 +    } else {
    2.62 +      if (t != LUA_TTABLE && !luaL_getmetafield(L, 1, "__index")) {
    2.63 +        luaL_checktype(L, 1, LUA_TTABLE);
    2.64 +      }
    2.65 +      lua_pushcfunction(L, seqlua_ipairsaux_index);
    2.66 +    }
    2.67 +    lua_pushvalue(L, 1);
    2.68 +    lua_pushinteger(L, 0);
    2.69    }
    2.70 -  lua_pushvalue(L, 1);
    2.71 -  lua_pushinteger(L, 0);
    2.72    return 3;
    2.73  }
    2.74  
     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);

Impressum / About Us