Well first of all, macro parsing in lisp usually happens in the parser itself, I.E. when it reaches a set of expressions it is executing as it encounters them, in a typed version I’m unsure how that will go.
Find instances of defmacro .
Find invocations of each of those macros.
If executed as encountered then a defmacro I’d imagine would just become like a function like normal (Elixir does defmacro as compiling to a function with a MACRO- prefix and another argument).
Convert the output of the parser into an Erie abstract syntax tree.
I’m torn on this, by keeping it a typed tree as much as possible you can know more inside the macro about what is going on, but by keeping it untyped you can be much more free-flowing with the code, with potentially much worse error messages, hmm…
Support “unquoting” somehow.
In Lisps’s unquot’ing is usually just the unquote call, so (quote (a b (unquote (quote c)) d) is handling by the quote call itself as just an ‘escaped’ name. Technically quote is a macro (or rather, a special form) that just takes its single argument as AST, transforms nested unquote's in it to inline sets, then just returns it. A lisp read-macro watches for prefix ’ and ` and just replace it and the following single expression with a (quote ...) and (unquote ...) wrapping respectively.
Replace the macros in the AST with their results.
Yep, just calling the macro function at compile-time. Elixir did itself hard by allowing calling macro’s defined in the same module. ^.^;
It became apparent pretty quickly on the implementation that this would be the right move. Or at the very least, using the same code. I would like to allow macros defined anywhere to be executed anywhere, so I think I’ll need to essentially parse the code twice. First to find the macros, and second to execute them in place.
Back at it with a basic type checker. Very happy with how little code it’s taken to write this much of a type checker. Still need to support user-defined types which is going to require me to figure out how to properly implement real polymorphic types.
Type checker now supports algebraic data types. It lead to a lot of decisions that I’m sure I’ll reverse later, but was definitely the most challenging aspect of the compiler so far. Why didn’t anyone tell me building a compiler is so fun?
New post about the type expansion the compiler does when trying to confirm that the inferred type matches the type signature. Everything seems to be working so far, except for recursively-defined types.