# pascal-rs — A Modern Pascal Compiler & Interpreter in Rust

**pascal-rs** is a Pascal compiler, interpreter, and package manager written in Rust. It supports standard Pascal and a growing subset of Object Pascal (classes, exceptions, inheritance, virtual dispatch), with a modern `cargo`/`npm`-style build system (`pascal.toml`), backed by 228 automated tests and 10 runnable example programs.

---

## Status

| Metric | Value |
|--------|-------|
| **Tests** | 228 passing (127 unit + 101 integration) |
| **Language** | Standard Pascal + Object Pascal subset |
| **Interpreter** | Full-featured tree-walking execution |
| **Compiler** | x86-64 assembly generation with optimizations |
| **Build System** | `pascal.toml` manifest, dependency management, lock file |
| **Edition** | Rust 2024 |

---

## What's New

### Object Pascal in the Interpreter

The interpreter now supports core Object Pascal features at the AST/runtime level:

- **Classes** — `Value::Object` with field access, constructors, destructors, method dispatch
- **Inheritance** — single inheritance with field/method merging
- **Virtual/Override** — vtable-style dispatch via `find_method_in_hierarchy`
- **`is` / `as`** — runtime type checks walking the inheritance chain
- **`inherited`** — call parent class methods
- **Properties** — read/write resolution infrastructure
- **Exceptions** — `try/except/finally`, `raise`, `on E: Type do` matching, re-raise

### Interpreter Enhancements

- **Arrays** — `SetLength`, indexing with bounds checking, `high()`, `low()`, `length()`
- **Records** — field access and assignment via dot notation
- **String indexing** — `s[i]` (1-indexed, Pascal style)
- **`exit`** — early return from functions/procedures, with optional return value (`exit(42)`)
- **`break`** — loop termination
- **`with` statement** — pushes object/record fields into scope
- **`uses` clause** — loads and imports `.pas` unit files
- **Nested functions** — proper scoping for functions declared inside functions

### Build System & Package Manager

A modern `cargo`/`npm`-style project management system:

- **`pascal init <name>`** — scaffold a new project with `pascal.toml`, `src/`, `tests/`, `examples/`
- **`pascal build`** — compile all units in dependency order (topological sort)
- **`pascal run`** — run the project's main program (or `pascal run file.pas` for single files)
- **`pascal add <dep>`** — add a dependency (version, `--path`, or `--git`)
- **`pascal remove <dep>`** — remove a dependency
- **`pascal.toml`** — project manifest (package metadata, dependencies, build config)
- **`pascal.lock`** — reproducible builds with SHA-256 checksums

### Parser Improvements

- **Source location tracking** — error messages now include `at line N, column M`
- **Error recovery** — `consume_or_skip` and `synchronize` for resilient parsing
- **`exit`/`break` tokens** — properly parsed as statements (not silently skipped)

### Bug Fixes

- **Recursive function scoping** — `set_local_variable` prevents recursive calls from clobbering parent scope return variables
- **`exit` inside `if`/`begin..end`** — `EarlyReturn` now propagates correctly through all control flow

### 10 Example Programs

Validated end-to-end (source → lexer → parser → interpreter):

| # | File | Features |
|---|------|----------|
| 01 | `01_basics.pas` | Variables, arithmetic, if/else, while, for, repeat/until, case |
| 02 | `02_functions.pas` | Factorial, Fibonacci, IsPrime, GCD, procedures |
| 03 | `03_strings.pas` | Reverse, palindrome, char counting, copy, pos, upcase |
| 04 | `04_arrays.pas` | SetLength, indexing, high/low/length |
| 05 | `05_classes.pas` | Shape area calculations with functions |
| 06 | `06_exceptions.pas` | try/except, try/finally, nested exceptions, raise |
| 07 | `07_nested_functions.pas` | exit() with return values, early return patterns |
| 08 | `08_math_algorithms.pas` | GCD, Collatz, digit sum, fast exponentiation |
| 09 | `09_class_hierarchy.pas` | Polymorphic dispatch, string comparisons |
| 10 | `10_comprehensive.pas` | Recursion, primes, strings, case, exceptions combined |

