Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Deploy Documentation

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'

- name: Install MkDocs and dependencies
run: pip install mkdocs-material

- name: Build documentation
run: mkdocs build --strict

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: site/

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

.idea/*
site/
56 changes: 56 additions & 0 deletions docs/architecture/ast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# AST Generation

## Overview

After ANTLR parses the source file it produces a **Parse Tree** whose nodes are ANTLR `Context` objects. `ASTGenerationSTVisitor` traverses this Parse Tree and converts it into a typed **Abstract Syntax Tree (AST)** built from the static inner classes defined in `AST.java`.

## AST Node Hierarchy

All nodes extend the abstract `Node` interface. Each concrete node carries the children and data that are relevant for later phases:

| Node | Description |
|---|---|
| `ProgLetInNode` | Top-level `let … in` program |
| `ProgNode` | Top-level expression-only program |
| `ClassNode` | Class declaration (fields + methods) |
| `FieldNode` | A class field (like a parameter) |
| `MethodNode` | A class method (like a function) |
| `FunNode` | Function declaration |
| `ParNode` | Function parameter |
| `VarNode` | Variable declaration |
| `IdNode` | Identifier use |
| `CallNode` | Function call |
| `ClassCallNode` | Method call (`obj.method(...)`) |
| `NewNode` | Object instantiation (`new C(...)`) |
| `EmptyNode` | `null` literal |
| `PlusNode` / `MinusNode` | Addition / subtraction |
| `TimesNode` / `DivNode` | Multiplication / division |
| `EqualNode` / `GreaterEqualNode` / `LessEqualNode` | Comparison |
| `AndNode` / `OrNode` / `NotNode` | Logical operators |
| `IfNode` | `if / then / else` |
| `PrintNode` | `print(exp)` |
| `IntNode` | Integer literal |
| `BoolNode` | Boolean literal |

## Type Nodes

Type information is represented separately as `TypeNode` subtypes that appear in symbol-table entries:

| Type Node | Meaning |
|---|---|
| `IntTypeNode` | `int` |
| `BoolTypeNode` | `bool` |
| `RefTypeNode` | Reference to a class |
| `EmptyTypeNode` | Type of `null` |
| `ArrowTypeNode` | Function type (only in STentry) |
| `MethodTypeNode` | Method type (only in STentry) |
| `ClassTypeNode` | Class type (maps field/method indices to types) |

## Accept–Visit Cycle

The visitor pattern uses a two-step dispatch:

1. The visitor calls `visit(node)` → delegates to `node.accept(this)`.
2. The node calls `visitor.visitNode(this)` passing its specific type.

This double dispatch allows the visitor to select the correct `visitNode` overload at runtime without explicit casts.
70 changes: 70 additions & 0 deletions docs/architecture/code-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Code Generation

## Overview

`CodeGenerationASTVisitor` translates the type-checked Enriched AST into assembly instructions for the **Stack-based Virtual Machine (SVM)**. The generated code is written to a `.fool.asm` file.

## Target Architecture

The SVM is a simple stack machine with:

- A **stack** for operands and activation records.
- A **heap** for dynamically allocated objects and dispatch tables.
- A **frame pointer (fp)** and **stack pointer (sp)**.
- A **heap pointer (hp)** that grows upward.

## Activation Record Layout

Each function/method call pushes an **activation record (AR)** onto the stack:

```
high address
┌──────────────────┐
│ arguments │ (pushed by caller, right-to-left)
├──────────────────┤
│ return address │
├──────────────────┤
│ control link │ (saved fp of caller)
├──────────────────┤
│ access link │ (fp of statically enclosing scope)
├──────────────────┤
│ local variables │ (pushed by callee)
└──────────────────┘
low address
```

## Object Layout (Heap)

Each object allocated with `new` is stored on the heap:

```
[ dispatch-table pointer ][ field_0 ][ field_1 ] …
```

The **dispatch table** is also stored on the heap and contains one entry per method (the code address of each method).

## Key Code Patterns

### Variable Access

A local variable at nesting level `nl` and offset `off` is reached by following `(current_level − nl)` access links and then loading from `fp + off`.

### Function Call

1. Push arguments.
2. Push the access link (fp of the statically enclosing scope).
3. Jump to the function label.
4. On return, pop the AR.

### Method Dispatch

1. Load the object reference.
2. Load the dispatch-table pointer from offset 0 of the object.
3. Load the method address from the dispatch table at the method's index.
4. Call the method address.

### Object Creation (`new`)

1. Push field values onto the heap.
2. Store the dispatch-table pointer at the base of the new object.
3. Return the object address.
67 changes: 67 additions & 0 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Architecture Overview

The FOOL compiler is structured as a classic multi-phase pipeline. Each phase transforms its input and passes the result to the next phase.

```
Source text
┌──────────────┐
│ Lexer & │ ANTLR-generated from FOOL.g4
│ Parser │
└──────┬───────┘
│ Parse Tree
┌──────────────┐
│ AST │ ASTGenerationSTVisitor
│ Generation │
└──────┬───────┘
│ AST (Node hierarchy)
┌──────────────┐
│ Symbol │ SymbolTableASTVisitor
│ Table │
└──────┬───────┘
│ Enriched AST (EAST) – nodes annotated with STentry
┌──────────────┐
│ Type │ TypeCheckEASTVisitor
│ Checking │
└──────┬───────┘
│ Type-checked EAST
┌──────────────┐
│ Code │ CodeGenerationASTVisitor
│ Generation │
└──────┬───────┘
│ SVM assembly
.fool.asm
```

## Key Classes

| Class | Role |
|---|---|
| `FOOL.g4` | ANTLR grammar defining the language syntax |
| `AST.java` | Defines all AST node types as static inner classes |
| `ASTGenerationSTVisitor` | Visits the Parse Tree and builds the AST |
| `SymbolTableASTVisitor` | Builds the symbol table and enriches the AST |
| `TypeCheckEASTVisitor` | Performs type checking on the enriched AST |
| `CodeGenerationASTVisitor` | Generates SVM assembly from the AST |
| `PrintEASTVisitor` | Pretty-prints the enriched AST (debugging aid) |
| `STentry` | Holds a symbol-table entry (type, nesting level, offset) |
| `TypeRels` | Encodes subtype relationships between types |
| `Test` | Entry point: orchestrates all compiler phases |

## Visitor Pattern

All tree-processing phases implement the **Visitor** pattern via `BaseASTVisitor<S, E>`.
Each AST node has an `accept(BaseASTVisitor)` method that calls back the corresponding `visitNode` overload on the visitor, enabling double-dispatch without casts.

See the individual phase pages for deeper details:

- [AST Generation](ast.md)
- [Symbol Table Analysis](symbol-table.md)
- [Type Checking](type-checking.md)
- [Code Generation](code-generation.md)
47 changes: 47 additions & 0 deletions docs/architecture/symbol-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Symbol Table Analysis

## Overview

`SymbolTableASTVisitor` traverses the AST and builds a **symbol table** that maps every identifier to its declaration. It also *enriches* each `IdNode`, `CallNode`, and `ClassCallNode` with a reference to its `STentry`, producing the **Enriched AST (EAST)**.

## Symbol Table Structure

The symbol table is a stack of hash-maps (one map per nesting level):

```
Level 0 (global) { Counter → STentry(ClassTypeNode, 0, 0), … }
Level 1 (function) { x → STentry(IntTypeNode, 1, -1), … }
```

Each `STentry` stores:

| Field | Description |
|---|---|
| `nl` | Nesting level of the declaration |
| `type` | Declared type (`TypeNode`) |
| `offset` | Memory offset in the activation record |

## Static Scoping

The visitor enforces two static-scoping rules:

1. A use of identifier `x` resolves to the declaration in the **most closely enclosing scope** that precedes the use.
2. An inner-scope declaration of `x` **hides** any outer-scope declaration of the same name.

`stLookup(id)` searches from the innermost (current) scope outward, returning the first matching `STentry`.

## Class Table & Virtual Table

For object-oriented features the visitor maintains an additional **class table** (a map from class name to its `STentry`) and, for each class, a **virtual table** (a map from method name to its `STentry`). These are used to:

- Resolve field and method accesses in `ClassCallNode`.
- Build the dispatch table layout used by the code generator.

## Error Handling

The visitor reports semantic errors without aborting, accumulating them in a counter. Detected errors include:

- Undeclared identifier
- Identifier declared twice in the same scope
- Calling a variable as a function
- Using an undeclared class type
39 changes: 39 additions & 0 deletions docs/architecture/type-checking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Type Checking

## Overview

`TypeCheckEASTVisitor` traverses the Enriched AST and verifies that every expression has a well-formed type. It returns a `TypeNode` for each node, propagating types bottom-up through the tree.

## Subtype Relations

Type compatibility is determined by `TypeRels.isSubtype(a, b)`:

- `BoolTypeNode` is a subtype of `IntTypeNode` (booleans can be used as integers).
- A class type `C` is a subtype of `D` if `C` extends `D` (directly or transitively).
- `EmptyTypeNode` (`null`) is a subtype of any `RefTypeNode`.

## Key Rules

| Construct | Type Rule |
|---|---|
| `IntNode` | `IntTypeNode` |
| `BoolNode` | `BoolTypeNode` |
| `PlusNode`, `MinusNode`, `TimesNode`, `DivNode` | Both operands must be `int`; result is `IntTypeNode` |
| `AndNode`, `OrNode` | Both operands must be `bool`; result is `BoolTypeNode` |
| `NotNode` | Operand must be `bool`; result is `BoolTypeNode` |
| `EqualNode` | Operands must share a common subtype |
| `GreaterEqualNode`, `LessEqualNode` | Both operands must be `int`; result is `BoolTypeNode` |
| `IfNode` | Condition must be `bool`; result is the **lowest common ancestor** of the two branch types |
| `CallNode` | Argument types must be subtypes of the declared parameter types; result is the function's return type |
| `NewNode` | Argument types must match class field types; result is `RefTypeNode(classID)` |
| `ClassCallNode` | Like `CallNode` but resolves through the virtual table |
| `VarNode` | Declared type must be a supertype of the initialiser type |
| `FunNode` / `MethodNode` | Body type must be a subtype of the declared return type |

## Lowest Common Ancestor

For `if / then / else`, the result type is the lowest common ancestor (LCA) of the two branch types in the subtype hierarchy. For example, if one branch has type `C` and another has type `D` where both extend `Base`, the result type is `Base`.

## Error Reporting

Type errors are printed to standard error and counted. Compilation continues after a type error so that further errors can be reported in the same run.
52 changes: 52 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Getting Started

## Prerequisites

- **Java 8+** (JDK)
- **ANTLR 4** runtime (included in `compiler/lib/`)
- An IDE such as IntelliJ IDEA (optional but recommended)

## Building the Compiler

The project is structured as an IntelliJ IDEA project. Open the root directory in IntelliJ and build with the standard build action, or compile the sources manually:

```bash
javac -cp compiler/lib/antlr-4.7.2-complete.jar compiler/*.java -d out/
```

## Running the Compiler

To compile a `.fool` source file:

```bash
java -cp out/:compiler/lib/antlr-4.7.2-complete.jar Test <source-file>.fool
```

The compiler will:

1. Parse the source file using the ANTLR-generated lexer/parser.
2. Build an AST and enrich it with symbol-table information.
3. Perform type checking.
4. Emit assembly code for the custom stack-based virtual machine (SVM).

The generated assembly is written to `<source-file>.fool.asm`.

## Running the Virtual Machine

Execute the generated assembly with the SVM interpreter:

```bash
java -cp out/:compiler/lib/antlr-4.7.2-complete.jar ExecuteVM <source-file>.fool.asm
```

## Example Programs

Several example `.fool` programs are included in the repository root:

| File | Description |
|---|---|
| `prova.fool` | Basic language features |
| `prova2.fool` | Additional feature tests |
| `quicksort.fool` | Quicksort algorithm |
| `bankloan.fool` | Bank-loan class example |
| `testClass.fool` | Class and method usage |
Loading