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;

Impressum / About Us