This blog started 13 years ago musing about a language. I'm back to the language.
The goal is a language to use for the user mode code of a concurrent communications based operating system. I need it to catchiest mistakes at compile time, be agile enough to try language experiments, and have reasonable performance which is maybe a small predictable integer off of top notch C.
The State of the Language
- I chose a name! Yes, this usually happens late for me, but it is
atuin. After the turtle.
- The compiler written in Swift, which is by far the most productive language I've used for writing a compiler. It emits LLVM IR (in text), which clang then compiles into an executable. I've gone to some length to keep my LLVM IR legible, it really helps diagnosing problems.
- It looks a lot like Swift, but without the hard parts. Call it a 50% of a 90% Swift, without the other 90%.
- The parser is built to be extended at runtime. Building new features into a language by forcing them into existing syntax seems to just make a mess of your source code. If I want to have something like async/await, I'll extend it into the language instead of making a dance of calls and wrapper types. Source code should be clean.
- I'm skipping a lot of the complications for now. There is no FFI (I don't need one) and no separate compilation (I don't need to). The parser is slow, it is a combinator based recursive descent which is supposed to be a Packrat Parser, but until I notice it taking up time the rats can wait. I build an AST which directly matches the parse, then walk it and create a whole new AST to generate the code (with a slightly different shape). Coming from single pass compilers with no AST back during the day, this seems colossally wasteful, but after buying big enough hardware to build Swift over and over during my porting attempt, I have cpu cycles.
- Using the parser is gorgeous! Swift operators and Unicode characters in the source file make it a bit like reading BNF. After each rule there is a binding from its products to variables which are used to build an AST node as a product. Very clean.
- integers, characters, strings, bools, arrays
- structs, classes, enums, functions, methods, closures
- all the usual flow of control, including
defer(there went a week of re-engineering)
- next up? reference counting (class instances are currently immortal).
- and then start writing some real programs instead of test programs and see what's needed.