A small Pascal-like language that compiles to MIPS-32 assembly — pairs with mips-emulator so you can compile your code and then run it.
A full compiler front-to-back: lexer → parser → AST → codegen → MIPS assembly. Also ships an interpreter so you can run programs without a MIPS simulator.
Most "build your own compiler" projects either stop at parsing or hand-wave the backend. tiny actually emits assembly that runs in SPIM / MARS. The whole pipeline lives in ~700 lines of straightforward Python.
Pascal-style syntax, integers only, with the usual control flow:
program factorial;
var n, result;
begin
read(n);
result := 1;
while n > 1 do
begin
result := result * n;
n := n - 1
end;
print(result)
end.Supported: variable declarations, := assignment, if/then/else, while/do, read(x) / print(expr), full arithmetic (+ - * /), relational ops (= <> < <= > >=), parenthesized expressions, unary minus, and { pascal-style comments }.
git clone https://github.com/forgehk/tiny-compiler.git
cd tiny-compiler
pip install -e ".[dev]"Compile to MIPS assembly:
tinycc compile examples/factorial.tiny -o factorial.sOpen factorial.s in MARS or SPIM, hit run, and enter an integer when prompted.
Interpret directly (no MIPS simulator needed):
echo 5 | tinycc run examples/factorial.tiny
# 120The pipeline is exactly the four phases every compiler textbook describes:
| Stage | File | What it does |
|---|---|---|
| Lexer | tinycc/lexer.py |
Hand-written scanner — keywords, multi-char ops (:=, <=, <>), comments, position tracking |
| Parser | tinycc/parser.py |
Recursive-descent, builds typed AST nodes |
| AST | tinycc/ast_nodes.py |
Dataclass nodes — Program, If, While, BinOp, etc. |
| Codegen | tinycc/codegen.py |
Emits MIPS-32 — stack-allocated temporaries, label generation, syscall conventions |
| Interpreter | tinycc/interp.py |
Reference implementation against the same AST |
Expression evaluation uses the classic stack discipline: evaluate left into $t0, push, evaluate right, pop into $t1, do the op. The data segment holds variables as .words. Syscalls 1/5/10/11 handle print_int / read_int / exit / newline.
pytest -q
# 25 passedThe suite covers the lexer (keywords, multi-char ops, comments, errors), the parser (precedence, control flow, error reporting), the codegen (data section, syscalls, labels, undeclared-variable detection), and the interpreter (factorial, GCD, division-by-zero, stdin handling).
examples/factorial.tiny— iterative factorialexamples/fizzbuzz.tiny— fizzbuzz via integer division (no modulo operator in the language, so(i/n)*n = isubstitutes)examples/gcd.tiny— Euclidean GCD
This pairs with mips-emulator — same author, same MIPS-32 dialect. Compile here, run there.
MIT — see LICENSE.