| 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@0
 | 
     6 * allow ipairs(...) to accept tables as well as functions or iterator triplets,
 | 
| 
jbe@0
 | 
     7 * provide a function iterator(...) that returns single functions unmodified,
 | 
| 
jbe@0
 | 
     8   but converts
 | 
| 
jbe@0
 | 
     9     * iterator triplets into closures, and
 | 
| 
jbe@0
 | 
    10     * tables into a function closure that iterates over the elements,
 | 
| 
jbe@0
 | 
    11 * provide the auxiliary C functions and macros to simplify iterating over both
 | 
| 
jbe@0
 | 
    12   tables and iterator functions with the same statement.
 | 
| 
jbe@0
 | 
    13 
 | 
| 
jbe@0
 | 
    14 This library completely ignores the ``__ipairs`` metamethod (as it is
 | 
| 
jbe@0
 | 
    15 deprecated since Lua 5.3.0-alpha). It respects, however, any ``__call``
 | 
| 
jbe@0
 | 
    16 metamethods (this may cause unexpected behavior when passing callable tables
 | 
| 
jbe@0
 | 
    17 to ``ipairs``).
 | 
| 
jbe@0
 | 
    18 
 | 
| 
jbe@0
 | 
    19 
 | 
| 
jbe@0
 | 
    20 
 | 
| 
jbe@0
 | 
    21 Lua part of the library
 | 
| 
jbe@0
 | 
    22 -----------------------
 | 
| 
jbe@0
 | 
    23 
 | 
| 
jbe@0
 | 
    24 The new ``ipairs(...)`` function works as follows:
 | 
| 
jbe@0
 | 
    25 
 | 
| 
jbe@0
 | 
    26     require "seqlua"
 | 
| 
jbe@0
 | 
    27 
 | 
| 
jbe@0
 | 
    28     t = {"a", "b", "c"}
 | 
| 
jbe@0
 | 
    29 
 | 
| 
jbe@0
 | 
    30     for i, v in ipairs(t) do
 | 
| 
jbe@0
 | 
    31       print(i, v)
 | 
| 
jbe@0
 | 
    32     end
 | 
| 
jbe@0
 | 
    33     -- prints:
 | 
| 
jbe@0
 | 
    34     --  1   a
 | 
| 
jbe@0
 | 
    35     --  2   b
 | 
| 
jbe@0
 | 
    36     --  3   c
 | 
| 
jbe@0
 | 
    37 
 | 
| 
jbe@8
 | 
    38     function alphabet(from, to)
 | 
| 
jbe@0
 | 
    39       local letter = nil
 | 
| 
jbe@0
 | 
    40       return function()
 | 
| 
jbe@0
 | 
    41         if letter == nil then
 | 
| 
jbe@8
 | 
    42           letter = from
 | 
| 
jbe@8
 | 
    43         elseif letter == to then
 | 
| 
jbe@0
 | 
    44           return nil
 | 
| 
jbe@0
 | 
    45         else
 | 
| 
jbe@0
 | 
    46           letter = string.char(string.byte(letter) + 1)
 | 
| 
jbe@0
 | 
    47         end
 | 
| 
jbe@0
 | 
    48         return letter
 | 
| 
jbe@0
 | 
    49       end
 | 
| 
jbe@0
 | 
    50     end
 | 
| 
jbe@0
 | 
    51 
 | 
| 
jbe@8
 | 
    52     f = alphabet("a", "z")
 | 
| 
jbe@0
 | 
    53 
 | 
| 
jbe@0
 | 
    54     for i, v in ipairs(f) do
 | 
| 
jbe@0
 | 
    55       print(i, v)
 | 
| 
jbe@0
 | 
    56     end
 | 
| 
jbe@0
 | 
    57     -- prints:
 | 
| 
jbe@0
 | 
    58     --  1   a
 | 
| 
jbe@0
 | 
    59     --  2   b
 | 
| 
jbe@0
 | 
    60     --  3   c
 | 
| 
jbe@0
 | 
    61     --  ...
 | 
| 
jbe@0
 | 
    62     --  25  y
 | 
| 
jbe@0
 | 
    63     --  26  z
 | 
| 
jbe@0
 | 
    64 
 | 
| 
jbe@8
 | 
    65     c = setmetatable(
 | 
| 
jbe@8
 | 
    66       { iter = alphabet("a", "f") },
 | 
| 
jbe@8
 | 
    67       { __call = function(t) return t.iter() end }
 | 
| 
jbe@8
 | 
    68     )
 | 
