

Debugging layers - ltratt
http://tratt.net/laurie/blog/entries/debugging_layers

======
kazinator
> _As the compiler creates an AST from the parse tree, it copies src infos
> over (sometimes from tokens, sometimes from non-terminals)._

In the TXR language, the same thing is done by the function rlcp (and a few
relatives). I don't remember what "rl" stands for: probably read-time
location.

You can see rlcp and rl calls throughout this parser:

[http://www.kylheku.com/cgit/txr/tree/parser.y](http://www.kylheku.com/cgit/txr/tree/parser.y)

In the evaluation module,
[http://www.kylheku.com/cgit/txr/tree/eval.c](http://www.kylheku.com/cgit/txr/tree/eval.c)
you can see this code:

    
    
        } else if ((macro = lookup_mac(menv, sym))) {
          val mac_expand = expand_macro(form, macro, menv);
          if (mac_expand == form)
            return form;
          form = rlcp_tree(mac_expand, form);
          goto tail;
    

User-defined macros aren't expected to do rlcp diligently; so we compensate
for their negligence with rlcp_tree(mac_expand, form). This basically
liberally sprinkles the source location of the original form over the nodes of
the macro-expansion. It works pretty well in practice, helping to relate
errors in macro expansions to the original source. rlcp_tree respects nodes
that already have source info; it gives it to those which don't have it.

Source info is associated with tree nodes using a weak global hash table. When
an object with source info attached goes away, the info goes away.

Trivial test:

macro-error-test.tl:

    
    
       1: (defmacro mac (expr)
       2:   expr)
       3: (mac (/ 1 0))
    
       $ txr macro-error-test.tl 
       txr: unhandled exception of type numeric-error:
       txr: possibly triggered at macro-error-test.tl:3 by form (/ 1 0)
       txr: message: /: division by zero
    

If we change the example to this:

    
    
       1: (defmacro mac (expr)
       2:   (copy expr))
       3: (mac (/ 1 0))
    

the error is exactly the same. This time the expr coming out of the macro is a
copy and so it has no source info. The macro expander adds it. This is less
accurate: the line number of the macro call isn't necessarily that of the (/ 1
0). Here it happens to be because it's a one-liner expression.

Example of an error during macro expansion:

    
    
       1: (defmacro mac (expr)
       2:   (/ 1 0))
       3: (mac foo)
    
       $ txr macro-error-test-2.tl 
       txr: unhandled exception of type numeric-error:
       txr: possibly triggered at macro-error-test-2.tl:2 by form (/ 1 0)
       txr: during expansion at macro-error-test-2.tl:3 of form (mac (/ 1 0))
       txr: message: /: division by zero

