lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2016-02-23 7:26 GMT+02:00 Paul K <paul@zerobrane.com>:
> Dirk,
>
>> A module is attached that contains the following comments:
>
> Missing attachment?
>
> Paul.
>
-- selfinit.lua  © Dirk Laurie 2016  MIT license
-- Published as a contribution to lua-l@lists.lua.org on 2015-02-23

-- Self-initializing table. The point is that `tbl[key]` is never nil.
--   tbl = selfinit(tbl)   -- makes an existing table self-initializing
--                            and returns it
--   tblx = tbl.x          -- tbl.x is an empty table with the same
--                            metatable as x
--   tbl.a.b.c.d = 'item'  -- tbl.a, tbl.a.b, tblc.a.b.c are created too,
--                            even if `item` is nil.
--   tbl = selfinit()      -- creates a new self-initializing table
-- WARNING: The implications are strongly counterintuitive. 
-- 1. You will need `rawget` to find out whether a table entry exists.
-- 2. If `ipairs` is found by the module loader to treat an empty self-
-- initializing table as non-empty, it is monkey-patched to use `rawget`.

-- The module is not paranoid about abuse. The only legitimate error
-- is to try to overwrite an existing __index metamethod. If you supply
-- a 'tbl' on which 'setmetatable' fails, that's the error you will get.

local selfinit, selfinit_mt, selfinit_index 

selfinit_index = function(tbl,item)
  local sit = setmetatable({},getmetatable(tbl))    
  rawset(tbl,item,sit)
  return sit
end

selfinit_mt = {__index = selfinit_index}

--- selfinit(tbl) makes 'tbl' self-initializing and returns it.
-- selfinit() is equivalent to selfinit{}
selfinit = function(tbl)
  local mt
  mt = getmetatable(tbl)
  if mt then
    if mt.__index then 
      error("Table already has an index metamethod",2)
    end
    mt.__index = selfinit_index
    return tbl
  end
  return setmetatable(tbl or {},selfinit_mt)
end

local old_ipairs = ipairs

local selfinit_inext = function(tbl,key)
  if not key then 
    local val = rawget(tbl,1)
    if val then return 1, val
    else return
    end
  end
  key=key+1
  return rawget(tbl,key),key
end
  
-- monkey-patch ipairs
for k in ipairs(selfinit{}) do
  debug.getregistry().ipairs_before_selfinit = ipairs
  ipairs = function(tbl) return selfinit_inext, tbl end
  break
end
  
return selfinit