| 
jbe@8
 | 
    69 
 | 
| 
jbe@8
 | 
    70     for i, v in ipairs(c) do
 | 
| 
jbe@8
 | 
    71       print(i, v)
 | 
| 
jbe@8
 | 
    72     end
 | 
| 
jbe@8
 | 
    73     -- prints:
 | 
| 
jbe@8
 | 
    74     --  1   a
 | 
| 
jbe@8
 | 
    75     --  2   b
 | 
| 
jbe@8
 | 
    76     --  3   c
 | 
| 
jbe@10
 | 
    77     --  4   d
 | 
| 
jbe@10
 | 
    78     --  5   e
 | 
| 
jbe@10
 | 
    79     --  6   f
 | 
| 
jbe@8
 | 
    80 
 | 
| 
jbe@12
 | 
    81     g = function()
 | 
| 
jbe@12
 | 
    82       coroutine.yield("Alice")
 | 
| 
jbe@12
 | 
    83       coroutine.yield("Bob")
 | 
| 
jbe@12
 | 
    84       for i = 1, 3 do
 | 
| 
jbe@12
 | 
    85         coroutine.yield("Person #" .. tostring(i))
 | 
| 
jbe@12
 | 
    86       end
 | 
| 
jbe@12
 | 
    87     end
 | 
| 
jbe@12
 | 
    88     h = coroutine.wrap(g)
 | 
| 
jbe@12
 | 
    89 
 | 
| 
jbe@12
 | 
    90     for i, v in ipairs(h) do
 | 
| 
jbe@12
 | 
    91       print(i, v)
 | 
| 
jbe@12
 | 
    92     end
 | 
| 
jbe@12
 | 
    93     -- prints:
 | 
| 
jbe@12
 | 
    94     --  1   Alice
 | 
| 
jbe@12
 | 
    95     --  2   Bob
 | 
| 
jbe@12
 | 
    96     --  3   Person #1
 | 
| 
jbe@12
 | 
    97     --  4   Person #2
 | 
| 
jbe@12
 | 
    98     --  5   Person #3
 | 
| 
jbe@12
 | 
    99 
 | 
| 
jbe@0
 | 
   100     set = {apple = true, banana = true}
 | 
| 
jbe@0
 | 
   101     for i, k, v in ipairs(pairs(set)) do
 | 
| 
jbe@0
 | 
   102       print(i, k, v)
 | 
| 
jbe@0
 | 
   103     end
 | 
| 
jbe@0
 | 
   104     -- prints:
 | 
| 
jbe@0
 | 
   105     --  1   banana  true
 | 
| 
jbe@0
 | 
   106     --  2   apple   true
 | 
| 
jbe@0
 | 
   107     -- (order of "apple" and "banana" may vary)
 | 
| 
jbe@0
 | 
   108 
 | 
| 
jbe@0
 | 
   109 The function ``iterator(...)`` may be used to convert any table, any function,
 | 
| 
jbe@0
 | 
   110 or any iterator triplet into a single function (possibly creating a closure):
 | 
| 
jbe@0
 | 
   111 
 | 
| 
jbe@2
 | 
   112     require "seqlua"
 | 
| 
jbe@2
 | 
   113 
 | 
| 
jbe@0
 | 
   114     function filter_strings(...)
 | 
| 
jbe@0
 | 
   115       nextvalue = iterator(...)
 | 
| 
jbe@0
 | 
   116       return function()
 | 
| 
jbe@0
 | 
   117         local value
 | 
| 
jbe@0
 | 
   118         repeat
 | 
| 
jbe@0
 | 
   119           value = nextvalue()
 | 
| 
jbe@0
 | 
   120         until value == nil or type(value) == "string"
 | 
| 
jbe@0
 | 
   121         return value
 | 
| 
jbe@0
 | 
   122       end
 | 
| 
jbe@0
 | 
   123     end
 | 
| 
jbe@0
 | 
   124 
 | 
| 
jbe@0
 | 
   125     for i, v in ipairs(filter_strings{"Hello", true, "World"}) do
 | 
| 
jbe@0
 | 
   126       print(i, v)
 | 
| 
jbe@0
 | 
   127     end
 | 
| 
jbe@0
 | 
   128     -- prints:
 | 
| 
jbe@0
 | 
   129     --  1   Hello
 | 
| 
jbe@0
 | 
   130     --  2   World
 | 
| 
jbe@0
 | 
   131 
 | 
