Testing, debugging, and error reporting
There is a handy macro for debugging a metaprogram. It is called ML99_abort
:
ML99_abort
evaluates a provided sequence of expressions and immediately aborts the interpretation. Despite that we call F
, the result of the interpretation is abc
. Here is how Metalang99 evaluates the aforementioned expression:
v(1)
=>1
v(2)
=>2
ML99_abort(v(abc))
=>abc
, then halt.
ML99_abort
is very handy when you need to figure out what is wrong with your metaprogram. I often use the bottom-up approach: first, I ensure that all lower-level macros work as expected, then move on to more general macros, and so on, till I find the problem. To test a specific macro, I call it inside ML99_EVAL(...)
separately from the rest of a metaprogram. With ML99_abort
, I usually trace macro parameters as well as any other interesting terms. You can see the result of interpretation with -E
(GCC/Clang flag; preprocess only).
Secondly, errors need to be somehow emitted. For example, if we try to pop the head of the empty list, we obtain the following compilation error:
To emit such an error, call ML99_fatal
like this:
[playground.c
]
[/bin/sh
]
Metalang99 also features the macros ML99_todo
and ML99_unimplemented
as well as their *WithMsg
versions. Using them, you can indicate a not yet implemented and unimplemented functionality, respectively. The difference is that ML99_todo
/ML99_todoWithMsg
convey an intent that some piece of code is to be implemented later, while ML99_unimplemented
/ML99_unimplementedWithMsg
do not make such claims. Consider this code:
Here, FOO(1)
works just fine but we are not yet to handle multiple arguments. No problem, just insert ML99_todoWithMsg
and implement it later.
And eventually, if you want to test your macro on certain input values, you can use assertions:
Learn more about assertions here.
Last updated