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, * 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. In other words: When calling ``ipairs(seq)`` or ``string.concat(separator, seq)``, then ``seq`` may either be a (table) sequence or a (function) iterator. Auxiliary C functions and macros are provided to simplify extending your own C functions in the same manner. This library completely ignores the ``__ipairs`` metamethod (as it is deprecated since Lua 5.3.0-alpha). It respects, however, any ``__index`` and ``__call`` metamethods (this may cause unexpected behavior when passing callable tables to ``ipairs``). The ``__call`` metamethod takes precedence over an existing ``__index`` metamethod. Lua part of the library ----------------------- The modified ``ipairs(seq)`` function and the new ``string.concat`` function work as 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"