| 
jbe@0
 | 
   132     tbl = {apple = true, banana = true, [1] = "array entry"}
 | 
| 
jbe@0
 | 
   133     for v in filter_strings(pairs(tbl)) do
 | 
| 
jbe@0
 | 
   134       print(v)
 | 
| 
jbe@0
 | 
   135     end
 | 
| 
jbe@0
 | 
   136     -- prints:
 | 
| 
jbe@0
 | 
   137     --   banana
 | 
| 
jbe@0
 | 
   138     --   apple
 | 
| 
jbe@0
 | 
   139     -- (order may vary)
 | 
| 
jbe@0
 | 
   140 
 | 
| 
jbe@0
 | 
   141 
 | 
| 
jbe@0
 | 
   142 
 | 
| 
jbe@0
 | 
   143 C part of the library
 | 
| 
jbe@0
 | 
   144 ---------------------
 | 
| 
jbe@0
 | 
   145 
 | 
| 
jbe@0
 | 
   146 In ``seqlualib.h``, the following macro is defined:
 | 
| 
jbe@0
 | 
   147 
 | 
| 
jbe@0
 | 
   148     #define seqlua_iterloop(L, iter, idx) \
 | 
| 
jbe@0
 | 
   149       for ( \
 | 
| 
jbe@0
 | 
   150         seqlua_iterinit((L), (iter), (idx)); \
 | 
| 
jbe@0
 | 
   151         seqlua_iternext(iter); \
 | 
| 
jbe@0
 | 
   152         lua_pop((L), 1) \
 | 
| 
jbe@0
 | 
   153       )
 | 
| 
jbe@0
 | 
   154 
 | 
| 
jbe@0
 | 
   155 This macro allows iteration over either tables or iterator functions (but not
 | 
| 
jbe@0
 | 
   156 iterator triplets) as the following example function demonstrates:
 | 
| 
jbe@0
 | 
   157 
 | 
| 
jbe@0
 | 
   158     int printcsv(lua_State *L) {
 | 
| 
jbe@0
 | 
   159       seqlua_Iterator iter;
 | 
| 
jbe@0
 | 
   160       seqlua_iterloop(L, &iter, 1) {
 | 
| 
jbe@0
 | 
   161         if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
 | 
| 
jbe@0
 | 
   162         fputs(luaL_tolstring(L, -1, NULL), stdout);
 | 
| 
jbe@1
 | 
   163         lua_pop(L, 1);  // pops value that luaL_tolstring pushed onto stack
 | 
| 
jbe@0
 | 
   164       }
 | 
| 
jbe@0
 | 
   165       fputs("\n", stdout);
 | 
| 
jbe@0
 | 
   166       return 0;
 | 
| 
jbe@0
 | 
   167     }
 | 
| 
jbe@0
 | 
   168 
 | 
| 
jbe@11
 | 
   169     printcsv{"a", "b", "c"}
 | 
| 
jbe@11
 | 
   170     -- prints: a,b,c
 | 
| 
jbe@11
 | 
   171 
 | 
| 
jbe@11
 | 
   172     printcsv(assert(io.open("testfile")):lines())
 | 
| 
jbe@11
 | 
   173     -- prints: line1,line2,... of "testfile"
 | 
| 
jbe@0
 | 
   174 
 | 
| 
jbe@7
 | 
   175 NOTE: ``seqlua_iterinit`` will store one extra element on the stack during
 | 
| 
jbe@7
 | 
   176 iteration. When ``seqlua_iternext`` returns 0, this extra element is popped
 | 
| 
jbe@7
 | 
   177 from the stack automatically.
 | 
| 
jbe@3
 | 
   178 
 | 
| 
jbe@0
 | 
   179 Additionally, ``seqlualib`` includes a function ``seqlua_iterclosure(L, idx)``,
 | 
| 
jbe@0
 | 
   180 which converts a table at a given stack index into a function closure (stored
 | 
| 
jbe@0
 | 
   181 on the same stack index) that iterates over the elements of the table. If the
 | 
| 
jbe@0
 | 
   182 value at the given stack index is already a function, it leaves the value
 | 
| 
jbe@4
 | 
   183 unchanged. If the value is convertible to a function using ``__call,`` then the
 | 
| 
jbe@7
 | 
   184 value is replaced by a closure calling the ``__call`` metamethod.
 | 
| 
jbe@0
 | 
   185 
 | 
| 
jbe@0
 | 
   186 
 |