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@0: * allow ipairs(...) to accept tables as well as functions or iterator triplets, jbe@0: * provide a function iterator(...) that returns single functions unmodified, jbe@0: but converts jbe@0: * iterator triplets into closures, and jbe@0: * tables into a function closure that iterates over the elements, jbe@0: * provide the auxiliary C functions and macros to simplify iterating over both jbe@0: tables and iterator functions with the same statement. jbe@0: jbe@0: This library completely ignores the ``__ipairs`` metamethod (as it is jbe@0: deprecated since Lua 5.3.0-alpha). It respects, however, any ``__call`` jbe@0: metamethods (this may cause unexpected behavior when passing callable tables jbe@0: to ``ipairs``). jbe@0: jbe@0: jbe@0: jbe@0: Lua part of the library jbe@0: ----------------------- jbe@0: jbe@0: The new ``ipairs(...)`` function works as follows: 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@0: function alphabet() jbe@0: local letter = nil jbe@0: return function() jbe@0: if letter == nil then jbe@0: letter = "a" jbe@0: 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@0: f = alphabet() jbe@0: jbe@0: for i, v in ipairs(f) 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@0: set = {apple = true, banana = true} jbe@0: for i, k, v in ipairs(pairs(set)) do jbe@0: print(i, k, v) jbe@0: end jbe@0: -- prints: jbe@0: -- 1 banana true jbe@0: -- 2 apple true jbe@0: -- (order of "apple" and "banana" may vary) jbe@0: jbe@0: The function ``iterator(...)`` may be used to convert any table, any function, jbe@0: or any iterator triplet into a single function (possibly creating a closure): jbe@0: jbe@0: function filter_strings(...) jbe@0: nextvalue = iterator(...) jbe@0: return function() jbe@0: local value jbe@0: repeat jbe@0: value = nextvalue() jbe@0: until value == nil or type(value) == "string" jbe@0: return value jbe@0: end jbe@0: end jbe@0: jbe@0: for i, v in ipairs(filter_strings{"Hello", true, "World"}) do jbe@0: print(i, v) jbe@0: end jbe@0: -- prints: jbe@0: -- 1 Hello jbe@0: -- 2 World jbe@0: jbe@0: tbl = {apple = true, banana = true, [1] = "array entry"} jbe@0: for v in filter_strings(pairs(tbl)) do jbe@0: print(v) jbe@0: end jbe@0: -- prints: jbe@0: -- banana jbe@0: -- apple jbe@0: -- (order may vary) jbe@0: jbe@0: 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@0: lua_pop((L), 1) \ jbe@0: ) jbe@0: jbe@0: This macro allows iteration over either tables or iterator functions (but not jbe@0: iterator triplets) as the 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@0: lua_pop(L, 1); jbe@0: } jbe@0: fputs("\n", stdout); jbe@0: return 0; jbe@0: } jbe@0: jbe@0: printcsv{"a", "b", "c"} -- prints: a,b,c jbe@0: jbe@0: Additionally, ``seqlualib`` includes a function ``seqlua_iterclosure(L, idx)``, jbe@0: which converts a table at a given stack index into a function closure (stored jbe@0: on the same stack index) that iterates over the elements of the table. If the jbe@0: value at the given stack index is already a function, it leaves the value jbe@0: unchanged. If the value is convertible to a function using ``__ipairs,`` then jbe@0: the function is replaced by its ``__ipairs`` metamethod. jbe@0: jbe@0: