Fern
Fern is a multi-stage optimizing compiler for a statically-typed imperative programming language.
This project exists primarily as a learning exercise in compiler design and implementation.
Compilation Pipeline
The compiler follows a modern compilation pipeline with several optimization stages:
- Source code is parsed into an Abstract Syntax Tree (AST)
- A symbol table is generated to track variable and function scopes
- The AST is converted to Three-Address Code (TAC) for optimization
- Dead code elimination removes unreachable code paths
- A Control Flow Graph is built for analysis
- The program is converted to Static Single Assignment (SSA) form
- LLVM Intermediate Representation is generated
- Finally, LLVM compiles the IR to a native executable
Language Features
The compiler supports core imperative programming constructs including static typing with int
, bool
, and void
types, function definitions with typed parameters, arithmetic and logical operations, control flow statements, and recursion.
Language Syntax
Fern uses a Lua-inspired syntax with static typing. Here's a quick overview:
Function definitions use the def
keyword, followed by the function name and typed parameters. The return type comes after the parameter list:
def add(x: int, y: int): int
return x + y
end
Variables must be declared with the var
keyword and require explicit type annotations. They can be initialized at declaration or will be zero-initialized:
var x: int = 42 // initialized to 42
var y: int // zero-initialized
var z: bool
Control flow uses if-then-end
blocks with optional else
clauses:
if x > y then
return x
else
return y
end
Example: Fibonacci Sequence
def fib(n: int): int
if n <= 1 then
return n
end
return fib(n - 1) + fib(n - 2)
end
def main(): int
return fib(10)
end
Source
The source code for Fern is available on GitHub.
License
Fern uses the Zero-Clause BSD license.