Advertisement
panraven

titer

Dec 27th, 2021 (edited)
597
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.94 KB | None | 0 0
  1. --
  2. --[[
  3.   table iterator
  4.  
  5.   updated:
  6.  
  7.   1. now items is iterated from array part first, and they are in order, then the hash park
  8.   2. leafCheck is removed, leaf item is tell from cb (return false)
  9.   3. cyclic check is done via iter function's parents parameter, it means allow process seen table as leaf item, see example
  10.  
  11.   note on the callback cb:
  12.   -- function cb(itm, parents, keys) ... end
  13.   1. cb can break the iter by return true, ... , and then titer return ... as result
  14.   2. if cb cause error, titer stop and return nil, < cb's error msg if any >
  15.   3. if cb return false then it is a leafitem, iter will not go down into it
  16.   4. any other returns means cb not following this protocal
  17.   5. cb should not modify parents and keys
  18.   6. cb happend before iterating into itm (if it is a table) so it can be genreate its member on the fly,
  19.      but it also may cause infinite loop
  20.  
  21.   -- error handle:
  22.     cb run on pcall, so titer has the reponsiblity to handle the error, eg.
  23.     <code>
  24.       local state, result = titer(...)
  25.       if state == nil and result~=nil then <error-handle> end
  26.     </code>
  27.      
  28. --]]
  29. local function array1stPairs(tbl, ignorepack)
  30.   local yield = coroutine.yield
  31.   return coroutine.wrap(function()
  32.     local ispack = not ignorepack and type(tbl.n)=='number' and math.floor(tbl.n)==tbl.n and tbl.n>=0
  33.     local arraysize = ispack and tbl.n or #tbl
  34.     local seen = { n = ispack }
  35.     for i=1,arraysize do seen[i] = true, yield(i, tbl[i]) end    
  36.     for k,v in pairs(tbl) do if not seen[k] then yield(k,v)end end
  37.   end)
  38. end
  39.  
  40. local function unpack_aux(t, from, to, ...)
  41.   if from > to or to < 1 then return ... else return unpack_aux(t,from, to-1, t[to],...)end
  42. end  
  43.  
  44. local insert, remove, pcall, type, pairs = table.insert, table.remove, pcall, type, pairs
  45. local pk  = table.pack   or pack   or function(...)return { ..., n=select('#',...) }end
  46. local upk = table.unpack or unpack or function(t, from, to)
  47.   from, to = from or 1, to or t.n or #t
  48.   return unpack_aux(t, from, to)  
  49. end
  50.  
  51. function titer(tbl, cb)
  52.  
  53.   if type(tbl)~='table'then return nil,'not a table'end
  54.  
  55.   local function iter(itm, parents, keys)
  56.     local docb, rets, isleaf = itm ~= tbl
  57.     if docb then
  58.       rets = pk( pcall(cb, itm, parents, keys ) )
  59.       if not rets[1] then -- cb cause error
  60.         return true, nil, upk(rets,2)
  61.       elseif rets[2]==nil and #rets>2 then -- error message
  62.         return true, nil, upk(rets,3)
  63.       elseif rets[2]==true then -- iter break
  64.         return true, upk(rets,3)
  65.       elseif rets[2]==false then -- leaf item or cyclic
  66.         isleaf = true
  67.       end      
  68.     end    
  69.    
  70.     if type(itm)=='table' and not isleaf  then
  71.    
  72.       insert(parents, itm)
  73.       parents[itm]=true
  74.       for k,v in array1stPairs(itm) do
  75.         if not parents[v] then
  76.           insert(keys, k)
  77.           rets = pk( pcall(iter, v, parents, keys) )
  78.           remove(keys)
  79.          
  80.           -- check break and error
  81.           if not rets[1] then -- error caused
  82.             return nil, upk(rets, 2)
  83.           elseif rets[2]==nil and #rets>2 then -- error message
  84.             return nil, upk(rets,3)
  85.           elseif rets[2]==true then -- is break or cb error
  86.             --print'return upk(rets, 3)'
  87.             return upk(rets, 3)        
  88.           --elseif #rets>1 or rets[2]~=false then -- cb return something not follow protocal, print warning
  89.           --print('in a titer call, cb not follow protocal')
  90.           end  
  91.         end
  92.       end      
  93.       remove(parents)
  94.       parents[itm]=nil
  95.     end  
  96.   end
  97.   return iter(tbl, {[tbl]=true}, {})
  98. end
  99.  
  100. -- a helper
  101. -- spearate cb into itemCheck and callback,
  102. -- so that callback only call when itemCheck return true
  103. -- function itemCheck(itm, parents, keys) ... end
  104. function processitems(itemCheck, callback)
  105.   return function(itm, parents, keys)
  106.     if itemCheck(itm, parents, keys)then
  107.       return callback( itm, parents, keys )
  108.     end    
  109.   end  
  110. end
  111.  
  112. -- test
  113. local seenCheckItem =  {
  114.             orange = '2',
  115.             water = '200ml',
  116.             sugar = '20g'
  117.         }
  118.  
  119. -- ref: https://forum.cheatengine.org/viewtopic.php?t=618791
  120. local table_ = {
  121.     taste = 'like freshly juiced delicious oranges',
  122.     name = 'orange juice',
  123.     recipe = {
  124.         {ingredients1 = seenCheckItem},
  125.         {ingredients2 = seenCheckItem},
  126.         {ingredients3 = {water='this should cause error'}},
  127.         {ingredients4 = seenCheckItem},
  128.         'blablabla',
  129.         'blablabla',
  130.         'Lastly, blablabla!'
  131.     },
  132. }
  133.  
  134. table_[1] = {cyclic_check = table_}
  135.  
  136. local function dmp(itm, parents, keys)
  137.   local ps = {}
  138.   for i=1,#parents do ps[i]=tostring(parents[i])end
  139.   --print('PARENTS',table.concat(ps,'->'))
  140.   print(string.format('%s %s : %s',string.rep(' > ', #parents), table.concat(keys,'.'), itm) )
  141. end
  142.  
  143. local sumwater = 0
  144. print('CHECK',titer(table_,
  145.   processitems(
  146.     function(itm, ps, ks) dmp(itm, ps, ks) return ks[#ks]=='water'end,
  147.     function(itm, ps, ks)
  148.       print'-- adding water'
  149.       if not itm:match'%d+'then return error'oops'end
  150.       sumwater = sumwater + tonumber(itm:match'%d+')
  151.   end)
  152. ))
  153. print(sumwater)
  154.  
  155. --[[ output:
  156.  
  157.  >  1 : table: 0077ec18
  158.  >  recipe : table: 0077e740
  159.  >  >  recipe.1 : table: 0077e8d0
  160.  >  >  >  recipe.1.ingredients1 : table: 0077e7e0
  161.  >  >  >  >  recipe.1.ingredients1.water : 200ml
  162. -- adding water
  163.  >  >  >  >  recipe.1.ingredients1.orange : 2
  164.  >  >  >  >  recipe.1.ingredients1.sugar : 20g
  165.  >  >  recipe.2 : table: 0077e880
  166.  >  >  >  recipe.2.ingredients2 : table: 0077e7e0
  167.  >  >  >  >  recipe.2.ingredients2.water : 200ml
  168. -- adding water
  169.  >  >  >  >  recipe.2.ingredients2.orange : 2
  170.  >  >  >  >  recipe.2.ingredients2.sugar : 20g
  171.  >  >  recipe.3 : table: 0077e8f8
  172.  >  >  >  recipe.3.ingredients3 : table: 0077e948
  173.  >  >  >  >  recipe.3.ingredients3.water : this should cause error
  174. -- adding water
  175. CHECK   nil C:\Users\<--snip-->\titer.lua:149: oops
  176. 400
  177. --]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement