| rev | 
   line source | 
| 
jbe@0
 | 
     1 seqlua: Extended sequences and iterators in Lua
 | 
| 
jbe@0
 | 
     2 ===============================================
 | 
| 
jbe@0
 | 
     3 
 | 
| 
jbe@0
 | 
     4 This is an experimental package to extend Lua in the following manner:
 | 
| 
jbe@0
 | 
     5 
 | 
| 
jbe@30
 | 
     6 * allow ``ipairs(seq)`` to accept tables as well as functions (i.e function
 | 
| 
jbe@30
 | 
     7   iterators),
 | 
| 
jbe@32
 | 
     8 * add a new function ``string.concat(separator, seq)`` to concat either
 | 
| 
jbe@32
 | 
     9   table entries or function return values,
 | 
| 
jbe@23
 | 
    10 * provide auxiliary C functions and macros to simplify iterating over both
 | 
| 
jbe@0
 | 
    11   tables and iterator functions with the same statement.
 | 
| 
jbe@0
 | 
    12 
 | 
| 
jbe@33
 | 
    13 Existing ``__ipairs`` or ``__index`` (but not ``__len``) metamethods are
 | 
| 
jbe@33
 | 
    14 respected by both the Lua functions and the C functions and macros. The
 | 
| 
jbe@32
 | 
    15 ``__ipairs`` metamethod takes precedence over ``__index``, while the
 | 
| 
jbe@32
 | 
    16 ``__len`` metamethod is never used.
 | 
| 
jbe@32
 | 
    17 
 | 
| 
jbe@0
 | 
    18 
 | 
| 
jbe@0
 | 
    19 
 | 
| 
jbe@0
 | 
    20 Lua part of the library
 | 
| 
jbe@0
 | 
    21 -----------------------
 | 
| 
jbe@0
 | 
    22 
 | 
| 
jbe@30
 | 
    23 The modified ``ipairs(seq)`` and the new ``string.concat(sep, seq)`` functions
 | 
| 
jbe@30
 | 
    24 accept either a table or a function as ``seq``. This is demonstrated in the
 | 
| 
jbe@30
 | 
    25 following examples:
 | 
| 
jbe@0
 | 
    26 
 | 
| 
jbe@0
 | 
    27     require "seqlua"
 | 
| 
jbe@0
 | 
    28 
 | 
| 
jbe@0
 | 
    29     t = {"a", "b", "c"}
 | 
| 
jbe@0
 | 
    30 
 | 
| 
jbe@0
 | 
    31     for i, v in ipairs(t) do
 | 
| 
jbe@0
 | 
    32       print(i, v)
 | 
| 
jbe@0
 | 
    33     end
 | 
| 
jbe@0
 | 
    34     -- prints:
 | 
| 
jbe@0
 | 
    35     --  1   a
 | 
| 
jbe@0
 | 
    36     --  2   b
 | 
| 
jbe@0
 | 
    37     --  3   c
 | 
| 
jbe@0
 | 
    38 
 | 
| 
jbe@25
 | 
    39     print(string.concat(",", t))
 | 
| 
jbe@25
 | 
    40     -- prints: a,b,c
 | 
| 
jbe@25
 | 
    41 
 | 
| 
jbe@19
 | 
    42     function alphabet()
 | 
| 
jbe@0
 | 
    43       local letter = nil
 | 
| 
jbe@0
 | 
    44       return function()
 | 
| 
jbe@0
 | 
    45         if letter == nil then
 | 
| 
jbe@19
 | 
    46           letter = "a"
 | 
| 
jbe@19
 | 
    47         elseif letter == "z" then
 | 
| 
jbe@0
 | 
    48           return nil
 | 
| 
jbe@0
 | 
    49         else
 | 
| 
jbe@0
 | 
    50           letter = string.char(string.byte(letter) + 1)
 | 
| 
jbe@0
 | 
    51         end
 | 
| 
jbe@0
 | 
    52         return letter
 | 
| 
jbe@0
 | 
    53       end
 | 
| 
jbe@0
 | 
    54     end
 | 
| 
jbe@0
 | 
    55 
 | 
| 
jbe@23
 | 
    56     for i, v in ipairs(alphabet()) do
 | 
| 
jbe@0
 | 
    57       print(i, v)
 | 
| 
jbe@0
 | 
    58     end
 | 
| 
jbe@0
 | 
    59     -- prints:
 | 
| 
jbe@0
 | 
    60     --  1   a
 | 
| 
jbe@0
 | 
    61     --  2   b
 | 
| 
jbe@0
 | 
    62     --  3   c
 | 
| 
jbe@0
 | 
    63     --  ...
 | 
| 
jbe@0
 | 
    64     --  25  y
 | 
| 
jbe@0
 | 
    65     --  26  z
 | 
| 
jbe@0
 | 
    66 
 | 
| 
jbe@25
 | 
    67     print(string.concat(",", alphabet()))
 | 
| 
jbe@25
 | 
    68     -- prints: a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
 | 
| 
jbe@25
 | 
    69 
 | 
| 
jbe@26
 | 
    70     function filter(f)
 | 
| 
jbe@26
 | 
    71       return function(seq)
 | 
| 
jbe@26
 | 
    72         return coroutine.wrap(function()
 | 
| 
jbe@26
 | 
    73           for i, v in ipairs(seq) do f(v) end
 | 
| 
jbe@26
 | 
    74         end)
 | 
| 
jbe@26
 | 
    75       end
 | 
| 
jbe@0
 | 
    76     end
 | 
