Oh man, my brain is so frazzled. Writing a compiler from scratch whilst going through years of sleep deprivation must surely rank high in the list of 'things not to do to yourself'.
I get the feeling that I'm writing this compiler/assembler the wrong way around. A professional would begin with the linker Intermediate Representation (LISP like S-expressions), adding one thing at a time and unit-testing as s/he goes and then build the higher level language using the AST of the well-defined LISPy IR. (A final LISP IR is how WebAssembly is being compiled)
I'm feeling wary of implementing an AST for an IR I don't yet know. At the same time, I know that I need to execute link-time expressions and function calls; but then I want to avoid implementing the linker in the LISP IR (the actual linking should be meta-data driven, not programmatically driven) .
A simple Reverse Polish Notation, FORTH-like will provide the ability to calculate expressions very easily, but this concept falls apart as soon as we allow for OZ80 user functions. What if an expression calls a function that loads an image from disk and manipulates it? FORTH will not handle that as easily as LISP.
Ergo, does my AST need to be a direct in-memory representation of a LISP, or should it be something in-between OZ80 and LISP? This is mind-numbingly broad and confusing. Why I struggle is that I'm not sure to what extent an Object file (for linking) is required to be an actual LISP program, vs. a data structure.