seqlua

changeset 52:3362ec36cb09

Do not automatically assume that functions passed to ipairs are iterators
but require ipairs(func, mode) to have an explicit mode set to "call" or "generator"
author jbe
date Tue Aug 26 21:10:03 2014 +0200 (2014-08-26)
parents 06c5f2f9ec41
children 664736a8fcbf
files README seqlua.c seqlua_c_example.c seqlualib.c seqlualib.h
line diff
     1.1 --- a/README	Mon Aug 25 12:12:46 2014 +0200
     1.2 +++ b/README	Tue Aug 26 21:10:03 2014 +0200
     1.3 @@ -1,11 +1,15 @@
     1.4  seqlua: Extension for handling sequential data in Lua
     1.5  =====================================================
     1.6  
     1.7 -This package is an extension for the Lua programming language (version 5.2)
     1.8 -which:
     1.9 +This package is an experimental extension for the Lua programming language
    1.10 +(version 5.2) which:
    1.11  
    1.12 -* allows ``ipairs(seq)`` to accept either tables or functions (i.e function
    1.13 -  iterators) as an argument,
    1.14 +* makes ``ipairs(tbl)`` respect both metamethods ``__index`` and ``__ipairs``
    1.15 +  (where ``__ipairs`` has precedence over ``__index``),
    1.16 +* allows ``ipairs(seq, "call")`` to accept either tables or functions as first
    1.17 +  argument where a function is used as iterator,
    1.18 +* allows ``ipairs(seq, "generator")`` to accept either tables or functions as
    1.19 +  first argument where a function is used as generator for an iterator,
    1.20  * adds a new function ``string.concat(separator, seq)`` that concats either
    1.21    table entries or function return values,
    1.22  * provides auxiliary C functions and macros to simplify iterating over both
    1.23 @@ -94,8 +98,15 @@
    1.24  
    1.25  This extension, however, modifies Lua's ``ipairs`` statement in such way that
    1.26  it automatically accepts either a table or an iterator function as argument.
    1.27 -Thus, the first of the three ``write_lines`` functions above will accept both
    1.28 -(table) sequences and (function) iterators.
    1.29 +Thus, the function below will accept both (table) sequences and (function)
    1.30 +iterators:
    1.31 +
    1.32 +    function write_lines(lines)
    1.33 +      for i, line in ipairs(lines, "call") do
    1.34 +        io.stdout:write(line)
    1.35 +        io.stdout:write("\n")
    1.36 +      end
    1.37 +    end
    1.38  
    1.39  In addition to the modification of ``ipairs``, it also provides C functions and
    1.40  macros to iterate over values in the same manner as a generic loop statement
    1.41 @@ -167,7 +178,7 @@
    1.42  
    1.43      t = {"a", "b", "c"}
    1.44  
    1.45 -    for i, v in ipairs(t) do
    1.46 +    for i, v in ipairs(t, "call") do
    1.47        print(i, v)
    1.48      end
    1.49      -- prints:
    1.50 @@ -192,7 +203,18 @@
    1.51        end
    1.52      end
    1.53  
    1.54 -    for i, v in ipairs(alphabet()) do
    1.55 +    for i, v in ipairs(alphabet(), "call") do
    1.56 +      print(i, v)
    1.57 +    end
    1.58 +    -- prints:
    1.59 +    --  1   a
    1.60 +    --  2   b
    1.61 +    --  3   c
    1.62 +    --  ...
    1.63 +    --  25  y
    1.64 +    --  26  z
    1.65 +
    1.66 +    for i, v in ipairs(alphabet, "generator") do
    1.67        print(i, v)
    1.68      end
    1.69      -- prints:
    1.70 @@ -209,7 +231,7 @@
    1.71      function filter(f)
    1.72        return function(seq)
    1.73          return coroutine.wrap(function()
    1.74 -          for i, v in ipairs(seq) do f(v) end
    1.75 +          for i, v in ipairs(seq, "call") do f(v) end
    1.76          end)
    1.77        end
    1.78      end
     2.1 --- a/seqlua.c	Mon Aug 25 12:12:46 2014 +0200
     2.2 +++ b/seqlua.c	Tue Aug 26 21:10:03 2014 +0200
     2.3 @@ -35,6 +35,9 @@
     2.4  }
     2.5  
     2.6  static int seqlua_ipairs(lua_State *L) {
     2.7 +  const char *method;
     2.8 +  int generated = 0;
     2.9 +  seqlua_ipairs_repeat:
    2.10    if (luaL_getmetafield(L, 1, "__ipairs")) {
    2.11      lua_pushvalue(L, 1);
    2.12      lua_call(L, 1, 3);
    2.13 @@ -55,13 +58,27 @@
    2.14    } else {
    2.15      int t = lua_type(L, 1);
    2.16      if (t == LUA_TFUNCTION) {
    2.17 -      lua_pushcfunction(L, seqlua_ipairsaux_call);
    2.18 +      if (generated) goto seqlua_ipairs_call;
    2.19 +      method = lua_tostring(L, 2);
    2.20 +      if (method) {
    2.21 +        if (!strcmp(method, "call")) {
    2.22 +          seqlua_ipairs_call:
    2.23 +          lua_pushcfunction(L, seqlua_ipairsaux_call);
    2.24 +          goto seqlua_ipairs_finish;
    2.25 +        } else if (!strcmp(method, "generator")) {
    2.26 +          lua_settop(L, 1);
    2.27 +          lua_call(L, 0, 1);
    2.28 +          generated = 1;
    2.29 +          goto seqlua_ipairs_repeat;
    2.30 +        }
    2.31 +      }
    2.32      } else if (luaL_getmetafield(L, 1, "__index")) {
    2.33        lua_pushcfunction(L, seqlua_ipairsaux_index);
    2.34 -    } else {
    2.35 -      luaL_checktype(L, 1, LUA_TTABLE);
    2.36 -      lua_pushcfunction(L, seqlua_ipairsaux_raw);
    2.37 +      goto seqlua_ipairs_finish;
    2.38      }
    2.39 +    luaL_checktype(L, 1, LUA_TTABLE);
    2.40 +    lua_pushcfunction(L, seqlua_ipairsaux_raw);
    2.41 +    seqlua_ipairs_finish:
    2.42      lua_pushvalue(L, 1);
    2.43      lua_pushinteger(L, 0);
    2.44    }
    2.45 @@ -77,7 +94,7 @@
    2.46    luaL_checkany(L, 2);
    2.47    lua_settop(L, 3);
    2.48    luaL_buffinit(L, &buf);
    2.49 -  seqlua_iterloop(L, &iter, 2) {
    2.50 +  seqlua_iterloop(L, &iter, SEQLUA_MODE_CALL, 2) {
    2.51      lua_replace(L, 3);
    2.52      if (seqlua_itercount(&iter) > 1) luaL_addlstring(&buf, sep, seplen);
    2.53      luaL_tolstring(L, 3, NULL);
     3.1 --- a/seqlua_c_example.c	Mon Aug 25 12:12:46 2014 +0200
     3.2 +++ b/seqlua_c_example.c	Tue Aug 26 21:10:03 2014 +0200
     3.3 @@ -5,7 +5,7 @@
     3.4  
     3.5  int seqlua_c_example_printcsv(lua_State *L) {
     3.6    seqlua_Iterator iter;
     3.7 -  seqlua_iterloop(L, &iter, 1) {
     3.8 +  seqlua_iterloop(L, &iter, SEQLUA_MODE_CALL, 1) {
     3.9      if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
    3.10      fputs(luaL_tolstring(L, -1, NULL), stdout);
    3.11      // two values need to be popped (the value pushed by
     4.1 --- a/seqlualib.c	Mon Aug 25 12:12:46 2014 +0200
     4.2 +++ b/seqlualib.c	Tue Aug 26 21:10:03 2014 +0200
     4.3 @@ -8,9 +8,11 @@
     4.4  #define SEQLUA_ITERTYPE_INDEX  3
     4.5  #define SEQLUA_ITERTYPE_RAW    4
     4.6  
     4.7 -void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx) {
     4.8 +void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int mode, int idx) {
     4.9 +  int generated = 0;
    4.10 +  seqlua_iterinit_repeat:
    4.11    if (luaL_getmetafield(L, idx, "__ipairs")) {
    4.12 -    lua_pushvalue(L, idx);
    4.13 +    if (!generated) lua_pushvalue(L, idx);
    4.14      lua_call(L, 1, 3);
    4.15      if (lua_type(L, -3) == LUA_TSTRING) {
    4.16        const char *method = lua_tostring(L, -3);
    4.17 @@ -29,16 +31,25 @@
    4.18      }
    4.19    } else {
    4.20      if (lua_type(L, idx) == LUA_TFUNCTION) {
    4.21 -      iter->itertype = SEQLUA_ITERTYPE_CALL;
    4.22 +      if (generated || mode == SEQLUA_MODE_CALL) {
    4.23 +        iter->itertype = SEQLUA_ITERTYPE_CALL;
    4.24 +        goto seqlua_iterinit_finish;
    4.25 +      } else if (mode == SEQLUA_MODE_GENERATOR) {
    4.26 +        lua_pushvalue(L, idx);
    4.27 +        lua_call(L, 0, 1);
    4.28 +        idx = lua_gettop(L);
    4.29 +        goto seqlua_iterinit_repeat;
    4.30 +      }
    4.31      } else if (luaL_getmetafield(L, idx, "__index")) {
    4.32        lua_pop(L, 1);
    4.33        iter->itertype = SEQLUA_ITERTYPE_INDEX;
    4.34 -    } else {
    4.35 -      luaL_checktype(L, idx, LUA_TTABLE);
    4.36 -      iter->itertype = SEQLUA_ITERTYPE_RAW;
    4.37 +      goto seqlua_iterinit_finish;
    4.38      }
    4.39 +    luaL_checktype(L, idx, LUA_TTABLE);
    4.40 +    iter->itertype = SEQLUA_ITERTYPE_RAW;
    4.41 +    seqlua_iterinit_finish:
    4.42      // always occupy 3 stack indicies
    4.43 -    lua_pushnil(L);
    4.44 +    if (!generated) lua_pushnil(L);
    4.45      lua_pushnil(L);
    4.46      lua_pushnil(L);
    4.47      iter->idx = idx;
     5.1 --- a/seqlualib.h	Mon Aug 25 12:12:46 2014 +0200
     5.2 +++ b/seqlualib.h	Tue Aug 26 21:10:03 2014 +0200
     5.3 @@ -8,19 +8,23 @@
     5.4    lua_Integer i;
     5.5  } seqlua_Iterator;
     5.6  
     5.7 -extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx);
     5.8 +#define SEQLUA_MODE_NONE      0
     5.9 +#define SEQLUA_MODE_CALL      1
    5.10 +#define SEQLUA_MODE_GENERATOR 2
    5.11 +
    5.12 +extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int mode, int idx);
    5.13  
    5.14  extern int seqlua_iternext(seqlua_Iterator *iter);
    5.15  
    5.16 -#define seqlua_iterloop(L, iter, idx) \
    5.17 +#define seqlua_iterloop(L, iter, mode, idx) \
    5.18    for ( \
    5.19 -    seqlua_iterinit((L), (iter), (idx)); \
    5.20 +    seqlua_iterinit((L), (iter), (mode), (idx)); \
    5.21      seqlua_iternext(iter); \
    5.22    )
    5.23  
    5.24 -#define seqlua_iterloopauto(L, iter, idx) \
    5.25 +#define seqlua_iterloopauto(L, iter, mode, idx) \
    5.26    for ( \
    5.27 -    seqlua_iterinit((L), (iter), (idx)); \
    5.28 +    seqlua_iterinit((L), (iter), (mode), (idx)); \
    5.29      seqlua_iternext(iter); \
    5.30      lua_pop((L), 1) \
    5.31    )

Impressum / About Us