seqlua
changeset 35:332216604f83
Support special return values of __ipairs metamethod (string as first return value)
author | jbe |
---|---|
date | Sun Aug 24 22:24:19 2014 +0200 (2014-08-24) |
parents | 2a43e8ffbced |
children | 4f82d3b3dbd9 |
files | README seqlua.c seqlua_c_example_test.lua seqlualib.c |
line diff
1.1 --- a/README Sun Aug 24 22:21:46 2014 +0200 1.2 +++ b/README Sun Aug 24 22:24:19 2014 +0200 1.3 @@ -144,3 +144,119 @@ 1.4 ``seqlua_iterloopauto`` is used. 1.5 1.6 1.7 +Respected metamethods 1.8 +--------------------- 1.9 + 1.10 +Regarding the behavior of the Lua functions and the C functions and macros 1.11 +provided by this extension, an existing ``__index`` metamethod will be 1.12 +respected automatically. An existing ``__ipairs`` metamethod, however, takes 1.13 +precedence. 1.14 + 1.15 +If the ``__ipairs`` field of a value's metatable is set, then it must always 1.16 +refer to a function. When starting iteration over a value with such a 1.17 +metamethod being set, then this function is called with ``self`` (i.e. the 1.18 +value itself) passed as first argument. The return values of the ``__ipairs`` 1.19 +metamethod may take one of the following 4 forms: 1.20 + 1.21 +* ``return function_or_callable, static_argument, startindex`` causes the three 1.22 + arguments to be returned by ``ipairs`` without further modification. Using 1.23 + the C macros and functions for iteration, the behavior is according to the 1.24 + generic loop statement in Lua: 1.25 + ``for i, v in function_or_callable, static_argument, startindex do ... end`` 1.26 +* ``return "raw", table`` will result in iteration over the table ``table`` 1.27 + using ``lua_rawgeti`` 1.28 +* ``return "index", table_or_userdata`` will result in iteration over the table 1.29 + or userdata while respecting any ``__index`` metamethod of the table or 1.30 + userdata value 1.31 +* ``return "call", function_or_callable`` will use the callable value as 1.32 + (function) iterator where the function is expected to return a single value 1.33 + without any index (the index is inserted automatically when using the 1.34 + ``ipairs`` function for iteration) 1.35 + 1.36 +These possiblities are demonstrated by the following example code: 1.37 + 1.38 + require "seqlua" 1.39 + 1.40 + do 1.41 + local function ipairsaux(t, i) 1.42 + i = i + 1 1.43 + if i <= 3 then 1.44 + return i, t[i] 1.45 + end 1.46 + end 1.47 + custom = setmetatable( 1.48 + {"one", "two", "three", "four", "five"}, 1.49 + { 1.50 + __ipairs = function(self) 1.51 + return ipairsaux, self, 0 1.52 + end 1.53 + } 1.54 + ) 1.55 + end 1.56 + print(string.concat(",", custom)) 1.57 + -- prints: one, two, three 1.58 + -- (note that "four" and "five" are not printed) 1.59 + 1.60 + tbl = {"alpha", "beta"} 1.61 + 1.62 + proxy1 = setmetatable({}, {__index = tbl}) 1.63 + for i, v in ipairs(proxy1) do print(i, v) end 1.64 + -- prints: 1.65 + -- 1 alpha 1.66 + -- 2 beta 1.67 + 1.68 + proxy2 = setmetatable({}, { 1.69 + __ipairs = function(self) 1.70 + return "index", proxy1 1.71 + end 1.72 + }) 1.73 + for i, v in ipairs(proxy2) do print(i, v) end 1.74 + -- prints: 1.75 + -- 1 alpha 1.76 + -- 2 beta 1.77 + print(proxy2[1]) 1.78 + -- prints: nil 1.79 + 1.80 + cursor = setmetatable({ 1.81 + "alice", "bob", "charlie", pos=1 1.82 + }, { 1.83 + __call = function(self) 1.84 + local value = self[self.pos] 1.85 + if value == nil then 1.86 + self.pos = 1 1.87 + else 1.88 + self.pos = self.pos + 1 1.89 + end 1.90 + return value 1.91 + end, 1.92 + __ipairs = function(self) 1.93 + return "call", self 1.94 + end 1.95 + }) 1.96 + for i, v in ipairs(cursor) do print(i, v) end 1.97 + -- prints: 1.98 + -- 1 alice 1.99 + -- 2 bob 1.100 + -- 3 charlie 1.101 + print(cursor()) 1.102 + -- prints: alice 1.103 + for i, v in ipairs(cursor) do print(i, v) end 1.104 + -- prints: 1.105 + -- 1 bob 1.106 + -- 2 charlie 1.107 + -- (note that "alice" has been returned earlier) 1.108 + 1.109 + coefficients = setmetatable({1.25, 3.14, 17.5}, { 1.110 + __index = function(self) return 1 end, 1.111 + __ipairs = function(self) return "raw", self end 1.112 + }) 1.113 + for i, v in ipairs(coefficients) do print(i, v) end 1.114 + -- prints: 1.115 + -- 1 1.25 1.116 + -- 2 3.14 1.117 + -- 3 17.5 1.118 + -- (note that iteration terminates even if coefficients[4] == 1) 1.119 + print(coefficients[4]) 1.120 + -- prints: 1 1.121 + 1.122 +
2.1 --- a/seqlua.c Sun Aug 24 22:21:46 2014 +0200 2.2 +++ b/seqlua.c Sun Aug 24 22:24:19 2014 +0200 2.3 @@ -1,6 +1,7 @@ 2.4 #include <lua.h> 2.5 #include <lauxlib.h> 2.6 #include "seqlualib.h" 2.7 +#include <string.h> 2.8 2.9 static int seqlua_ipairsaux_raw(lua_State *L) { 2.10 lua_Integer i; 2.11 @@ -37,6 +38,20 @@ 2.12 if (luaL_getmetafield(L, 1, "__ipairs")) { 2.13 lua_pushvalue(L, 1); 2.14 lua_call(L, 1, 3); 2.15 + if (lua_type(L, -3) == LUA_TSTRING) { 2.16 + const char *method = lua_tostring(L, -3); 2.17 + if (!strcmp(method, "raw")) { 2.18 + lua_pushcfunction(L, seqlua_ipairsaux_raw); 2.19 + } else if (!strcmp(method, "index")) { 2.20 + lua_pushcfunction(L, seqlua_ipairsaux_index); 2.21 + } else if (!strcmp(method, "call")) { 2.22 + lua_pushcfunction(L, seqlua_ipairsaux_call); 2.23 + } else { 2.24 + luaL_error(L, "Unexpected string returned by __ipairs metamethod"); 2.25 + } 2.26 + lua_pushvalue(L, -3); 2.27 + lua_pushinteger(L, 0); 2.28 + } 2.29 } else { 2.30 int t = lua_type(L, 1); 2.31 if (t == LUA_TFUNCTION) {
3.1 --- a/seqlua_c_example_test.lua Sun Aug 24 22:21:46 2014 +0200 3.2 +++ b/seqlua_c_example_test.lua Sun Aug 24 22:24:19 2014 +0200 3.3 @@ -1,4 +1,4 @@ 3.4 -require "seqlua_c_example" -- defines functions "printcsv" and "printthree" 3.5 +require "seqlua_c_example" -- defines function "printcsv" 3.6 3.7 function alphabet() 3.8 local letter = nil 3.9 @@ -14,7 +14,9 @@ 3.10 end 3.11 end 3.12 3.13 -printcsv{"a", "b", "c"} 3.14 +t = {"a", "b", "c"} 3.15 + 3.16 +printcsv(t) 3.17 -- prints: 3.18 -- a,b,c 3.19 3.20 @@ -22,3 +24,56 @@ 3.21 -- prints: 3.22 -- 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 3.23 3.24 +call_proxy = setmetatable({}, {__ipairs = function() return "call", alphabet() end}) 3.25 + 3.26 +printcsv(call_proxy) 3.27 +-- prints: 3.28 +-- 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 3.29 + 3.30 +random_access_alphabet = setmetatable({}, { 3.31 + __index = function(self, i) 3.32 + if type(i) == "number" and i >= 1 and i <= 26 then 3.33 + return string.char(string.byte("a") + i - 1) 3.34 + end 3.35 + end 3.36 +}) 3.37 + 3.38 +printcsv(random_access_alphabet) 3.39 +-- prints: 3.40 +-- 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 3.41 + 3.42 +index_proxy = setmetatable({}, { 3.43 + __ipairs = function() return "index", random_access_alphabet end 3.44 +}) 3.45 + 3.46 +printcsv(index_proxy) 3.47 +-- prints: 3.48 +-- 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 3.49 + 3.50 +raw_proxy = setmetatable({"dummy"}, { 3.51 + __ipairs = function() return "raw", t end 3.52 +}) 3.53 + 3.54 +printcsv(raw_proxy) 3.55 +-- prints: 3.56 +-- a,b,c 3.57 + 3.58 +require "seqlua" 3.59 + 3.60 +do 3.61 + local function ipairsaux(s, i) 3.62 + i = i + 1 3.63 + if i <= s.count then 3.64 + return i, s.entry 3.65 + end 3.66 + end 3.67 + custom_proxy = setmetatable({entry = "zebra", count=5}, { 3.68 + __ipairs = function(self) 3.69 + return ipairsaux, self, 0 3.70 + end 3.71 + }) 3.72 +end 3.73 + 3.74 +printcsv(custom_proxy) 3.75 +-- prints: zebra,zebra,zebra,zebra,zebra 3.76 +
4.1 --- a/seqlualib.c Sun Aug 24 22:21:46 2014 +0200 4.2 +++ b/seqlualib.c Sun Aug 24 22:24:19 2014 +0200 4.3 @@ -1,6 +1,7 @@ 4.4 #include <lua.h> 4.5 #include <lauxlib.h> 4.6 #include "seqlualib.h" 4.7 +#include <string.h> 4.8 4.9 #define SEQLUA_ITERTYPE_IPAIRS 1 4.10 #define SEQLUA_ITERTYPE_CALL 2 4.11 @@ -11,7 +12,21 @@ 4.12 if (luaL_getmetafield(L, idx, "__ipairs")) { 4.13 lua_pushvalue(L, idx); 4.14 lua_call(L, 1, 3); 4.15 - iter->itertype = SEQLUA_ITERTYPE_IPAIRS; 4.16 + if (lua_type(L, -3) == LUA_TSTRING) { 4.17 + const char *method = lua_tostring(L, -3); 4.18 + if (!strcmp(method, "raw")) { 4.19 + iter->itertype = SEQLUA_ITERTYPE_RAW; 4.20 + } else if (!strcmp(method, "index")) { 4.21 + iter->itertype = SEQLUA_ITERTYPE_INDEX; 4.22 + } else if (!strcmp(method, "call")) { 4.23 + iter->itertype = SEQLUA_ITERTYPE_CALL; 4.24 + } else { 4.25 + luaL_error(L, "Unexpected string returned by __ipairs metamethod"); 4.26 + } 4.27 + iter->idx = lua_gettop(L) - 1; 4.28 + } else { 4.29 + iter->itertype = SEQLUA_ITERTYPE_IPAIRS; 4.30 + } 4.31 } else { 4.32 if (lua_type(L, idx) == LUA_TFUNCTION) { 4.33 iter->itertype = SEQLUA_ITERTYPE_CALL;