---

## Why pascal-rs?

### Compared to Alternatives

| | **pascal-rs** | **Free Pascal (FPC)** | **Delphi** | **GNU Pascal** |
|---|---|---|---|---|
| **Implementation language** | Rust (memory-safe) | Object Pascal/C | C++ | C |
| **Interpreter mode** | Built-in tree-walker | No | No | No |
| **Package manager** | Built-in (`pascal.toml`) | No | No | No |
| **Test suite** | 228 automated tests | Large but external | Proprietary | Minimal |
| **Object Pascal** | Subset (classes, exceptions, virtual) | Full | Full | Partial |
| **Trait-based design** | Yes — testable, extensible | No | No | No |
| **Error messages** | Line/column, colored | Basic | Good | Basic |
| **x86-64 codegen** | Yes, with optimizations | Mature, multi-target | Mature | Basic |
| **Active** | Yes | Yes | Yes | No (2006) |
| **Cross-platform** | Yes (Rust targets) | Yes | Windows-focused | Yes |
| **License** | Apache-2.0 | LGPL | Commercial | GPL |

### When to Use pascal-rs

- **Learning compiler construction** — clean Rust codebase, trait-based architecture, well-tested
- **Running Pascal programs quickly** — `pascal run program.pas` with no assembly/linking step
- **Research & experimentation** — easy to extend with new optimizations or language features
- **Small projects & algorithms** — full standard Pascal with functions, recursion, strings, arrays

### When to Use Something Else

- **Large production codebases** — FPC or Delphi have decades of maturity
- **Full Delphi compatibility** — pascal-rs covers a subset of Object Pascal
- **GUI applications** — FPC/Lazarus or Delphi have mature widget libraries
- **Multi-target compilation** — FPC supports ARM, MIPS, PowerPC, WebAssembly, etc.

---

## Quick Start

### Installation

```bash
git clone https://github.com/yingkitw/pascal-rs.git
cd pascal-rs
cargo build --release
```

### Create a Project

```bash
# Scaffold a new project
./target/release/pascal init myapp
cd myapp

# Build all units in dependency order
./target/release/pascal build

# Run the project
./target/release/pascal run
```

This creates:
```
myapp/
├── pascal.toml          # Project manifest
├── src/
│   └── myapp.pas        # Main program
├── tests/
├── examples/
├── .gitignore
└── README.md
```

### Run a Single File

```bash
# Interpret directly (no project needed)
./target/release/pascal run examples/01_basics.pas

# Compile to x86-64 assembly
./target/release/pascal compile examples/01_basics.pas -S
```

### Run All Tests

```bash
cargo test
```

```
test result: ok. 228 passed; 0 failed; 0 ignored
```

---

## Usage

### Commands

| Command | Description | Example |
|---------|-------------|---------|
| `init` | Create a new project | `pascal init myapp` |
| `build` | Build the current project | `pascal build` |
| `run` | Run a program or project | `pascal run` / `pascal run file.pas` |
| `add` | Add a dependency | `pascal add mathlib` |
| `remove` | Remove a dependency | `pascal remove mathlib` |
| `compile` | Compile a single file | `pascal compile prog.pas -S` |
| `info` | Inspect a PPU file | `pascal info module.ppu` |
| `clean` | Remove build artifacts | `pascal clean` |

### Project Management

```bash
# Create a new project
pascal init calculator
cd calculator

# Add dependencies
pascal add mathlib -V "1.0"
pascal add utils --path "../shared/utils"
pascal add network --git "https://github.com/example/network.git"

# Remove a dependency
pascal remove network

# Build (compiles all units in dependency order)
pascal build
pascal build -v    # verbose

# Run the project's main program
pascal run
```

### pascal.toml

```toml
[package]
name = "calculator"
version = "0.2.0"
description = "A calculator app"
authors = ["Alice"]
license = "MIT"
src = "src"
main = "calculator.pas"

[dependencies]
mathlib = "1.0"
utils = { path = "../shared/utils" }
network = { git = "https://github.com/example/network.git", branch = "main" }

[build]
optimization = 2
output = "build"
```

