Log In  
[ :: Read More :: ]

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.


  • 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
 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
    ind,l,cnt>1).."] => "..
    ..(cnt>1 and ","..(co and ""
    or "\n") or "")
  local sp=rptsp(l*ind)
  st..=(co and "" or "\n")..sp
   .."}"..(cnt > 1 and ","
   or "")
  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

 return st

-- a special case of rptchr()
-- that only produces spaces.
function rptsp(c)
 c=c>0 and c or 0
 for i=1,c do
  s..=" "

 return s

-- 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
  local cnt=0
  for _ in pairs(t) do

  return cnt

Example usage:

itable={thisis="an internal table", witha=tinternaltable}
tablekey[itable][tinternaltable] = "final"
table[tablekey]="some value"
table.stringkey="stringkey value"
omtv={okthis="is the last one", i={pinky="promise"}}
  stkb="a value in a table",



  [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.

P#128977 2023-04-25 00:06 ( Edited 2023-05-04 21:14)