# HG changeset patch # User jbe # Date 1408487950 -7200 # Node ID 47f9b323d68c8b3c1117c10e0a54b1981dae7e6e Initial commit diff -r 000000000000 -r 47f9b323d68c LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,19 @@ +Copyright (c) 2014 Public Software Group e. V., Berlin, Germany + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff -r 000000000000 -r 47f9b323d68c Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,24 @@ +LUA_INCLUDE=/usr/local/include/lua52 + +all:: seqlualib.so seqlua.so seqlua_c_example.so + +seqlualib.so: seqlualib.o + ld -shared -o seqlualib.so seqlualib.o + +seqlualib.o: seqlualib.c seqlualib.h + cc -O2 -c -fpic -I $(LUA_INCLUDE) -o seqlualib.o seqlualib.c + +seqlua.so: seqlua.o seqlualib.o + ld -shared -o seqlua.so seqlua.o seqlualib.o + +seqlua.o: seqlua.c seqlualib.h + cc -O2 -c -fpic -I $(LUA_INCLUDE) -o seqlua.o seqlua.c + +seqlua_c_example.so: seqlua_c_example.o seqlua.o + ld -shared -o seqlua_c_example.so seqlua_c_example.o seqlualib.o + +seqlua_c_example.o: seqlua_c_example.c seqlualib.h + cc -O2 -c -fpic -I $(LUA_INCLUDE) -o seqlua_c_example.o seqlua_c_example.c + +clean:: + rm -f *.o *.so diff -r 000000000000 -r 47f9b323d68c README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,141 @@ +seqlua: Extended sequences and iterators in Lua +=============================================== + +This is an experimental package to extend Lua in the following manner: + +* allow ipairs(...) to accept tables as well as functions or iterator triplets, +* provide a function iterator(...) that returns single functions unmodified, + but converts + * iterator triplets into closures, and + * tables into a function closure that iterates over the elements, +* provide the auxiliary C functions and macros to simplify iterating over both + tables and iterator functions with the same statement. + +This library completely ignores the ``__ipairs`` metamethod (as it is +deprecated since Lua 5.3.0-alpha). It respects, however, any ``__call`` +metamethods (this may cause unexpected behavior when passing callable tables +to ``ipairs``). + + + +Lua part of the library +----------------------- + +The new ``ipairs(...)`` function works as follows: + + require "seqlua" + + t = {"a", "b", "c"} + + for i, v in ipairs(t) do + print(i, v) + end + -- prints: + -- 1 a + -- 2 b + -- 3 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 + + f = alphabet() + + for i, v in ipairs(f) do + print(i, v) + end + -- prints: + -- 1 a + -- 2 b + -- 3 c + -- ... + -- 25 y + -- 26 z + + set = {apple = true, banana = true} + for i, k, v in ipairs(pairs(set)) do + print(i, k, v) + end + -- prints: + -- 1 banana true + -- 2 apple true + -- (order of "apple" and "banana" may vary) + +The function ``iterator(...)`` may be used to convert any table, any function, +or any iterator triplet into a single function (possibly creating a closure): + + function filter_strings(...) + nextvalue = iterator(...) + return function() + local value + repeat + value = nextvalue() + until value == nil or type(value) == "string" + return value + end + end + + for i, v in ipairs(filter_strings{"Hello", true, "World"}) do + print(i, v) + end + -- prints: + -- 1 Hello + -- 2 World + + tbl = {apple = true, banana = true, [1] = "array entry"} + for v in filter_strings(pairs(tbl)) do + print(v) + end + -- prints: + -- banana + -- apple + -- (order may vary) + + + +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); \ + lua_pop((L), 1) \ + ) + +This macro allows iteration over either tables or iterator functions (but not +iterator triplets) 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); + lua_pop(L, 1); + } + fputs("\n", stdout); + return 0; + } + + printcsv{"a", "b", "c"} -- prints: a,b,c + +Additionally, ``seqlualib`` includes a function ``seqlua_iterclosure(L, idx)``, +which converts a table at a given stack index into a function closure (stored +on the same stack index) that iterates over the elements of the table. If the +value at the given stack index is already a function, it leaves the value +unchanged. If the value is convertible to a function using ``__ipairs,`` then +the function is replaced by its ``__ipairs`` metamethod. + + diff -r 000000000000 -r 47f9b323d68c seqlua.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seqlua.c Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,123 @@ +#include +#include +#include "seqlualib.h" + +static int seqlua_ipairsaux_raw(lua_State *L) { + lua_Integer i; + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_checkinteger(L, 2) + 1; + lua_pushinteger(L, i); + lua_rawgeti(L, 1, i); // TODO: Lua 5.3 returns type + return lua_isnil(L, -1) ? 1 : 2; +} + +static int seqlua_ipairsaux_meta(lua_State *L) { + lua_Integer i; + i = luaL_checkinteger(L, 2) + 1; + lua_pushinteger(L, i); + lua_pushinteger(L, i); + lua_gettable(L, 1); // TODO: Lua 5.3 returns type + return lua_isnil(L, -1) ? 1 : 2; +} + +static int seqlua_ipairsaux_func(lua_State *L) { + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_pushinteger(L, luaL_checkinteger(L, 2) + 1); + lua_insert(L, 1); + lua_settop(L, 2); + lua_call(L, 0, LUA_MULTRET); + if (lua_isnoneornil(L, 2)) { + lua_settop(L, 0); + lua_pushnil(L); + return 1; + } else { + return lua_gettop(L); + } +} + +static int seqlua_ipairsaux_funcclosure(lua_State *L) { + lua_Integer i = lua_tointeger(L, lua_upvalueindex(4)) + 1; + lua_settop(L, 0); + lua_pushinteger(L, i); + lua_replace(L, lua_upvalueindex(4)); + lua_pushinteger(L, i); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pushvalue(L, lua_upvalueindex(2)); + lua_pushvalue(L, lua_upvalueindex(3)); + lua_call(L, 2, LUA_MULTRET); + if (lua_isnoneornil(L, 2)) { + lua_settop(L, 0); + lua_pushnil(L); + return 1; + } else { + lua_pushvalue(L, 2); + lua_replace(L, lua_upvalueindex(3)); + return lua_gettop(L); + } +} + +static int seqlua_ipairs(lua_State *L) { + if (luaL_getmetafield(L, 1, "__call")) { + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); + lua_replace(L, 1); + } + if (lua_type(L, 1) == LUA_TFUNCTION) { + if (!lua_isnoneornil(L, 2) || !lua_isnoneornil(L, 3)) { + lua_settop(L, 3); + lua_pushinteger(L, 0); + lua_pushcclosure(L, seqlua_ipairsaux_funcclosure, 4); + return 1; + } + lua_pushcfunction(L, seqlua_ipairsaux_func); + } else if (luaL_getmetafield(L, 1, "__index")) { + lua_pushcfunction(L, seqlua_ipairsaux_meta); + } else { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushcfunction(L, seqlua_ipairsaux_raw); + } + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; +} + +static int seqlua_iteratoraux(lua_State *L) { + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pushvalue(L, lua_upvalueindex(2)); + lua_pushvalue(L, lua_upvalueindex(3)); + lua_call(L, 2, LUA_MULTRET); + if (lua_isnoneornil(L, 1)) { + lua_settop(L, 1); + return 1; + } + lua_pushvalue(L, 1); + lua_replace(L, lua_upvalueindex(3)); + return lua_gettop(L); +} + +static int seqlua_iterator(lua_State *L) { + if (luaL_getmetafield(L, 1, "__call")) { + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); + lua_replace(L, 1); + } + if (lua_type(L, 1) == LUA_TFUNCTION) { + if (lua_isnoneornil(L, 2) && lua_isnoneornil(L, 3)) { + lua_settop(L, 1); + } else { + lua_settop(L, 3); + lua_pushcclosure(L, seqlua_iteratoraux, 3); + } + } else { + seqlua_iterclosure(L, 1); + lua_settop(L, 1); + } + return 1; +} + +int luaopen_seqlua(lua_State *L) { + lua_pushcfunction(L, seqlua_ipairs); + lua_setglobal(L, "ipairs"); + lua_pushcfunction(L, seqlua_iterator); + lua_setglobal(L, "iterator"); + return 1; +} diff -r 000000000000 -r 47f9b323d68c seqlua_c_example.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seqlua_c_example.c Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,21 @@ +#include +#include +#include "seqlualib.h" +#include + +static int seqlua_c_example_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); + lua_pop(L, 1); + } + fputs("\n", stdout); + return 0; +} + +int luaopen_seqlua_c_example(lua_State *L) { + lua_pushcfunction(L, seqlua_c_example_printcsv); + lua_setglobal(L, "printcsv"); + return 0; +} diff -r 000000000000 -r 47f9b323d68c seqlua_c_example_test.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seqlua_c_example_test.lua Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,24 @@ +require "seqlua_c_example" -- defines function "printcsv" + +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 + +printcsv{"a", "b", "c"} +-- prints: +-- a,b,c + +printcsv(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 + diff -r 000000000000 -r 47f9b323d68c seqlualib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seqlualib.c Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,92 @@ +#include +#include +#include "seqlualib.h" + +#define SEQLUA_ITERTYPE_FUNC 1 +#define SEQLUA_ITERTYPE_META 2 +#define SEQLUA_ITERTYPE_RAW 3 + +void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx) { + luaL_checkany(L, idx); // provides better error message + iter->L = L; + if (luaL_getmetafield(L, idx, "__call")) { + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); + iter->itertype = SEQLUA_ITERTYPE_FUNC; + } else if (lua_type(L, idx) == LUA_TFUNCTION) { + lua_pushvalue(L, idx); + iter->itertype = SEQLUA_ITERTYPE_FUNC; + } else { + if (luaL_getmetafield(L, idx, "__index")) { + lua_pop(L, 1); + iter->itertype = SEQLUA_ITERTYPE_META; + } else { + luaL_checktype(L, idx, LUA_TTABLE); + iter->itertype = SEQLUA_ITERTYPE_RAW; + } + lua_pushvalue(L, idx); + } + iter->i = 0; +} + +int seqlua_iternext(seqlua_Iterator *iter) { + lua_State *L = iter->L; + lua_Integer i = ++iter->i; + switch (iter->itertype) { + case SEQLUA_ITERTYPE_FUNC: + lua_pushvalue(L, -1); + lua_call(L, 0, 1); + break; + case SEQLUA_ITERTYPE_META: + lua_pushinteger(L, i); + lua_gettable(L, -2); + break; + case SEQLUA_ITERTYPE_RAW: + lua_rawgeti(L, -1, i); + } + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + return 0; + } + return 1; +} + +static int seqlua_iterclosureaux_raw(lua_State *L) { + lua_Integer i = lua_tointeger(L, lua_upvalueindex(2)) + 1; + lua_rawgeti(L, lua_upvalueindex(1), i); + if (lua_isnil(L, -1)) return 1; + lua_pushinteger(L, i); + lua_replace(L, lua_upvalueindex(2)); + return 1; +} + +static int seqlua_iterclosureaux_meta(lua_State *L) { + lua_Integer i = lua_tointeger(L, lua_upvalueindex(2)) + 1; + lua_pushinteger(L, i); + lua_gettable(L, lua_upvalueindex(1)); + if (lua_isnil(L, -1)) return 1; + lua_pushinteger(L, i); + lua_replace(L, lua_upvalueindex(2)); + return 1; +} + +void seqlua_iterclosure(lua_State *L, int idx) { + if (lua_type(L, idx) == LUA_TFUNCTION) { + // do nothing + } else if (luaL_getmetafield(L, idx, "__call")) { + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); + lua_replace(L, idx); + } else if (luaL_getmetafield(L, idx, "__index")) { + lua_pop(L, 1); + lua_pushvalue(L, idx); + lua_pushinteger(L, 0); + lua_pushcclosure(L, seqlua_iterclosureaux_raw, 2); + lua_replace(L, idx); + } else { + luaL_checktype(L, idx, LUA_TTABLE); + lua_pushvalue(L, idx); + lua_pushinteger(L, 0); + lua_pushcclosure(L, seqlua_iterclosureaux_meta, 2); + lua_replace(L, idx); + } +} + diff -r 000000000000 -r 47f9b323d68c seqlualib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seqlualib.h Wed Aug 20 00:39:10 2014 +0200 @@ -0,0 +1,24 @@ +#include +#include + +typedef struct { + lua_State *L; + int itertype; + lua_Integer i; +} seqlua_Iterator; + +extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx); + +extern int seqlua_iternext(seqlua_Iterator *iter); + +#define seqlua_iterloop(L, iter, idx) \ + for ( \ + seqlua_iterinit((L), (iter), (idx)); \ + seqlua_iternext(iter); \ + lua_pop((L), 1) \ + ) + +#define seqlua_itercount(iter) ((iter)->i) + +extern void seqlua_iterclosure(lua_State *L, int idx); +