# HG changeset patch # User jbe # Date 1409080203 -7200 # Node ID 3362ec36cb095bc1cc398b0d88a086f5b72959bf # Parent 06c5f2f9ec41ead79f82957db06f7cb4f9609e41 Do not automatically assume that functions passed to ipairs are iterators but require ipairs(func, mode) to have an explicit mode set to "call" or "generator" diff -r 06c5f2f9ec41 -r 3362ec36cb09 README --- a/README Mon Aug 25 12:12:46 2014 +0200 +++ b/README Tue Aug 26 21:10:03 2014 +0200 @@ -1,11 +1,15 @@ seqlua: Extension for handling sequential data in Lua ===================================================== -This package is an extension for the Lua programming language (version 5.2) -which: +This package is an experimental extension for the Lua programming language +(version 5.2) which: -* allows ``ipairs(seq)`` to accept either tables or functions (i.e function - iterators) as an argument, +* makes ``ipairs(tbl)`` respect both metamethods ``__index`` and ``__ipairs`` + (where ``__ipairs`` has precedence over ``__index``), +* allows ``ipairs(seq, "call")`` to accept either tables or functions as first + argument where a function is used as iterator, +* allows ``ipairs(seq, "generator")`` to accept either tables or functions as + first argument where a function is used as generator for an iterator, * adds a new function ``string.concat(separator, seq)`` that concats either table entries or function return values, * provides auxiliary C functions and macros to simplify iterating over both @@ -94,8 +98,15 @@ This extension, however, modifies Lua's ``ipairs`` statement in such way that it automatically accepts either a table or an iterator function as argument. -Thus, the first of the three ``write_lines`` functions above will accept both -(table) sequences and (function) iterators. +Thus, the function below will accept both (table) sequences and (function) +iterators: + + function write_lines(lines) + for i, line in ipairs(lines, "call") do + io.stdout:write(line) + io.stdout:write("\n") + end + end In addition to the modification of ``ipairs``, it also provides C functions and macros to iterate over values in the same manner as a generic loop statement @@ -167,7 +178,7 @@ t = {"a", "b", "c"} - for i, v in ipairs(t) do + for i, v in ipairs(t, "call") do print(i, v) end -- prints: @@ -192,7 +203,18 @@ end end - for i, v in ipairs(alphabet()) do + for i, v in ipairs(alphabet(), "call") do + print(i, v) + end + -- prints: + -- 1 a + -- 2 b + -- 3 c + -- ... + -- 25 y + -- 26 z + + for i, v in ipairs(alphabet, "generator") do print(i, v) end -- prints: @@ -209,7 +231,7 @@ function filter(f) return function(seq) return coroutine.wrap(function() - for i, v in ipairs(seq) do f(v) end + for i, v in ipairs(seq, "call") do f(v) end end) end end diff -r 06c5f2f9ec41 -r 3362ec36cb09 seqlua.c --- a/seqlua.c Mon Aug 25 12:12:46 2014 +0200 +++ b/seqlua.c Tue Aug 26 21:10:03 2014 +0200 @@ -35,6 +35,9 @@ } static int seqlua_ipairs(lua_State *L) { + const char *method; + int generated = 0; + seqlua_ipairs_repeat: if (luaL_getmetafield(L, 1, "__ipairs")) { lua_pushvalue(L, 1); lua_call(L, 1, 3); @@ -55,13 +58,27 @@ } else { int t = lua_type(L, 1); if (t == LUA_TFUNCTION) { - lua_pushcfunction(L, seqlua_ipairsaux_call); + if (generated) goto seqlua_ipairs_call; + method = lua_tostring(L, 2); + if (method) { + if (!strcmp(method, "call")) { + seqlua_ipairs_call: + lua_pushcfunction(L, seqlua_ipairsaux_call); + goto seqlua_ipairs_finish; + } else if (!strcmp(method, "generator")) { + lua_settop(L, 1); + lua_call(L, 0, 1); + generated = 1; + goto seqlua_ipairs_repeat; + } + } } else if (luaL_getmetafield(L, 1, "__index")) { lua_pushcfunction(L, seqlua_ipairsaux_index); - } else { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushcfunction(L, seqlua_ipairsaux_raw); + goto seqlua_ipairs_finish; } + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushcfunction(L, seqlua_ipairsaux_raw); + seqlua_ipairs_finish: lua_pushvalue(L, 1); lua_pushinteger(L, 0); } @@ -77,7 +94,7 @@ luaL_checkany(L, 2); lua_settop(L, 3); luaL_buffinit(L, &buf); - seqlua_iterloop(L, &iter, 2) { + seqlua_iterloop(L, &iter, SEQLUA_MODE_CALL, 2) { lua_replace(L, 3); if (seqlua_itercount(&iter) > 1) luaL_addlstring(&buf, sep, seplen); luaL_tolstring(L, 3, NULL); diff -r 06c5f2f9ec41 -r 3362ec36cb09 seqlua_c_example.c --- a/seqlua_c_example.c Mon Aug 25 12:12:46 2014 +0200 +++ b/seqlua_c_example.c Tue Aug 26 21:10:03 2014 +0200 @@ -5,7 +5,7 @@ int seqlua_c_example_printcsv(lua_State *L) { seqlua_Iterator iter; - seqlua_iterloop(L, &iter, 1) { + seqlua_iterloop(L, &iter, SEQLUA_MODE_CALL, 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 diff -r 06c5f2f9ec41 -r 3362ec36cb09 seqlualib.c --- a/seqlualib.c Mon Aug 25 12:12:46 2014 +0200 +++ b/seqlualib.c Tue Aug 26 21:10:03 2014 +0200 @@ -8,9 +8,11 @@ #define SEQLUA_ITERTYPE_INDEX 3 #define SEQLUA_ITERTYPE_RAW 4 -void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx) { +void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int mode, int idx) { + int generated = 0; + seqlua_iterinit_repeat: if (luaL_getmetafield(L, idx, "__ipairs")) { - lua_pushvalue(L, idx); + if (!generated) lua_pushvalue(L, idx); lua_call(L, 1, 3); if (lua_type(L, -3) == LUA_TSTRING) { const char *method = lua_tostring(L, -3); @@ -29,16 +31,25 @@ } } else { if (lua_type(L, idx) == LUA_TFUNCTION) { - iter->itertype = SEQLUA_ITERTYPE_CALL; + if (generated || mode == SEQLUA_MODE_CALL) { + iter->itertype = SEQLUA_ITERTYPE_CALL; + goto seqlua_iterinit_finish; + } else if (mode == SEQLUA_MODE_GENERATOR) { + lua_pushvalue(L, idx); + lua_call(L, 0, 1); + idx = lua_gettop(L); + goto seqlua_iterinit_repeat; + } } else if (luaL_getmetafield(L, idx, "__index")) { lua_pop(L, 1); iter->itertype = SEQLUA_ITERTYPE_INDEX; - } else { - luaL_checktype(L, idx, LUA_TTABLE); - iter->itertype = SEQLUA_ITERTYPE_RAW; + goto seqlua_iterinit_finish; } + luaL_checktype(L, idx, LUA_TTABLE); + iter->itertype = SEQLUA_ITERTYPE_RAW; + seqlua_iterinit_finish: // always occupy 3 stack indicies - lua_pushnil(L); + if (!generated) lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); iter->idx = idx; diff -r 06c5f2f9ec41 -r 3362ec36cb09 seqlualib.h --- a/seqlualib.h Mon Aug 25 12:12:46 2014 +0200 +++ b/seqlualib.h Tue Aug 26 21:10:03 2014 +0200 @@ -8,19 +8,23 @@ lua_Integer i; } seqlua_Iterator; -extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx); +#define SEQLUA_MODE_NONE 0 +#define SEQLUA_MODE_CALL 1 +#define SEQLUA_MODE_GENERATOR 2 + +extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int mode, int idx); extern int seqlua_iternext(seqlua_Iterator *iter); -#define seqlua_iterloop(L, iter, idx) \ +#define seqlua_iterloop(L, iter, mode, idx) \ for ( \ - seqlua_iterinit((L), (iter), (idx)); \ + seqlua_iterinit((L), (iter), (mode), (idx)); \ seqlua_iternext(iter); \ ) -#define seqlua_iterloopauto(L, iter, idx) \ +#define seqlua_iterloopauto(L, iter, mode, idx) \ for ( \ - seqlua_iterinit((L), (iter), (idx)); \ + seqlua_iterinit((L), (iter), (mode), (idx)); \ seqlua_iternext(iter); \ lua_pop((L), 1) \ )