seqlua
changeset 0:47f9b323d68c
Initial commit
author | jbe |
---|---|
date | Wed Aug 20 00:39:10 2014 +0200 (2014-08-20) |
parents | |
children | 158dfce546c0 |
files | LICENSE Makefile README seqlua.c seqlua_c_example.c seqlua_c_example_test.lua seqlualib.c seqlualib.h |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LICENSE Wed Aug 20 00:39:10 2014 +0200 1.3 @@ -0,0 +1,19 @@ 1.4 +Copyright (c) 2014 Public Software Group e. V., Berlin, Germany 1.5 + 1.6 +Permission is hereby granted, free of charge, to any person obtaining a 1.7 +copy of this software and associated documentation files (the "Software"), 1.8 +to deal in the Software without restriction, including without limitation 1.9 +the rights to use, copy, modify, merge, publish, distribute, sublicense, 1.10 +and/or sell copies of the Software, and to permit persons to whom the 1.11 +Software is furnished to do so, subject to the following conditions: 1.12 + 1.13 +The above copyright notice and this permission notice shall be included in 1.14 +all copies or substantial portions of the Software. 1.15 + 1.16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1.17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1.18 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1.19 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1.20 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1.21 +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 1.22 +DEALINGS IN THE SOFTWARE.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile Wed Aug 20 00:39:10 2014 +0200 2.3 @@ -0,0 +1,24 @@ 2.4 +LUA_INCLUDE=/usr/local/include/lua52 2.5 + 2.6 +all:: seqlualib.so seqlua.so seqlua_c_example.so 2.7 + 2.8 +seqlualib.so: seqlualib.o 2.9 + ld -shared -o seqlualib.so seqlualib.o 2.10 + 2.11 +seqlualib.o: seqlualib.c seqlualib.h 2.12 + cc -O2 -c -fpic -I $(LUA_INCLUDE) -o seqlualib.o seqlualib.c 2.13 + 2.14 +seqlua.so: seqlua.o seqlualib.o 2.15 + ld -shared -o seqlua.so seqlua.o seqlualib.o 2.16 + 2.17 +seqlua.o: seqlua.c seqlualib.h 2.18 + cc -O2 -c -fpic -I $(LUA_INCLUDE) -o seqlua.o seqlua.c 2.19 + 2.20 +seqlua_c_example.so: seqlua_c_example.o seqlua.o 2.21 + ld -shared -o seqlua_c_example.so seqlua_c_example.o seqlualib.o 2.22 + 2.23 +seqlua_c_example.o: seqlua_c_example.c seqlualib.h 2.24 + cc -O2 -c -fpic -I $(LUA_INCLUDE) -o seqlua_c_example.o seqlua_c_example.c 2.25 + 2.26 +clean:: 2.27 + rm -f *.o *.so
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/README Wed Aug 20 00:39:10 2014 +0200 3.3 @@ -0,0 +1,141 @@ 3.4 +seqlua: Extended sequences and iterators in Lua 3.5 +=============================================== 3.6 + 3.7 +This is an experimental package to extend Lua in the following manner: 3.8 + 3.9 +* allow ipairs(...) to accept tables as well as functions or iterator triplets, 3.10 +* provide a function iterator(...) that returns single functions unmodified, 3.11 + but converts 3.12 + * iterator triplets into closures, and 3.13 + * tables into a function closure that iterates over the elements, 3.14 +* provide the auxiliary C functions and macros to simplify iterating over both 3.15 + tables and iterator functions with the same statement. 3.16 + 3.17 +This library completely ignores the ``__ipairs`` metamethod (as it is 3.18 +deprecated since Lua 5.3.0-alpha). It respects, however, any ``__call`` 3.19 +metamethods (this may cause unexpected behavior when passing callable tables 3.20 +to ``ipairs``). 3.21 + 3.22 + 3.23 + 3.24 +Lua part of the library 3.25 +----------------------- 3.26 + 3.27 +The new ``ipairs(...)`` function works as follows: 3.28 + 3.29 + require "seqlua" 3.30 + 3.31 + t = {"a", "b", "c"} 3.32 + 3.33 + for i, v in ipairs(t) do 3.34 + print(i, v) 3.35 + end 3.36 + -- prints: 3.37 + -- 1 a 3.38 + -- 2 b 3.39 + -- 3 c 3.40 + 3.41 + function alphabet() 3.42 + local letter = nil 3.43 + return function() 3.44 + if letter == nil then 3.45 + letter = "a" 3.46 + elseif letter == "z" then 3.47 + return nil 3.48 + else 3.49 + letter = string.char(string.byte(letter) + 1) 3.50 + end 3.51 + return letter 3.52 + end 3.53 + end 3.54 + 3.55 + f = alphabet() 3.56 + 3.57 + for i, v in ipairs(f) do 3.58 + print(i, v) 3.59 + end 3.60 + -- prints: 3.61 + -- 1 a 3.62 + -- 2 b 3.63 + -- 3 c 3.64 + -- ... 3.65 + -- 25 y 3.66 + -- 26 z 3.67 + 3.68 + set = {apple = true, banana = true} 3.69 + for i, k, v in ipairs(pairs(set)) do 3.70 + print(i, k, v) 3.71 + end 3.72 + -- prints: 3.73 + -- 1 banana true 3.74 + -- 2 apple true 3.75 + -- (order of "apple" and "banana" may vary) 3.76 + 3.77 +The function ``iterator(...)`` may be used to convert any table, any function, 3.78 +or any iterator triplet into a single function (possibly creating a closure): 3.79 + 3.80 + function filter_strings(...) 3.81 + nextvalue = iterator(...) 3.82 + return function() 3.83 + local value 3.84 + repeat 3.85 + value = nextvalue() 3.86 + until value == nil or type(value) == "string" 3.87 + return value 3.88 + end 3.89 + end 3.90 + 3.91 + for i, v in ipairs(filter_strings{"Hello", true, "World"}) do 3.92 + print(i, v) 3.93 + end 3.94 + -- prints: 3.95 + -- 1 Hello 3.96 + -- 2 World 3.97 + 3.98 + tbl = {apple = true, banana = true, [1] = "array entry"} 3.99 + for v in filter_strings(pairs(tbl)) do 3.100 + print(v) 3.101 + end 3.102 + -- prints: 3.103 + -- banana 3.104 + -- apple 3.105 + -- (order may vary) 3.106 + 3.107 + 3.108 + 3.109 +C part of the library 3.110 +--------------------- 3.111 + 3.112 +In ``seqlualib.h``, the following macro is defined: 3.113 + 3.114 + #define seqlua_iterloop(L, iter, idx) \ 3.115 + for ( \ 3.116 + seqlua_iterinit((L), (iter), (idx)); \ 3.117 + seqlua_iternext(iter); \ 3.118 + lua_pop((L), 1) \ 3.119 + ) 3.120 + 3.121 +This macro allows iteration over either tables or iterator functions (but not 3.122 +iterator triplets) as the following example function demonstrates: 3.123 + 3.124 + int printcsv(lua_State *L) { 3.125 + seqlua_Iterator iter; 3.126 + seqlua_iterloop(L, &iter, 1) { 3.127 + if (seqlua_itercount(&iter) > 1) fputs(",", stdout); 3.128 + fputs(luaL_tolstring(L, -1, NULL), stdout); 3.129 + lua_pop(L, 1); 3.130 + } 3.131 + fputs("\n", stdout); 3.132 + return 0; 3.133 + } 3.134 + 3.135 + printcsv{"a", "b", "c"} -- prints: a,b,c 3.136 + 3.137 +Additionally, ``seqlualib`` includes a function ``seqlua_iterclosure(L, idx)``, 3.138 +which converts a table at a given stack index into a function closure (stored 3.139 +on the same stack index) that iterates over the elements of the table. If the 3.140 +value at the given stack index is already a function, it leaves the value 3.141 +unchanged. If the value is convertible to a function using ``__ipairs,`` then 3.142 +the function is replaced by its ``__ipairs`` metamethod. 3.143 + 3.144 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/seqlua.c Wed Aug 20 00:39:10 2014 +0200 4.3 @@ -0,0 +1,123 @@ 4.4 +#include <lua.h> 4.5 +#include <lauxlib.h> 4.6 +#include "seqlualib.h" 4.7 + 4.8 +static int seqlua_ipairsaux_raw(lua_State *L) { 4.9 + lua_Integer i; 4.10 + luaL_checktype(L, 1, LUA_TTABLE); 4.11 + i = luaL_checkinteger(L, 2) + 1; 4.12 + lua_pushinteger(L, i); 4.13 + lua_rawgeti(L, 1, i); // TODO: Lua 5.3 returns type 4.14 + return lua_isnil(L, -1) ? 1 : 2; 4.15 +} 4.16 + 4.17 +static int seqlua_ipairsaux_meta(lua_State *L) { 4.18 + lua_Integer i; 4.19 + i = luaL_checkinteger(L, 2) + 1; 4.20 + lua_pushinteger(L, i); 4.21 + lua_pushinteger(L, i); 4.22 + lua_gettable(L, 1); // TODO: Lua 5.3 returns type 4.23 + return lua_isnil(L, -1) ? 1 : 2; 4.24 +} 4.25 + 4.26 +static int seqlua_ipairsaux_func(lua_State *L) { 4.27 + luaL_checktype(L, 1, LUA_TFUNCTION); 4.28 + lua_pushinteger(L, luaL_checkinteger(L, 2) + 1); 4.29 + lua_insert(L, 1); 4.30 + lua_settop(L, 2); 4.31 + lua_call(L, 0, LUA_MULTRET); 4.32 + if (lua_isnoneornil(L, 2)) { 4.33 + lua_settop(L, 0); 4.34 + lua_pushnil(L); 4.35 + return 1; 4.36 + } else { 4.37 + return lua_gettop(L); 4.38 + } 4.39 +} 4.40 + 4.41 +static int seqlua_ipairsaux_funcclosure(lua_State *L) { 4.42 + lua_Integer i = lua_tointeger(L, lua_upvalueindex(4)) + 1; 4.43 + lua_settop(L, 0); 4.44 + lua_pushinteger(L, i); 4.45 + lua_replace(L, lua_upvalueindex(4)); 4.46 + lua_pushinteger(L, i); 4.47 + lua_pushvalue(L, lua_upvalueindex(1)); 4.48 + lua_pushvalue(L, lua_upvalueindex(2)); 4.49 + lua_pushvalue(L, lua_upvalueindex(3)); 4.50 + lua_call(L, 2, LUA_MULTRET); 4.51 + if (lua_isnoneornil(L, 2)) { 4.52 + lua_settop(L, 0); 4.53 + lua_pushnil(L); 4.54 + return 1; 4.55 + } else { 4.56 + lua_pushvalue(L, 2); 4.57 + lua_replace(L, lua_upvalueindex(3)); 4.58 + return lua_gettop(L); 4.59 + } 4.60 +} 4.61 + 4.62 +static int seqlua_ipairs(lua_State *L) { 4.63 + if (luaL_getmetafield(L, 1, "__call")) { 4.64 + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); 4.65 + lua_replace(L, 1); 4.66 + } 4.67 + if (lua_type(L, 1) == LUA_TFUNCTION) { 4.68 + if (!lua_isnoneornil(L, 2) || !lua_isnoneornil(L, 3)) { 4.69 + lua_settop(L, 3); 4.70 + lua_pushinteger(L, 0); 4.71 + lua_pushcclosure(L, seqlua_ipairsaux_funcclosure, 4); 4.72 + return 1; 4.73 + } 4.74 + lua_pushcfunction(L, seqlua_ipairsaux_func); 4.75 + } else if (luaL_getmetafield(L, 1, "__index")) { 4.76 + lua_pushcfunction(L, seqlua_ipairsaux_meta); 4.77 + } else { 4.78 + luaL_checktype(L, 1, LUA_TTABLE); 4.79 + lua_pushcfunction(L, seqlua_ipairsaux_raw); 4.80 + } 4.81 + lua_pushvalue(L, 1); 4.82 + lua_pushinteger(L, 0); 4.83 + return 3; 4.84 +} 4.85 + 4.86 +static int seqlua_iteratoraux(lua_State *L) { 4.87 + lua_settop(L, 0); 4.88 + lua_pushvalue(L, lua_upvalueindex(1)); 4.89 + lua_pushvalue(L, lua_upvalueindex(2)); 4.90 + lua_pushvalue(L, lua_upvalueindex(3)); 4.91 + lua_call(L, 2, LUA_MULTRET); 4.92 + if (lua_isnoneornil(L, 1)) { 4.93 + lua_settop(L, 1); 4.94 + return 1; 4.95 + } 4.96 + lua_pushvalue(L, 1); 4.97 + lua_replace(L, lua_upvalueindex(3)); 4.98 + return lua_gettop(L); 4.99 +} 4.100 + 4.101 +static int seqlua_iterator(lua_State *L) { 4.102 + if (luaL_getmetafield(L, 1, "__call")) { 4.103 + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); 4.104 + lua_replace(L, 1); 4.105 + } 4.106 + if (lua_type(L, 1) == LUA_TFUNCTION) { 4.107 + if (lua_isnoneornil(L, 2) && lua_isnoneornil(L, 3)) { 4.108 + lua_settop(L, 1); 4.109 + } else { 4.110 + lua_settop(L, 3); 4.111 + lua_pushcclosure(L, seqlua_iteratoraux, 3); 4.112 + } 4.113 + } else { 4.114 + seqlua_iterclosure(L, 1); 4.115 + lua_settop(L, 1); 4.116 + } 4.117 + return 1; 4.118 +} 4.119 + 4.120 +int luaopen_seqlua(lua_State *L) { 4.121 + lua_pushcfunction(L, seqlua_ipairs); 4.122 + lua_setglobal(L, "ipairs"); 4.123 + lua_pushcfunction(L, seqlua_iterator); 4.124 + lua_setglobal(L, "iterator"); 4.125 + return 1; 4.126 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/seqlua_c_example.c Wed Aug 20 00:39:10 2014 +0200 5.3 @@ -0,0 +1,21 @@ 5.4 +#include <lua.h> 5.5 +#include <lauxlib.h> 5.6 +#include "seqlualib.h" 5.7 +#include <stdio.h> 5.8 + 5.9 +static int seqlua_c_example_printcsv(lua_State *L) { 5.10 + seqlua_Iterator iter; 5.11 + seqlua_iterloop(L, &iter, 1) { 5.12 + if (seqlua_itercount(&iter) > 1) fputs(",", stdout); 5.13 + fputs(luaL_tolstring(L, -1, NULL), stdout); 5.14 + lua_pop(L, 1); 5.15 + } 5.16 + fputs("\n", stdout); 5.17 + return 0; 5.18 +} 5.19 + 5.20 +int luaopen_seqlua_c_example(lua_State *L) { 5.21 + lua_pushcfunction(L, seqlua_c_example_printcsv); 5.22 + lua_setglobal(L, "printcsv"); 5.23 + return 0; 5.24 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/seqlua_c_example_test.lua Wed Aug 20 00:39:10 2014 +0200 6.3 @@ -0,0 +1,24 @@ 6.4 +require "seqlua_c_example" -- defines function "printcsv" 6.5 + 6.6 +function alphabet() 6.7 + local letter = nil 6.8 + return function() 6.9 + if letter == nil then 6.10 + letter = "a" 6.11 + elseif letter == "z" then 6.12 + return nil 6.13 + else 6.14 + letter = string.char(string.byte(letter) + 1) 6.15 + end 6.16 + return letter 6.17 + end 6.18 +end 6.19 + 6.20 +printcsv{"a", "b", "c"} 6.21 +-- prints: 6.22 +-- a,b,c 6.23 + 6.24 +printcsv(alphabet()) 6.25 +-- prints: 6.26 +-- 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 6.27 +
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/seqlualib.c Wed Aug 20 00:39:10 2014 +0200 7.3 @@ -0,0 +1,92 @@ 7.4 +#include <lua.h> 7.5 +#include <lauxlib.h> 7.6 +#include "seqlualib.h" 7.7 + 7.8 +#define SEQLUA_ITERTYPE_FUNC 1 7.9 +#define SEQLUA_ITERTYPE_META 2 7.10 +#define SEQLUA_ITERTYPE_RAW 3 7.11 + 7.12 +void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx) { 7.13 + luaL_checkany(L, idx); // provides better error message 7.14 + iter->L = L; 7.15 + if (luaL_getmetafield(L, idx, "__call")) { 7.16 + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); 7.17 + iter->itertype = SEQLUA_ITERTYPE_FUNC; 7.18 + } else if (lua_type(L, idx) == LUA_TFUNCTION) { 7.19 + lua_pushvalue(L, idx); 7.20 + iter->itertype = SEQLUA_ITERTYPE_FUNC; 7.21 + } else { 7.22 + if (luaL_getmetafield(L, idx, "__index")) { 7.23 + lua_pop(L, 1); 7.24 + iter->itertype = SEQLUA_ITERTYPE_META; 7.25 + } else { 7.26 + luaL_checktype(L, idx, LUA_TTABLE); 7.27 + iter->itertype = SEQLUA_ITERTYPE_RAW; 7.28 + } 7.29 + lua_pushvalue(L, idx); 7.30 + } 7.31 + iter->i = 0; 7.32 +} 7.33 + 7.34 +int seqlua_iternext(seqlua_Iterator *iter) { 7.35 + lua_State *L = iter->L; 7.36 + lua_Integer i = ++iter->i; 7.37 + switch (iter->itertype) { 7.38 + case SEQLUA_ITERTYPE_FUNC: 7.39 + lua_pushvalue(L, -1); 7.40 + lua_call(L, 0, 1); 7.41 + break; 7.42 + case SEQLUA_ITERTYPE_META: 7.43 + lua_pushinteger(L, i); 7.44 + lua_gettable(L, -2); 7.45 + break; 7.46 + case SEQLUA_ITERTYPE_RAW: 7.47 + lua_rawgeti(L, -1, i); 7.48 + } 7.49 + if (lua_isnil(L, -1)) { 7.50 + lua_pop(L, 2); 7.51 + return 0; 7.52 + } 7.53 + return 1; 7.54 +} 7.55 + 7.56 +static int seqlua_iterclosureaux_raw(lua_State *L) { 7.57 + lua_Integer i = lua_tointeger(L, lua_upvalueindex(2)) + 1; 7.58 + lua_rawgeti(L, lua_upvalueindex(1), i); 7.59 + if (lua_isnil(L, -1)) return 1; 7.60 + lua_pushinteger(L, i); 7.61 + lua_replace(L, lua_upvalueindex(2)); 7.62 + return 1; 7.63 +} 7.64 + 7.65 +static int seqlua_iterclosureaux_meta(lua_State *L) { 7.66 + lua_Integer i = lua_tointeger(L, lua_upvalueindex(2)) + 1; 7.67 + lua_pushinteger(L, i); 7.68 + lua_gettable(L, lua_upvalueindex(1)); 7.69 + if (lua_isnil(L, -1)) return 1; 7.70 + lua_pushinteger(L, i); 7.71 + lua_replace(L, lua_upvalueindex(2)); 7.72 + return 1; 7.73 +} 7.74 + 7.75 +void seqlua_iterclosure(lua_State *L, int idx) { 7.76 + if (lua_type(L, idx) == LUA_TFUNCTION) { 7.77 + // do nothing 7.78 + } else if (luaL_getmetafield(L, idx, "__call")) { 7.79 + if (lua_type(L, -1) != LUA_TFUNCTION) luaL_error(L, "__call is not a function"); 7.80 + lua_replace(L, idx); 7.81 + } else if (luaL_getmetafield(L, idx, "__index")) { 7.82 + lua_pop(L, 1); 7.83 + lua_pushvalue(L, idx); 7.84 + lua_pushinteger(L, 0); 7.85 + lua_pushcclosure(L, seqlua_iterclosureaux_raw, 2); 7.86 + lua_replace(L, idx); 7.87 + } else { 7.88 + luaL_checktype(L, idx, LUA_TTABLE); 7.89 + lua_pushvalue(L, idx); 7.90 + lua_pushinteger(L, 0); 7.91 + lua_pushcclosure(L, seqlua_iterclosureaux_meta, 2); 7.92 + lua_replace(L, idx); 7.93 + } 7.94 +} 7.95 +
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/seqlualib.h Wed Aug 20 00:39:10 2014 +0200 8.3 @@ -0,0 +1,24 @@ 8.4 +#include <lua.h> 8.5 +#include <lauxlib.h> 8.6 + 8.7 +typedef struct { 8.8 + lua_State *L; 8.9 + int itertype; 8.10 + lua_Integer i; 8.11 +} seqlua_Iterator; 8.12 + 8.13 +extern void seqlua_iterinit(lua_State *L, seqlua_Iterator *iter, int idx); 8.14 + 8.15 +extern int seqlua_iternext(seqlua_Iterator *iter); 8.16 + 8.17 +#define seqlua_iterloop(L, iter, idx) \ 8.18 + for ( \ 8.19 + seqlua_iterinit((L), (iter), (idx)); \ 8.20 + seqlua_iternext(iter); \ 8.21 + lua_pop((L), 1) \ 8.22 + ) 8.23 + 8.24 +#define seqlua_itercount(iter) ((iter)->i) 8.25 + 8.26 +extern void seqlua_iterclosure(lua_State *L, int idx); 8.27 +