Ruby, 143142 138 bytes
->i{c,*e=i.split ?\n;puts" "*(m=4)+c;c.scan(/[^<>+-.,\[\]]+|./){|b|r=" ".*c.size+5;r[m,z=b.size]=b;puts r+e["<>+-.,[]".index(b)||8];m+=z}}
The function expects input to be lines separated by \n
. It scans the program using regexp /[^<>+\.,\[\]-]+|./
. It outputs to stdout instead of returning. Which is all there is to it.
Thanks to @Fmbalbuena for removing a space!
Saved 4 more bytes by re-reading ruby golfing tips :)