Gat Language Specification
Version: 0.4.0
Target: x86-64 Native (Windows PE32+ via IAT, Linux ELF64 via Direct Kernel Syscalls)
Memory Model: Automatic Reference Counting (ARC)
Table of Contents
- Overview
- Lexical Structure
- Type System & Generics
- Memory Model & ARC
- Declarations
- Statements & Control Flow
- Expressions & Operators
- Module & Import System
- Grammar (EBNF)
1. Overview
Gat is a statically-typed, memory-safe, self-hosting systems language designed for high performance, direct native code emission, and deterministic automatic reference counted memory management without a bulky runtime.
2. Lexical Structure
2.1 Identifiers
Identifiers start with an ASCII letter or underscore, followed by letters, digits, or underscores: [a-zA-Z_][a-zA-Z0-9_]*
2.2 Keywords
fn, let, if, else, while, for, in, return, match, struct, class, enum, import, new, nil, true, false
2.3 Literals
- Integer: Decimal literals (
42,0,1000000). Stored as 64-bit signed integers (i64). - Boolean:
true,false. - String: Enclosed in double quotes (
"hello\n"). Supports escape sequences\n,\r,\t,\\,\". - String Interpolation: Expressions enclosed in
{}within double quotes ("count: {x + 1}"). - Array Literals: Bracketed comma-separated items (
[10, 20, 30]). - Nil:
nilrepresents null pointers for structs, classes, and references.
2.4 Comments
- Line comments:
// ...(continues to end of line).
3. Type System & Generics
3.1 Primitive Types
i64: 64-bit signed integer.bool: Boolean value (trueorfalse).string: Immutable reference-counted byte buffer.void: Return type for procedures with no return value.array: Dynamic array of reference-counted elements.
3.2 Composite Types
- Struct: Value aggregate defined with
struct Name { field: Type; ... }. - Class: Reference-counted heap object defined with
class Name { field: Type; ... }. - Enum: Tagged algebraic sum type with optional payload:
enum Result<T, E> { Ok(T), Err(E) }
3.3 Generics Model (Uniform Word-Sized Type Erasure)
Gat implements Uniform 64-bit Word-Sized Generics via compile-time type erasure:
- Generic parameters
<T, U>represent any 64-bit word-sized type:- Primitives:
i64,bool - References & Handles:
string,classinstances,array, raw pointers
- Primitives:
- Representation: Every generic type parameter occupies exactly 1 machine word (8 bytes).
- Value Type Restriction: Multi-word value types (multi-field
structvalue types) cannot be used directly as generic type arguments; they must be wrapped in aclass(heap reference) or passed via pointers. - Benefits: Eliminates template code-bloat, enables fast single-pass compilation, and provides 100% type soundness across all 64-bit word-sized types.
class Pair<T, U> {
first: T;
second: U;
}
fn make_pair<T, U>(a: T, b: U) -> Pair<T, U> {
return new Pair<T, U> {
first: a,
second: b
};
}
3.4 Function Types & First-Class Functions
Gat supports first-class function values (function pointers):
- Type Syntax:
fn(ParamType1, ParamType2, ...) -> ReturnType - Function Values: Top-level function declarations can be referenced as values without call parentheses (
let f = add;). - Indirect Calls: Functions stored in variables, passed as arguments, or stored as struct/class fields are called indirectly via machine code
call reg(Windows fastcall ABI compliant). - Zero Overhead: Function values are represented as 64-bit code addresses requiring no runtime allocations or ARC tracking.
fn apply<T, U>(f: fn(T) -> U, val: T) -> U {
return f(val);
}
fn double_num(x: i64) -> i64 {
return x * 2;
}
let result = apply(double_num, 21); // 42
4. Memory Model & ARC
4.1 Header Layout
All heap-allocated objects (classes, strings, arrays) are prefixed with a 24-byte metadata header:
Offset -24: [ strong_count : i64 ] (64-bit strong reference counter)
Offset -16: [ weak_count : i64 ] (64-bit weak reference counter)
Offset -8: [ type_tag : i64 ] (64-bit type discriminator / size)
Offset 0: [ User Payload ] <-- Object pointer returned to user
4.2 Retain & Release Semantics
- Allocation:
new Class { ... }oralloc_mem(sz)initializesstrong_count = 1, weak_count = 0. - Strong Retain: Passing or assigning heap references invokes
__gat_retain(inc qword ptr [ptr-24]). - Strong Release: Reassignment or explicit release invokes
__gat_release(dec qword ptr [ptr-24]). Whenstrong_count == 0, deterministically runsdeinitand releases child references. Ifweak_count == 0, immediately deallocates backing memory. - Weak Retain / Release: Creating a
weak Treference incrementsweak_count([ptr-16]). Dropping aweak Treference decrementsweak_count. If bothstrong_count == 0andweak_count == 0, backing memory is freed.
4.3 Weak References & Cycle Breaking (weak T)
Reference cycles (such as parent-child tree links or doubly linked lists) are resolved using weak T back-references:
- Modifier:
weak Trepresents a non-owning reference to a classT. - Creation:
weak_from(obj)creates aweak Tand incrementsweak_count. - Upgrade:
weak_upgrade(w: weak T) -> Option<T>atomically inspectsstrong_count. If alive (> 0), retains and returnsOption.Some(obj). If dead (== 0), returnsOption.None.
class Node {
next: Node;
prev: weak Node; // Back-pointer does not keep parent alive, preventing cycle leak
}
4.4 Concurrency & Thread-Boundary Isolation
Gat provides native OS threading via std/thread.gat while preserving non-atomic ARC performance and memory soundness through compile-time thread-boundary isolation:
- Thread-Local Heaps: Each thread manages its own reference-counted heap. Reference counts (
strong_count,weak_count) remain non-atomic and fast. - Thread-Boundary Safety: The compiler enforces at compile time that reference-counted types (
class,string,weak T, or structs containing them) cannot be passed across thread boundaries inthread_spawn. - Value & Raw Data Sharing: Threads can receive primitive data (
i64,bool), valuestructs containing only plain data, and explicitraw Tpointers. - Synchronization (
Mutex):std/sync.gatprovidesMutex(wrapping Win32 Critical Sections) for safe, serialized mutation of sharedraw Tstate across threads.
import "std/thread.gat";
import "std/sync.gat";
struct Task {
id: i64;
out_ptr: raw i64;
}
fn worker(task: raw Task) {
task.out_ptr[0] = task.id * 10;
}
let out: raw i64 = alloc_mem(8);
let t = new Task { id: 5, out_ptr: out };
let h = thread_spawn(worker, raw t);
thread_join(h);
// out[0] == 50
5. Declarations
5.1 Functions
fn add(a: i64, b: i64) -> i64 {
return a + b;
}
5.2 Structs & Classes
struct Point {
x: i64;
y: i64;
}
class LinkedListNode<T> {
data: T;
next: LinkedListNode<T>;
}
5.3 Enums
enum Result<T, E> {
Ok(T),
Err(E)
}
5.4 Modules & Namespaces (import ... as ...)
import "std/str.gat"; // Spliced relative import
import "pkg/math.gat" as math; // Namespaced module import
import "dep_json" as json; // Cached dependency in .gat/deps/
let res = math.add(1, 2);
let pt = new math.Point { x: 10, y: 20 };
6. Statements & Control Flow
6.1 Variable Binding (let)
let x = 10;
let y: i64 = 20;
6.2 Conditionals (if / else)
if x > 10 {
print("Greater\n");
} else if x == 10 {
print("Equal\n");
} else {
print("Lesser\n");
}
6.3 Loops (while and for)
- While Loop:
let i = 0; while i < 10 { i = i + 1; } - Range For Loop:
for i in 0..10 { print("i: {i}\n"); } - Iterator For Loop:
for item in items { print("item: {item}\n"); }
6.4 Pattern Matching (match)
match opt {
Option::Some(v) => {
print("Value: {v}\n");
}
Option::None => {
print("None\n");
}
}
7. Expressions & Operators
- Binary Arithmetic:
+,-,*,/,% - Comparison:
==,!=,<,<=,>,>= - Logical:
&&,||,! - Member Access:
obj.field - Instantiation:
let pt = new Point { x: 10, y: 20 }; - String Interpolation:
let msg = "Hello {name}, score: {score * 2}";
8. Module & Import System
Source files import other modules using relative paths:
import "std/str.gat";
import "std/fs.gat";
import "std/math.gat";
Imports are resolved transitively and deduplicated at compile-time.
9. Grammar (EBNF)
Program ::= ( ImportDecl | TopLevelDecl )*
ImportDecl ::= 'import' StringLit ';'
TopLevelDecl ::= FnDecl | StructDecl | ClassDecl | EnumDecl
FnDecl ::= 'fn' Ident [ TypeParams ] '(' [ ParamList ] ')' [ '->' Type ] Block
StructDecl ::= 'struct' Ident [ TypeParams ] '{' ( Ident ':' Type ';' )* '}'
ClassDecl ::= 'class' Ident [ TypeParams ] '{' ( Ident ':' Type ';' )* '}'
EnumDecl ::= 'enum' Ident [ TypeParams ] '{' ( EnumVariant ( ',' EnumVariant )* )? '}'
EnumVariant ::= Ident [ '(' Type ')' ]
Block ::= '{' Stmt* '}'
Stmt ::= LetStmt | IfStmt | WhileStmt | ForStmt | MatchStmt | ReturnStmt | ExprStmt
LetStmt ::= 'let' Ident [ ':' Type ] '=' Expr ';'
IfStmt ::= 'if' Expr Block [ 'else' ( IfStmt | Block ) ]
WhileStmt ::= 'while' Expr Block
ForStmt ::= 'for' Ident 'in' ( Expr '..' Expr | Expr ) Block
MatchStmt ::= 'match' Expr '{' MatchArm* '}'
MatchArm ::= Ident '::' Ident [ '(' Ident ')' ] '=>' Block
ReturnStmt ::= 'return' [ Expr ] ';'
ExprStmt ::= Expr ';'
Expr ::= BinaryExpr
BinaryExpr ::= UnaryExpr ( BinaryOp UnaryExpr )*
UnaryExpr ::= ( '-' | '!' ) UnaryExpr | PrimaryExpr
PrimaryExpr ::= IntLit | StrLit | BoolLit | 'nil' | Ident | MemberExpr | CallExpr | NewExpr | '(' Expr ')'
NewExpr ::= 'new' Ident [ TypeParams ] '{' ( Ident ':' Expr ( ',' Ident ':' Expr )* )? '}'