Case study · Compilers

Alpha Compiler & VM

A language taken the whole way: source text in one end, running program out the other, on a virtual machine I also had to build.

The Alpha compiler and virtual machine project
Six phases, one binary, and a stack machine to execute what comes out.
100/100 Course grade
6 Compiler phases
C With Flex and Bison

What this is

A complete implementation of the Alpha language: a compiler that turns source into instructions, and a stack-based virtual machine that executes them. Written in C, from the tokeniser to the interpreter loop.

Most developers use a compiler every day and have never opened one. This is the project where the box stops being black — where a scoping rule stops being something the language does and becomes a table you maintain, and a function call stops being syntax and becomes a stack frame you lay out yourself.

The phases

  • Lexical analysis — source text into tokens, with Flex.
  • Syntax analysis — a grammar for Alpha, parsed with Bison, producing the structure everything downstream depends on.
  • Semantic analysis — symbol tables, scoping, and the rules that make a syntactically valid program still wrong.
  • Intermediate code generation — a representation that is no longer the source and not yet the target.
  • Target code generation — instructions for the machine that will actually run this.
  • The virtual machine — a stack-based executor, with its own memory model, call convention and instruction dispatch.

The parts that are genuinely hard

Scoping is a data structure, not a rule

"A variable is visible inside the block that declares it" is one sentence in a language spec and an entire subsystem in a compiler. Symbol tables have to be scoped, nested, entered and left in step with the parse, and the lookup has to answer correctly for a name that exists three times at different depths.

Function calls are a contract between two phases

The code generator decides how arguments are passed and where the return value lives; the virtual machine has to agree exactly. Nothing enforces that agreement, so when a call goes wrong the symptom appears in the VM and the cause is in the generator.

Intermediate code earns its keep late

Going straight from parse tree to target instructions looks shorter until the target changes, or until an error has to be reported in terms of the source. The intermediate representation is the layer that keeps the front end and the back end from becoming one inseparable thing.

What was hard

Debugging across phases. A crash in the virtual machine is rarely a bug in the virtual machine — it is a bug in what the code generator emitted, which is itself a consequence of what semantic analysis recorded. Finding it means walking the pipeline backwards from a wrong number in a stack frame to a wrong entry in a symbol table.

The other lesson was that a compiler is judged as much by its error messages as by its output. Accepting a correct program is the easy half; rejecting a wrong one at the right place, with something a person can act on, is the other.

The result

Graded 100/100. The full C source, grammar and virtual machine are on GitHub.

If you want to know whether I can hold a large system in my head, this is the one to read. It is the largest thing I have written where every line has to be exactly right for anything at all to work.

Have a project in mind?

I take on client websites, web apps, and product builds across Cyprus and Greece.

Start a conversation