seqlua: Extended sequences and iterators in Lua
===============================================

This is an experimental package to extend Lua in the following manner:

* allow ``ipairs(seq)`` to accept tables as well as functions (i.e function
  iterators),
* add a new function ``string.concat(separator, seq)`` to concat either
  table entries or function return values,
* provide auxiliary C functions and macros to simplify iterating over both
  tables and iterator functions with the same statement.

Existing ``__ipairs`` or ``__index`` (but not ``__len``) metamethods are
respected by both the Lua functions and the C functions and macros. The
``__ipairs`` metamethod takes precedence over ``__index``, while the
``__len`` metamethod is never used.



Lua part of the library
-----------------------

The modified ``ipairs(seq)`` and the new ``string.concat(sep, seq)`` functions
accept either a table or a function as ``seq``. This is demonstrated in the
following examples:

    require "seqlua"

    t = {"a", "b", "c"}

    for i, v in ipairs(t) do
      print(i, v)
    end
    -- prints:
    --  1   a
    --  2   b
    --  3   c

    print(string.concat(",", t))
    -- prints: a,b,c

    function alphabet()
      local letter = nil
      return function()
        if letter == nil then
          letter = "a"
        elseif letter == "z" then
          return nil
        else
          letter = string.char(string.byte(letter) + 1)
        end
        return letter
      end
    end

    for i, v in ipairs(alphabet()) do
      print(i, v)
    end
    -- prints:
    --  1   a
    --  2   b
    --  3   c
    --  ...
    --  25  y
    --  26  z

    print(string.concat(",", alphabet()))
    -- 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

    function filter(f)
      return function(seq)
        return coroutine.wrap(function()
          for i, v in ipairs(seq) do f(v) end
        end)
      end
    end

    alpha_beta_x = filter(function(v)
      if v == "a" then
        coroutine.yield("alpha")
      elseif v == "b" then
        coroutine.yield("beta")
      elseif type(v) == "number" then
        for i = 1, v do
          coroutine.yield("X")
        end
      end
    end)

    print((","):concat(alpha_beta_x{"a", 3, "b", "c", "d"}))
    -- prints: alpha,X,X,X,beta

    print((","):concat(alpha_beta_x(alphabet())))
    -- prints: alpha,beta


C part of the library
---------------------

In ``seqlualib.h``, the following macro is defined:

    #define seqlua_iterloop(L, iter, idx) \
      for ( \
        seqlua_iterinit((L), (iter), (idx)); \
        seqlua_iternext(iter); \
      )

and

    #define seqlua_iterloopauto(L, iter, idx) \
      for ( \
        seqlua_iterinit((L), (iter), (idx)); \
        seqlua_iternext(iter); \
        lua_pop((L), 1) \
      )

This macro allows iteration over either tables or iterator functions as the
following example function demonstrates:

    int printcsv(lua_State *L) {
      seqlua_Iterator iter;
      seqlua_iterloop(L, &iter, 1) {
        if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
        fputs(luaL_tolstring(L, -1, NULL), stdout);
        // two values need to be popped (the value pushed by
        // seqlua_iternext and the value pushed by luaL_tolstring)
        lua_pop(L, 2);
      }
      fputs("\n", stdout);
      return 0;
    }

    printcsv{"a", "b", "c"}
    -- prints: a,b,c

    printcsv(assert(io.open("testfile")):lines())
    -- prints: line1,line2,... of "testfile"

NOTE: During iteration using ``seqlua_iterloop``, ``seqlua_iterloopauto``, or
``seqlua_iterinit``, three extra elements are stored on the stack (additionally
to the value). These extra elements are removed automatically when the loop ends
(i.e. when ``seqlua_iternext`` returns zero). The value pushed onto the stack
for every iteration step has to be removed manually from the stack, unless
``seqlua_iterloopauto`` is used.


