RakuAST Grant Report

2025-04-10 12:18:00

The RakuAST project was a rewrite and redesign of the compiler frontend, i.e. the part that parses the original source code, figures out what the different parts are, does checks and optimizations and creates a low level representation which will then be turned into bytecode by the backend.

 

When I applied for the grant a lot of basic infrastructure was already in place. Many simple programs would already run, e.g. you could define variables, classes, functions, create objects, call methods and a lot more.

 

However Raku is a large language. E.g. there's not just methods. There are also private methods, meta methods, methods with fully qualified names (i.e. when you want a method of a specific base class), method calls where the name is determined at compile time (e.g. $foo."$bar"()), hypered method calls and even more obscure ways of calling code. These were all still left to do. The same was true for all other areas of the compiler.

 

My method for attacking this was simple: one spec test at a time. I went through the list of failing spec test files in lexicographical order. The spec tests are grouped by synopses which themselves are numbered vaguely in order of how fundamental they are. E.g. at the beginning there are tests for names and handling of types whereas latter spectests deal with specifics of the standard library. This order helped keeping my focus on a specific area. At least up to a point. As the tests are themselves Raku code, even tests for something very fundamental may use advanced syntax features simply because they are a convenient way to express the test. Thus I often first had to support those before I could tackle the more basic features.

 

If I had to name the single hardest part of the project, it's certainly timing or sequencing. Raku is not an easy language to compile. A lot of code is already run during compilation. E.g. in class Foo does Bar { }, we call the Bar role's body which is really just a sub in disguise and we do so during compilation. There are also more obvious cases like BEGIN blocks or statements, constant declarations and trait applications (which again, are really just compile time called subs with funny names).

 

This code that is run during compilation may reference variables from scopes that may not even exist yet (because we're still compiling it) or subroutines that haven't been defined. Traits especially modify meta objects (e.g. the class, function or parameter itself) which are not even fully defined at that point.

 

All of this means that it is of utmost importance that declarations, definitions, type setup and code generation for different parts of the program are done in exactly the right order. This order however is neither specified nor documented. It is purely emergent from the old compiler frontend's implementation as it was done over the course of 20 years of development. I had to discover and re-create this order one spectest at a time.

 

When I wrote the grant application I already had done a lot of this work. My estimations of 200 commits remaining were based on past experience with large refactors in Rakudo where once the basics were in place, each additional fix had a good chance of fixing multiple spec tests. This hope however did not materialize in the RakuAST project. I did not anticipate the very long tail of special cases that needed handling. This includes hundreds of compile time checks for different error cases to provide informational and helpful messages. Thus the opposite of what I had in mind happened: fixes unlocking multiple spec test files became more and more rare. In the end the total number of commits I made during the work on this grant was more than 900.

 

The second goal of the project was thankfully a lot less work though it still wasn't easy. The Rakudo compiler is bootstrapped, i.e. it is itself written in Raku. For the spec test fixing work I still used the old compiler frontend to compile the compiler itself. Fully replacing the old frontend meant that RakuAST must be able to do this without help. This poses interesting problems. A driving goal of RakuAST was to enable proper AST-based macro support. This exposes a lot of compiler internals to the user. Thus these internals should use the Raku types the user expects instead of some low level representations. When we are compiling the compiler and its standard library itself, these types may not even be defined yet or they may not have the methods that would do what we need.

 

To get around these circular dependencies I needed to add workarounds like accessing an objects internal attributes directly instead of going through the proper accessor methods. A lot of this was simply to get error reporting up to a point where I could even find out what it was that the compiler stumbled over.

 

The standard library itself is a body of roughly 100 thousand lines of code written by people who are really into Raku, meaning they did know and not shy away from using advanced features. This revealed quite a few deficiencies that the spec tests did not cover.

 

Despite numbering in the hundreds of thousands, the spec tests were also not even just incomplete. Sometimes they were nonsensical or just plain wrong. They contained obvious bugs that only didn't surface before because bugs in the old compiler frontend meant that the code sort of worked anyway. They also often depended on the compiler aborting with a specific error before encountering other errors. The RakuAST frontend however tries to avoid bailing out and instead aims at reporting all errors it was able to find. Thus a sizable number of spec tests needed adjustments or clarification.

 

Reading my own report I understand why the influx of community contributions to this project were a lot lower than I had hoped for. It's easy to feel intimidated by the size of the code base and the complexity of the problems. Nevertheless help came from Elizabeth Mattijsen (who took care of all of RakuDoc and other things), John Haltiwanger, Vadim Belman, Jimmy Zhuo and Daniel Green. Without them I'd still be despairing in the thick of things.

Managed by CiderCMS