A tool to help with print debugging based on the Python package IceCream. There's already a Lua version of IceCream but it won't work with Pico-8/Picotron as far as I know. I've called it db for debug rather than ic but it's very similar in concept.
You can download the code here or copy/paste below:
db.lua
Summary

Displaying variables
The most basic thing you can do with db is just display values and variables. When displaying variables with db it displays both the variable name and its value.
x = 1 db(2) db(x) |
Output
db: 2 db: x=1 |
-
Note: Duplicate values
dbcreates its output by searching the global environment by key/value pairs looking for a matching value and then displaying it with the corresponding key. This mostly works. But if more than one variable has the same valuedbmay log the wrong one. For example:x = 1 y = 1 db(x)
Because the ordering of keys in a table isn't guaranteed this will sometimes (correctly) display
db: x=1but will also sometimes (incorrectly) displaydb: y=1.dbautomatically ignores variable/function names defined in the global environment before thedebug.luacode—which includes all built-ins—to try to minimize these collisions.
Inline debug output
db returns its input so you can debug variables where they're actually used.
x = 1 y = 2 z = db(x) + db(y) db(z) |
Output
db: x=1 db: y=2 db: z=3 |
User defined types
Since db is essentially just a fancy print statement it should work properly with any object as long as it has a __tostring meta-method defined.
do
local vector_meta = {
__tostring=function(_ENV)
return 'vec('..x..','..y..')'
end
}
function vector(x, y)
return setmetatable({x=x, y=y}, vector_meta)
end
end
v = vector(1, 2)
db(v) |
Output
db: v=vec(1,2) |
Function calls
Since functions return values, by default db displays a function call as a bare value:
function plus(a, b) return a + b end db(plus(1, 2)) |
Output
db: 3 |
You can use db.wrap to enable functions to output their name, input parameters and output value, like so:
function plus(a, b) return a + b end db.wrap(plus) db(plus(1, 2)) |
Output
db: plus(1,2) --> 3 |
Selective debug output
The db.display property can be used to turn debug output on and off wherever you want throughout your code. This lets you leave debug statements in place if you're not sure you're done with them yet but only display those parts you're interested in at the moment.
x = 1 y = 2 function plus(a, b) return db(x) + db(y) end function minus(a, b) return db(x) - db(y) end db.wrap(plus) db.wrap(minus) db.display = false p = db(plus(x, y)) db.display = true m = db(minus(x, y)) db(p) db(m) |
Output
db: x=1 db: y=1 db: minus(1,2) --> -1 db: p=3 db: m=-1 |
In the above example the debug output for plus is not displayed. However, db.display doesn't prevent the plus function from being called or returning a value: You can see via db(p) that p has the correct value.
Debugging functions
The Problem
Consider the following example:
x = 1 y = 2 function plus(a, b) local c = 'who am i?' db(c) return db(a) + db(b) end db(plus(x, y)) |
Output
db: who am i? db: x=1 db: y=2 db: 3 |
First notice that the output of the function call is logged simply as db: 3 because the function hasn't been wrapped with db.wrap. But more importantly, db(c) is displaying the string but not the variable name while db(a) and db(b) are displaying as variables x and y respectively. That's becuase a, b and c are all local variables and Lua doesn't give us direct access to those in the same way as globals. Standard Lua has debug tools which can get a hold of those values but we don't have access to those here in Pico-land.
The Solution
If you want/need to debug values within a function use db.local_env and db.reset_env like so:
x = 1
y = 2
function plus(a, b)
--local _ENV = db.local_env() -- can be called with no arguments
local _ENV = db.local_env({a=a, b=b}) -- note the 'local' keyword!
c = 'who am i?' -- note the *lack* of 'local'
db(c)
return db(a) + db(b), db.reset_env()
end
db.wrap(plus)
db(plus(x, y))
db(c)
db(x) |
Output
db: c=who am i? db: a=1 db: b=2 db: plus(1,2) --> 3 db: [nil] db: x=1 |
db.local_env creates a temporary enviroment and tells db to use that environment for building its output. db.reset_env just tells db to go back to using the global environment. Including the call db.reset_env() as a second return value let's us reset the environment "after" returning from the function.
- Note: The
localkeyword
Importantly_ENVmust be defined as local here or you'll overwrite the global environment. Equally importantly, local variables should not be defined with thelocalkeyword or they'll go back to outputting just values without names. These variables are still local though. As you can see in the outputdb(c), when called outside the function, displaysdb: [nil]. When you're done debugging you'll probably want to remove that_ENV: be sure to addlocalwhere necessary or all your function local variables will suddenly be globals and you'll have whole new errors to hunt down!
Providing a table to db.local_env is optional. It's mainly included to ensure that input parameters display correctly: Since input parameters are actually defined before our temporary environment, they don't exist in it unless we specifically put them there. You can still use those values but they won't display the variable name when logged.
function no_param(a) local _ENV = db.local_env() return db(a), db.reset_env() end no_param(1) |
Output
db: 1 |
[Please log in to post a comment]