### Single-File Mode

```bash
pascal run program.pas        # Interpret directly
pascal run program.pas -v     # With trace output
pascal compile program.pas -S # Emit x86-64 assembly
pascal compile program.pas -O2 -o build
```

### Compile Options

| Option | Description |
|--------|-------------|
| `-o, --output <DIR>` | Output directory |
| `-O, --optimize <LEVEL>` | Optimization level (0–3) |
| `-v, --verbose` | Verbose output |
| `-S, --assembly` | Generate assembly output |
| `-d, --debug` | Debug information |
| `-I, --include <DIR>` | Add unit search path |
| `--no-cache` | Disable PPU caching |

---

## Examples

### Recursive Fibonacci with Exit

```pascal
program Fibonacci;
var
  i: integer;

function Fib(n: integer): integer;
begin
  if n <= 1 then
    exit(n);
  Fib := Fib(n - 1) + Fib(n - 2);
end;

begin
  for i := 0 to 10 do
    writeln('Fib(', i, ') = ', Fib(i));
end.
```

### Prime Sieve with Early Return

```pascal
program Primes;
var
  i, count: integer;

function IsPrime(n: integer): boolean;
var
  i: integer;
begin
  if n < 2 then
  begin
    IsPrime := false;
    exit;
  end;
  i := 2;
  while i * i <= n do
  begin
    if n mod i = 0 then
    begin
      IsPrime := false;
      exit;
    end;
    inc(i);
  end;
  IsPrime := true;
end;

begin
  count := 0;
  for i := 2 to 100 do
    if IsPrime(i) then
      inc(count);
  writeln('Primes under 100: ', count);  { Output: 25 }
end.
```

### String Operations

```pascal
program Strings;
var
  s, rev: string;
  i, len: integer;

function ReverseStr(s: string): string;
var
  i, n: integer;
  r: string;
begin
  n := length(s);
  r := '';
  for i := n downto 1 do
    r := concat(r, copy(s, i, 1));
  ReverseStr := r;
end;

begin
  s := 'Hello, Pascal!';
  writeln('Original: ', s);
  writeln('Length: ', length(s));
  writeln('Upper: ', upcase(s));
  writeln('Reversed: ', ReverseStr(s));
  writeln('Substring: ', copy(s, 8, 6));  { Pascal }
  writeln('Char 1: ', s[1]);              { H }
end.
```

### Exception Handling

```pascal
program Exceptions;
begin
  try
    writeln('Before raise');
    raise Exception.Create('something went wrong');
    writeln('SHOULD NOT PRINT');
  except
    on E: Exception do
      writeln('Caught: ', E.Message);
  end;

  try
    raise Exception.Create('error');
  finally
    writeln('Finally always runs');
  end;
end.
```

### Math Algorithms

```pascal
program Math;
var
  a, b: integer;

function GCD(a, b: integer): integer;
begin
  while b <> 0 do
  begin
    a := a mod b;
    if a = 0 then
    begin
      GCD := b;
      exit;
    end;
    b := b mod a;
  end;
  GCD := a;
end;

function Power(base, exp: integer): integer;
var
  result: integer;
begin
  result := 1;
  while exp > 0 do
  begin
    if exp mod 2 = 1 then
      result := result * base;
    base := base * base;
    exp := exp div 2;
  end;
  Power := result;
end;

begin
  writeln('GCD(48, 18) = ', GCD(48, 18));    { 6 }
  writeln('2^10 = ', Power(2, 10));           { 1024 }
end.
```

### Unit System

**MathUtils.pas:**
```pascal
unit MathUtils;

interface

function Add(a, b: integer): integer;
function IsEven(n: integer): boolean;

implementation

function Add(a, b: integer): integer;
begin
  Add := a + b;
end;

function IsEven(n: integer): boolean;
begin
  IsEven := (n mod 2) = 0;
end;

end.
```

**Main.pas:**
```pascal
program Main;
uses MathUtils;
var
  x: integer;
begin
  x := Add(10, 5);
  writeln('10 + 5 = ', x);
  writeln('Is even: ', IsEven(x));
end.
```

