Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --
- --[[
- table iterator
- updated:
- 1. now items is iterated from array part first, and they are in order, then the hash park
- 2. leafCheck is removed, leaf item is tell from cb (return false)
- 3. cyclic check is done via iter function's parents parameter, it means allow process seen table as leaf item, see example
- note on the callback cb:
- -- function cb(itm, parents, keys) ... end
- 1. cb can break the iter by return true, ... , and then titer return ... as result
- 2. if cb cause error, titer stop and return nil, < cb's error msg if any >
- 3. if cb return false then it is a leafitem, iter will not go down into it
- 4. any other returns means cb not following this protocal
- 5. cb should not modify parents and keys
- 6. cb happend before iterating into itm (if it is a table) so it can be genreate its member on the fly,
- but it also may cause infinite loop
- -- error handle:
- cb run on pcall, so titer has the reponsiblity to handle the error, eg.
- <code>
- local state, result = titer(...)
- if state == nil and result~=nil then <error-handle> end
- </code>
- --]]
- local function array1stPairs(tbl, ignorepack)
- local yield = coroutine.yield
- return coroutine.wrap(function()
- local ispack = not ignorepack and type(tbl.n)=='number' and math.floor(tbl.n)==tbl.n and tbl.n>=0
- local arraysize = ispack and tbl.n or #tbl
- local seen = { n = ispack }
- for i=1,arraysize do seen[i] = true, yield(i, tbl[i]) end
- for k,v in pairs(tbl) do if not seen[k] then yield(k,v)end end
- end)
- end
- local function unpack_aux(t, from, to, ...)
- if from > to or to < 1 then return ... else return unpack_aux(t,from, to-1, t[to],...)end
- end
- local insert, remove, pcall, type, pairs = table.insert, table.remove, pcall, type, pairs
- local pk = table.pack or pack or function(...)return { ..., n=select('#',...) }end
- local upk = table.unpack or unpack or function(t, from, to)
- from, to = from or 1, to or t.n or #t
- return unpack_aux(t, from, to)
- end
- function titer(tbl, cb)
- if type(tbl)~='table'then return nil,'not a table'end
- local function iter(itm, parents, keys)
- local docb, rets, isleaf = itm ~= tbl
- if docb then
- rets = pk( pcall(cb, itm, parents, keys ) )
- if not rets[1] then -- cb cause error
- return true, nil, upk(rets,2)
- elseif rets[2]==nil and #rets>2 then -- error message
- return true, nil, upk(rets,3)
- elseif rets[2]==true then -- iter break
- return true, upk(rets,3)
- elseif rets[2]==false then -- leaf item or cyclic
- isleaf = true
- end
- end
- if type(itm)=='table' and not isleaf then
- insert(parents, itm)
- parents[itm]=true
- for k,v in array1stPairs(itm) do
- if not parents[v] then
- insert(keys, k)
- rets = pk( pcall(iter, v, parents, keys) )
- remove(keys)
- -- check break and error
- if not rets[1] then -- error caused
- return nil, upk(rets, 2)
- elseif rets[2]==nil and #rets>2 then -- error message
- return nil, upk(rets,3)
- elseif rets[2]==true then -- is break or cb error
- --print'return upk(rets, 3)'
- return upk(rets, 3)
- --elseif #rets>1 or rets[2]~=false then -- cb return something not follow protocal, print warning
- --print('in a titer call, cb not follow protocal')
- end
- end
- end
- remove(parents)
- parents[itm]=nil
- end
- end
- return iter(tbl, {[tbl]=true}, {})
- end
- -- a helper
- -- spearate cb into itemCheck and callback,
- -- so that callback only call when itemCheck return true
- -- function itemCheck(itm, parents, keys) ... end
- function processitems(itemCheck, callback)
- return function(itm, parents, keys)
- if itemCheck(itm, parents, keys)then
- return callback( itm, parents, keys )
- end
- end
- end
- -- test
- local seenCheckItem = {
- orange = '2',
- water = '200ml',
- sugar = '20g'
- }
- -- ref: https://forum.cheatengine.org/viewtopic.php?t=618791
- local table_ = {
- taste = 'like freshly juiced delicious oranges',
- name = 'orange juice',
- recipe = {
- {ingredients1 = seenCheckItem},
- {ingredients2 = seenCheckItem},
- {ingredients3 = {water='this should cause error'}},
- {ingredients4 = seenCheckItem},
- 'blablabla',
- 'blablabla',
- 'Lastly, blablabla!'
- },
- }
- table_[1] = {cyclic_check = table_}
- local function dmp(itm, parents, keys)
- local ps = {}
- for i=1,#parents do ps[i]=tostring(parents[i])end
- --print('PARENTS',table.concat(ps,'->'))
- print(string.format('%s %s : %s',string.rep(' > ', #parents), table.concat(keys,'.'), itm) )
- end
- local sumwater = 0
- print('CHECK',titer(table_,
- processitems(
- function(itm, ps, ks) dmp(itm, ps, ks) return ks[#ks]=='water'end,
- function(itm, ps, ks)
- print'-- adding water'
- if not itm:match'%d+'then return error'oops'end
- sumwater = sumwater + tonumber(itm:match'%d+')
- end)
- ))
- print(sumwater)
- --[[ output:
- > 1 : table: 0077ec18
- > recipe : table: 0077e740
- > > recipe.1 : table: 0077e8d0
- > > > recipe.1.ingredients1 : table: 0077e7e0
- > > > > recipe.1.ingredients1.water : 200ml
- -- adding water
- > > > > recipe.1.ingredients1.orange : 2
- > > > > recipe.1.ingredients1.sugar : 20g
- > > recipe.2 : table: 0077e880
- > > > recipe.2.ingredients2 : table: 0077e7e0
- > > > > recipe.2.ingredients2.water : 200ml
- -- adding water
- > > > > recipe.2.ingredients2.orange : 2
- > > > > recipe.2.ingredients2.sugar : 20g
- > > recipe.3 : table: 0077e8f8
- > > > recipe.3.ingredients3 : table: 0077e948
- > > > > recipe.3.ingredients3.water : this should cause error
- -- adding water
- CHECK nil C:\Users\<--snip-->\titer.lua:149: oops
- 400
- --]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement