It might be interesting to compare how the error locations are reported, particularly when macros are involved. A typical AST can generate a file, line, and character offset for each node (at least), for printing errors. But if you don't have line numbers, there must be some alternative?
You have the compiler use a special reader which records the source locations of each form in e.g. a hash table. When compiling a specific source, look it up in the hash table to get a source location mapping. There's a couple of downsides to this:
- This only works when the reader returns unique objects. It's possible to track the location of a cons cell, but it's not possible to do it for any specific use of a symbol or a integer.
- CL macros can do arbitrary transformations on the data. If those transformations involve loss of object identity, the tracking breaks down. (E.g. code-walking macros will often end up doing spurious copies, and cause unnecessary loss of source location mappings).
Racket has "syntax objects" to handle this [1].
[1] https://docs.racket-lang.org/guide/stx-obj.html