See the `examples/` directory for all 10 validated example programs.

---

## Architecture

```
Source (.pas) → Lexer (logos) → Parser (recursive descent) → AST
                                                              │
                                    ┌─────────────────────────┼──────────────────┐
                                    ▼                         ▼                  ▼
                              Interpreter              Optimizer           Type Checker
                           (tree-walking)          (const fold, DCE)    (basic validation)
                                    │                     │
                                    ▼                     ▼
                              Direct Output        Code Generator → Assembly (.s)
```

### Project Structure

```
pascal-rs/
├── src/
│   ├── lexer.rs                # Lexical analysis (logos-based, 100+ tokens)
│   ├── parser/                 # Recursive descent parser
│   │   ├── mod.rs              # Parser core with source location tracking
│   │   ├── expression.rs       # Expression parsing (precedence climbing)
│   │   ├── statement.rs        # Statement parsing (if, while, for, try, exit)
│   │   └── decl.rs             # Declaration parsing (var, type, class, function)
│   ├── ast.rs                  # AST node definitions
│   ├── interpreter.rs          # Tree-walking interpreter (Object Pascal support)
│   ├── build_system.rs         # Package manager & build system (pascal.toml)
│   ├── type_checker.rs         # Type validation
│   ├── optimizer.rs            # Optimization passes
│   ├── unit_codegen.rs         # x86-64 code generation
│   ├── resolver.rs             # Symbol resolution
│   └── traits/                 # Trait abstractions for testability
├── tests/                      # 101 integration tests across 8 test files
├── examples/                   # 10 validated example programs
├── ARCHITECTURE.md
├── SPEC.md
└── TODO.md
```

### Built-in Functions & Procedures

**Math:** `abs`, `sqr`, `sqrt`, `sin`, `cos`, `ln`, `exp`, `round`, `trunc`

**String:** `length`, `concat`, `copy`, `pos`, `upcase`, `lowercase`, `inttostr`, `strtoint`

**Ordinal:** `ord`, `chr`, `odd`, `succ`, `pred`, `inc`, `dec`

**Array:** `length`, `high`, `low`, `setlength`

**I/O:** `write`, `writeln`, `readln`

**Control:** `exit`, `break`, `halt`, `random`

---

## Testing

```bash
cargo test                                        # All 228 tests
cargo test --lib                                  # 127 unit tests
cargo test --test run_example_tests               # 19 example pipeline tests
cargo test --test run_integration_tests           # 10 integration tests
cargo test --test run_compiler_tests              # 10 codegen tests
cargo test --test run_complex_validation_tests    # 9 complex validation tests
cargo test --test run_interpreter_tests           # 11 interpreter tests
cargo test --test run_simple_compiler_tests       # 18 parser tests
cargo test --test run_simple_interpreter_tests    # 13 simple interpreter tests
cargo test --test run_type_checker_tests          # 10 type checker tests
```

### Test Breakdown

```
Library (unit tests)        127  (includes 12 build system tests)
Example pipeline tests       19
Compiler codegen tests       10
Complex validation            9
Integration tests            10
Interpreter tests            11
Simple compiler (parser)     18
Simple interpreter           13
Type checker                 10
Basic                         1
─────────────────────────────────
Total                       228
```

---

## Documentation

- **[ARCHITECTURE.md](ARCHITECTURE.md)** — module design and data flow
- **[SPEC.md](SPEC.md)** — language feature matrix (parser/interpreter/codegen status)
- **[TODO.md](TODO.md)** — development roadmap and completed phases

---

## Current Limitations

- **Inline class method bodies** — parser doesn't yet support method bodies inside `type` class declarations (interpreter supports classes via AST)
- **Array element assignment** — `arr[i] := val` not yet supported
- **Multi-dimensional arrays** — single dimension only in interpreter
- **File I/O** — not implemented
- **Generics/templates** — not implemented
- **GUI framework** — not included (use FPC/Lazarus for GUI apps)

---

## License

Apache-2.0

---

**Made with Rust** — *228 tests passing | Standard Pascal + Object Pascal subset | Compiler + Interpreter + Package Manager*
