MiniC is a small C-like programming language with a full compiler pipeline implemented in Rust: parser → type checker → tree-walking interpreter. The codebase is intentionally compact and well-documented, making it easy to read and extend.
This repository started as the base project for the CIn-UFPE compilers course and was extended by student teams. Each team implemented a language feature end-to-end across all pipeline stages.
int factorial(int n) {
if n <= 1 { return 1; }
return n * factorial(n - 1);
}
void main() {
int result = factorial(10);
print(result);
}| Area | What's included |
|---|---|
| Language | Functions, blocks, if/else, while, arrays, recursion |
| Types | int, float, bool, str, void, arrays (T[]) |
| Pipeline | Parser (nom), static type checker, tree-walking interpreter |
| Stdlib | print, readInt, readFloat, readString, sqrt, pow |
| CLI | --check (parse + type-check) and --run (full execution) |
| Testing | Integration tests, CLI tests (shelltest), property-based tests (proptest) |
| Tooling | Nix flake for reproducible dev environment |
- Rust (2021 edition)
- nom — parser combinators
- proptest — property-based testing
- Nix — reproducible development shell (optional)
Source (.minic)
│
▼
┌──────────┐
│ Parser │ src/parser/ → unchecked AST
└──────────┘
│
▼
┌──────────────┐
│ Type Checker │ src/semantic/ → checked AST
└──────────────┘
│
▼
┌─────────────┐
│ Interpreter │ src/interpreter/ → stdout / runtime errors
└─────────────┘
Shared infrastructure lives in src/ir/ (AST), src/environment/ (symbol table), and src/stdlib/ (built-in functions).
Group 7 extended MiniC with immutable bindings via the const keyword, implemented across the full pipeline.
| Member |
|---|
| @EGMux |
| @Shellyda_Barbosa |
| @rsb6 |
| @Juliana_serafim |
| @oruns |
- New syntax:
const T name = expr; - Local constants inside function bodies →
Statement::ConstDecl - Global constants at program scope → collected in
Program::constants
- Extended the symbol table with mutability (
Mutable/Const) alongside type information - Rejects any assignment to a
constbinding (local or global) - Type-checks and registers global constants before user-defined functions
- Global constants are visible in every function body
- Executes
constdeclarations like regular bindings (enforcement is static) - Global constants are evaluated before any function runs
- Unit tests —
tests/constant_declaration.rs(parser, type checker, interpreter) - Property-based tests —
tests/property/conststatementproperties.rswith configurable complexity viaConstStrategy
const int MAX_SIZE = 100;
const float PI = 3.14159;
int capped(int x) {
if x > MAX_SIZE { return MAX_SIZE; }
return x;
}
void main() {
const int n = 42;
// n = 5; ← type error: cannot assign to constant
print(capped(n));
}$ minic --check program.minic
'program.minic' is well-typed.Requirements: Rust toolchain (or Nix — see Nix Development Environment)
cargo build
cargo test
cargo run -- --run tests/fixtures/full_program.minicRun CLI integration tests (requires a prior cargo build):
shelltest tests/cli/minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret
void main() {
str name = "Alice";
print(name)
}$ cargo run -- --run hello.minic
Alice$ cargo run -- --check hello.minic
'hello.minic' is well-typed.| Program | Error |
|---|---|
int x = "hello" |
expected Int, got Str |
double("hello") where double(int) |
expected Int, got Str |
x + true in arithmetic |
arithmetic operands must be Int or Float |
n = 5 where n is const |
assignment to constant rejected |
| Situation | Code |
|---|---|
| Success | 0 |
| Parse, type, or runtime error | 1 |
| Wrong arguments or missing file | 1 |
| # | Document | Topic |
|---|---|---|
| 1 | Language reference | Syntax, types, operators |
| 2 | Pipeline overview | Source → execution flow |
| 3 | The AST | Program representation |
| 4 | The parser | Grammar and parsing |
| 5 | The type checker | Static analysis |
| 6 | The interpreter | Execution model |
| 7 | The standard library | Built-in functions |
| 8 | Testing | Test organisation |
| 9 | Evolution projects | All team project specs |
src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/
├── constant_declaration.rs # Project 7 — const feature tests
├── property/ # Property-based tests (proptest)
└── cli/ # Shelltest CLI integration tests
docs/ # Full documentation
This project ships a Nix flake that pins Rust, Clippy, rustfmt, rust-analyzer, and shelltestrunner to reproducible versions.
-
Enable flakes in
~/.config/nix/nix.conf:experimental-features = nix-command flakes -
(Optional) direnv + nix-direnv — the
.envrcactivates the shell oncd
nix develop # enter the dev shell
direnv allow # or auto-activate with direnvInside the shell:
cargo build
cargo test
shelltest tests/cli/
cargo fmt --check
cargo clippy -- -D warningsBase MiniC implementation and course materials by Prof. Rodrigo Bonifacio and the CIN compilers course. Team extensions (Projects 1–10) were developed collaboratively by student groups - see docs/09-projects01.md for the full list.