monome / norns

norns is many sound instruments.
http://monome.org
GNU General Public License v3.0
633 stars 147 forks source link

tab.readonly doesn't endow pairs/ipairs for proxy #1792

Open dndrks opened 4 months ago

dndrks commented 4 months ago

issue description + repro

going through documentation tasks, i realized that i expect tab.readonly to create a proxy which also inherits other tab functionality, eg. tab.print.

repro script

function init()

  mana = {
    white = {"order", "peace", "light"},
    blue = {"intellect", "logic", "manipulation"},
    black = {"power", "death", "corruption"},
    red = {"freedom", "chaos", "fury"},
    green = {"life", "nature", "evolution"}
  }

  mana_green_editable = tab.readonly{
    table = mana, -- which table to copy
    except = {'green'}, -- which keys should remain writeable
  }

  tab.print(mana_green_editable)

end

since tab.print calls pairs, running the above results in a C stack overflow pointing to https://github.com/monome/norns/blob/15c9cf9304d500b28c7ad04d6ddf4f85a4a6d095/lua/lib/tabutil.lua#L231

workaround

i'm v dumb about metatable stuff, but i'm able to resolve the stack overflow issue by building and adjusting a local tpairs table which reflects expose + except params. see https://www.diffchecker.com/fNViUcZr/ for side-by-side to current code (chose a different name just for differentiation in testing):

pairs workaround ```lua function treadonly(params) local t = params.table local exceptions = params.except or {} local proxy = {} local tpairs = {} for k,v in pairs(t) do if params.expose == nil or tab.contains(params.expose, k) then tpairs[k] = v end end local mt = { __index = function(_, k) if params.expose == nil or tab.contains(params.expose, k) then tpairs[k] = t[k] return t[k] end return nil end, __newindex = function (_,k,v) if (tab.contains(exceptions, k)) then t[k] = v tpairs[k] = v else error("'"..k.."', a read-only key, cannot be re-assigned.") end end, __pairs = function (_) return pairs(tpairs) end, __ipairs = function (_) return ipairs(tpairs) end, } setmetatable(proxy, mt) return proxy end ```

if this is at all reasonable, i'd be happy to PR this fix -- or i'd be happy to PR a much better one with some quick guidance <3