This is some code I wrote to nicely stringify tables. This code started life as a printh()
replacement that output directly to the console, but I rewrote it as seen below so it can be used in other contexts.
The strngfy()
function can produce either indented, multi-line code, or single-line compact output. Both scalar values and tables are supported as both keys and values.
The rptsp()
and tcnt()
functions are support functions used by strngfy()
, but they also have utility as stand-alone functions.
The code is probably not particularly elegant, but it works for the scenarios I tested. There are some comments in the code to help users get started. All lines fit within the line-width of Pico-8's built-in editor.
The code is 238 tokens.
I'm new to both Pico-8 and Lua, so if I did something the hard way, or missed some tricks, or if you find bugs, let me know.
Updates
- 04/25/2023
- Updated function to format function types in table
- 04/30/2023
- Same thing for booleans. Code is now 246 tokens.
- Shaved a few tokens by combining assignments. Code is now 238 tokens.
- 05/02/2023
- Turns out nils exist. Code no longer chokes if you pass it a nil type.
The code:
-- t=table -- cm=compact output. if true, -- no newlines or indentation -- are added. -- ind=indent size -- l=indent level -- lst=last item in table? -- in practice, you only need -- to provide the first -- argument. the others are for -- tweaking output spacing -- (ind and l) and, in the case -- of lst, for internal use by -- the function . -- does not currently handle -- recursive tables, so be -- careful not to create -- infinite loops in any tables -- you send to this function. function strngfy(t,co,ind,l,lst) -- make sure ind, l, lst, and -- co have sane values. prep -- some other variables for -- use. we're shadowing ind, -- but i don't think it should -- matter under any reasonable -- set of assumptions. local ind,l,lst,co,tp,st =max(ind or 2,ind),l and l or 0,lst or false,co or false ,type(t),"" -------- l=co and 0 or l+1 local sp=rptsp(l*ind) if tp=='table' then local cnt=tcnt(t) st..="{"..(co and "" or "\n") -- order is not guaranteed, so -- two executions of this loop -- on the same table might -- produce two different -- orders of the keys in the -- output. -- ipairs() does guarantee -- order, but only iterates -- over consecutive numerical -- indices, and is therefore -- useless if we want to deal -- with sparsely-keyed tables, -- or tables which can have -- other tables for keys. for k,v in next,t do st..=sp.."["..strngfy(k,co, ind,l,cnt>1).."] => ".. strngfy(v,co,ind,l,cnt>1) ..(cnt>1 and ","..(co and "" or "\n") or "") cnt-=1 end l-=1 local sp=rptsp(l*ind) st..=(co and "" or "\n")..sp .."}"..(cnt > 1 and "," or "") else st..=tp=="function" and "<function>" or tp=="boolean" and t==true and "true" or t==false and "false" or tp=="nil" and "nil" or t end return st end -- a special case of rptchr() -- that only produces spaces. function rptsp(c) c=c>0 and c or 0 s="" for i=1,c do s..=" " end return s end -- insanity, but as far as i can -- tell, this is the only way to -- get the number of entries in -- a table that doesn't have -- contiguous numerical keys. function tcnt(t) if type(t) ~= "table" then -- to remove ambiguity -- between this and a table -- with no entries. return -1 end local cnt=0 for _ in pairs(t) do cnt+=1 end return cnt end |
Example usage:
tablekey={thisisakey="asdf",thisisanotherkey="fdsa"} itable={thisis="an internal table", witha=tinternaltable} tablekey[itable]={yetanother="table"} tinternaltable={tintkey="tintvalue"} tablekey[itable][tinternaltable] = "final" table={} table[tablekey]="some value" table.stringkey="stringkey value" omt={onemore="table",withsome="values",includingint=128,float=2.24} omtv={okthis="is the last one", i={pinky="promise"}} table.stringkeyb={ stkb="a value in a table", stkc=32, stkd={}, stke={} } table.stringkeyb.stke[omt]=omtv printh(strngfy(table)) |
Output:
{ [stringkey] => stringkey value, [stringkeyb] => { [stke] => { [{ [includingint] => 128, [float] => 2.24, [onemore] => table, [withsome] => values }] => { [okthis] => is the last one, [i] => { [pinky] => promise } } }, [stkd] => { }, [stkc] => 32, [stkb] => a value in a table }, [{ [thisisanotherkey] => fdsa, [{ [thisis] => an internal table }] => { [{ [tintkey] => tintvalue }] => final, [yetanother] => table }, [thisisakey] => asdf }] => some value } |
This code is released under the "Do whatever you want with it, I don't care" license, although I'd like to hear about it if you find it useful.