PINS Compiler Course
8 progressive compiler assignments from lexer to interpreter for 100+ university students
Overview
A complete set of 8 progressive compiler assignments (DN1-DN8) designed and maintained for the Compilers and Virtual Machines (PINS) course at the University of Ljubljana. Each assignment builds incrementally on the previous one, guiding students from lexical analysis through to a working interpreter -- growing from 12 Java source files to 73 files. The course framework includes a clean visitor-based architecture, external semantic maps (NodeDescription<T>), progressive pretty-printers, and an annotation-driven CLI.
Architecture
The architecture uses the visitor pattern for phase separation -- an 18-overload Visitor interface where each compiler phase is a separate implementation. External semantic maps via NodeDescription<T> (a generic HashMap<Ast, T> wrapper) attach semantic information to AST nodes without mutating them. Phase-gated execution chains phases sequentially with early termination, letting students test each assignment in isolation.
Code Highlights
public class NodeDescription<T> {
private Map<Ast, T> storage = new HashMap<>();
public Optional<T> valueFor(Ast node) {
return Optional.ofNullable(storage.get(node));
}
public boolean store(T value, Ast forNode) {
return storage.put(forNode, value) == null;
}
}Highlights
- Designed a complete 8-assignment compiler course from lexical analysis to a working interpreter (12 to 73 source files incrementally)
- NodeDescription<T> as central teaching abstraction: semantic analysis results as maps from AST nodes to values, enforcing phase separation
- Progressive pretty-printers (V1-V4) where each version accumulates more semantic data, visualizing compiler understanding growth
- Course framework directly derived from own BSc/MSc compilers, compressed into an accessible educational format
Related Projects
Blang (Bitis)
Lazy functional language with compile-time ownership and borrowing instead of garbage collection
Novel PL contribution: first lazy functional language using ownership/borrowing instead of GC, introducing self-borrows for cyclic data structures (graphs, infinite streams)
Atheris
Complete compiler for a Swift-like OOP language with vtables and polymorphic dispatch
Designed and implemented a complete compiler from scratch for a Swift-like OOP language (~120 Java source files, ~8,000+ lines)
SML-to-Racket
Source-to-source compiler with lambda calculus IR and Church-encoded algebraic datatypes
Dual-target compiler with direct Racket emission and a lambda calculus IR featuring Church-encoded booleans, tuples, and algebraic datatypes