affinescript

= AffineScript

image:https://img.shields.io/badge/License-MPL_2.0-blue.svg[MPL-2.0,link="https://opensource.org/licenses/MPL-2.0"] image:https://img.shields.io/badge/Philosophy-Palimpsest-purple.svg[Palimpsest,link="https://github.com/hyperpolymath/palimpsest-licence"]

:toc: macro :toclevels: 3 :icons: font :source-highlighter: rouge

A Rust-inspired programming language combining affine types, dependent types, row polymorphism, and extensible effectsβ€”compiling to WebAssembly with no garbage collector.

[.lead] AffineScript brings the safety of linear types, the expressiveness of dependent types, and the modularity of algebraic effects into a cohesive, practical language.

πŸ“– New to this repository? See link:NAVIGATION.adoc[Navigation Guide] for directory structure and quick links.

toc::[]

== Overview

AffineScript is designed for systems programming where correctness matters. It combines ideas from:

=== Key Features

[cols="1,2"] |=== |Feature |Description

|Affine Types |Track resource usage with quantities: 0 (erased), 1 (linear), Ο‰ (unrestricted)

|Dependent Types |Types that depend on valuesβ€”length-indexed vectors, refinement types

|Row Polymorphism |Extensible records with {x: Int, ..r} syntax for flexible data structures

|Extensible Effects |User-defined effects with effect declarations and handle/resume

|Ownership |own, ref, mut modifiers for memory safety without GC

|Totality Checking |Mark functions as total to prove termination

|WebAssembly Target |Compiles to WASM for portable, high-performance execution |===

== Language Examples

=== Hello World with Effects

[source,affinescript] ---- effect IO { fn print(s: String); fn println(s: String); }

fn main() -> () / IO { println("Hello, AffineScript!"); } ----

=== Ownership and Resource Safety

[source,affinescript] ---- type File = own { fd: Int }

fn open(path: ref String) -> Result[own File, IOError] / IO + Exn[IOError] { Ok(File { fd: 42 }) }

fn read(file: ref File) -> Result[String, IOError] / IO { // Borrows file β€” doesn't consume it Ok("file contents") }

fn close(file: own File) -> Result[(), IOError] / IO { // Consumes file β€” can't use it after this Ok(()) }

// Safe resource handling with RAII pattern fn withFile[T]( path: ref String, action: (ref File) -> Result[T, IOError] / IO ) -> Result[T, IOError] / IO { let file = open(path)?; let result = action(ref file); close(file)?; result } ----

=== Row Polymorphism

[source,affinescript] ---- // Works on any record that has a 'name' field fn greet[..r](person: {name: String, ..r}) -> String / Pure { "Hello, " ++ person.name }

// Extend records while preserving other fields fn fullName[..r]( person: {first: String, last: String, ..r} ) -> {first: String, last: String, fullName: String, ..r} / Pure { {fullName: person.first ++ " " ++ person.last, ..person} }

fn main() -> () / Pure { let alice = {name: "Alice", age: 30, role: "Engineer"}; let bob = {name: "Bob", department: "Sales"};

// Both work despite different record shapes let greeting1 = greet(alice); // βœ“ let greeting2 = greet(bob); // βœ“ } ----

=== Dependent Types: Length-Indexed Vectors

[source,affinescript] ---- type Vec[n: Nat, T: Type] = | Nil : Vec[0, T] | Cons(head: T, tail: Vec[n, T]) : Vec[n + 1, T]

// Can only be called on non-empty vectors β€” enforced by types! total fn head[n: Nat, T](v: Vec[n + 1, T]) -> T / Pure { match v { Cons(h, _) => h } }

// Concatenate: result length is sum of input lengths total fn append[n: Nat, m: Nat, T]( a: Vec[n, T], b: Vec[m, T] ) -> Vec[n + m, T] / Pure { match a { Nil => b, Cons(h, t) => Cons(h, append(t, b)) } } ----

== Project Status

=== Implementation Progress

[cols="2,1,3"] |=== |Component |Status |Details

|Lexer |βœ… Complete |sedlex-based, Unicode support, full test coverage

|Parser Grammar |βœ… Complete |615-line Menhir grammar covering entire syntax

|Abstract Syntax Tree |βœ… Complete |395 lines representing all language constructs

|Error Handling |βœ… Complete |Rich diagnostics with 50+ error codes, colored output

|CLI Interface |βœ… Complete |lex, parse, check, compile commands

|Name Resolution |πŸ”² Planned |Scope analysis, module resolution

|Type Checker |πŸ”² Planned |Bidirectional inference, constraint solving

|Borrow Checker |πŸ”² Planned |Non-lexical lifetimes, linearity enforcement

|Code Generation |πŸ”² Planned |WASM backend |===

=== What Works Today

The compiler frontend is complete. You can:

[source,bash] ----

Tokenize a file

dune exec affinescript -- lex examples/hello.as

Parse and display AST

dune exec affinescript -- parse examples/ownership.as ----

== Building

=== Prerequisites

=== Commands

[source,bash] ----

Build

dune build

Run tests

dune runtest

Format code

dune fmt

Generate documentation

dune build @doc

Run compiler

dune exec affinescript -- ----

== Repository Structure

[source] ---- affinescript/ β”œβ”€β”€ lib/ # Core compiler library β”‚ β”œβ”€β”€ ast.ml # Abstract syntax tree (395 lines) β”‚ β”œβ”€β”€ token.ml # Token definitions (222 lines) β”‚ β”œβ”€β”€ lexer.ml # Lexer β€” sedlex-based (323 lines) β”‚ β”œβ”€β”€ parser.mly # Parser β€” Menhir grammar (615 lines) β”‚ β”œβ”€β”€ parse.ml # Parser driver β”‚ β”œβ”€β”€ span.ml # Source location tracking β”‚ └── error.ml # Diagnostics and error handling β”œβ”€β”€ bin/ # CLI executable β”‚ └── main.ml # Command-line interface β”œβ”€β”€ test/ # Test suite β”‚ β”œβ”€β”€ test_lexer.ml # Lexer tests (~145 cases) β”‚ └── test_parser.ml # Parser tests (~80 cases) β”œβ”€β”€ examples/ # Example programs β”‚ β”œβ”€β”€ hello.as # Basic IO effect β”‚ β”œβ”€β”€ ownership.as # Resource management β”‚ β”œβ”€β”€ rows.as # Row polymorphism β”‚ └── vectors.as # Dependent types β”œβ”€β”€ docs/ # Documentation β”‚ └── wiki/ # Compiler & language documentation └── affinescript-spec.md # Complete language specification (53KB) ----

== Documentation

== Design Philosophy

. Safety by default β€” Ownership and effects make unsafe code explicit . Types as documentation β€” Dependent types encode invariants in the type system . Composable abstractions β€” Row polymorphism and effects compose cleanly . No runtime cost for safety β€” Linear types enable compile-time resource management . Partial by default β€” Functions may diverge unless marked total

== License

SPDX-License-Identifier: PMPL-1.0-or-later

== See Also