| 
jbe@19
 | 
    77 
 | 
| 
jbe@29
 | 
    78     alpha_beta_x = filter(function(v)
 | 
| 
jbe@28
 | 
    79       if v == "a" then
 | 
| 
jbe@28
 | 
    80         coroutine.yield("alpha")
 | 
| 
jbe@28
 | 
    81       elseif v == "b" then
 | 
| 
jbe@28
 | 
    82         coroutine.yield("beta")
 | 
| 
jbe@28
 | 
    83       elseif type(v) == "number" then
 | 
| 
jbe@23
 | 
    84         for i = 1, v do
 | 
| 
jbe@28
 | 
    85           coroutine.yield("X")
 | 
| 
jbe@23
 | 
    86         end
 | 
| 
jbe@0
 | 
    87       end
 | 
| 
jbe@26
 | 
    88     end)
 | 
| 
jbe@0
 | 
    89 
 | 
| 
jbe@29
 | 
    90     print((","):concat(alpha_beta_x{"a", 3, "b", "c", "d"}))
 | 
| 
jbe@28
 | 
    91     -- prints: alpha,X,X,X,beta
 | 
| 
jbe@25
 | 
    92 
 | 
| 
jbe@29
 | 
    93     print((","):concat(alpha_beta_x(alphabet())))
 | 
| 
jbe@28
 | 
    94     -- prints: alpha,beta
 | 
| 
jbe@27
 | 
    95 
 | 
| 
jbe@0
 | 
    96 
 | 
| 
jbe@0
 | 
    97 C part of the library
 | 
| 
jbe@0
 | 
    98 ---------------------
 | 
| 
jbe@0
 | 
    99 
 | 
| 
jbe@0
 | 
   100 In ``seqlualib.h``, the following macro is defined:
 | 
| 
jbe@0
 | 
   101 
 | 
| 
jbe@0
 | 
   102     #define seqlua_iterloop(L, iter, idx) \
 | 
| 
jbe@0
 | 
   103       for ( \
 | 
| 
jbe@0
 | 
   104         seqlua_iterinit((L), (iter), (idx)); \
 | 
| 
jbe@0
 | 
   105         seqlua_iternext(iter); \
 | 
| 
jbe@25
 | 
   106       )
 | 
| 
jbe@25
 | 
   107 
 | 
| 
jbe@25
 | 
   108 and
 | 
| 
jbe@25
 | 
   109 
 | 
| 
jbe@25
 | 
   110     #define seqlua_iterloopauto(L, iter, idx) \
 | 
| 
jbe@25
 | 
   111       for ( \
 | 
| 
jbe@25
 | 
   112         seqlua_iterinit((L), (iter), (idx)); \
 | 
| 
jbe@25
 | 
   113         seqlua_iternext(iter); \
 | 
| 
jbe@0
 | 
   114         lua_pop((L), 1) \
 | 
| 
jbe@0
 | 
   115       )
 | 
| 
jbe@0
 | 
   116 
 | 
| 
jbe@23
 | 
   117 This macro allows iteration over either tables or iterator functions as the
 | 
| 
jbe@23
 | 
   118 following example function demonstrates:
 | 
| 
jbe@0
 | 
   119 
 | 
| 
jbe@0
 | 
   120     int printcsv(lua_State *L) {
 | 
| 
jbe@0
 | 
   121       seqlua_Iterator iter;
 | 
| 
jbe@0
 | 
   122       seqlua_iterloop(L, &iter, 1) {
 | 
| 
jbe@0
 | 
   123         if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
 | 
| 
jbe@0
 | 
   124         fputs(luaL_tolstring(L, -1, NULL), stdout);
 | 
| 
jbe@25
 | 
   125         // two values need to be popped (the value pushed by
 | 
| 
jbe@25
 | 
   126         // seqlua_iternext and the value pushed by luaL_tolstring)
 | 
| 
jbe@25
 | 
   127         lua_pop(L, 2);
 | 
| 
jbe@0
 | 
   128       }
 | 
| 
jbe@0
 | 
   129       fputs("\n", stdout);
 | 
| 
jbe@0
 | 
   130       return 0;
 | 
| 
jbe@0
 | 
   131     }
 | 
| 
jbe@0
 | 
   132 
 | 
| 
jbe@11
 | 
   133     printcsv{"a", "b", "c"}
 | 
| 
jbe@11
 | 
   134     -- prints: a,b,c
 | 
| 
jbe@11
 | 
   135 
 | 
| 
jbe@11
 | 
   136     printcsv(assert(io.open("testfile")):lines())
 | 
| 
jbe@11
 | 
   137     -- prints: line1,line2,... of "testfile"
 | 
| 
jbe@0
 | 
   138 
 | 
| 
jbe@31
 | 
   139 NOTE: During iteration using ``seqlua_iterloop``, ``seqlua_iterloopauto``, or
 | 
| 
jbe@31
 | 
   140 ``seqlua_iterinit``, three extra elements are stored on the stack (additionally
 | 
| 
jbe@31
 | 
   141 to the value). These extra elements are removed automatically when the loop ends
 | 
| 
jbe@31
 | 
   142 (i.e. when ``seqlua_iternext`` returns zero). The value pushed onto the stack
 | 
| 
jbe@31
 | 
   143 for every iteration step has to be removed manually from the stack, unless
 | 
| 
jbe@31
 | 
   144 ``seqlua_iterloopauto`` is used.
 | 
| 
jbe@0
 | 
   145 
 | 
| 
jbe@31
 | 
   146 
 |