The Compiler

This section explains how Refingere source becomes a running program: the steps the compiler takes, in order, what each one is given and what it gives the next, how it works, and what it reports when something's wrong. Each step has a page of its own. It's for anyone who wants to know why the compiler says what it says, and for anyone working on it - Working on the Compiler has the practical side: where things are, and how to test a change.

The steps

Every step reads what the ones before it made, and adds something: the files, their tokens, a tree, the links between names and definitions, the types, the program in a simpler form. Nothing is thrown away - the syntax tree is kept as it is, and later steps add their findings beside it rather than changing it - so each step's result can be looked at, and kept, on its own.

The steps up to type checking understand the program; the ones after it change its form. The intermediate language sits between them: compile-time code and macros run from it, and the backends generate code from it, so code behaves the same whether it runs while compiling or compiled.

Two compilers

There are two implementations of these steps in the repository. The TypeScript compiler, in src/, is the one that runs programs today - bun run src/index.ts, the editor's language server, the docs' examples. The bootstrapping compiler, in bootstrapping/, is the same compiler written in Refingere, so the language can eventually compile itself; it's compiled and run by the TypeScript one. Its steps are exactly the ones on these pages, one numbered folder each, and it's what these pages describe. Each page says what the TypeScript compiler does at the same point, where it differs.

StepFlagFolder
Collecting files--files0-collect
Lexing--tokens1-lex
Parsing--ast2-parse
Analysis--analyze3-analyze
Type checking--typecheck4-typecheck
Lowering--ir5-intermediate
Macros--expand-macros, --macro-tokens ... --typecheck-macros6-expand-macros, 7-lex-macros ... 10-typecheck-macros
Compile-time code--comptimecomptime - an engine the macro rounds call
Optimization--optimized-iroptimize - turned off by --no-optimize
Code generation--wasm, --js, --run11-compile - js/ and wasm/, from the intermediate language

Running it

The bootstrapping compiler takes a file or folder - the current folder when none is given - and options saying what to do with it. --check runs every checking step and reports what's wrong; with nothing asked for, it shows its help. Name stages, and it gives their results: printed, one section per file, or written to a folder with --out. Any stages can be asked for together, and the run goes as far as the last of them.

The bootstrapping compiler also comes compiled - by itself, to JavaScript and to WebAssembly - so it runs without this repository, anywhere Bun does: refingere-compiler.tar.gz. It holds both builds, the standard library they compile programs with, and the runtimes the programs they compile run on; its README has the rest.

With --out, each file's results are kept - build/main.rfn.tokens, build/main.rfn.ast, ... - with a hash of the source they came from. A later run with --from=stage starts there, reading the step before it back for every file whose source hasn't changed, and running the steps again for the ones that have. The steps that look at the whole program at once - analysis, type checking - keep a hash of every file, since a change in one file can change another's result. Results are written in a text form of Refingere values (bootstrapping/store), so they can be read by eye as well as read back.

Diagnostics

What a step finds wrong is a diagnostic: an error, a warning or a note, at a span of the file - where it starts and ends, as positions in the source. The line and column are only worked out when it's shown. Each step collects what it finds rather than stopping at the first problem, so a run reports everything a step can see; the steps after it don't run on a program with errors, since what they'd find would mostly be the same problem again.

Diagnostics are grouped by file, each with its message, where it is, and the line with the stretch marked. They're in color when the terminal supports it - --no-color or NO_COLOR turns it off.

Where the builtins come from

The builtin types, their methods, the interfaces, the context values and the builtin macros aren't listed by the compiler itself: docs/src/core.md is the source of truth, the same file the reference pages are built from. The TypeScript compiler reads it when it starts; the bootstrapping one carries it as Refingere data, bootstrapping/4-typecheck/core.rfn, generated from it by bun run bootstrap:core - a test fails when the two differ.

Next: Collecting Files.