jbe@0: seqlua: Extended sequences and iterators in Lua jbe@0: =============================================== jbe@0: jbe@0: This is an experimental package to extend Lua in the following manner: jbe@0: jbe@25: * allow ``ipairs(seq)`` to accept tables as well as functions, jbe@25: * add a new function ``string.concat(separator, seq)`` to concat either table jbe@25: entries or function return values, jbe@23: * provide auxiliary C functions and macros to simplify iterating over both jbe@0: tables and iterator functions with the same statement. jbe@0: jbe@25: In other words: jbe@25: When calling ``ipairs(seq)`` or ``string.concat(separator, seq)``, jbe@25: then ``seq`` may either be a (table) sequence or a (function) iterator. jbe@25: Auxiliary C functions and macros are provided to simplify extending your own jbe@25: C functions in the same manner. jbe@25: jbe@0: This library completely ignores the ``__ipairs`` metamethod (as it is jbe@18: deprecated since Lua 5.3.0-alpha). It respects, however, any ``__index`` and jbe@18: ``__call`` metamethods (this may cause unexpected behavior when passing jbe@18: callable tables to ``ipairs``). The ``__call`` metamethod takes precedence over jbe@18: an existing ``__index`` metamethod. jbe@0: jbe@0: jbe@0: jbe@0: Lua part of the library jbe@0: ----------------------- jbe@0: jbe@25: The modified ``ipairs(seq)`` function and the new ``string.concat`` function jbe@25: work as demonstrated in the following examples: jbe@0: jbe@0: require "seqlua" jbe@0: jbe@0: t = {"a", "b", "c"} jbe@0: jbe@0: for i, v in ipairs(t) do jbe@0: print(i, v) jbe@0: end jbe@0: -- prints: jbe@0: -- 1 a jbe@0: -- 2 b jbe@0: -- 3 c jbe@0: jbe@25: print(string.concat(",", t)) jbe@25: -- prints: a,b,c jbe@25: jbe@19: function alphabet() jbe@0: local letter = nil jbe@0: return function() jbe@0: if letter == nil then jbe@19: letter = "a" jbe@19: elseif letter == "z" then jbe@0: return nil jbe@0: else jbe@0: letter = string.char(string.byte(letter) + 1) jbe@0: end jbe@0: return letter jbe@0: end jbe@0: end jbe@0: jbe@23: for i, v in ipairs(alphabet()) do jbe@0: print(i, v) jbe@0: end jbe@0: -- prints: jbe@0: -- 1 a jbe@0: -- 2 b jbe@0: -- 3 c jbe@0: -- ... jbe@0: -- 25 y jbe@0: -- 26 z jbe@0: jbe@25: print(string.concat(",", alphabet())) jbe@25: -- 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: jbe@26: function filter(f) jbe@26: return function(seq) jbe@26: return coroutine.wrap(function() jbe@26: for i, v in ipairs(seq) do f(v) end jbe@26: end) jbe@26: end jbe@0: end jbe@19: jbe@29: alpha_beta_x = filter(function(v) jbe@28: if v == "a" then jbe@28: coroutine.yield("alpha") jbe@28: elseif v == "b" then jbe@28: coroutine.yield("beta") jbe@28: elseif type(v) == "number" then jbe@23: for i = 1, v do jbe@28: coroutine.yield("X") jbe@23: end jbe@0: end jbe@26: end) jbe@0: jbe@29: print((","):concat(alpha_beta_x{"a", 3, "b", "c", "d"})) jbe@28: -- prints: alpha,X,X,X,beta jbe@25: jbe@29: print((","):concat(alpha_beta_x(alphabet()))) jbe@28: -- prints: alpha,beta jbe@27: jbe@0: jbe@0: C part of the library jbe@0: --------------------- jbe@0: jbe@0: In ``seqlualib.h``, the following macro is defined: jbe@0: jbe@0: #define seqlua_iterloop(L, iter, idx) \ jbe@0: for ( \ jbe@0: seqlua_iterinit((L), (iter), (idx)); \ jbe@0: seqlua_iternext(iter); \ jbe@25: ) jbe@25: jbe@25: and jbe@25: jbe@25: #define seqlua_iterloopauto(L, iter, idx) \ jbe@25: for ( \ jbe@25: seqlua_iterinit((L), (iter), (idx)); \ jbe@25: seqlua_iternext(iter); \ jbe@0: lua_pop((L), 1) \ jbe@0: ) jbe@0: jbe@23: This macro allows iteration over either tables or iterator functions as the jbe@23: following example function demonstrates: jbe@0: jbe@0: int printcsv(lua_State *L) { jbe@0: seqlua_Iterator iter; jbe@0: seqlua_iterloop(L, &iter, 1) { jbe@0: if (seqlua_itercount(&iter) > 1) fputs(",", stdout); jbe@0: fputs(luaL_tolstring(L, -1, NULL), stdout); jbe@25: // two values need to be popped (the value pushed by jbe@25: // seqlua_iternext and the value pushed by luaL_tolstring) jbe@25: lua_pop(L, 2); jbe@0: } jbe@0: fputs("\n", stdout); jbe@0: return 0; jbe@0: } jbe@0: jbe@11: printcsv{"a", "b", "c"} jbe@11: -- prints: a,b,c jbe@11: jbe@11: printcsv(assert(io.open("testfile")):lines()) jbe@11: -- prints: line1,line2,... of "testfile" jbe@0: jbe@0: