This isn’t the release of a new language, but rather me trying to document my journey building one. Future posts will range between tutorial-like and brain-dump.
This is my first attempt at writing a compiler, so I’m nowhere near expert level. The code isn’t available on Github yet, but I plan to put it up soon.
Ah! That’s the textual form! There’s an Erlang term based form that is typically constructed using the cerl module (the documentation for which I had to read in the Erlang OTP source code).
Documentation for the compiler in OTP 22 includes an “internal documentation” section that has the cerl module and some of the other modules related to core erlang - http://erlang.org/doc/apps/compiler/internal_docs.html
If anyone else has implemented macros before and knows why this is a bad approach, I’d love to hear about it and save myself from going the wrong direction.
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.