From b3e5335deb75923ace9b60493a214e6bd80a0d5e Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Tue, 27 Jan 2026 10:42:49 +0000 Subject: [PATCH] add PBS docs --- docs/README.md | 2 +- docs/specs/{ => hardware}/README.md | 0 docs/specs/{ => hardware}/topics/chapter-1.md | 0 .../specs/{ => hardware}/topics/chapter-10.md | 0 .../specs/{ => hardware}/topics/chapter-11.md | 0 .../specs/{ => hardware}/topics/chapter-12.md | 0 .../specs/{ => hardware}/topics/chapter-13.md | 0 .../specs/{ => hardware}/topics/chapter-14.md | 0 .../specs/{ => hardware}/topics/chapter-15.md | 0 docs/specs/{ => hardware}/topics/chapter-2.md | 0 docs/specs/{ => hardware}/topics/chapter-3.md | 0 docs/specs/{ => hardware}/topics/chapter-4.md | 0 docs/specs/{ => hardware}/topics/chapter-5.md | 0 docs/specs/{ => hardware}/topics/chapter-6.md | 0 docs/specs/{ => hardware}/topics/chapter-7.md | 0 docs/specs/{ => hardware}/topics/chapter-8.md | 0 docs/specs/{ => hardware}/topics/chapter-9.md | 0 .../topics/table-of-contents.md | 0 docs/specs/pbs/PBS - Canonical Addenda.md | 359 ++ docs/specs/pbs/PRs para Junie.md | 357 ++ ...Base Script (PBS) - Implementation Spec.md | 446 ++ .../pbs/Prometeu Scripting - Language Tour.md | 341 ++ ...ipting - Prometeu Bytecode Script (PBS).md | 4882 +++++++++++++++++ .../src/prometeu_scripting.md | 648 --- 24 files changed, 6386 insertions(+), 649 deletions(-) rename docs/specs/{ => hardware}/README.md (100%) rename docs/specs/{ => hardware}/topics/chapter-1.md (100%) rename docs/specs/{ => hardware}/topics/chapter-10.md (100%) rename docs/specs/{ => hardware}/topics/chapter-11.md (100%) rename docs/specs/{ => hardware}/topics/chapter-12.md (100%) rename docs/specs/{ => hardware}/topics/chapter-13.md (100%) rename docs/specs/{ => hardware}/topics/chapter-14.md (100%) rename docs/specs/{ => hardware}/topics/chapter-15.md (100%) rename docs/specs/{ => hardware}/topics/chapter-2.md (100%) rename docs/specs/{ => hardware}/topics/chapter-3.md (100%) rename docs/specs/{ => hardware}/topics/chapter-4.md (100%) rename docs/specs/{ => hardware}/topics/chapter-5.md (100%) rename docs/specs/{ => hardware}/topics/chapter-6.md (100%) rename docs/specs/{ => hardware}/topics/chapter-7.md (100%) rename docs/specs/{ => hardware}/topics/chapter-8.md (100%) rename docs/specs/{ => hardware}/topics/chapter-9.md (100%) rename docs/specs/{ => hardware}/topics/table-of-contents.md (100%) create mode 100644 docs/specs/pbs/PBS - Canonical Addenda.md create mode 100644 docs/specs/pbs/PRs para Junie.md create mode 100644 docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md create mode 100644 docs/specs/pbs/Prometeu Scripting - Language Tour.md create mode 100644 docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md delete mode 100644 test-cartridges/color-square-pbs/src/prometeu_scripting.md diff --git a/docs/README.md b/docs/README.md index 620cd2fe..915003f5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,7 +6,7 @@ This directory contains the technical documentation and specifications for the P ### 📜 [Specifications (Specs)](./specs) Detailed documentation on system architecture, cartridge format, VM instruction set, and more. -- [Topic Index](./specs/topics/table-of-contents.md) +- [Topic Index](specs/hardware/topics/table-of-contents.md) ### 🐞 [Debugger](./debugger) Documentation on debugging tools and how to integrate new tools into the ecosystem. diff --git a/docs/specs/README.md b/docs/specs/hardware/README.md similarity index 100% rename from docs/specs/README.md rename to docs/specs/hardware/README.md diff --git a/docs/specs/topics/chapter-1.md b/docs/specs/hardware/topics/chapter-1.md similarity index 100% rename from docs/specs/topics/chapter-1.md rename to docs/specs/hardware/topics/chapter-1.md diff --git a/docs/specs/topics/chapter-10.md b/docs/specs/hardware/topics/chapter-10.md similarity index 100% rename from docs/specs/topics/chapter-10.md rename to docs/specs/hardware/topics/chapter-10.md diff --git a/docs/specs/topics/chapter-11.md b/docs/specs/hardware/topics/chapter-11.md similarity index 100% rename from docs/specs/topics/chapter-11.md rename to docs/specs/hardware/topics/chapter-11.md diff --git a/docs/specs/topics/chapter-12.md b/docs/specs/hardware/topics/chapter-12.md similarity index 100% rename from docs/specs/topics/chapter-12.md rename to docs/specs/hardware/topics/chapter-12.md diff --git a/docs/specs/topics/chapter-13.md b/docs/specs/hardware/topics/chapter-13.md similarity index 100% rename from docs/specs/topics/chapter-13.md rename to docs/specs/hardware/topics/chapter-13.md diff --git a/docs/specs/topics/chapter-14.md b/docs/specs/hardware/topics/chapter-14.md similarity index 100% rename from docs/specs/topics/chapter-14.md rename to docs/specs/hardware/topics/chapter-14.md diff --git a/docs/specs/topics/chapter-15.md b/docs/specs/hardware/topics/chapter-15.md similarity index 100% rename from docs/specs/topics/chapter-15.md rename to docs/specs/hardware/topics/chapter-15.md diff --git a/docs/specs/topics/chapter-2.md b/docs/specs/hardware/topics/chapter-2.md similarity index 100% rename from docs/specs/topics/chapter-2.md rename to docs/specs/hardware/topics/chapter-2.md diff --git a/docs/specs/topics/chapter-3.md b/docs/specs/hardware/topics/chapter-3.md similarity index 100% rename from docs/specs/topics/chapter-3.md rename to docs/specs/hardware/topics/chapter-3.md diff --git a/docs/specs/topics/chapter-4.md b/docs/specs/hardware/topics/chapter-4.md similarity index 100% rename from docs/specs/topics/chapter-4.md rename to docs/specs/hardware/topics/chapter-4.md diff --git a/docs/specs/topics/chapter-5.md b/docs/specs/hardware/topics/chapter-5.md similarity index 100% rename from docs/specs/topics/chapter-5.md rename to docs/specs/hardware/topics/chapter-5.md diff --git a/docs/specs/topics/chapter-6.md b/docs/specs/hardware/topics/chapter-6.md similarity index 100% rename from docs/specs/topics/chapter-6.md rename to docs/specs/hardware/topics/chapter-6.md diff --git a/docs/specs/topics/chapter-7.md b/docs/specs/hardware/topics/chapter-7.md similarity index 100% rename from docs/specs/topics/chapter-7.md rename to docs/specs/hardware/topics/chapter-7.md diff --git a/docs/specs/topics/chapter-8.md b/docs/specs/hardware/topics/chapter-8.md similarity index 100% rename from docs/specs/topics/chapter-8.md rename to docs/specs/hardware/topics/chapter-8.md diff --git a/docs/specs/topics/chapter-9.md b/docs/specs/hardware/topics/chapter-9.md similarity index 100% rename from docs/specs/topics/chapter-9.md rename to docs/specs/hardware/topics/chapter-9.md diff --git a/docs/specs/topics/table-of-contents.md b/docs/specs/hardware/topics/table-of-contents.md similarity index 100% rename from docs/specs/topics/table-of-contents.md rename to docs/specs/hardware/topics/table-of-contents.md diff --git a/docs/specs/pbs/PBS - Canonical Addenda.md b/docs/specs/pbs/PBS - Canonical Addenda.md new file mode 100644 index 00000000..f5e34d40 --- /dev/null +++ b/docs/specs/pbs/PBS - Canonical Addenda.md @@ -0,0 +1,359 @@ +# PBS v0 Canonical Addenda + +> **Purpose:** eliminate ambiguity for Junie and for golden tests. +> +> This document is **normative** for PBS Frontend v0 and complements: +> +> * **PBS Frontend Spec v0 — Implementer Edition** +> * **Junie PR Plan** +> +> These addenda define: +> +> 1. operator precedence & associativity +> 2. canonical AST JSON shape (v0) +> 3. canonical diagnostic codes (v0) + +--- + +## 1) Operator Precedence and Associativity (v0) + +### 1.1 Guiding rule + +PBS v0 prioritizes **minimal ambiguity** and **easy parsing**. + +* Most operators are **left-associative**. +* Assignment is **not** an expression in v0 (no `=` operator expressions). +* Member access and indexing are not part of v0 surface syntax unless already defined elsewhere. + +### 1.2 Precedence table + +From **highest** to **lowest**: + +1. **Primary** + + * literals (`10`, `3.14`, `"text"`, `none`, `some(x)`, `ok(x)`, `err(e)`) + * identifiers (`foo`) + * parenthesized expression (`(expr)`) + * block expression (`{ ... }`) (when allowed as `Expr`) + +2. **Call** (left-associative) + + * `callee(arg1, arg2, ...)` + +3. **Unary prefix** (right-associative) + + * `-expr` + * `!expr` + * `as` casts are **not unary**; see level 6. + +4. **Multiplicative** (left-associative) + + * `*`, `/`, `%` + +5. **Additive** (left-associative) + + * `+`, `-` + +6. **Cast** (left-associative) + + * `expr as Type` + +7. **Comparison** (non-associative) + + * `<`, `<=`, `>`, `>=` + +8. **Equality** (non-associative) + + * `==`, `!=` + +9. **Logical AND** (left-associative) + + * `&&` + +10. **Logical OR** (left-associative) + + * `||` + +11. **Control expressions** (special) + + * `if ... { ... } else { ... }` (expression form only if your v0 allows it; otherwise statement) + * `when { ... }` (expression) + +### 1.3 Non-associative rule + +Comparison and equality are **non-associative**: + +* `a < b < c` is an error +* `a == b == c` is an error + +Diagnostic: `E_PARSE_NON_ASSOC`. + +### 1.4 Notes on `when` + +`when` binds weaker than all binary operators. + +Example: + +```pbs +let x = a + b when { ... }; +``` + +Parses as: + +``` +let x = (a + b) when { ... }; +``` + +--- + +## 2) Canonical AST JSON (v0) + +### 2.1 Canonicalization goals + +Canonical AST JSON is used for: + +* golden tests +* frontend determinism validation +* diff-friendly debugging + +Rules: + +* JSON keys are **stable** and **ordered** (when writing JSON) +* All nodes include `kind` and `span` +* Spans are byte offsets into the file content + +### 2.2 Span encoding + +```json +{"file":"main.pbs","start":12,"end":18} +``` + +Where: + +* `start` is inclusive +* `end` is exclusive + +### 2.3 Root + +```json +{ + "kind": "File", + "span": {"file":"...","start":0,"end":123}, + "imports": [ ... ], + "decls": [ ... ] +} +``` + +### 2.4 Import node + +```json +{ + "kind": "Import", + "span": {"file":"...","start":0,"end":20}, + "spec": {"kind":"ImportSpec","path":["Foo","Bar"]}, + "from": "./lib.pbs" +} +``` + +`ImportSpec.path` is an array of identifiers. + +### 2.5 Declarations + +#### 2.5.1 Service + +```json +{ + "kind": "ServiceDecl", + "span": {"file":"...","start":0,"end":50}, + "vis": "pub", + "name": "Audio", + "extends": null, + "members": [ ... ] +} +``` + +A service member (method signature only in v0): + +```json +{ + "kind": "ServiceFnSig", + "span": {"file":"...","start":0,"end":10}, + "name": "play", + "params": [ {"name":"sound","ty": {"kind":"TypeName","name":"Sound"}} ], + "ret": {"kind":"TypeName","name":"void"} +} +``` + +#### 2.5.2 Function + +```json +{ + "kind": "FnDecl", + "span": {"file":"...","start":0,"end":80}, + "name": "main", + "params": [], + "ret": null, + "else": null, + "body": {"kind":"Block", ... } +} +``` + +#### 2.5.3 TypeDecl (struct/contract/error) + +```json +{ + "kind": "TypeDecl", + "span": {"file":"...","start":0,"end":100}, + "vis": "pub", + "typeKind": "struct", + "name": "Vector", + "body": {"kind":"TypeBody","members":[ ... ]} +} +``` + +### 2.6 Blocks and statements + +Block: + +```json +{ + "kind": "Block", + "span": {"file":"...","start":0,"end":20}, + "stmts": [ ... ], + "tail": null +} +``` + +* `stmts` are statements. +* `tail` is an optional final expression (only if your parser supports expression blocks). + +Statement kinds (v0 minimum): + +* `LetStmt` +* `ExprStmt` +* `ReturnStmt` + +Let: + +```json +{ + "kind": "LetStmt", + "span": {"file":"...","start":0,"end":20}, + "name": "x", + "isMut": false, + "ty": null, + "init": {"kind":"IntLit", "value": 10, "span": ...} +} +``` + +Return: + +```json +{ + "kind": "ReturnStmt", + "span": {"file":"...","start":0,"end":10}, + "expr": null +} +``` + +### 2.7 Expressions + +All expressions include `kind` and `span`. + +Minimal v0 expression node kinds: + +* `IntLit` `{ value: i64 }` +* `FloatLit` `{ value: f64 }` +* `BoundedLit` `{ value: u32 }` +* `StringLit` `{ value: string }` +* `Ident` `{ name: string }` +* `Call` `{ callee: Expr, args: Expr[] }` +* `Unary` `{ op: "-"|"!", expr: Expr }` +* `Binary` `{ op: string, left: Expr, right: Expr }` +* `Cast` `{ expr: Expr, ty: TypeRef }` +* `IfExpr` `{ cond: Expr, then: Block, els: Block }` (if expression is supported) +* `WhenExpr` `{ arms: WhenArm[] }` + +Type references: + +```json +{"kind":"TypeName","name":"int"} +``` + +Generics: + +```json +{"kind":"TypeApp","base":"optional","args":[{"kind":"TypeName","name":"int"}]} +``` + +### 2.8 Canonical JSON ordering + +When writing AST JSON, always order fields as: + +1. `kind` +2. `span` +3. semantic fields (stable order) + +This makes diffs deterministic. + +--- + +## 3) Diagnostic Codes (v0) + +### 3.1 Diagnostic format + +All diagnostics must be serializable to canonical JSON: + +```json +{ + "severity": "error", + "code": "E_PARSE_UNEXPECTED_TOKEN", + "message": "Unexpected token '}'", + "span": {"file":"main.pbs","start":12,"end":13} +} +``` + +Severity is one of: `error`, `warning`. + +### 3.2 Parse/Lex errors (E_PARSE_*) + +* `E_LEX_INVALID_CHAR` — invalid character +* `E_LEX_UNTERMINATED_STRING` — string literal not closed +* `E_PARSE_UNEXPECTED_TOKEN` — token not expected in current context +* `E_PARSE_EXPECTED_TOKEN` — missing required token +* `E_PARSE_NON_ASSOC` — chained comparison/equality (non-associative) + +### 3.3 Symbol/Resolve errors (E_RESOLVE_*) + +* `E_RESOLVE_UNDEFINED` — undefined identifier +* `E_RESOLVE_DUPLICATE_SYMBOL` — duplicate symbol in same namespace +* `E_RESOLVE_NAMESPACE_COLLISION` — name exists in both type and value namespaces +* `E_RESOLVE_VISIBILITY` — symbol not visible from this scope/module +* `E_RESOLVE_INVALID_IMPORT` — import spec/path invalid + +### 3.4 Type errors (E_TYPE_*) + +* `E_TYPE_MISMATCH` — type mismatch +* `E_TYPE_UNKNOWN_TYPE` — unknown type name +* `E_TYPE_MUTABILITY` — mutability violation +* `E_TYPE_RETURN_PATH` — not all paths return a value +* `E_TYPE_INVALID_CAST` — invalid cast + +### 3.5 Lowering errors (E_LOWER_*) + +* `E_LOWER_UNSUPPORTED` — feature not supported in v0 lowering + +### 3.6 Warnings (W_*) + +Warnings are allowed in v0 but should be used sparingly. + +* `W_UNUSED_LET` — unused local binding +* `W_SHADOWING` — local shadows another binding + +--- + +## Implementation Notes (Non-normative) + +* Keep these addenda in `spec/` in the repo. +* Use them to drive golden tests for AST and diagnostics. +* If a future change alters canonical AST, bump a version and regenerate goldens deliberately. diff --git a/docs/specs/pbs/PRs para Junie.md b/docs/specs/pbs/PRs para Junie.md new file mode 100644 index 00000000..5a15af11 --- /dev/null +++ b/docs/specs/pbs/PRs para Junie.md @@ -0,0 +1,357 @@ +# PBS Compiler — Junie PR Plan + +> **Purpose:** this document defines a sequence of small, focused Pull Requests to be implemented by *Junie*, one at a time. +> +> **Audience:** compiler implementer (AI or human). +> +> **Scope:** PBS-first compiler architecture. TS and Lua frontends are assumed **removed**. +> +> **Hard rules:** +> +> * Each PR must compile and pass tests. +> * Each PR must include tests. +> * No speculative features. +> * Follow the `Prometeu Base Script (PBS) - Implementation Spec`. + +--- + +## Global Architectural Direction (Non-negotiable) + +* PBS is the **primary language**. +* Frontend is implemented **before** runtime integration. +* Architecture uses **two IR layers**: + + * **Core IR** (PBS-semantic, typed, resolved) + * **VM IR** (stack-based, backend-friendly) +* VM IR remains simple and stable. +* Lowering is explicit and testable. + +--- + +# PR-01 — ProjectConfig and Frontend Selection + +### Goal + +Introduce a project-level configuration that selects the frontend and entry file explicitly. + +### Motivation + +The compiler must not hardcode entry points or languages. PBS will be the first frontend, others may return later. + +### Scope + +* Add `ProjectConfig` (serde-deserializable) loaded from `prometeu.json` +* Fields (v0): + + * `script_fe: "pbs"` + * `entry: "main.pbs"` +* Refactor compiler entry point to: + + * load config + * select frontend by `script_fe` + * resolve entry path relative to project root + +### Files Likely Touched + +* `compiler/mod.rs` +* `compiler/driver.rs` +* `common/config.rs` (new) + +### Tests (mandatory) + +* unit test: load valid `prometeu.json` +* unit test: invalid frontend → diagnostic +* integration test: project root + entry resolution + +### Notes to Junie + +Do **not** add PBS parsing yet. This PR is infrastructure only. + +--- + +# PR-02 — Core IR Skeleton (PBS-first) + +### Goal + +Introduce a **Core IR** layer independent from the VM IR. + +### Motivation + +PBS semantics must be represented before lowering to VM instructions. + +### Scope + +* Add new module: `ir_core` +* Define minimal structures: + + * `Program` + * `Module` + * `Function` + * `Block` + * `Instr` + * `Terminator` +* IDs only (no string-based calls): + + * `FunctionId` + * `ConstId` + * `TypeId` + +### Constraints + +* Core IR must NOT reference VM opcodes +* No lowering yet + +### Tests + +* construct Core IR manually in tests +* snapshot test (JSON) for deterministic shape + +--- + +# PR-03 — Constant Pool and IDs + +### Goal + +Introduce a stable constant pool shared by Core IR and VM IR. + +### Scope + +* Add `ConstPool`: + + * strings + * numbers +* Replace inline literals in VM IR with `ConstId` +* Update existing VM IR to accept `PushConst(ConstId)` + +### Tests + +* const pool deduplication +* deterministic ConstId assignment +* IR snapshot stability + +--- + +# PR-04 — VM IR Cleanup (Stabilization) + +### Goal + +Stabilize VM IR as a **lowering target**, not a language IR. + +### Scope + +* Replace string-based calls with `FunctionId` +* Ensure locals are accessed via slots +* Remove or internalize `PushScope` / `PopScope` + +### Tests + +* golden VM IR tests +* lowering smoke test (Core IR → VM IR) + +--- + +# PR-05 — Core IR → VM IR Lowering Pass + +### Goal + +Implement the lowering pass from Core IR to VM IR. + +### Scope + +* New module: `lowering/core_to_vm.rs` +* Lowering rules: + + * Core blocks → labels + * Core calls → VM calls + * Host calls preserved +* No PBS frontend yet + +### Tests + +* lowering correctness +* instruction ordering +* label resolution + +--- + +# PR-06 — PBS Frontend: Lexer + +### Goal + +Implement PBS lexer according to the spec. + +### Scope + +* Token kinds +* Keyword table +* Span tracking + +### Tests + +* tokenization tests +* keyword vs identifier tests +* bounded literals + +--- + +# PR-07 — PBS Frontend: Parser (Raw AST) + +### Goal + +Parse PBS source into a raw AST. + +### Scope + +* Imports +* Top-level declarations +* Blocks +* Expressions (calls, literals, control flow) + +### Tests + +* valid programs +* syntax error recovery + +--- + +# PR-08 — PBS Frontend: Symbol Collection and Resolver + +### Goal + +Resolve names, modules, and visibility. + +### Scope + +* Type namespace vs value namespace +* Visibility rules +* Import resolution + +### Tests + +* duplicate symbols +* invalid imports +* visibility errors + +--- + +# PR-09 — PBS Frontend: Type Checking + +### Goal + +Validate PBS semantics. + +### Scope + +* Primitive types +* Structs +* `optional` and `result` +* Mutability rules +* Return path validation + +### Tests + +* type mismatch +* mutability violations +* implicit `none` behavior + +--- + +# PR-10 — PBS Frontend: Semantic Lowering to Core IR + +### Goal + +Lower typed PBS AST into Core IR. + +### Scope + +* ID-based calls +* ConstPool usage +* Control flow lowering +* SAFE vs HIP effects represented explicitly + +### Tests + +* PBS → Core IR snapshots +* semantic correctness + +--- + +# PR-11 — Host-bound Contracts and Syscall Mapping + +### Goal + +Connect PBS host-bound contracts to runtime syscalls (without executing them). + +### Scope + +* Contract registry +* Mapping: contract.method → syscall id +* Core IR host call nodes + +### Tests + +* invalid contract calls +* correct syscall mapping + +--- + +# PR-12 — Diagnostics Canonicalization + +### Goal + +Standardize diagnostics output. + +### Scope + +* Error codes (`E_*`, `W_*`) +* Stable messages +* Span accuracy + +### Tests + +* golden diagnostics + +--- + +# PR-13 — Backend Integration (VM IR → Bytecode) + +### Goal + +Reconnect the pipeline to the Prometeu runtime backend. + +### Scope + +* VM IR → bytecode emission +* No PBS semantics here + +### Tests + +* bytecode emission smoke test + +--- + +# PR-14 — End-to-End PBS Compile Test + +### Goal + +Prove the full pipeline works. + +### Scope + +* Sample PBS project +* Compile → bytecode +* Diagnostics only (no execution) + +### Tests + +* golden bytecode snapshot + +--- + +## Final Note to Junie + +Do **not** skip PRs. +Do **not** merge multiple PRs together. +If the spec is unclear, create a failing test and document the ambiguity. + +This plan is the authoritative roadmap for PBS frontend implementation. diff --git a/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md b/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md new file mode 100644 index 00000000..5f3e53bc --- /dev/null +++ b/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md @@ -0,0 +1,446 @@ +# Prometeu Base Script (PBS) + +## Frontend Spec v0 — Implementer Edition + +> **Normative specification for building a PBS frontend (lexer, parser, AST, resolver, typechecker) targeting the Prometeu Fantasy Console runtime.** +> +> This document is **not** a user guide. +> It exists to make PBS *implementable*, *deterministic*, and *testable*. + +--- + +## 0. Scope and Non‑Goals + +### 0.1 Scope + +This specification defines: + +* lexical structure (tokens) +* grammar and parsing rules +* canonical AST shapes +* frontend phases and their responsibilities +* name resolution and visibility rules +* type system rules +* desugaring and lowering rules +* diagnostic categories (errors vs warnings) +* required guarantees for runtime integration + +The goal is that **two independent frontends** built from this spec: + +* accept the same programs +* reject the same programs +* produce equivalent ASTs +* emit equivalent diagnostics + +### 0.2 Non‑Goals + +This spec does **not** define: + +* runtime performance characteristics +* bytecode layout +* JIT or interpreter design +* editor tooling or IDE features + +Those are explicitly out of scope. + +--- + +## 1. Frontend Pipeline Overview + +A PBS frontend **must** be structured as the following pipeline: + +``` +Source Text + ↓ +Lexer + ↓ +Parser + ↓ +Raw AST + ↓ +Symbol Collection + ↓ +Resolver + ↓ +Typed AST + ↓ +Desugaring / Lowering + ↓ +Runtime‑ready IR / AST +``` + +Each stage has **strict responsibilities**. +No stage may perform work assigned to a later stage. + +--- + +## 2. Lexical Structure + +### 2.1 Tokens + +A PBS lexer must recognize at minimum the following token classes: + +* identifiers +* keywords +* numeric literals +* string literals +* punctuation +* operators +* comments + +Whitespace is insignificant except as a separator. + +--- + +### 2.2 Keywords (Reserved) + +The following keywords are **reserved** and may not be used as identifiers: + +``` +import +pub +mod +service +fn +let +mut +declare +struct +contract +host +error +optional +result +some +none +ok +err +if +else +when +for +in +return +handle +borrow +mutate +peek +take +alloc +weak +as +``` + +--- + +### 2.3 Literals + +#### Numeric Literals + +* `int` — decimal digits +* `float` — decimal with `.` +* `bounded` — decimal digits suffixed with `b` + +Examples: + +```pbs +10 +42 +3.14 +0b +255b +``` + +#### String Literals + +* delimited by `"` +* UTF‑8 encoded +* immutable + +--- + +### 2.4 Comments + +* line comment: `// until end of line` +* block comments are **not supported** in v0 + +--- + +## 3. Grammar (EBNF‑style) + +> This grammar is **normative** but simplified for readability. +> Implementers may refactor internally as long as semantics are preserved. + +### 3.1 File Structure + +``` +File ::= Import* TopLevelDecl* +Import ::= 'import' ImportSpec 'from' StringLiteral +TopLevelDecl::= TypeDecl | ServiceDecl | FnDecl +``` + +--- + +### 3.2 Type Declarations + +``` +TypeDecl ::= Visibility? 'declare' TypeKind Identifier TypeBody +TypeKind ::= 'struct' | 'contract' | 'error' +``` + +--- + +### 3.3 Services + +``` +ServiceDecl ::= Visibility 'service' Identifier (':' Identifier)? Block +``` + +Visibility is mandatory for services. + +--- + +### 3.4 Functions + +``` +FnDecl ::= 'fn' Identifier ParamList ReturnType? ElseFallback? Block +``` + +Top‑level `fn` are always file‑private. + +--- + +### 3.5 Expressions (Partial) + +``` +Expr ::= Literal + | Identifier + | CallExpr + | Block + | IfExpr + | WhenExpr + | ForExpr + | ReturnExpr +``` + +Expression grammar is intentionally restricted in v0. + +--- + +## 4. Canonical AST + +The frontend **must** produce a canonical AST. + +### 4.1 AST Invariants + +* AST nodes are immutable after creation +* Parent pointers are optional +* Source spans must be preserved for diagnostics + +--- + +### 4.2 Core Node Kinds + +Minimal required node kinds: + +* `FileNode` +* `ImportNode` +* `ServiceNode` +* `FunctionNode` +* `StructDeclNode` +* `ContractDeclNode` +* `BlockNode` +* `LetNode` +* `CallNode` +* `IfNode` +* `WhenNode` +* `ForNode` +* `ReturnNode` + +Implementers may add internal nodes but must normalize before later phases. + +--- + +## 5. Symbol Collection Phase + +### 5.1 Purpose + +Symbol Collection builds **module‑level symbol tables** without resolving bodies. + +Collected symbols: + +* `pub` and `mod` type declarations +* `pub` and `mod` services + +Excluded: + +* function bodies +* expressions +* local bindings + +--- + +### 5.2 Namespaces + +PBS has **two namespaces**: + +* Type namespace +* Value namespace + +Rules: + +* A name may not exist in both namespaces +* Violations are compile‑time errors + +--- + +## 6. Resolver Phase + +### 6.1 Responsibilities + +Resolver must: + +* resolve all identifiers +* enforce visibility rules +* bind references to symbols + +Resolution order (per namespace): + +1. local bindings +2. file‑private declarations +3. module symbols +4. imported symbols + +--- + +### 6.2 Visibility Rules (Normative) + +* file‑private: visible only in the same file +* `mod`: visible within the module +* `pub`: visible across modules via import + +Violations are errors. + +--- + +## 7. Type Checking + +### 7.1 Type Categories + +Frontend must support: + +* primitive types +* struct value types +* `optional` +* `result` +* gate‑backed types (opaque at frontend level) + +--- + +### 7.2 Mutability Rules + +* mutability belongs to bindings, not types +* `mut` is part of binding metadata +* mutability violations are compile‑time errors + +--- + +### 7.3 Function Checking + +Rules: + +* all paths must return a value unless `else` fallback exists +* `optional` may implicitly return `none` +* `result` must return explicitly + +--- + +## 8. Desugaring and Lowering + +### 8.1 Purpose + +Lowering transforms surface syntax into a minimal core language. + +Examples: + +* `take x.push(v)` → `mutate x as t { t.push(v) }` +* `when` → conditional expression node +* implicit `return none` for `optional` + +Lowered AST must contain **no syntactic sugar**. + +--- + +## 9. Diagnostics Model + +Diagnostics are first‑class frontend outputs. + +### 9.1 Categories + +* Error — compilation must fail +* Warning — compilation may continue + +--- + +### 9.2 Required Errors + +Examples: + +* unresolved identifier +* visibility violation +* type mismatch +* mutability violation +* invalid gate conversion + +--- + +## 10. Runtime Interface Assumptions + +Frontend assumes: + +* host‑bound contracts map to runtime syscalls +* allocation primitives exist (`alloc`) +* reference counting is handled by runtime + +Frontend must **not** assume GC or heap layout. + +--- + +## 11. Determinism Guarantees + +A valid PBS frontend **must guarantee**: + +* deterministic parsing +* deterministic name resolution +* deterministic type checking +* deterministic diagnostics + +No frontend stage may depend on execution order or host state. + +--- + +## 12. Conformance Criteria + +A frontend is PBS‑v0‑conformant if: + +* it implements all rules in this document +* it produces canonical ASTs +* it rejects all invalid programs defined herein + +This document is the **source of truth** for PBS v0 frontend behavior. + +--- + +## 13. Future Evolution (Non‑Normative) + +Future versions may add: + +* pattern matching +* richer type inference +* macros + +No v0 frontend is required to support these. + +--- + +## End of Spec diff --git a/docs/specs/pbs/Prometeu Scripting - Language Tour.md b/docs/specs/pbs/Prometeu Scripting - Language Tour.md new file mode 100644 index 00000000..d3066571 --- /dev/null +++ b/docs/specs/pbs/Prometeu Scripting - Language Tour.md @@ -0,0 +1,341 @@ +# Prometeu Base Script (PBS) + +> **A didactic scripting language for game development with explicit cost, explicit memory, and predictable runtime.** + +--- + +## 0. What PBS Is (and What It Is Not) + +PBS (Prometeu Base Script) is a **small, explicit scripting language designed for game engines and real‑time runtimes**. + +Its core goals are: + +* **Didactic clarity** — the language teaches how memory, data, and APIs really work. +* **Game‑friendly execution** — predictable runtime, no hidden allocation, no tracing GC. +* **Explicit cost model** — you always know *when* you allocate, *where* data lives, and *who* can mutate it. + +PBS is **not**: + +* a general‑purpose application language +* a productivity scripting language like Python or Lua +* a language that hides runtime cost + +PBS is intentionally opinionated. It is built for developers who want **control, predictability, and understanding**, especially in **game and engine contexts**. + +--- + +## 1. The Core Philosophy + +PBS is built around one rule: + +> **If you do not allocate, you are safe. If you allocate, you are responsible.** + +From this rule, everything else follows. + +### 1.1 Two Worlds + +PBS has **two explicit memory worlds**: + +| World | Purpose | Properties | +| ----- | -------------- | ---------------------------------------------- | +| SAFE | Stack / values | No aliasing, no leaks, no shared mutation | +| HIP | Storage / heap | Explicit aliasing, shared mutation, refcounted | + +You never cross between these worlds implicitly. + +--- + +## 2. First Contact: SAFE‑Only PBS + +A PBS program that never allocates lives entirely in the SAFE world. + +SAFE code: + +* uses value semantics +* copies data on assignment (conceptually) +* cannot leak memory +* cannot accidentally share mutable state + +This makes SAFE PBS ideal for: + +* gameplay logic +* math and simulation +* AI logic +* scripting without fear + +Example: + +```pbs +let a = Vector(1, 2); +let b = a; // conceptual copy + +b.scale(2); +// a is unchanged +``` + +No pointers. No references. No surprises. + +--- + +## 3. Values, Not Objects + +PBS does not have objects with identity by default. + +### 3.1 Value Types + +All basic types are **values**: + +* numbers +* bool +* string (immutable) +* tuples +* user‑defined `struct` + +A value: + +* has no identity +* has no lifetime beyond its scope +* is safe to copy + +This matches how most gameplay data *should* behave. + +--- + +## 4. Structs (Data First) + +Structs are **pure data models**. + +```pbs +declare struct Vector(x: float, y: float) +{ + pub fn len(self: this): float { ... } + pub fn scale(self: mut this, s: float): void { ... } +} +``` + +Rules: + +* fields are private +* mutation is explicit (`mut this`) +* no inheritance +* no identity + +Structs are designed to be: + +* cache‑friendly +* predictable +* easy to reason about + +--- + +## 5. Mutability Is a Property of Bindings + +In PBS, **types are never mutable**. Bindings are. + +```pbs +let v = Vector.ZERO; +v.scale(); // ERROR + +let w = mut Vector.ZERO; +w.scale(); // OK +``` + +This single rule eliminates entire classes of bugs. + +--- + +## 6. When You Need Power: Allocation (HIP World) + +Allocation is **explicit**. + +```pbs +let enemies = alloc list(); +``` + +Once you allocate: + +* data lives in Storage (heap) +* access happens through **gates** (handles) +* aliasing is real and visible + +This is intentional and explicit. + +--- + +## 7. Gates (Handles, Not Pointers) + +A gate is a small value that refers to heap storage. + +Properties: + +* cheap to copy +* may alias shared data +* managed by reference counting + +### 7.1 Strong vs Weak Gates + +* **Strong gate** — keeps data alive +* **Weak gate** — observes without ownership + +```pbs +let a = alloc Node; +let w: weak = a as weak; +let maybeA = w as strong; +``` + +This model mirrors real engine constraints without hiding them. + +--- + +## 8. Controlled Access to Storage + +Heap data is never accessed directly. + +You must choose *how*: + +* `peek` — copy to SAFE +* `borrow` — temporary read‑only access +* `mutate` — temporary mutable access + +```pbs +mutate enemies as e +{ + e.push(newEnemy); +} +``` + +This keeps mutation visible and scoped. + +--- + +## 9. Errors Are Values + +PBS has no exceptions. + +### 9.1 `optional` + +Used when absence is normal. + +```pbs +let x = maybeValue else 0; +``` + +### 9.2 `result` + +Used when failure matters. + +```pbs +let v = loadTexture(path)?; +``` + +Error propagation is explicit and typed. + +--- + +## 10. Control Flow Without Surprises + +PBS favors explicit flow: + +* `if` — control only +* `when` — expression +* `for` — bounded loops only + +```pbs +for i in [0b..count] { + update(i); +} +``` + +No unbounded iteration by accident. + +--- + +## 11. Services: Explicit API Boundaries + +A `service` is how behavior crosses module boundaries. + +```pbs +pub service Audio +{ + fn play(sound: Sound): void; +} +``` + +Services are: + +* explicit +* statically checked +* singleton‑like + +They map naturally to engine subsystems. + +--- + +## 12. Contracts: Static Guarantees + +Contracts define **what exists**, not how. + +```pbs +pub declare contract Gfx host +{ + fn drawText(x: int, y: int, msg: string): void; +} +``` + +Contracts: + +* have no runtime cost +* are validated at compile time +* define engine ↔ script boundaries + +--- + +## 13. Modules (Simple and Predictable) + +* one directory = one module +* only `pub` symbols cross modules +* no side effects on import + +This keeps build and runtime deterministic. + +--- + +## 14. Why PBS Works for Games + +PBS is designed around real engine needs: + +* frame‑based execution +* explicit allocation +* deterministic cleanup +* predictable performance + +It teaches developers **why engines are written the way they are**, instead of hiding reality. + +--- + +## 15. Who PBS Is For + +PBS is for: + +* game developers who want control +* engine developers +* students learning systems concepts +* educators teaching memory and runtime models + +PBS is *not* for: + +* rapid prototyping without constraints +* general app scripting +* hiding complexity + +--- + +## 16. Design Promise + +PBS makes one promise: + +> **Nothing happens behind your back.** + +If you understand PBS, you understand your runtime. + +That is the product. diff --git a/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md b/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md new file mode 100644 index 00000000..84a1f3d3 --- /dev/null +++ b/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md @@ -0,0 +1,4882 @@ +# Prometeu Base Script (PBS) + +**Status:** v0 (frontend-freeze) +**Goal:** +* explicit semantics +* predictable runtime +* two worlds: + - SAFE: stack/value-first, no aliasing + - HIP storage/heap: gate-baked with aliasing +* no GC tracing, but with explicit memory management by refcounts + reclaim by frame + +--- + +## 0. Philosophy (anchor section) + +## 0. Philosophy (anchor section) + +PBS is a **stack-only**, **value-first** language designed for **explicit semantics** and a **predictable runtime**. + +### 0.1 Non-negotiable constraints (v0) + +In v0, the following are true: + +* **No implicit references on SAFE model**. + - The HIP introduces handles (gates) and aliasing with an explicit sintax (`alloc`, `borrow`, `mutate`, `peek`, `take`). +* **No aliasing of mutable state between values on the stack**. Gate-baked types may alias and mutate state. +* **No exceptions**: failure is expressed only via explicit types (e.g. `optional`, `result`) and explicit syntax (`else`, `?`, `handle`). +* **No top-level execution**: modules/files contain declarations only; initialization occurs only through explicit function/service calls. + +### 0.2 Design principles (how v0 is shaped) + +PBS favors: + +* **Value-first**: values are conceptually independent; copying is always safe; identity does not exist for user-defined values. +* **Explicit mutability**: mutability is a property of bindings, never a property of types. +* **Didactic rules**: important rules must be visible in syntax, not hidden in conventions. +* **Runtime-cheap execution**: PBS shifts complexity to compile time (frontend-heavy) to keep the runtime simple and predictable. + +--- + +## 1. Project, Files & Modules + +This section defines the physical and logical structure of a PBS project. +All rules in this section are **normative**. + +--- + +### 1.1 Project Root and Source Layout + +A PBS project has a single **project root directory** (`{root}`). + +Rules: + +* `{root}` is the directory that contains the file `prometeu.json`. +* A project **must** contain the directory `{root}/src/main/modules`. +* All PBS modules of the project live **exclusively** under `{root}/src/main/modules`. + +Import resolution: + +* The import prefix `@project:` is resolved relative to `{root}/src/main/modules`. +* Any path after `@project:` is interpreted as a **module path**, not a file path. + +If `{root}/src/main/modules` does not exist, compilation fails. + +--- + +### 1.2 Module Model + +* **One directory = one module**. +* The name of a module is the directory name as it appears on disk. +* Module names are case-sensitive and are not normalized. + +A module boundary is **absolute**: + +* Parent directories do **not** implicitly see child modules. +* Child directories do **not** implicitly see parent modules. +* Sibling directories are completely isolated. + +Example: +``` +{root}/src/main/modules/ +├─ gfx/ +│ └─ draw.pbs +└─ gfx/math/ +└─ vec.pbs +``` + +In this structure: + +* `@project:gfx` and `@project:gfx/math` are **two distinct modules**. +* `@project:gfx` does **not** see any declarations in `@project:gfx/math`. +* `@project:gfx/math` does **not** see any declarations in `@project:gfx`. + +Cross-module access is **only** possible via `import`, and only for `pub` symbols. + +--- + +### 1.3 Automatic Module Index + +PBS has no mandatory barrel or index file. + +For each module directory `M`: + +* The compiler scans all `.pbs` files **directly inside** `M`. +* Files in subdirectories of `M` are ignored. +* The module’s **public index** consists of: + * all `pub` symbols + * declared in those `.pbs` files + * separated by namespace (type and value). + +The public index is the **only** surface visible to other modules. + +--- + +### 1.4 Visibility Modifiers + +Visibility is explicit and exhaustive. There are exactly three visibility levels. + +#### File-private (default) + +* No visibility modifier. +* Visible **only** within the declaring `.pbs` file. +* Never visible to other files, even inside the same module. + +#### `mod` (module-visible) + +* Visible to all files in the same module (same directory). +* Not visible outside the module. +* No `import` is required within the module. + +#### `pub` (public) + +* Exported as part of the module’s public API. +* Visible to other modules via `import`. +* Within the same module, `pub` behaves exactly like `mod`. + +Important rules: + +* A symbol has **exactly one** visibility level. +* There is no implicit or inferred visibility. +* Visibility is checked independently in the **type namespace** and the **value namespace**. +* Directory structure, file names, and compilation order have **no effect** on visibility. + +--- + +### 1.5 Symbols Covered by Visibility Rules + +Visibility rules apply uniformly to: + +* Type-level declarations: + * `declare struct` + * `declare error` + * `declare contract` +* Value-level declarations: + * `service` + * `fn` + * `let` + +No other mechanism may expose a symbol. + +--- + +## 2. Namespaces & Visibility + +PBS uses **two distinct and independent namespaces**: Type and Value. +This separation is strict, explicit, and fully enforced by the compiler. + +There are no implicit namespaces and no name overloading across namespaces. + +--- + +### 2.1 Namespace Model + +PBS defines exactly **two namespaces**: + +#### Type namespace + +The **type namespace** contains only type-level declarations introduced by `declare`. + +Valid type declarations are: + +```pbs +declare struct Name { ... } +declare error Name { ... } +declare contract Name { ... } +``` + +Rules: + +* Only declarations introduced by `declare` may appear in the type namespace. +* No executable or runtime symbol may appear in this namespace. +* Type declarations participate in visibility rules (`file-private`, `mod`, `pub`) but always remain in the type namespace. + +--- + +#### Value namespace + +The **value namespace** contains executable and runtime-visible symbols. + +Symbols in the value namespace are introduced by: + +* `service` +* top-level `fn` - always file-private. +* top-level `let` are not allowed. + +Rules: + +* Only top-level declarations participate in the value namespace. +* Local `let` bindings do **not** participate in the file, module, or project namespace. +* Local bindings are scoped strictly to their enclosing block. + +--- + +### 2.2 Strict Namespace Separation + +A name **MUST NOT** exist in both namespaces. + +Rules: + +* If a name appears in the type namespace, it cannot appear in the value namespace. +* If a name appears in the value namespace, it cannot appear in the type namespace. +* Any attempt to declare the same name in both namespaces is a **compile-time error**. + +Example (INVALID): + +```pbs +declare struct Vector { ... } + +service Vector { ... } // ERROR: `Vector` already exists in the type namespace +``` + +--- + +### 2.3 Scope of Name Uniqueness + +Name uniqueness is enforced based on **visibility scope**. + +--- + +#### File-private symbols (default visibility) + +* File-private symbols are visible **only** within the declaring file. +* File-private symbols may reuse names that appear in other files or modules. + +Example (VALID): + +```pbs +// file A.pbs +fn helper() { ... } + +// file B.pbs (same module) +fn helper() { ... } // OK: file-private symbols are isolated per file +``` + +--- + +#### `mod` and `pub` symbols + +* Symbols declared with `mod` or `pub` visibility must be **unique inside the module**. +* Declaring two `mod` or `pub` symbols with the same name in the same module is a **compile-time error**, even if they appear in different files. + +Example (INVALID): + +```pbs +// file A.pbs +mod service Gfx { ... } + +// file B.pbs (same module) +mod service Gfx { ... } // ERROR: duplicate symbol in module + +// file C.pbs (same module) +mod declare error Gfx { ... } // ERROR: duplicate symbol in module +``` + +--- + +### 2.4 Visibility Does Not Create Namespaces + +Visibility controls **who can see a symbol**, but does **not** create separate namespaces. + +Rules: + +* `file-private`, `mod`, and `pub` symbols all live in the same namespace. +* Visibility only restricts access; it never permits name reuse at the same scope. + +Example (INVALID): + +```pbs +mod service Audio { ... } +pub service Audio { ... } // ERROR: duplicate symbol in module +``` + +--- + +### 2.5 Summary of Namespace Rules + +* PBS has exactly **two namespaces**: type and value. +* A name cannot exist in both namespaces. +* File-private symbols are unique only within a file. +* `mod` and `pub` symbols are unique at the module level. +* Visibility never creates new namespaces or exceptions to uniqueness rules. + +--- + +## 3. Top-level Declarations + +This section defines which declarations are allowed at the top level of a `.pbs` file and their semantic role in the language. +All rules in this section are **normative**. + +--- + +### 3.1 Allowed Top-level Declarations + +A `.pbs` file may contain the following declarations, in any order: + +* `import` +* **type declarations** via `declare`: + + * `declare struct` + * `declare error` + * `declare contract` +* `service` +* `fn` + +No other constructs are allowed at the top level. +In particular: + +* `let` is **not allowed** at the top level. +* No executable statements may appear at the top level. +* There is no top-level initialization or execution. + +--- + +### 3.2 Top-level Execution Model + +PBS has **no top-level execution**. + +Rules: + +* A `.pbs` file is a collection of declarations only. +* Code is executed only when: + + * a `service` method is invoked, or + * a `fn` is called from within another function or service method. +* Module loading does not execute any user-defined code. + +This rule ensures that: + +* module import order has no semantic effect +* import cycles can be resolved purely at the symbol level +* the runtime remains predictable and side-effect free during loading + +--- + +### 3.3 Functions (`fn`) + +Top-level `fn` declarations define reusable executable logic. + +Rules: + +* A top-level `fn` is always **file-private**. +* A top-level `fn` cannot be declared as `mod` or `pub`. +* A top-level `fn` is visible only within the file where it is declared. + +Example (VALID): + +```pbs +// math.pbs +fn clamp(x: int, min: int, max: int): int { ... } +``` + +Example (INVALID): + +```pbs +pub fn clamp(x: int): int { ... } // ERROR: top-level fn cannot be pub +``` + +Rationale: + +* `fn` exists for implementation and helper logic. +* Any logic intended to cross file or module boundaries must be exposed via a `service`. + +--- + +### 3.4 Services (`service`) + +A `service` is a top-level declaration that represents an explicit API boundary. + +Rules: + +* A `service` must always be declared with an explicit visibility modifier: + + * `pub service` — public API of the module + * `mod service` — internal API of the module +* There is no private service. +* A service may optionally implement a `contract`. + +Example: + +```pbs +pub service Audio +{ + fn play(sound: Sound): void { ... } +} +``` + +Services are the **only** mechanism for exposing executable behavior across files or modules. + +--- + +### 3.5 Type Declarations (`declare`) + +Type declarations introduce symbols into the **type namespace**. + +Rules: + +* All type declarations must use the `declare` keyword. +* Type declarations may be `file-private`, `mod`, or `pub`. +* Visibility controls where the type can be referenced. + +Example: + +```pbs +pub declare struct Vector { ... } +mod declare error IOError { ... } +``` + +--- + +### 3.6 Imports at the Top Level + +Rules: + +* `import` declarations are allowed only at the top level of a file. +* Imports must appear before any usage of imported symbols. +* Imports have no runtime effect and exist solely for name resolution. + +Example: + +```pbs +import { Vector } from "@project:math"; + +pub service Physics { ... } +``` + +--- + +### 3.7 Summary of Top-level Rules + +* A `.pbs` file contains **declarations only**. +* No `let` or executable statements are allowed at the top level. +* Top-level `fn` are always file-private. +* `service` is the only way to expose executable logic beyond a single file. +* Module loading is side effect free. + +--- + +## 4. Resolver Rules + +This section defines how the compiler resolves modules, imports, and symbol names into concrete declarations. +All rules in this section are **normative** and fully deterministic. + +The resolver operates purely at the **symbol level**. There is no top-level execution, and resolution never depends on runtime behavior. + +--- + +### 4.1 Units of Compilation + +PBS is compiled as a **project**. + +Rules: + +* The compilation unit is a **project**, consisting of: + + * the project root, + * all `.pbs` files under `{root}/src/main/modules`, and + * all resolved dependency projects copied into `prometeu-cache`. +* A **module** is exactly one directory. +* A **file unit** is exactly one `.pbs` file. + +There is no partial compilation of individual files or modules. + +--- + +### 4.2 Resolver Phases + +Name resolution is performed in **two explicit phases**. + +#### Phase 1 — Symbol Collection + +The compiler performs a project-wide scan to collect symbols. + +Rules: + +* For each module: + + * All `.pbs` files directly inside the module directory are scanned. + * All `mod` and `pub` symbols are collected into the module symbol table. +* Symbols are collected separately for the **type namespace** and the **value namespace**. +* Function bodies and service method bodies are **not** inspected in this phase. + +If any of the following occur during Phase 1, compilation fails: + +* Duplicate `mod` or `pub` symbols in the same namespace within a module. +* A symbol declared in both the type and value namespaces. + +--- + +#### Phase 2 — File Resolution + +Each file is resolved independently using the symbol tables built in Phase 1. + +Rules: + +* All names referenced in a file must resolve to a symbol that is: + + * visible under visibility rules, and + * uniquely identifiable. +* Resolution of one file does not depend on the resolution of another file. + +--- + +### 4.3 Visibility Gates + +For any candidate symbol `S`, visibility is checked explicitly. + +Rules: + +* **file-private (default)** — visible only within the declaring file. +* **`mod`** — visible to all files in the same module. +* **`pub`** — visible to other modules via `import`. + +No implicit visibility exists. + +--- + +### 4.4 Module Public Index + +For each module directory `M`, the compiler builds a **public index**. + +Rules: + +* The public index contains **only** `pub` symbols. +* Only symbols declared in `.pbs` files directly inside `M` are considered. +* Subdirectories never contribute to the parent module’s index. +* The public index is immutable once constructed. + +The public index is the **only source** for resolving imports from other modules. + +--- + +### 4.5 Import Resolution + +Imports are the **only mechanism** for cross-module name resolution. + +Syntax: + +```pbs +import { X, Y as Z } from "@project:module"; +``` + +Rules: + +* Imports may only reference **modules**, never individual files. +* Only `pub` symbols may be imported. +* Each imported name must exist in the target module’s public index. +* Aliased imports (`as`) introduce a new local name bound to the imported symbol. + +Invalid imports are compile-time errors. + +--- + +### 4.6 Local Name Resolution Order + +Within a file, name lookup follows this order **within each namespace**: + +1. Local bindings (`let`, parameters), innermost scope first. +2. File-level declarations in the same file (file-private, `mod`, `pub`). +3. Imported symbols. + +Rules: + +* Local `let` shadowing is allowed, with warnings. +* Shadowing of `service` names is **not allowed**. +* Shadowing of top-level `fn` names is **not allowed**. +* If an imported symbol conflicts with an existing `mod` or `pub` symbol in the module, compilation fails. + +--- + +### 4.7 Cross-file and Cross-module Access + +#### Within the same module + +* `mod` and `pub` symbols are visible across files without `import`. +* File-private symbols are never visible outside their declaring file. + +#### Across modules + +* Only `pub` symbols are visible. +* Access always requires an explicit `import`. + +--- + +### 4.8 Import Cycles + +Import cycles are permitted **only** if symbol resolution can be completed. + +Rules: + +* PBS has no top-level execution. +* Cycles are evaluated purely at the symbol level. +* Any cycle that prevents completion of Phase 1 (symbol collection) is a compile-time error. + +--- + +### 4.9 Contracts and Services + +Rules: + +* `declare contract C` introduces `C` in the **type namespace**. +* `service S: C` resolves `C` as a type during Phase 2. +* The compiler validates that `S` implements all signatures declared in `C`. +* Signature matching is exact: + + * name + * parameter types + * parameter mutability + * return type + +Contracts themselves contain no executable logic and have no runtime behavior. + +--- + +### 4.10 Resolver Guarantees + +The resolver guarantees that: + +* Name resolution is deterministic. +* Resolution does not depend on file order or import order. +* All symbol conflicts are reported at compile time. +* No runtime name lookup is required. + +--- + +## 5. Services + +This section defines the `service` construct. +A service represents an **explicit API boundary** and the only mechanism for exposing executable behavior beyond a single file. +All rules in this section are **normative**. + +--- + +### 5.1 Role of a Service + +A `service` has the following properties: + +* an explicit API boundary between PBS modules +* conceptually a **singleton**. +* it has no identity beyond its name. +* there is never an implementation detail. + +Any behavior intended to be visible outside a file **must** be exposed via a service. + +--- + +### 5.2 Declaration and Visibility + +A service must always be declared at the **top level** of a `.pbs` file. + +Rules: + +* A service **must** declare its visibility explicitly. +* Valid visibility modifiers are: + + * `pub service` — public API of the module + * `mod service` — internal API, visible only inside the module +* There is **no such thing** as a private service. + +Example: + +```pbs +pub service Audio +{ + fn play(sound: Sound): void { ... } +} +``` + +--- + +### 5.3 Services and Contracts + +A service may optionally implement a contract. + +Syntax: + +```pbs +service S: ContractName { ... } +``` + +Rules: + +* `ContractName` must resolve to a `declare contract` type. +* If a service declares a contract, it **must** implement all declared signatures. +* Signature matching is exact and includes: + + * method name + * parameter count and order + * parameter types + * parameter mutability + * return type + +Failure to satisfy a contract is a **compile-time error**. + +--- + +### 5.4 Service Methods + +Methods declared inside a service define its executable interface. + +Rules: + +* All service methods are **public within the service**. +* There are no private, protected, or helper methods inside a service. +* Service methods are **not** top-level symbols. +* Service methods are accessible only via the service name. + +Example: + +```pbs +Audio.play(sound); +``` + +--- + +### 5.5 Interaction with Other Code + +A service method may call: + +* top-level `fn` declared in the same file +* other services that are visible under normal visibility rules + +Rules: + +* A service method may not access file-private symbols from other files. +* A service may not directly access implementation details of another service. + +--- + +### 5.6 Implementation Rule + +If logic is **not conceptually part of the service API**, it **must not** live inside the service. + +Rules: + +* Implementation logic must be written as file-private `fn`. +* Services act as thin API layers that delegate to internal functions. + +Example: + +```pbs +pub service Math +{ + fn add(a: int, b: int): int + { + return add_impl(a, b); + } +} + +fn add_impl(a: int, b: int): int { ... } +``` + +--- + +### 5.7 Summary of Service Rules + +* `service` is the only construct for exposing executable behavior across files or modules. +* A service is always declared with explicit visibility. +* Services may implement contracts, which are checked statically. +* All service methods are public within the service. +* Services contain no hidden implementation logic. + +--- + +## 6. Types + +This section defines all value types available in PBS v0 and their semantic properties. +All rules in this section are **normative**. + +PBS is divided into: +* Value types (stack): usually primitive types, including `string`, `struct`, `optional`, `result` and `tuples`. +* Gate-backed types (storage/heap): `text` (string builders and concats), `array[N]` (fixed-size arrays), +`list` (dynamic storage), `map` (hash maps) and `declare storage struct` (custom gate-backed). + - Gate-backed types are **not** value types, they are handles. Copy is inexpensive, not a deep copy. + - On HIP a deep copy should be made using `copy(x)`. + +--- + +### 6.1 Type Categories + +PBS defines three categories of types: + +1. **Primitive types** +2. **User-defined struct types** +3. **Composite container types** (defined elsewhere, e.g. fixed arrays) + +All types in PBS are **value types**. + +--- + +### 6.2 Primitive Types + +PBS provides a small, fixed set of primitive types: + +* `void` +* `int` — 32-bit signed integer +* `long` — 64-bit signed integer +* `float` — 32-bit IEEE-754 +* `double` — 64-bit IEEE-754 +* `bool` +* `char` — Unicode scalar value (32-bit) +* `string` +* `bounded` — unsigned 16-bit integer for indices and sizes + +Rules: + +* Primitive types have no identity. +* Primitive values are copied on assignment (except for `string` when on constant pool). +* Primitive values cannot be partially mutated. + +--- + +### 6.3 `string` + +The `string` type represents an immutable sequence of Unicode characters. + +Rules: + +* A `string` value is immutable. +* String literals produce pool-backed string values. +* Some operations may produce stack-owned (for example, `concat`) string snapshots. + - when pool-backed, copy is cheap, since it is just a "handle" (pool-backed, compile time, constant pool). + - when stack-owned, copy is expensive, since it is a deep copy (stack-owned, runtime). +* Doesn't exist such a thing like `alloc string`, it never goes to HIP. +* There is no heap/storage allocation for string at runtime. + +Example: + +```pbs +let s1: string = "hello"; // pool-backed (cheap copy) +let s2: mut string = mut "hello"; // ERROR: string cannot be mutated + +s1 += " world"; // ERROR: no mutation, no += +let s3: string = s1 + " world"; // OK: produces a runtime-owned snapshot (stack-owned) +``` + +--- + +### 6.4 User-defined Struct Types + +User-defined value types are declared using `declare struct`. + +Syntax: + +```pbs +declare struct Name(field1: T1, field2: T2) { ... } +``` + +Rules: + +* A struct is a **pure value type**. +* A struct has no identity. +* A struct has no subtyping, inheritance, or polymorphism. +* A struct value conceptually exists only as data. + +Assignment and passing: + +* Assigning a struct value copies the value. +* Passing a struct as a function argument passes a value. +* Returning a struct returns a value. + +The compiler may optimize copies internally, but such optimizations must not be observable. + +--- + +### 6.5 Mutability and Structs + +Mutability is a property of **bindings**, not of struct types. + +Rules: + +* A struct bound to an immutable binding cannot be mutated. +* A struct bound to a mutable binding may be mutated via mutating methods. +* Mutating a struct affects only that binding. + +Example: + +```pbs +let v = Vector.ZERO; +v.scale(); // ERROR: immutable binding + +let w = mut Vector.ZERO; +w.scale(); // OK +``` + +--- + +### 6.6 The `this` Type + +Inside a `declare struct` body, the special type `this` refers to the enclosing struct type. + +Rules: + +* `this` may appear only inside a struct declaration. +* `this` always refers to the concrete struct type, not to an abstract or dynamic type. +* Outside a struct body, use of `this` is illegal. + +Example: + +```pbs +declare struct Vector(x: float, y: float) +{ + pub fn len(self: this): float { ... } + pub fn scale(self: mut this): void { ... } +} +``` + +--- + +### 6.7 Type Visibility + +Type declarations follow the same visibility rules as other symbols. + +Rules: + +* A type declaration may be file-private, `mod`, or `pub`. +* Visibility controls where the type name can be referenced. +* Visibility does not affect the runtime representation of a type. + +--- + +### 6.8 Summary of Type Rules + +* All types in PBS are value types. +* Types have no identity, ownership, or lifetime. +* Mutability applies only to bindings. +* Structs are copied by value; optimizations are not observable. +* `this` provides explicit self-reference inside struct bodies. + +--- + +## 7. Contracts + +This section defines **contracts** in PBS. +Contracts specify **pure interface-level guarantees** and may represent either: + +* an interface that a `service` must implement, or +* a **host-bound API** implemented directly by the Prometeu runtime / host environment. + +All rules in this section are **normative**. + +Contracts introduce **no user-defined runtime behavior** and contain no executable code bodies. + +--- + +### 7.1 Role of a Contract + +A `contract` represents a **static promise** about an available API surface. + +A contract: + +* defines a set of method signatures, +* is validated entirely at compile time, +* may be bound to a PBS `service` or directly to the runtime, +* has no identity and no state. + +Contracts are never instantiated and never called directly (unless it is a host-bound contract). + +--- + +### 7.2 Declaring a Contract + +Contracts are declared using `declare contract`. + +Syntax: + +```pbs +declare contract ContractName +{ + fn methodName(param1: T1, param2: T2): R; +} +``` + +Rules: + +* A contract declaration introduces a symbol into the **type namespace**. +* Contract declarations may be `mod`, or `pub` (same as services). +* A contract body should contain **only method signatures** (no {}). +* Contract methods have no bodies and no default implementations. + +--- + +### 7.3 Contract Method Signatures + +A contract method signature specifies: + +* method name +* parameter list +* parameter types +* parameter mutability +* return type + +Rules: + +* Contract methods must not declare bodies. +* Contract methods must not declare `else` fallbacks (it is implementation specifics). +* Contract methods must not declare visibility modifiers. +* Contract methods exist only for static validation. + +Example: + +```pbs +declare contract AudioAPI +{ + fn play(sound: Sound): void; + fn stop(id: int): void; +} +``` + +--- + +### 7.4 Host-bound Contracts + +A contract may be declared as **host-bound**, meaning its implementation is provided directly by the runtime or host environment. + +Syntax: + +```pbs +pub declare contract Gfx host +{ + fn drawText(x: int, y: int, message: string, color: Color): void; +} +``` + +Rules: + +* A host-bound contract **should be called directly** and **should never be implemented by a `service`** (`Gfx.drawText(...)`). +* A host-bound contract is always bound to the target runtime or host environment. +* A call of the form `ContractName.method(...)` resolves to a **host call** (syscall). +* The runtime must provide an implementation for every method declared in a host-bound contract. +* If a host-bound contract is unavailable in the target runtime, compilation or linking fails. + +Host-bound contracts define the **official API boundary between PBS and the runtime**. + +--- + +### 7.5 Calling Contracts + +Rules: + +* Contract methods are invoked using the syntax: + +```pbs +ContractName.method(...); +``` + +* This syntax is valid **only** for host-bound contracts. +* Calling a non-host-bound contract directly is a compile-time error. + +Example (INVALID): + +```pbs +declare contract Foo +{ + fn bar(): void; +} + +Foo.bar(); // ERROR: Foo is not host-bound +``` + +--- + +### 7.6 Implementing a Contract in a Service + +A service may implement a non-host-bound contract. + +Syntax: + +```pbs +pub service Audio: AudioAPI +{ + fn play(sound: Sound): void { ... } + fn stop(id: int): void { ... } +} +``` + +Rules: + +* A service that declares a contract **must** implement all contract methods. +* Method matching is **exact**. +* Missing or mismatched methods are compile-time errors. + +--- + +### 7.7 Signature Matching Rules + +For a service method to satisfy a contract method, all of the following must match exactly: + +* method name +* number of parameters +* parameter order +* parameter types +* parameter mutability +* return type + +Rules: + +* Parameter names do not need to match. +* Overloading is not permitted. +* A contract method may be implemented by **exactly one service method**. + +--- + +### 7.8 Contracts and Visibility + +Rules: + +* A contract may be implemented only by a service that can legally see it. +* A host-bound contract must be visible at the call site. +* Contract visibility affects name resolution only, never runtime behavior. + +--- + +### 7.9 Runtime Semantics of Contracts + +Contracts: + +* do not generate PBS code bodies, +* do not introduce dynamic dispatch, +* do not allocate memory, +* do not exist as runtime values. + +All contract validation is performed at compile time. + +--- + +### 7.10 Summary of Contract Rules + +* Contracts define static API surfaces only. +* Contracts live in the type namespace. +* Contracts may be **service-bound** or **host-bound**. +* Host-bound contracts define the PBS ↔ runtime API. +* Only host-bound contracts may be called directly. +* Contract satisfaction is checked statically and exactly. + +--- + +## 8. Structs & Constructors + +This section defines `declare struct`, its fields, constructor aliases, static constants, and struct methods. +All rules in this section are **normative**. + +Structs are **pure value types** (§6). They have no identity and no inheritance. + +--- + +### 8.1 Declaring a Struct + +A struct is declared using `declare struct`. + +Syntax: + +```pbs +declare struct Name(field1: T1, field2: T2) +[ + // constructor aliases +] +[[ + // static constants +]] +{ + // methods +} +``` + +Rules: + +* The field list in parentheses defines the struct’s stored data. +* The constructor alias block `[...]` and static constant block `[[...]]` are optional. +* The method body block `{...}` is optional. + +--- + +### 8.2 Fields + +Rules: + +* Struct fields are **private by default**. +* Fields cannot be accessed directly outside the struct declaration. +* All field reads/writes must occur through struct methods. + +Example (INVALID): + +```pbs +declare struct Vector(x: float, y: float) +let v = Vector(1, 2); +let x = v.x; // ERROR: field `x` is private +``` + +Example (VALID): + +```pbs +declare struct Vector(x: float, y: float) +{ + pub fn getX(self: this): float { ... } +} +``` + +--- + +### 8.3 Constructor Aliases + +Constructor aliases are **named constructors** defined inside the alias block `[...]`. + +Example: + +```pbs +declare struct Vector(x: float, y: float) +[ + (): (0.0, 0.0) as default { } + (a: float): (a, a) as square { } +] +{ +} +``` + +Rules: + +* each struct has a **default constructor** that produces a full-initialized value. +* Each alias defines a parameter list uses a default constructor or alias to create a new struct value. +* Alias bodies `{ ... }` are **compile-time only** and may contain only compile-time constructs (see §8.5). +* Constructor aliases live in the **type namespace**. +* Constructor aliases are **not importable**. Only the struct type name is importable. + +Calling a constructor and constructor alias: + +```pbs +declare struct Vector(x: float, y: float) +[ + (s: float): (s, s) as square { } + (s: float): square(s * s) as doubleSquare { } + (x, float, y, float): (x, y) as normalized + { + let l = sqrt(x * x + y * y); + this.x /= l; + this.y /= l; + } + (): square(0) as zero { } +] +``` + +That is the default constructor, it takes all parameters always; there is no alias: +```pbs +let v0 = Vector(1, 3); +``` + +That is the zero alias, it will call the default constructor with x = 0 and y = 0 +```pbs +let v1 = Vector.zero(); +``` + +That is the square alias, it will call the default constructor with x = 2.0 and y = 2.0 +```pbs +let v2 = Vector.square(2.0); +``` + +That is the doubleSquare alias, it will call the square alias, and load x = 4.0 and y = 4.0 +```pbs +let v3 = Vector.doubleSquare(2.0); +``` + +That is the normalized alias, it will call the default constructor with x = 3.0 and y = 4.0 and normalize it (x = 0.6, y = 0.8) +```pbs +let v4 = Vector.normalized(3.0, 4.0); +``` + +Name rules: + +* Alias names must be unique within the struct. +* Alias names must not conflict with method names (to keep it didactic). + +--- + +### 8.4 Methods + +Struct methods are declared inside the struct body `{ ... }`. + +Rules: + +* Struct methods may be `mod`, or `pub`. `file-private` methods are not allowed and should rely on `fn`. +* Methods are not top-level symbols. +* Methods are invoked on a struct value using dot syntax. + +Example: + +```pbs +declare struct Vector(x: float, y: float) +[ + (): (0.0, 0.0) as default { } +] +{ + pub fn len(self: this): float { ... } +} + +let v = Vector.default(); +let l = v.len(); +``` + +Receiver rules: + +* Methods must declare the receiver as the first parameter, named `self`. +* The receiver type must be either `this` or `mut this`. + +Example: + +```pbs +declare struct Vector(x: float, y: float) +{ + pub fn len(self: this): float { ... } + pub fn scale(self: mut this, s: float): void { ... } +} +``` + +Mutability of `self` follows the binding rules in §9. + +--- + +### 8.5 Static Constants Block + +The static constants block `[[ ... ]]` declares named constants associated with the struct. + +Example: + +```pbs +declare struct Vector(x: float, y: float) +[ + (): (0.0, 0.0) as default { } + (a: float): (a, a) as square { } +] +[[ + ZERO: default() + ONE: square(1.0) +]] +{ +} +``` + +Rules: + +* Static constants are **compile-time constants**. +* Static constants are stored in the constant pool. +* Static constants are **immutable** and can never be mutated. +* The initializer of a static constant may use only: + + * struct constructor aliases of the same struct, and + * other static constants of the same struct declared earlier in the same static block. + +Static constant access: + +```pbs +let z = Vector.ZERO; +``` + +--- + +### 8.6 No Static Methods + +PBS has no static methods on structs. + +Rules: + +* All executable behavior must be expressed as instance methods. +* Constructor aliases are the only “static-like” callable members of a struct. +* Static constants are values only. + +This avoids overloading the meaning of `TypeName.member`. + +--- + +### 8.7 Summary of Struct Rules + +* Structs are declared with `declare struct`. +* Fields are private and cannot be accessed directly. +* Constructor aliases exist only inside the type and are called as `Type.alias(...)`. +* Constructor aliases are not importable. +* Static constants are compile-time values and are immutable. +* Struct methods use an explicit `self: this` / `self: mut this` receiver. +* There are no static methods. + +--- + +## 9. Mutability & Borrow Model + +PBS defines two distinct and explicit mutability worlds: +* SAFE world (Stack / Value-first) +* HIP world (Storage / Gate-backed) + +These worlds have *different guarantees*, *different costs*, and *different risks*. +Crossing between them is always explicit in the language. + +--- + +### 9.1 The SAFE World (Stack / Value-first) +The SAFE world includes: +* Primitive types +* declare struct value types +* `optional` and `result` +* All values that are *not allocated* via `alloc` + +Rules (SAFE World): +* All values are value-first. +* Assignment copies values conceptually. +* Mutation is always local to the binding. +* There is no observable aliasing between stack values. +* No pointers, references, or handles exist in this world. + +Example: +``` +let a: Vector = Vector(1, 2); +let b = a; // conceptual copy +b.x = 10; + +// a.x is still 1 +``` + +Mutation in the SAFE world never affects other bindings. + +--- + +### 9.2 Method Receivers + +Methods declare mutability explicitly on the receiver. + +Rules: + +* A receiver declared as `self: this` is read-only. +* A receiver declared as `self: mut this` allows mutation. +* A mutating method requires a **mutable lvalue** at the call site. Whatever it is passed in will be a copy from there on. + +Example: + +```pbs +let v = Vector.ZERO; +v.scale(); // ERROR + +let w = mut Vector.ZERO; +w.scale(); // OK +``` + +A mutating method may not be called on: + +* static constants +* immutable bindings + +--- + +### 9.3 The HIP World (Storage / Gate-Backed) + +The HIP world is entered explicitly via allocation: +``` +let a = alloc array[4b]; +``` + +Types allocated with alloc are gate-backed and live in Storage (heap). +* Properties of the HIP World +* Values are accessed via gates (handles). +* Assignment copies the handle, not the underlying data. +* Aliasing is intentional and observable. +* Mutation affects all aliases. +* Lifetime is managed via reference counting (RC). + +Example: +``` +let a = alloc array[2b]; +let b = a; // alias (shared storage) + +mutate a as aa +{ + aa[0b] = 10; +} + +let x = peek b[0b]; // x == 10 + +``` + +### 9.4 Gate Semantics (Strong and Weak) + +All gate-backed types are strong gates by default. + +* T (gate-backed type) → strong gate +* weak → weak gate + +Strong Gates +* Increment reference count (RC) +* Keep the storage object alive +* Assignment (let b = a) increases RC + +Weak Gates +* Do not increment RC +* Do not keep objects alive +* Observe storage without ownership + +Conversions: +``` +let s: Node = alloc Node; +let w: weak = s as weak; // always succeeds + +let maybeS: optional = w as strong; // may fail +``` + +* `strong as weak` is always valid +* `weak as strong` returns `optional` + +--- + +### 9.5 Reference Counting and Collection + +PBS does not use tracing garbage collection. + +Instead: +* Storage objects are managed via reference counting +* RC tracks: + - references from the stack + - references stored inside other storage objects +* When RC reaches zero, objects become eligible for reclamation + +*Collection Model* +* Reclamation occurs at safe runtime points, typically: + - end of frame + - explicit GC step (implementation-defined) +* Cycles of strong references are not collected automatically + +This behavior is intentional and part of the learning model. + +--- + +### 9.6 Weak References and Cycles + +Cycles of strong references prevent RC from reaching zero: +``` +a -> b +b -> a +``` + +To break cycles, PBS provides weak gates: + +``` +declare storage struct Node +{ + next: optional>; +} +``` + +Weak gates allow cyclic graphs without leaks, at the cost of requiring explicit validation when accessing. + +--- + +### 9.7 Controlled Access: peek, borrow, and mutate + +All access to gate-backed data is explicit and controlled. + +`peek` — Copy to SAFE World (it is a `borrow` sugar) +``` +let v = peek a[i]; +``` + +* Returns a *value copy* +* Never exposes references +* Always SAFE + +`borrow` — Read-only Access (HIP) +``` +let len = borrow a as aa +{ + aa.len() +}; +``` + +* `aa` is a read-only reference +* The reference cannot escape +* The block returns a value + +`mutate` — Mutable Access (HIP) +``` +mutate a as aa +{ + aa[1b] = 42; +} +``` + +* `aa` is a mutable reference +* Mutation is shared across aliases +* The reference cannot escape + +--- + +#### 9.8 take — Mutation Sugar + +For single mutation operations, PBS provides take as syntactic sugar: +``` +take out.push(value); +``` + +Equivalent to: +``` +mutate out as o +{ + o.push(value); +} +``` + +`take`: +* Is only valid on gate-backed types +* Signals intent clearly +* Keeps HIP usage concise + +--- + +### 9.9 Summary of Guarantees +| Context | Aliasing | Mutation | Risk | +| SAFE (stack) | None | Local only | None | +| HIP (storage) | Explicit | Shared | Intentional | + +PBS makes power explicit and risk visible. +If you do not allocate, you are safe. +If you allocate, you are responsible. + +--- + +## 10. Expressions & Control Flow + +This section defines the expression model and control-flow constructs of PBS. +All rules in this section are **normative**. + +PBS favors **explicit, expression-oriented control flow** with no hidden execution paths. + +--- + +### 10.1 Expression Model + +In PBS, most constructs are expressions. + +Rules: + +* An expression always evaluates to a value. +* The value of an expression is immutable unless bound to a mutable binding. +* Expressions have no side effects except through explicit mutation of mutable bindings. + +Statements exist only as a syntactic convenience; semantically, they are expressions whose value is ignored. + +--- + +### 10.2 Blocks + +A block is a sequence of expressions enclosed in `{ ... }`. + +Rules: + +* A block introduces a new lexical scope. +* The value of a block is the value of its last expression. +* A block with no final expression evaluates to `void`. + +Example: + +```pbs +let x = { + let a = 10; + let b = 20; + a + b +}; // x == 30 +``` + +--- + +### 10.3 `if / else` + +The `if / else` construct is an expression. The blocks on `if` doesn't return a value, it is purely a control flow construct. + +Syntax: + +```pbs +if (condition) +{ + // do something +} +else +{ + // do something else +} +``` + +Rules: + +* `condition` must be of type `bool`. +* Both branches will not produce any value. + +--- + +### 10.4 `when` Expression + +`when` is a conditional expression equivalent to a ternary operator. + +Syntax: + +```pbs +when condition then expr1 else expr2 +``` + +Rules: + +* `when` always requires an `else` branch. +* Both branches must produce values of the same type. +* `when` is an expression and cannot be used without binding or return. + +Example: + +```pbs +let sign: int = when x >= 0 then 1 else -1; + +let complex: int = when x < 0 then +{ + // do something really complex here + return 0; +} +else +{ + // do something else even more complex here + return 1; +} +``` + +--- + +### 10.5 Loops + +PBS supports explicit looping constructs. + +--- + +#### 10.5.1 `for` Loop + +The `for` loop iterates over a bounded range. + +Syntax: + +```pbs +for i in [start .. end] { + // body +} +``` + +Rules: + +* `start` and `end` must be of type `bounded` unless explicitly annotated otherwise. +* The loop variable `i` is immutable by default. +* The range is **half-open**: `[start .. end)`. +* The body executes zero or more times. + +Example: + +```pbs +for i in [0b..10b] { + sum += i; +} +``` + +```pbs +for i in [..10b] { + sum += i; +} +``` + +```pbs +for i in [0b..] { + sum += i; +} +``` +--- + +### 10.6 `return` + +The `return` expression exits the current function or service method. + +Rules: + +* `return` must return a value compatible with the enclosing function’s return type. +* `return` immediately terminates execution of the current body. +* `return` is permitted anywhere inside a function or method body. +* `fn` and `service` allow a falback `return` value. + +Examples: + +```pbs +fn abs(x: int): int // no fallback +{ + return when x >= 0 then x else -x; +} + +fn abs(x: int): int // ERROR: compilation error, there is no fallback +{ +} + +fn abs(x: int): int else 1000 // fallback value, no error +{ + if (x>0) return x; + + // missing a return... +} + +``` + +--- + +### 10.7 Control Flow Restrictions + +Rules: + +* There is `break` and `continue`. + - `break` terminates the innermost enclosing loop. + - `continue` continues to the next iteration of the innermost enclosing loop. +* There is no implicit fallthrough. +* All control flows are explicit and structured. + +--- + +### 10.8 Summary of Expression Rules + +* `when`, and blocks are expressions. +* All branching is explicit and typed. +* `if` is flow control. +* Loops operate over bounded ranges. +* Control flow is predictable and structured. + +--- + +## 10. Return Fallback (`else`) + +A function can have an `else` clause, it is sugar for the sake of a fallback value. +* `else` can be explicit for `optional` but it is not required, since `none` is always a return value if nothing else is returned. + - `fn f(): optional else none {}` == `fn f(): optional { return none; }` +* `result` can have an `else` clause. however, it should be explicit `error` or provided by function body (no fallback). + +```pbs +fn v(): Vector else Vector.ZERO +{ + if cond return Vector.ONE; +} +``` + +Used to guarantee total functions without boilerplate. + +--- + +## 11. Numeric Rules & `bounded` + +This section defines numeric types, numeric conversions, the special `bounded` type, and the built-in `range` value type. +All rules in this section are **normative**. + +PBS numeric rules are designed to be **explicit**, **predictable**, and **safe by default**. + +--- + +### 11.1 Numeric Type Hierarchy + +PBS defines the following numeric types: + +* `int` — 32-bit signed integer +* `long` — 64-bit signed integer +* `float` — 32-bit IEEE-754 floating point +* `double` — 64-bit IEEE-754 floating point +* `bounded` — unsigned 16-bit integer (`0 .. 65535`) + +Rules: + +* All numeric types are value types. +* Numeric types have no identity and no implicit mutability. + +--- + +### 11.2 Implicit Widening Conversions + +PBS allows **implicit widening** conversions only. + +Allowed implicit conversions: + +``` +int → long +int → float +int → double +long → float +long → double +float → double +bounded → int +bounded → long +``` + +Rules: + +* Implicit widening never loses information. +* Any conversion not listed above requires an explicit cast. + +--- + +### 11.3 Explicit Casts + +Explicit casts use the syntax: + +```pbs +expr as Type +``` + +Rules: + +* Explicit casts are required for any narrowing or potentially lossy conversion. +* Explicit casts are checked at compile time and/or runtime as specified below. + +--- + +### 11.4 The `bounded` Type + +`bounded` is a dedicated scalar type for **indices, sizes, and counters**. + +Purpose: + +* Make indexing and counting explicit in the type system. +* Prevent accidental misuse of general-purpose integers. +* Enable predictable and safe looping semantics. + +Representation: + +* Internally represented as an unsigned 16-bit integer (`u16`). +* Valid range: `0 .. 65535`. + +Literal syntax: + +```pbs +let a: bounded = 10b; +let b = 0b; +``` + +--- + +### 11.5 Conversions Involving `bounded` + +Rules: + +* `bounded → int` and `bounded → long` are implicit. +* `int → bounded` and `long → bounded` require an explicit cast. +* `float` and `double` cannot be cast to `bounded`. + +Casting to `bounded`: + +```pbs +let b: bounded = x as bounded; +``` + +Semantics: + +* Casting to `bounded` performs a **clamping operation**: + + * values `< 0` become `0b` + * values `> 65535` become `65535b` +* A compile-time or runtime **warning** must be issued when clamping occurs. + +--- + +### 11.6 Arithmetic on `bounded` + +Operations on `bounded` are intentionally limited. + +Allowed operations: + +* comparison (`==`, `!=`, `<`, `<=`, `>`, `>=`) +* addition (`+`) +* subtraction (`-`) + +Disallowed operations: + +* multiplication +* division +* modulo +* bitwise operations + +Rules: + +* Addition and subtraction on `bounded` are **checked**. +* If an operation would overflow or underflow: + + * the result is clamped to the valid range, and + * a warning is emitted. + +--- + +### 11.7 The `range` Type + +PBS provides a built-in value type `range` for representing bounded intervals. + +A `range` represents a **half-open interval**: + +`[min .. max)` - min is inclusive and max is exclusive. + +That is, the sequence: + +`min, min + 1, ..., max - 1` + +--- + +#### Construction + +A `range` is constructed using the built-in `range` constructor. + +```pbs +range(max: bounded) +range(min: bounded, max: bounded) +``` + +Examples: + +```pbs +let r1: range = range(10b); // [0b .. 10b) +let r2: range = range(5b, 15b); // [5b .. 15b) +let r3: range = range(5b, a.len()); // [5b .. a.len()) +``` + +Rules: + +* The single-argument form sets `min` to `0b`. +* The two-argument form sets both bounds explicitly. +* `max` must always be provided. +* If `min >= max`, the range is empty. +* `range` values are immutable. + +--- + +#### API + +A `range` exposes the following read-only fields: + +* `min: bounded` +* `max: bounded` + +Optional helper operations: + +* `count(): bounded` +* `isEmpty(): bool` + +--- + +### 11.8 Numeric Safety Guarantees + +PBS guarantees that: + +* No implicit narrowing conversions occur. +* All potentially lossy conversions are explicit. +* `bounded` operations cannot produce undefined behavior. +* `range` values have well-defined, deterministic semantics. +* Numeric behavior is deterministic across platforms. + +--- + +### 11.9 Summary of Numeric Rules + +* Implicit conversions are widening only. +* Narrowing conversions require explicit casts. +* `bounded` is used for indices and sizes. +* `bounded` arithmetic is limited and checked. +* `range` represents half-open bounded intervals. +* Numeric behavior is explicit and predictable. + +--- + +## 12. `optional` + +This section defines the `optional` type. +All rules in this section are **normative**. + +`optional` represents the **explicit presence or absence** of a value, without exceptions, traps, or implicit unwrapping. + +--- + +### 12.1 Purpose and Design Goals + +`optional` exists to model situations where a value: + +* may or may not be present, +* is not an error condition by itself, +* must be handled explicitly by the programmer. + +Design goals: + +* No implicit unwrapping +* No runtime traps +* No heap allocation +* Stack-only representation + +--- + +### 12.2 Type Definition + +`optional` is a built-in generic value type. + +Rules: + +* `T` must be a valid value type. +* `optional` itself is a value type. +* `optional` has no identity and no aliasing semantics. + +--- + +### 12.3 Construction + +An `optional` value is constructed using one of the following forms: + +```pbs +some(value) +none +``` + +Rules: + +* `some(value)` produces an `optional` containing `value`. `T` follow the same type as `value`. +* `none` produces an empty `optional`. +* `none` represents the absence of a value. +* `none` must be typed by context. + +Examples: + +```pbs +let a: optional = some(10); +let b: optional = none; +``` + +The following is invalid: + +```pbs +let x = none; // ERROR: type of `none` cannot be inferred +``` + +--- + +### 12.4 Extraction with `else` + +The **only** way to extract a value from an `optional` is using the `else` operator. + +Syntax: + +```pbs +value = opt else fallback +``` + +Rules: + +* If `opt` is `some(v)`, the expression evaluates to `v`. +* If `opt` is `none`, the expression evaluates to `fallback`. +* The type of `fallback` must be compatible with `T`. + +Example: + +```pbs +let v: int = maybeInt else 0; +``` + +No other form of extraction is permitted. + +--- + +### 12.5 Control Flow and Functions + +Functions returning `optional` may omit an explicit `return none` if no value is returned. + +Example: + +```pbs +fn find(id: int): optional +{ + if (id == 0) return some(42); + // implicit return none +} +``` + +Rules: + +* Falling off the end of a function returning `optional` is equivalent to `return none`. +* This behavior applies only to `optional`. + +--- + +### 12.6 API Surface + +`optional` exposes a minimal, explicit API: + +```pbs +opt.hasSome(): bool +opt.hasNone(): bool +``` + +Rules: + +* These methods do not extract the contained value. +* They exist only for explicit branching logic. + +Example: + +```pbs +if opt.hasSome() then { + // handle presence +} else { + // handle absence +} +``` + +--- + +### 12.7 Mutability and Copying + +Rules: + +* `optional` follows normal value semantics. +* Assigning an `optional` copies the container and its contents. +* Mutability of `T` inside `optional` follows normal binding rules. + +--- + +### 12.8 Summary of `optional` Rules + +* Absence is explicit and normal. +* No implicit unwrapping or traps exist. +* Extraction is only possible via `else`. +* `optional` is stack-only and allocation-free. + +--- + +## 13. `result` + +This section defines the `result` type. +All rules in this section are **normative**. + +`result` represents an **explicit success-or-error outcome**. +It is the only mechanism for error signaling in PBS. + +--- + +### 13.1 Purpose and Design Goals + +`result` exists to model operations that: + +* may succeed and produce a value of type `T`, or +* may fail with a **typed error** `E`. + +Design goals: + +* No exceptions +* No implicit error propagation +* Explicit, typed error handling +* Stack-only representation + +--- + +### 13.2 Type Definition + +`result` is a built-in generic value type. + +Rules: + +* `T` must be a valid value type. +* `E` must be an `error` label type declared via `declare error`. +* `result` itself is a value type. +* `result` has no identity and no aliasing semantics. + +--- + +### 13.3 Construction + +A `result` value is constructed using one of the following forms: + +```pbs +ok(value) +err(errorLabel) +``` + +Rules: + +* `ok(value)` produces a successful result containing `value`. +* `err(label)` produces a failed result containing an `error` label of type `E`. +* The type of `label` must match `E` exactly. + +Examples: + +```pbs +let r1: result = ok(42); +let r2: result = err(IOError.not_found); +``` + +--- + +### 13.4 The `?` Propagation Operator + +The `?` operator propagates errors **only when error types match**. + +Syntax: + +```pbs +value = expr? +``` + +Rules: + +* `expr` must have type `result`. +* The enclosing function must return `result<_, E>` with the **same error type**. +* If `expr` is `ok(v)`, the expression evaluates to `v`. +* If `expr` is `err(e)`, the enclosing function returns `err(e)` immediately. + +Example: + +```pbs +fn g(): result +{ + return ok(true); +} + +fn f(): result +{ + let x: int = when g()? then 1 else 0; + return ok(x + 1); +} +``` + +--- + +### 13.5 Error Handling with `handle` + +`handle` is the **only** construct that allows extracting a value from a `result` while mapping errors. + +Syntax: + +```pbs +value = handle expr +{ + ErrorA.case1 => ErrorB.mapped1, + ErrorA.case2 => ErrorB.mapped2, + _ => ErrorB.default, +}; +``` + +Rules: + +* `expr` must have type `result`. +* The enclosing function must return `result<_, E2>`. +* Each arm maps an error label of `E1` to a label of `E2`. +* Arms must be exhaustive, either explicitly or via `_`. + +Semantics: + +* If `expr` is `ok(v)`, the expression evaluates to `v`. +* If `expr` is `err(e)`, the first matching arm is selected and the function returns `err(mapped)` immediately. + +--- + +### 13.6 Restrictions + +Rules: + +* There is no implicit extraction of `result`. +* There is no pattern matching beyond `handle`. +* `result` cannot be implicitly converted to `optional` or vice versa. +* Falling off the end of a function returning `result` is a compile-time error. + +--- + +### 13.7 Mutability and Copying + +Rules: + +* `result` follows normal value semantics. +* Assigning a `result` copies the container and its contents. +* Mutability of `T` follows normal binding rules. + +--- + +### 13.8 Summary of `result` Rules + +* `result` is the only error signaling mechanism. +* Errors are typed and explicit. +* `?` propagates errors only when types match. +* `handle` is the only construct for error mapping and extraction. +* No exceptions or traps exist. + +--- + +## 14. Tuples + +PBS provides **tuples** as a lightweight, stack-only aggregation type. + +Tuples exist to support **small, local groupings of values** without introducing heap allocation, gates, aliasing, or reference counting. They intentionally fill the ergonomic gap left by removing arrays from the stack. + +Tuples belong entirely to the **SAFE world**. + +--- + +### 14.1 Tuple Type + +The tuple type is written as: + +```pbs +Tuple(T1, T2, ..., Tn) +``` + +Each element has a fixed position and type. Tuples are **heterogeneous** and their size is known at compile time. + +Example: + +```pbs +let t: Tuple(int, float, bool) = tuple(1, 2.0, true); +``` + +--- + +### 14.2 Construction + +Tuples are constructed using the `tuple` expression: + +```pbs +tuple(expr1, expr2, ..., exprN) +``` + +Each expression is evaluated and stored **inline on the stack**. + +Example: + +```pbs +let position = tuple(10, 20); +let state = tuple("idle", true, 3); +``` + +--- + +### 14.3 Properties + +Tuples have the following properties: + +* Tuples are **value types** +* Tuples live exclusively on the **stack** +* Assignment performs a **conceptual copy** of all elements +* Tuples have **no identity** +* Tuples have **no observable aliasing** +* Tuples never allocate storage + +Because of these properties, tuples are always safe to use and cannot leak memory. + +--- + +### 14.4 Element Access + +Tuple elements are accessed by **zero-based positional index** using dot syntax: + +```pbs +let t = tuple(1, 2.0, true); + +let a: int = t.0; +let b: float = t.1; +let c: bool = t.2; +``` + +Index access is statically checked and always safe. + +--- + +### 14.5 Destructuring + +Tuples may be destructured into individual bindings: + +```pbs +let t = tuple(1, 2.0, true); +let (a, b, c) = t; +``` + +Types may be explicitly annotated if desired: + +```pbs +let (x: int, y: float, z: bool) = t; +``` + +Destructuring copies each element into a new stack binding. + +--- + +### 14.6 Mutability + +Tuple bindings may be mutable: + +```pbs +let t = mut tuple(1, 2, 3); +t.0 = 10; +``` + +Mutation is always **local to the binding** and never affects other values. + +Example: + +```pbs +let a = tuple(1, 2, 3); +let b = a; // copy + +b.0 = 99; +// a.0 is still 1 +``` + +--- + +### 14.7 Tuples vs Structs + +Tuples and structs are closely related but serve different purposes: + +| Struct | Tuple | +| --------------------- | ----------------- | +| Named type | Anonymous type | +| Declared once, reused | Created inline | +| Named fields | Positional fields | +| Stack value | Stack value | + +Tuples are intended for **local composition**, while structs are intended for **named data models**. + +--- + +### 14.8 Tuples vs Arrays + +Tuples are **not arrays**. + +| Tuple | Array | +| ------------- | --------------------- | +| Stack-only | Gate-backed (storage) | +| Heterogeneous | Homogeneous | +| No aliasing | Aliasing possible | +| No allocation | Requires `alloc` | +| SAFE world | HIP world | + +Tuples are suitable for small, fixed groupings of values. Arrays are suitable for collections and data structures. + +--- + +### 14.9 Didactic Intent + +Tuples exist to reinforce the core PBS design principle: + +> If you do not allocate, you are safe. + +They allow expressive local grouping without weakening the guarantees of the SAFE world or introducing hidden costs. + +--- + +### 14.10 Summary + +* Tuples are stack-only value types +* Tuples are heterogeneous and fixed-size +* Tuples never allocate or alias +* Tuples provide ergonomic local aggregation + +Tuples completes the SAFE world without compromising the explicit nature of the PBS memory model. + +--- + +## 15. Memory Model + +PBS defines a **segmented and explicit memory model** designed to be: + +* **Didactic**: each memory region teaches a distinct mental model +* **Predictable**: no implicit allocation or tracing garbage collection +* **Game-oriented**: execution happens inside loops (frames), with controlled reclamation points +* **Explicit about cost and risk**: moving between regions is always visible in code + +The memory model is divided into four regions: + +1. Constant Pool +2. Stack +3. Gate Pool +4. Storage (Heap) + +Each region has a distinct role, lifetime, and set of guarantees. + +--- + +### 15.1 Constant Pool + +The **Constant Pool** stores **immutable data known at compile time**. + +Examples include: + +* Numeric literals +* String literals +* Compile-time constant structs (e.g. `Vector.ZERO`) + +### Properties + +* Immutable +* Read-only +* Shared globally +* Never reclaimed during execution + +### Example + +```pbs +let a: int = 10; +let s: string = "Hello World"; +let v: Vector = Vector.ZERO; +``` + +In the example above: + +* `10`, `"Hello World"`, and `Vector.ZERO` live in the Constant Pool +* Assignments copy references to constant data, not mutable memory +* Constant Pool values can never be mutated + +The Constant Pool is **not** a general-purpose memory region and cannot store dynamically created data. + +--- + +### 15.2 Stack (SAFE World) + +The **Stack** is the default execution memory for PBS. + +It stores: + +* Local variables +* Function parameters +* Temporary values +* Return values + +### Properties + +* Value-first semantics +* Conceptual copy on assignment +* No observable aliasing +* No pointers or handles +* Deterministic lifetime (scope-bound) + +### Example + +```pbs +fn example(): void +{ + let a: Vector = Vector(1, 2); + let b = a; // conceptual copy + + b.x = 10; + // a.x is still 1 +} +``` + +All values in the Stack are **isolated**. Mutating one value never affects another binding. + +The Stack is the **SAFE world** of PBS. If a program only uses stack values and the Constant Pool, it cannot create memory leaks or aliasing bugs. + +--- + +### 15.3 Gate Pool + +The **Gate Pool** is an internal runtime structure that manages **handles (gates)** to objects stored in the Storage region. + +A **gate** is a small, copyable value that represents access to a storage object. + +### Conceptual Structure + +Each Gate Pool entry contains: + +* A pointer to a storage object +* A strong reference count (RC) +* A weak reference count +* Optional runtime metadata (type id, flags, debug info) + +The Gate Pool is **not directly addressable** by user code, but all gate-backed types are implemented on top of it. + +--- + +### 15.4 Storage (Heap / HIP World) + +The **Storage** region (heap) stores **dynamically allocated, mutable data**. + +Objects in Storage are created explicitly using `alloc`. + +### Example + +```pbs +let a = alloc array[4b]; +``` + +This allocation: + +* Creates a new storage object +* Creates a Gate Pool entry +* Returns a **strong gate** to the caller + +### Properties + +* Mutable +* Aliasing is possible and intentional +* Lifetime managed via reference counting +* Not reclaimed immediately when references disappear + +The Storage region is the **HIP world** of PBS. Power and responsibility are explicit. + +--- + +### 15.5 Strong and Weak Gates + +All gate-backed types use **gates** to access storage objects. + +### Strong Gates + +* Represented directly by the type `T` +* Increment strong reference count +* Keep the storage object alive + +```pbs +let a: Node = alloc Node; +let b = a; // RC++ +``` + +### Weak Gates + +* Represented by `weak` +* Do not increment strong RC +* Do not keep storage alive + +```pbs +let w: weak = a as weak; +``` + +### Conversion Rules + +```pbs +let w: weak = strongNode as weak; // always valid +let o: optional = w as strong; // may fail +``` + +Weak gates must be promoted to strong gates before use. + +--- + +### 15.6 Reference Counting (RC) + +PBS uses **reference counting** to manage storage lifetimes. + +### RC Tracks + +* Strong gates stored on the stack +* Strong gates stored inside other storage objects + +RC is adjusted automatically when: + +* A strong gate is copied or destroyed +* A strong gate is assigned into or removed from storage + +### Important Notes + +* Cycles of strong references are not reclaimed automatically +* Weak gates do not participate in RC + +This behavior is intentional and mirrors real-world systems such as C++ `shared_ptr` / `weak_ptr`. + +--- + +### 15.7 Reclamation and Frame Boundaries + +PBS does not use tracing garbage collection. + +Instead, storage reclamation happens at **safe runtime points**, typically: + +* End of a frame +* Explicit runtime GC step (implementation-defined) + +At reclamation time: + +* Storage objects with strong RC == 0 are destroyed +* Associated Gate Pool entries are cleaned up when no weak references remain + +This model is predictable and suitable for real-time systems. + +--- + +### 15.8 Crossing Memory Regions + +Transitions between memory regions are always explicit: + +| Operation | Effect | +| --------- | -------------------------- | +| `alloc` | Stack → Storage | +| `peek` | Storage → Stack (copy) | +| `borrow` | Temporary read-only access | +| `mutate` | Temporary mutable access | +| `take` | Single mutation sugar | + +### Example + +```pbs +let a = alloc array[2b]; + +mutate a as aa +{ + aa[0b] = 10; +} + +let v = peek a[0b]; // copy back to stack +``` + +--- + +### 15.9 Didactic Intent + +The memory model is intentionally structured to teach: + +* Stack vs heap +* Value vs identity +* Ownership and aliasing +* Cost of allocation +* Why cycles leak under RC + +Programs that remain in the Stack and Constant Pool are **safe by construction**. +Programs that enter Storage gain power, flexibility, and responsibility. + +PBS does not hide these trade-offs. + +--- + +### 15.10 Summary + +* Constant Pool: immutable, global +* Stack: safe, value-first +* Gate Pool: handle management and RC +* Storage: mutable, shared, explicit + +PBS makes memory **visible**, **intentional**, and **teachable**. + +--- + +## 16. Gates (Strong & Weak) + +PBS introduces **gates** as the fundamental mechanism for accessing dynamically allocated storage. + +Gates make **identity, aliasing, and lifetime** explicit, while keeping the SAFE world free of references and pointers. + +This chapter defines what gates are, how **strong** and **weak** gates behave, and how they interact with the memory model. + +--- + +### 16.1 What Is a Gate + +A **gate** is a small, copyable value that represents access to an object stored in **Storage (heap)**. + +Gates are not pointers in the traditional sense, but they serve a similar role: + +* They refer to a storage object +* They can be copied cheaply +* They may alias the same underlying data + +All gate-backed types are created explicitly using `alloc`. + +```pbs +let a = alloc array[4b]; +``` + +The value returned by `alloc` is a **gate**. + +--- + +### 16.2 Gate-Backed Types + +The following kinds of types are gate-backed: + +* Built-in collections (`array`, `list`, `map`, `text`) +* User-defined storage types (`declare storage struct`) + +Gate-backed types live in Storage and are accessed exclusively through gates. + +--- + +### 16.3 Strong Gates + +A **strong gate** is the default form of a gate-backed value. + +Properties: + +* Represented directly by the type `T` +* Increment the strong reference count (RC) +* Keep the storage object alive +* Assignment creates an alias + +Example: + +```pbs +let a: Node = alloc Node; +let b = a; // alias, RC++ +``` + +Both `a` and `b` refer to the same storage object. + +--- + +### 16.4 Weak Gates + +A **weak gate** is a non-owning reference to a storage object. + +Properties: + +* Represented by the type `weak` +* Do not increment strong RC +* Do not keep the storage object alive +* May become invalid when the object is reclaimed + +Weak gates are used to break reference cycles and express non-owning relationships. + +Example: + +```pbs +let s: Node = alloc Node; +let w: weak = s as weak; +``` + +--- + +### 16.5 Conversions Between Strong and Weak + +PBS provides explicit conversions between strong and weak gates. + +### Strong to Weak + +```pbs +let w: weak = s as weak; +``` + +* Always succeeds +* Does not affect strong RC + +### Weak to Strong + +```pbs +let o: optional = w as strong; +``` + +* May fail +* Returns `none` if the object has already been reclaimed +* Returns `some(T)` if promotion succeeds + +These conversions make ownership and lifetime explicit. + +--- + +### 16.6 Reference Counting and Lifetime + +Each storage object has: + +* A strong reference count (RC) +* A weak reference count + +Rules: + +* Strong gates increment and decrement RC automatically +* Weak gates do not affect strong RC +* When strong RC reaches zero, the storage object becomes eligible for reclamation + +Reclamation occurs at safe runtime points (e.g. end of frame). + +Weak gates may outlive the storage object they reference. + +--- + +### 16.7 Gate Validity + +A strong gate is always valid. + +A weak gate may be: + +* **Valid**: the storage object still exists +* **Expired**: the storage object has been reclaimed + +Expired weak gates cannot be used directly and must be promoted first. + +```pbs +let maybeNode: optional = w as strong; +``` + +--- + +### 16.8 Interaction with Access Control + +Only **strong gates** may be used with: + +* `peek` +* `borrow` +* `mutate` +* `take` + +Weak gates must be promoted before any access is allowed. + +This rule prevents accidental use of expired references. + +--- + +### 16.9 Didactic Intent + +Gates are designed to teach: + +* The difference between value and identity +* Ownership versus observation +* Why aliasing exists and how it propagates +* Why reference cycles leak under RC +* How weak references break cycles safely + +PBS does not hide these concepts. + +If a program does not allocate, gates do not exist. +If it allocates, gates make responsibility explicit. + +--- + +### 16.10 Summary + +* Gates are explicit handles to storage objects +* Strong gates own and keep objects alive +* Weak gates observe without ownership +* Conversions between strong and weak are explicit +* Access to storage always requires a strong gate + +Gates are the foundation of the HIP world in PBS. + +--- + +# 17. Gate-backed Array + +PBS provides **gate-backed arrays** as fixed-size, homogeneous collections stored in **Storage (heap)**. + +Gate-backed arrays are the primary indexed collection type in the HIP world. They deliberately trade safety for power and explicit control. + +--- + +## 17.1 Array Type + +The array type is written as: + +```pbs +array[N] +``` + +Where: + +* `T` is the element type +* `N` is a compile-time constant size (`bounded`) + +Arrays are **homogeneous** and **fixed-size**. + +--- + +## 17.2 Allocation + +Arrays are created explicitly using `alloc`: + +```pbs +let a: array[4b] = alloc array[4b]; +``` + +Allocation: + +* Creates a storage object +* Registers it in the Gate Pool +* Returns a **strong gate** + +Arrays cannot exist on the stack. + +--- + +## 17.3 Properties + +Gate-backed arrays have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Elements are laid out contiguously +* Size is fixed for the lifetime of the array + +Arrays are **not value types**. + +--- + +## 17.4 Aliasing Semantics + +Assigning an array copies the gate, not the data: + +```pbs +let a = alloc array[2b]; +let b = a; // alias +``` + +Mutations through one alias are visible through all aliases. + +--- + +## 17.5 Element Access + +Direct element access using `a[i]` is **not allowed**. + +All element access is explicit and controlled. + +### Reading Elements (`peek`) + +```pbs +let v: int = peek a[1b]; +``` + +* Returns a value copy +* SAFE operation +* Never exposes references + +### Writing Elements (`mutate`) + +```pbs +mutate a as aa +{ + aa[1b] = 42; +} +``` + +* Requires mutable access +* Mutation is shared across aliases + +--- + +## 17.6 Iteration + +Arrays may be iterated by value: + +```pbs +for v in a +{ + // v is a value copy +} +``` + +Iteration copies each element from storage into the stack. + +To iterate indices explicitly: + +```pbs +for i in a.indices() +{ + let v = peek a[i]; +} +``` + +--- + +## 17.7 Passing Arrays to Functions + +Arrays are passed by gate (handle), not by value: + +```pbs +fn setFirst(a: array[2b], v: int): void +{ + mutate a as aa + { + aa[0b] = v; + } +} +``` + +Calling this function mutates the caller's array. + +--- + +## 17.8 Copying Arrays + +To duplicate an array's contents, use `copy`: + +```pbs +let b = copy(a); +``` + +`copy`: + +* Allocates a new array +* Copies all elements +* Returns a new strong gate + +--- + +## 17.9 Bounds and Safety + +Array indices are statically bounded by `N`. + +* Out-of-bounds access is a runtime error +* Index values are of type `bounded` + +These checks preserve safety without hiding cost. + +--- + +## 17.10 Didactic Intent + +Gate-backed arrays exist to teach: + +* Heap allocation +* Aliasing through handles +* Explicit mutation +* Copy versus alias +* Cost of indexed collections + +Arrays are powerful but intentionally not implicit or convenient. + +--- + +## 17.11 Summary + +* Arrays are gate-backed, fixed-size collections +* Arrays live in Storage and require `alloc` +* Assignment aliases; `copy` duplicates +* Element access is explicit (`peek`, `mutate`) +* Arrays belong to the HIP world + +Arrays are the foundation for structured data in PBS. + +--- + +## 18. Gate-backed List + +PBS provides **gate-backed lists** as dynamic, growable collections stored in **Storage (heap)**. + +Lists complement gate-backed arrays by supporting variable size and incremental construction, at the cost of additional runtime overhead and indirection. + +--- + +### 18.1 List Type + +The list type is written as: + +```pbs +list +``` + +Where `T` is the element type. + +Lists are **homogeneous** and **dynamically sized**. + +--- + +### 18.2 Allocation + +Lists are created explicitly using `alloc`: + +```pbs +let xs: list = alloc list; +``` + +Allocation: + +* Creates an empty list in Storage +* Registers it in the Gate Pool +* Returns a **strong gate** + +Lists cannot exist on the stack. + +--- + +### 18.3 Properties + +Gate-backed lists have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Dynamic length +* Elements may be reallocated internally + +Lists are **not value types**. + +--- + +### 18.4 Aliasing Semantics + +Assigning a list copies the gate, not the data: + +```pbs +let a = alloc list; +let b = a; // alias +``` + +Mutations through one alias are visible through all aliases. + +--- + +### 18.5 Core Operations + +### Length + +```pbs +let n: bounded = borrow xs as xsr { xsr.len() }; +``` + +### Appending Elements + +Elements are appended using mutation: + +```pbs +mutate xs as xsr +{ + xsr.push(10); + xsr.push(20); +} +``` + +For single operations, `take` may be used as syntactic sugar: + +```pbs +take xs.push(30); +``` + +--- + +### 18.6 Element Access + +Direct element access using `xs[i]` is **not allowed**. + +### Reading Elements (`peek`) + +```pbs +let v: int = peek xs[i]; +``` + +* Returns a value copy +* SAFE operation + +### Writing Elements (`mutate`) + +```pbs +mutate xs as xsr +{ + xsr[i] = 42; +} +``` + +--- + +### 18.7 Iteration + +Lists may be iterated by value: + +```pbs +for v in xs +{ + // v is a value copy +} +``` + +To iterate indices explicitly: + +```pbs +for i in xs.indices() +{ + let v = peek xs[i]; +} +``` + +--- + +### 18.8 Passing Lists to Functions + +Lists are passed by gate (handle), not by value: + +```pbs +fn append(xs: list, v: int): void +{ + take xs.push(v); +} +``` + +Calling this function mutates the caller's list. + +--- + +### 18.9 Copying Lists + +To duplicate a list and its contents, use `copy`: + +```pbs +let ys = copy(xs); +``` + +`copy`: + +* Allocates a new list +* Copies all elements +* Returns a new strong gate + +--- + +### 18.10 Performance Notes + +* Appending may trigger internal reallocation +* Element access is O(1) +* Iteration copies elements to the stack + +Lists trade predictability for flexibility. + +--- + +### 18.11 Didactic Intent + +Gate-backed lists exist to teach: + +* Dynamic allocation +* Growth and reallocation cost +* Aliasing through handles +* Explicit mutation patterns + +Lists are powerful but require discipline. + +--- + +### 18.12 Summary + +* Lists are gate-backed, dynamic collections +* Lists live in Storage and require `alloc` +* Assignment aliases; `copy` duplicates +* Mutation is explicit (`mutate`, `take`) +* Lists belong to the HIP world + +Lists provide flexibility while preserving explicit control. + +--- + +# 19. Gate-backed Set + +PBS provides **gate-backed sets** as unordered collections of unique values stored in **Storage (heap)**. + +Sets are designed for membership tests, uniqueness constraints, and dynamic collections where ordering is irrelevant. + +Like all gate-backed collections, sets belong to the **HIP world** and make allocation, aliasing, and mutation explicit. + +--- + +## 19.1 Set Type + +The set type is written as: + +```pbs +set +``` + +Where `T` is the element type. + +Elements of a set must be **hashable and comparable**. + +--- + +## 19.2 Allocation + +Sets are created explicitly using `alloc`: + +```pbs +let s: set = alloc set; +``` + +Allocation: + +* Creates an empty set in Storage +* Registers it in the Gate Pool +* Returns a **strong gate** + +Sets cannot exist on the stack. + +--- + +## 19.3 Properties + +Gate-backed sets have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Dynamic size +* No duplicate elements + +Sets are **not value types**. + +--- + +## 19.4 Aliasing Semantics + +Assigning a set copies the gate, not the data: + +```pbs +let a = alloc set; +let b = a; // alias +``` + +Mutations through one alias are visible through all aliases. + +--- + +## 19.5 Core Operations + +### Insertion + +Elements are inserted explicitly: + +```pbs +mutate s as ss +{ + ss.add(10); + ss.add(20); +} +``` + +For single operations, `take` may be used: + +```pbs +take s.add(30); +``` + +Adding an element already present has no effect. + +--- + +### Membership Test + +Membership checks do not mutate the set: + +```pbs +let exists: bool = borrow s as ss +{ + ss.contains(10); +}; +``` + +--- + +### Removal + +Elements are removed explicitly: + +```pbs +mutate s as ss +{ + ss.remove(20); +} +``` + +--- + +## 19.6 Iteration + +Sets may be iterated by value: + +```pbs +for v in s +{ + // v is a value copy +} +``` + +Iteration order is implementation-defined. + +--- + +## 19.7 Passing Sets to Functions + +Sets are passed by gate (handle), not by value: + +```pbs +fn addIfMissing(s: set, v: int): void +{ + let exists = borrow s as ss { ss.contains(v) }; + if (!exists) + { + take s.add(v); + } +} +``` + +Calling this function mutates the caller's set. + +--- + +## 19.8 Copying Sets + +To duplicate a set and its contents, use `copy`: + +```pbs +let s2 = copy(s); +``` + +`copy`: + +* Allocates a new set +* Copies all elements +* Returns a new strong gate + +--- + +## 19.9 Performance Notes + +* Membership tests are expected to be O(1) average +* Insertions and removals may rehash internally +* Iteration copies elements into the stack + +Sets trade ordering guarantees for fast membership checks. + +--- + +## 19.10 Didactic Intent + +Gate-backed sets exist to teach: + +* Unordered collections +* Uniqueness constraints +* Membership-oriented logic +* Explicit mutation and aliasing + +Sets are ideal for flags, visited markers, and dynamic uniqueness tracking. + +--- + +## 19.11 Summary + +* Sets are gate-backed, unordered collections +* Sets live in Storage and require `alloc` +* Assignment aliases; `copy` duplicates +* Mutation is explicit (`mutate`, `take`) +* Iteration order is undefined + +Sets complete the family of associative gate-backed collections. + +--- + +## 20. Gate-backed Text + +PBS provides **gate-backed text** as the primary dynamic string type. + +`text` exists to support **runtime string construction, mutation, and concatenation** without weakening the guarantees of the SAFE world. String literals and compile-time strings remain immutable and live in the Constant Pool. + +--- + +### 20.1 Text Type + +The text type is written as: + +```pbs +text +``` + +`text` is a **gate-backed**, dynamically sized sequence of characters stored in **Storage (heap)**. + +--- + +### 20.2 Text vs String + +PBS distinguishes clearly between `string` and `text`: + +| string | text | +| ------------- | -------------- | +| Immutable | Mutable | +| Constant Pool | Storage (heap) | +| Compile-time | Runtime | +| SAFE world | HIP world | + +`string` values can never be mutated or concatenated. All dynamic string operations require `text`. + +--- + +### 20.3 Allocation + +Text objects are created explicitly using `alloc`: + +```pbs +let t: text = alloc text; +``` + +Allocation: + +* Creates an empty text object in Storage +* Registers it in the Gate Pool +* Returns a **strong gate** + +--- + +### 20.4 Properties + +Gate-backed text has the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Dynamic length +* Internally growable + +`text` is **not** a value type. + +--- + +### 20.5 Appending and Mutation + +Text is mutated explicitly using `mutate` or `take`. + +### Appending String Literals + +```pbs +let t = alloc text; + +take t.append("Hello"); +take t.append(", world"); +``` + +### Appending Other Text + +```pbs +let a = alloc text; +let b = alloc text; + +take a.append("Hello"); +take b.append("World"); + +take a.appendText(b); +``` + +All mutations affect all aliases. + +--- + +### 20.6 Reading Text + +Text contents are read by copying into the SAFE world. + +### Convert to String + +```pbs +let s: string = borrow t as tt { tt.toString() }; +``` + +The returned `string` is immutable snapshot copied from text into a SAFE string value runtime-owned. + +--- + +### 20.7 Aliasing Semantics + +Assigning a text value copies the gate: + +```pbs +let a = alloc text; +let b = a; // alias + +take a.append("!"); +``` + +Both `a` and `b` observe the same content. + +--- + +### 20.8 Passing Text to Functions + +Text values are passed by gate (handle): + +```pbs +fn appendExclamation(t: text): void +{ + take t.append("!"); +} +``` + +Calling this function mutates the caller's text. + +--- + +### 20.9 Copying Text + +To duplicate a text value, use `copy`: + +```pbs +let t2 = copy(t); +``` + +`copy`: + +* Allocates a new text object +* Copies all characters +* Returns a new strong gate + +--- + +### 20.10 Performance Notes + +* Appending may trigger internal reallocation +* Converting to `string` copies data +* Text mutation is O(n) in the worst case + +Text trades immutability for flexibility. + +--- + +### 20.11 Didactic Intent + +Gate-backed text exists to teach: + +* The cost of string mutation +* The difference between immutable and mutable strings +* Explicit heap allocation for dynamic text +* Aliasing through handles + +PBS does not allow silent string mutation. + +--- + +### 20.12 Summary + +* `text` is the dynamic string type in PBS +* `string` remains immutable and SAFE +* Text lives in Storage and requires `alloc` +* Mutation is explicit (`mutate`, `take`) +* Assignment aliases; `copy` duplicates + +Text completes the set of fundamental gate-backed collections. + +--- + +## 21. Gate-backed Map + +PBS provides **gate-backed maps** as associative collections stored in **Storage (heap)**. + +Maps associate keys to values and are designed for lookup-oriented data structures where indexed access is not sufficient. + +Like all gate-backed collections, maps belong to the **HIP world** and make aliasing, mutation, and cost explicit. + +--- + +### 21.1 Map Type + +The map type is written as: + +```pbs +map +``` + +Where: + +* `K` is the key type +* `V` is the value type + +Both `K` and `V` must be **value-safe types** or gate-backed types. + +--- + +### 21.2 Allocation + +Maps are created explicitly using `alloc`: + +```pbs +let m: map = alloc map; +``` + +Allocation: + +* Creates an empty map in Storage +* Registers it in the Gate Pool +* Returns a **strong gate** + +Maps cannot exist on the stack. + +--- + +### 21.3 Properties + +Gate-backed maps have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Dynamic size +* Key-based lookup + +Maps are **not value types**. + +--- + +### 21.4 Aliasing Semantics + +Assigning a map copies the gate, not the data: + +```pbs +let a = alloc map; +let b = a; // alias +``` + +Mutations through one alias are visible through all aliases. + +--- + +### 21.5 Core Operations + +### Insertion and Update + +Entries are inserted or updated via mutation: + +```pbs +mutate m as mm +{ + mm.put("health", 100); + mm.put("mana", 50); +} +``` + +For single operations, `take` may be used: + +```pbs +take m.put("score", 1000); +``` + +--- + +### Lookup + +Lookup operations do not mutate the map and return copied values: + +```pbs +let hp: optional = borrow m as mm +{ + mm.get("health"); +}; +``` + +* Returns `some(value)` if the key exists +* Returns `none` otherwise + +--- + +### Removal + +Entries are removed explicitly: + +```pbs +mutate m as mm +{ + mm.remove("mana"); +} +``` + +--- + +### 21.6 Iteration + +Maps may be iterated by key-value pairs copied into the SAFE world: + +```pbs +for pair in m +{ + let (k, v) = pair; // Tuple(K, V) +} +``` + +Iteration order is implementation-defined. + +To iterate keys or values explicitly: + +```pbs +for k in m.keys() { } +for v in m.values() { } +``` + +--- + +### 21.7 Passing Maps to Functions + +Maps are passed by gate (handle): + +```pbs +fn incrementScore(m: map): void +{ + take m.put("score", (peek m.get("score") else 0) + 1); +} +``` + +Calling this function mutates the caller's map. + +--- + +### 21.8 Copying Maps + +To duplicate a map and its contents, use `copy`: + +```pbs +let m2 = copy(m); +``` + +`copy`: + +* Allocates a new map +* Copies all entries +* Returns a new strong gate + +--- + +### 21.9 Performance Notes + +* Lookup is expected to be O(1) average +* Insertion and removal may rehash internally +* Iteration copies key-value pairs into the stack + +Maps trade predictability for flexibility. + +--- + +### 21.10 Didactic Intent + +Gate-backed maps exist to teach: + +* Associative data structures +* Explicit mutation and lookup +* Cost of dynamic containers +* Aliasing through handles + +Maps are powerful but must be used deliberately. + +--- + +### 21.11 Summary + +* Maps are gate-backed, associative collections +* Maps live in Storage and require `alloc` +* Assignment aliases; `copy` duplicates +* Mutation is explicit (`mutate`, `take`) +* Lookup returns optional values + +Maps complete the core set of gate-backed collection types in PBS. + +--- + +# 22. Gate-backed Deque + +PBS provides **gate-backed deques** (double-ended queues) as flexible, dynamic collections stored in **Storage (heap)**. + +A `deque` supports efficient insertion and removal at both ends and serves as the **foundational structure** for queues and stacks in PBS. + +Like all gate-backed collections, deques belong to the **HIP world** and make allocation, aliasing, and mutation explicit. + +--- + +## 22.1 Deque Type + +The deque type is written as: + +```pbs +deque +``` + +Where `T` is the element type. + +Deques are **homogeneous** and **dynamically sized**. + +--- + +## 22.2 Allocation + +Deques are created explicitly using `alloc`: + +```pbs +let d: deque = alloc deque; +``` + +Allocation: + +* Creates an empty deque in Storage +* Registers it in the Gate Pool +* Returns a **strong gate** + +Deques cannot exist on the stack. + +--- + +## 22.3 Properties + +Gate-backed deques have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Dynamic size +* Efficient operations at both ends + +Deques are **not value types**. + +--- + +## 22.4 Aliasing Semantics + +Assigning a deque copies the gate, not the data: + +```pbs +let a = alloc deque; +let b = a; // alias +``` + +Mutations through one alias are visible through all aliases. + +--- + +## 22.5 Core Operations + +### Length + +```pbs +let n: bounded = borrow d as dd { dd.len() }; +``` + +### Push Operations + +```pbs +take d.pushFront(10); +take d.pushBack(20); +``` + +### Pop Operations + +```pbs +let a: optional = borrow d as dd { dd.popFront() }; +let b: optional = borrow d as dd { dd.popBack() }; +``` + +Pop operations return `optional` and do not mutate when the deque is empty. + +--- + +## 22.6 Element Access + +Direct indexed access is supported for convenience: + +```pbs +let v: int = peek d[i]; +``` + +Mutation by index requires `mutate`: + +```pbs +mutate d as dd +{ + dd[i] = 42; +} +``` + +--- + +## 22.7 Iteration + +Deques may be iterated by value: + +```pbs +for v in d +{ + // v is a value copy +} +``` + +Iteration order is front to back. + +--- + +## 22.8 Passing Deques to Functions + +Deques are passed by gate (handle), not by value: + +```pbs +fn pushPair(d: deque, a: int, b: int): void +{ + take d.pushBack(a); + take d.pushBack(b); +} +``` + +Calling this function mutates the caller's deque. + +--- + +## 22.9 Copying Deques + +To duplicate a deque and its contents, use `copy`: + +```pbs +let d2 = copy(d); +``` + +`copy`: + +* Allocates a new deque +* Copies all elements +* Returns a new strong gate + +--- + +## 22.10 Queue and Stack Views + +In PBS, **queues** and **stacks** are not separate data structures. Instead, they are **usage conventions** built on top of `deque`. + +### Queue (FIFO) + +A queue uses the following operations: + +* Enqueue: `pushBack` +* Dequeue: `popFront` + +Example: + +```pbs +let q: deque = alloc deque; + +take q.pushBack(evt1); +take q.pushBack(evt2); + +let e1 = borrow q as qq { qq.popFront() }; +``` + +### Stack (LIFO) + +A stack uses the following operations: + +* Push: `pushBack` +* Pop: `popBack` + +Example: + +```pbs +let s: deque = alloc deque; + +take s.pushBack(1); +take s.pushBack(2); + +let top = borrow s as ss { ss.popBack() }; +``` + +This approach reduces the number of built-in types while preserving expressiveness. + +--- + +## 22.11 Performance Notes + +* Push and pop operations are expected to be O(1) amortized +* Indexed access may be O(1) or O(n), implementation-defined +* Iteration copies elements into the stack + +--- + +## 22.12 Didactic Intent + +Gate-backed deques exist to teach: + +* Flexible data structures +* FIFO vs LIFO semantics +* Explicit mutation and aliasing +* How abstractions emerge from disciplined usage + +Queues and stacks are concepts, not primitives. + +--- + +## 22.13 Summary + +* `deque` is a gate-backed, double-ended queue +* Deques live in Storage and require `alloc` +* Assignment aliases; `copy` duplicates +* Queue and stack are usage patterns + +Deques complete the set of core dynamic collections in PBS. + +--- + +## 23. Gate-backed Blob + +PBS provides **gate-backed blobs** as raw, byte-addressable buffers stored in **Storage (heap)**. + +A `blob` is the fundamental building block for: + +* Save data and serialization +* Asset loading (sprites, maps, audio chunks) +* Networking payloads +* Bridging to host APIs + +Blobs are intentionally low-level, but they remain **explicit** and **teachable** in the HIP world. + +--- + +### 23.1 Blob Type + +The blob type is written as: + +```pbs +blob +``` + +A `blob` is a dynamic buffer of bytes. + +--- + +### 23.2 Allocation + +Blobs are created explicitly using `alloc` with a size: + +```pbs +let b: blob = alloc blob(256b); +``` + +Allocation: + +* Creates a storage object containing `size` bytes +* Registers it in the Gate Pool +* Returns a **strong gate** + +--- + +### 23.3 Properties + +Gate-backed blobs have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Byte-addressable +* Size is fixed after allocation (v0) + +Blobs are **not value types**. + +--- + +### 23.4 Aliasing Semantics + +Assigning a blob copies the gate, not the data: + +```pbs +let a = alloc blob(16b); +let c = a; // alias + +take a.writeU8(0b, 255); +let x = borrow c as cc { cc.readU8(0b) }; // x == 255 +``` + +Mutations through one alias are visible through all aliases. + +--- + +### 23.5 Core Operations + +The following operations are required for `blob`. + +### Size + +```pbs +let n: bounded = borrow b as bb { bb.size() }; +``` + +### Read/Write Bytes + +```pbs +take b.writeU8(0b, 42); +let v: bounded = borrow b as bb { bb.readU8(0b) }; +``` + +Indices are `bounded` and refer to byte offsets. + +--- + +### 23.6 Typed Reads and Writes + +For convenience and portability, blobs may support typed operations. + +The endianness of typed operations is **explicit**. + +Examples (LE shown): + +```pbs +take b.writeU16LE(0b, 0xBEEF); +let x: bounded = borrow b as bb { bb.readU16LE(0b) }; + +take b.writeI32LE(2b, -10); +let y: int = borrow b as bb { bb.readI32LE(2b) }; +``` + +Implementations may provide a minimal subset initially (e.g. only U8/U16/U32). + +--- + +### 23.7 Slicing and Views (Deliberately Deferred) + +`blob` does not expose raw pointers or escaping views in v0. + +If slicing is introduced later, it must preserve PBS rules: + +* No escaping references +* Explicit cost +* Explicit lifetime + +--- + +### 23.8 Bounds and Safety + +* Out-of-bounds reads and writes are runtime errors +* Typed reads/writes must also bounds-check their byte width + +These checks keep the model teachable without hiding risk. + +--- + +### 23.9 Passing Blobs to Functions + +Blobs are passed by gate (handle), not by value: + +```pbs +fn fill(b: blob, value: bounded): void +{ + let n = borrow b as bb { bb.size() }; + let i = 0b; + while (i < n) + { + take b.writeU8(i, value); + i = i + 1b; + } +} +``` + +Calling this function mutates the caller's blob. + +--- + +### 23.10 Copying Blobs + +To duplicate a blob and its contents, use `copy`: + +```pbs +let b2 = copy(b); +``` + +`copy`: + +* Allocates a new blob with the same size +* Copies all bytes +* Returns a new strong gate + +--- + +### 23.11 Didactic Intent + +Gate-backed blobs exist to teach: + +* Raw memory as bytes +* Bounds checking +* Serialization patterns +* Explicit mutation and aliasing + +Blobs provide low-level power without introducing implicit pointers. + +--- + +### 23.12 Summary + +* `blob` is a gate-backed byte buffer +* Blobs live in Storage and require `alloc blob(size)` +* Assignment aliases; `copy` duplicates +* Mutation is explicit (`mutate`, `take`) +* Bounds are checked at runtime + +Blobs are the foundation for binary data handling in PBS. + +--- + +# 24. Custom Storage API + +PBS allows users to define **custom gate-backed storage types** using the `declare storage struct` construct. + +A `storage struct` follows the same declaration model as a regular `struct`, but lives in **Storage (heap)** and obeys **HIP-world semantics**. Value-only features are intentionally not available. + +This chapter defines the rules, capabilities, and limitations of custom storage types. + +--- + +## 24.1 Storage Struct Declaration + +A storage struct is declared using: + +```pbs +declare storage struct S(x: int, b: bool) +{ + fn f(self: mut this): void + { + // ... + } +} +``` + +The declaration consists of: + +* A **name** (`S`) +* A **field list** declared in the header +* A **method body** block + +Fields declared in the header are the **only fields** of the storage struct. + +--- + +## 24.2 Allocation + +Storage structs are created explicitly using `alloc`: + +```pbs +let s: S = alloc S; +``` + +Allocation: + +* Creates a storage object +* Registers it in the Gate Pool +* Returns a **strong gate** + +Storage structs cannot exist on the stack and cannot be created by value construction. + +--- + +## 24.3 Properties + +Storage structs have the following properties: + +* Stored in Storage (heap) +* Accessed via gates +* Assignment copies the gate (aliasing) +* Lifetime managed via reference counting +* Fields may contain: + + * value types + * gate-backed types + * `weak` and `optional` + +Storage structs are **not value types**. + +--- + +## 24.4 Field Access Rules + +Fields of a storage struct may only be accessed inside `borrow` or `mutate` blocks. + +### Reading Fields + +```pbs +let hp: int = borrow s as ss +{ + ss.x; +}; +``` + +### Writing Fields + +```pbs +mutate s as ss +{ + ss.b = true; +} +``` + +Direct field access outside these blocks is not allowed. + +--- + +## 24.5 Methods on Storage Structs + +Storage structs may define methods using the same syntax as regular structs. + +```pbs +fn f(self: mut this): void +{ + self.x = self.x + 1; +} +``` + +Rules: + +* `self: this` provides read-only access +* `self: mut this` provides mutable access +* Mutating methods require a mutable context to be called + +--- + +## 24.6 Mutation Sugar (`take`) + +For single mutating method calls, `take` may be used: + +```pbs +take s.f(); +``` + +Equivalent to: + +```pbs +mutate s as ss +{ + ss.f(); +} +``` + +--- + +## 24.7 Strong and Weak Fields + +Fields of a storage struct may be declared as strong or weak gates. + +```pbs +declare storage struct Node(value: int, parent: optional>) +{ + fn attach(self: mut this, p: Node): void + { + self.parent = some(p as weak); + } +} +``` + +Rules: + +* Strong fields increment reference counts +* Weak fields do not keep objects alive +* Weak fields must be promoted before use + +This enables cyclic graphs without leaks. + +--- + +## 24.8 Copying Storage Structs + +To duplicate a storage struct and its contents, use `copy`: + +```pbs +let s2 = copy(s); +``` + +`copy`: + +* Allocates a new storage object +* Performs a deep copy of all fields +* Returns a new strong gate + +--- + +## 24.9 Differences from `struct` + +While `storage struct` shares syntax with `struct`, several features are intentionally missing. + +### Not Available in `storage struct` + +* Constructors (`[]`) +* Static blocks (`[[]]`) +* Value initialization syntax (`S(1, true)`) +* Stack allocation + +Initialization of storage structs must be performed explicitly after allocation. + +```pbs +let s = alloc S; +mutate s as ss +{ + ss.x = 10; + ss.b = false; +} +``` + +These restrictions preserve the explicit nature of heap allocation. + +--- + +## 24.10 Interaction with Built-in Collections + +Storage structs may freely contain and manipulate gate-backed collections. + +```pbs +declare storage struct Inventory(items: list) +{ + fn add(self: mut this, it: Item): void + { + take self.items.push(it); + } +} +``` + +All collection semantics (aliasing, mutation, copying) apply recursively. + +--- + +## 24.11 Didactic Intent + +The Custom Storage API exists to teach: + +* Heap allocation without implicit safety nets +* Ownership and aliasing in user-defined types +* Explicit initialization and mutation +* How complex data structures are built from primitives + +Storage structs intentionally expose power without hiding cost. + +--- + +## 24.12 Summary + +* `declare storage struct` defines custom gate-backed types +* Storage structs live in Storage and require `alloc` +* Fields are declared in the header +* No constructors, static blocks, or value initialization +* Access is controlled via `borrow`, `mutate`, and `take` +* Strong and weak fields control lifetime + +Custom storage structs complete the PBS HIP world. + +--- + +# 25. Box and Unbox (Built-in Heap Values) + +PBS is a **value-first language**: primitive types and `struct` values live on the **stack** and follow SAFE-world semantics. + +In some situations, however, a value must acquire **identity**: + +* It must be shared between multiple owners +* It must be mutated through aliases +* It must outlive a stack frame + +For these cases, PBS provides the built-in **Box** abstraction. + +`Box` is a **gate-backed built-in type**, and `box` / `unbox` are **built-in operations**. + +--- + +## 25.1 Box Type + +```pbs +Box +``` + +A `Box`: + +* Stores exactly one value of type `T` in **Storage (heap)** +* Is gate-backed (strong gate) +* Participates in aliasing and reference counting + +`Box` is not a value type. + +--- + +## 25.2 Boxing a Value (`box`) + +The `box` builtin allocates a `Box` in Storage and initializes it with a **copy** of a SAFE value. + +```pbs +let v: Vector = Vector.ZERO; // SAFE (stack) +let b: Box = box(v); // HIP (heap) +``` + +Rules: + +* `box(x)` performs a copy of `x` +* The original value remains unchanged on the stack +* The result is a strong gate + +--- + +## 25.3 Unboxing a Value (`unbox`) + +The `unbox` builtin copies the value stored in a box back into the SAFE world. + +```pbs +let vv: Vector = unbox(b); +``` + +Rules: + +* `unbox(b)` always returns a **copy** +* The returned value has no identity +* Mutating the returned value does not affect the box + +--- + +## 25.4 Generic Form + +The `box` and `unbox` builtins are fully generic: + +```pbs +let b: Box = box(t); +let t2: T = unbox(b); +``` + +This works for any SAFE type `T`. + +--- + +## 25.5 Aliasing and Mutation + +Boxes follow standard gate semantics. + +```pbs +let a = box(10); +let b = a; // alias + +take a.set(20); +let x = unbox(b); // x == 20 +``` + +Assignment copies the gate, not the boxed value. + +--- + +## 25.6 Mutating a Box + +A boxed value may be mutated via methods on `Box`. + +```pbs +take b.set(newValue); +``` + +Reading without mutation uses `unbox`. + +Direct access to internal storage is not exposed. + +--- + +## 25.7 Relationship to SAFE and HIP Worlds + +* SAFE world: values without identity (stack) +* HIP world: values with identity (heap) + +`box` is the explicit transition from SAFE to HIP. +`unbox` is the explicit transition from HIP to SAFE. + +--- + +## 25.8 Informative: Possible Lowering Using `storage struct` + +This section is **informative**, not normative. + +A `Box` can be implemented internally using a storage struct: + +```pbs +declare storage struct _BoxImpl(value: T) +{ + fn get(self: this): T { self.value } + fn set(self: mut this, v: T): void { self.value = v } +} +``` + +With lowering rules: + +* `box(x)` → `alloc _BoxImpl` + store copy of `x` +* `unbox(b)` → `borrow b as bb { bb.get() }` + +This implementation detail is hidden from the user. + +--- + +## 25.9 Didactic Intent + +The Box abstraction exists to teach: + +* How values acquire identity +* Why heap allocation must be explicit +* The boundary between SAFE and HIP worlds +* Controlled sharing and mutation of values + +Boxing is explicit by design. + +--- + +## 25.10 Summary + +* `Box` is a built-in gate-backed type +* `box` and `unbox` are built-in operations +* Boxing copies a SAFE value into the heap +* Unboxing copies the value back to the stack +* `Box` participates fully in aliasing and RC + +Box and unbox complete the bridge between value semantics and identity semantics in PBS. \ No newline at end of file diff --git a/test-cartridges/color-square-pbs/src/prometeu_scripting.md b/test-cartridges/color-square-pbs/src/prometeu_scripting.md deleted file mode 100644 index 6a0f0d0a..00000000 --- a/test-cartridges/color-square-pbs/src/prometeu_scripting.md +++ /dev/null @@ -1,648 +0,0 @@ -# Prometeu Base Script (PBS) - -**Status:** v0 (frontend-freeze) -**Goal:** stack-only language, no GC, explicit semantics, predictable runtime - ---- - -## 0. Philosophy (anchor section) - -PBS is designed as: - -* **Value-first** (no implicit references, no aliasing of mutable state) -* **Stack-only** (no heap, no GC) -* **Explicit mutability** (bindings, not types) -* **Didactic** (rules visible in syntax) -* **Runtime-cheap** (frontend-heavy, backend-simple) - -Everything in the language flows from these constraints. - ---- - -## 1. Project, Files & Modules - -### 1.1 Files - -* Extension: `.pbs` -* Project has a **root directory** -* `@path` is resolved relative to project root - -### 1.2 Module Model - -* **One directory = one module**. -* A module boundary is **real**: parent/child/sibling directories do **not** get automatic visibility. - * Code in `@project:modA` does **not** automatically see declarations in `@project:modA/sub`. - * Code in `@project:modA/sub` does **not** automatically see declarations in `@project:modA`. - * Sibling modules (e.g., `@project:modA` and `@project:modB`) are completely isolated unless you `import`. -* **Cross-module access always requires `import`**, and only `pub` symbols may cross module boundaries. - -### 1.3 Automatic Module Index - -* No mandatory barrel file -* Compiler builds an index from: - - * all `pub` symbols - * in `.pbs` files **directly inside the directory** -* Subdirectories are excluded - -### 1.4 Visibility Modifiers - -PBS uses explicit visibility modifiers to control symbol exposure. Visibility is the **only** mechanism that decides whether another file/module can see a symbol. - -* **default (no modifier)** — **file-private** - - * Visible **only** within the declaring `.pbs` file. - * Never visible from any other file, even inside the same module. - -* **`mod`** — **module-visible** - - * Visible to **all files** in the same module (same directory). - * Not visible outside the module. - * No `import` is required for other files in the same module to refer to it. - -* **`pub`** — **public API** - - * Exported as part of the module’s public surface. - * May be imported by other modules. - * Within the same module, `pub` behaves like `mod` (visible across files without import). - -Important: - -* A symbol is either **file-private**, **module-visible**, or **public**. There is no other visibility level. -* Visibility is checked independently in the **type** and **value** namespaces. - -Visibility applies uniformly to: - -* `declare` type declarations -* `service` -* value-level symbols - ---- - -## 2. Namespaces & Visibility - -### 2.1 Global Namespaces - -Existem **dois namespaces globais**: - -**Type namespace** - -Tipos são introduzidos **exclusivamente** por declarações `declare`. -O que vem após `declare` define a categoria do tipo. - -Formas válidas: - -```pbs -declare struct Name { ... } -declare error Name { ... } -declare contract Name { ... } -``` - -**Value namespace** - -Introduzido por declarações executáveis: - -* `fn` -* `service` -* `let` - ---- - -## 3. Top-level Declarations - -A `.pbs` file may contain: - -* `import` -* **type declarations** via `declare`: - - * `declare struct` - * `declare error` - * `declare contract` -* `service` -* `fn` (always file-private) - -Order is free. - ---- - -## 4. Resolver Rules - -This section defines how the compiler resolves modules, imports, and symbol names into concrete declarations. - -### 4.1 Units of compilation - -* The compilation unit is a **project** (root + all referenced modules/files). -* The root of the project is associated with the src/main/modules -* A **module** is exactly one directory. -* A **file unit** is one `.pbs` file. - -Examples, lets say we have a project called: `project`: -``` -project - |- prometeu.json - |- prometeu-cache - | |-cache.lock - |- src - | |- main - | | |- modules - | | | |- module-1 - | | | | |- file-1.pbs - | | | | |- file-2.pbs - | | | | | |- fearture A - | | | | |- file-3.pbs - | | | | |- module-2 - | | | | | |- file-1.pbs - | | | | | |- file-2.pbs - | | | | | | |- fearture B - | | | | | |- file-3.pbs - | |- resources - | | |- resource-1.txt - | |- test - | | |- modules - | | | |- module-1 - | | | | |- file-1.pbs - | | | | |- file-2.pbs - | | | | |- file-3.pbs - | | |- resources -``` - -For a project `project/src/main/` is the root, and it will be presented in the import as (when looking for feature A and B): -``` -import { featureA } from "@project:module-1" -import { featureB } from "@project:module-1/module-2" -``` - -The project name is a configuration inside `prometeu.json`. A project always defines a unit compilation. Other compilation -units could be linked to the project as a dependency. When building a project, all dependencies will be copied into a -directory called `prometeu-cache` (read-only) inside the project root. -A `project.json` could be look like. -```json -{ - "name": "project", - "dependencies":{ - "project-A": { - "version": "1.0.0", - "source": "path:../project-A" - }, - "project-B": { - "version": "1.5.3", - "source": "git:https://github.com/project-B.git" - } - } -} -``` - -When compiling, all deps and current project will be flattened, and Project names should be unique otherwise it will -cause conflicts and compilation errors on the current project. - -### 4.2 Namespaces - -Resolution is performed in **two independent namespaces**: - -* **Type namespace**: declarations introduced by `declare` (`struct`, `error`, `contract`). -* **Value namespace**: declarations introduced by `service`, `fn`, and `let`. - -A name may exist in both namespaces, but this is **discouraged**; toolchains may report this as an error or warning. - -### 4.3 Visibility gates - -For any candidate symbol `S`, visibility is checked explicitly: - -* **file-private (default)** — visible only within the same file -* **`mod`** — visible to any file in the same module -* **`pub`** — visible to other modules via `import` - -No implicit visibility exists beyond these rules. - -### 4.4 Module index construction - -For each module directory `M`: - -* The compiler scans all `.pbs` files **directly inside** `M`. -* The module’s **public index** contains **only** `pub` symbols from those files. -* Subdirectories never contribute to the parent module’s index. - -Duplicate rules: - -* Any `structs, services, contracts, errors` duplicate `pub` names in the same namespace cause a compile error: - - * `duplicate public symbol in module`. - -### 4.5 Import resolution - -Imports are the **only** mechanism for cross-module access. - -```pbs -import { X, Y as Z } from "@project:module"; -``` - -Rules: - -* Only `pub` symbols may be imported. -* Import-by-module resolves against the module public index. -* Import-by-file is not allowed at all. - -### 4.6 Local resolution order - -Within a file, name lookup follows this order **within each namespace**: - -1. Local bindings (parameters, local `let`, innermost scope first) but with warnings - - when `import { X as Y } from "@project:module"` and `let Y: int = 0;` `let Y` wins but could generate a warning -2. File-level declarations (file-private, `mod`, and `pub` in the same file) -3. Imported bindings -4. When `import { X } from "@project:module"` (or when using `as`) and we already have a `mod X ...` for this module, compilation error. - -Shadowing rules: - -* Local `let` shadowing is allowed. -* Shadowing of `fn` and `service` names is not allowed. - -### 4.7 Cross-file and cross-module access - -**Within the same module:** - -* `mod` and `pub` symbols are visible across files without `import`. -* file-private symbols are never visible. - -**Across modules:** - -* only `pub` symbols are visible -* access always requires an explicit `import`. - -### 4.8 Cycles - -* Import cycles are allowed only if name resolution can be completed. -* PBS has no top-level execution, so cycles are resolved purely at the symbol level. -* Any cycle that prevents construction of a complete symbol table is a compile error. - -### 4.9 Contracts and services - -* `declare contract C` introduces `C` in the **type namespace**. -* `service S: C` resolves `C` as a type and validates that `S` implements all declared signatures. -* they can be implemented by the Prometeu runtime / host environment. - ---- - -## 4. Services - -A `service` represents an **explicit API boundary** between PBS code and either: - -* other PBS modules, or -* it is definitely a singleton by design -* can implement a contract, or not - - when implementing a contract, signatures must match exactly -* every `fn` inside in a `service` will follow the same visibility as the service it belongs (`pub` or `mod`). - - once being able to see the service, it can see all its methods. - -A service is never an implementation detail. - -### 4.1 Declaration and visibility - -A service must always be declared with an explicit visibility modifier: - -* `pub service` — part of the module’s public API -* `mod service` — internal API, visible only inside the module - -There is **no such thing** as a private service. - -### 4.2 Service methods - -* All methods declared inside a service are **public within that service**. -* There are no private or helper methods inside a service. -* A service method may call: - - * top-level `fn` in the same file - * other services it can legally see - -### 4.3 Implementation rule - -If logic is not conceptually part of the API, it **must not** live inside a service. -Such logic must be implemented as file-private `fn`. - ---- - -## 6. Types - -This section describes all value types available in PBS and how they are declared. - -### 6.1 Primitive types - -PBS provides a small, fixed set of primitive types: - -* `void` -* `int` (32-bit signed) -* `long` (64-bit signed) -* `float` (32-bit IEEE-754) -* `double` (64-bit IEEE-754) -* `bool` -* `char` (Unicode code point, 32-bit) -* `string` - -Important notes: - -* `string` values are **immutable**. -* Strings exist only as literals stored in a constant pool. -* There is no dynamic string allocation at runtime. - -### 6.2 Struct types - -User-defined value types are declared using `declare struct`. - -A struct: - -* is a **pure value type** -* has no identity -* follow the rules of **mutability and borrowing" described below -* has no inheritance or subtyping -* it is only copied when mutated, otherwise it is **viewed as immutable** and passed as ref by default. - -All behavior related to the type must be defined inside the struct body. - -### 6.3 `this` - -Inside a struct body, the special type `this` refers to the struct itself. - -Rules: - -* `this` may only appear inside a `declare struct` body. -* Outside a struct, `this` is illegal. - ---- - -## 7. Structs & Constructors - -```pbs -declare struct Vector(x: float, y: float) -[ - (): (0,0) as default { } - (a: float): (a,a) as square { } - (): (1,1) as normalize { } -] -[[ - ZERO: default() - ONE: square(1.0) -]] -{ - pub fn len(self: this): float { ... } - pub fn scale(self: mut this): void { ... } - pub fn normalize(self: mut this): void { ... } -} -``` - -Key rules: - -* fields private by default -* constructor aliases live in type namespace but can never be imported, in the exemplo only `Vector` can be imported. -* no static methods -* static block values are compile-time constants - -There will never be a conflict between an alias and a method of the same name. -A constructor will be called as `let v = Vector.normalize()` and methods will be called as `v.normalize()`. -In fact, they call different things, one is static, and the other is an instance method (there are no static methods on structs). - -The static block `[[ ]]` should be static (and generate static on constant pool) and can use only constructors and static values, as helpers. - ---- - -## 8. Mutability & Borrow Model (Core Rule) - -This is a foundational rule of PBS. - -Mutability is **never** a property of a type. -Mutability belongs **only** to bindings (variables and parameters). - -### 8.1 Immutable by default - -All bindings are immutable unless explicitly marked `mut`. - -```pbs -let v = Vector.ZERO; // immutable binding -let mut w = Vector.ZERO; // mutable binding (local copy) -``` - -Some examples of mutable bindings: -```pbs -let v = Vector.ZERO; -v.scale(); // ERROR: cannot mutate a read-only binding - -let mut w = v; -w.scale(); // OK: when mut it copies the value, and could be changed - -fn f(v: Vector): Vector -{ - let mut w = v; - return w; // when it happens compiler will generate a copy otherwise w will be lost in the stack -} - -fn g(v: Vector): void -{ - let vro = f(Vector.ZERO); // OK! and it will be a readonly copy - let vrw = mut f(Vector.ZERO); // OK! and it will be a mutable copy of the copy -} -``` - -### 8.2 Consequences of binding-based mutability - -* Mutating a value never affects any other binding. -* Static values can never be mutated. -* There is no aliasing of mutable state. - -### 8.3 Parameters and copying - -Function parameters follow these rules: - -* `T` parameters are passed as **read-only borrows**. -* `mut T` parameters create a **local mutable copy**. - -The caller is never affected by mutations performed inside the function. - -### 8.4 Method receivers - -Methods on structs declare mutability explicitly on the receiver: - -```pbs -fn len(self: this): float // read-only -fn scale(self: mut this): void // mutating -``` - -A mutating method: - -* requires a **mutable lvalue** at the call site -* cannot be called on temporaries or static values - ---- - -## 9. Expressions & Control Flow - -* `if / else` -* `for` with ranges -* `when` expression -* `return` - -`when`: - -```pbs -let x = when a > b then 1 else 2; -``` - ---- - -## 10. Return Fallback (`else`) - -```pbs -fn v(): Vector else Vector.ZERO -{ - if cond return Vector.ONE; -} -``` - -Used to guarantee total functions without boilerplate. - ---- - -## 11. Numeric Rules - -* implicit widen: `int → long → float → double` -* no implicit narrowing -* cast syntax: `expr as Type` - ---- - -## 12. `bounded` - -`bounded` is a dedicated scalar type for **indices and sizes**. - -### 12.1 Purpose - -The goal of `bounded` is to make indexing and counting: - -* explicit -* safe -* visually distinct from general arithmetic - -### 12.2 Representation - -* Internally represented as an unsigned 16-bit integer (`u16`). -* Valid range: `0 .. 65535`. - -### 12.3 Usage rules - -`bounded` is used for: - -* array indices -* array lengths -* default `for` loop counters - -Operations are intentionally limited: - -* allowed: comparison, checked `+` and `-` -* disallowed: multiplication, division, bitwise operations - ---- - -## 13. Fixed Arrays - -```pbs -Array[Nb] -``` - -* `Nb` is compile-time `bounded` literal -* immutable bindings = view -* mutable bindings = owned storage - -Supports: - -* indexing (`bounded` only) -* slicing `[a..b[` (half-open) - -Return escape model prevents dangling views. - ---- - -## 14. `optional` - -`optional` represents the explicit presence or absence of a value. - -### 14.1 Design intent - -* Absence is a **normal, explicit state**. -* No traps or implicit unwraps exist. -* No heap allocation is involved. - -### 14.2 Extraction rule - -The **only** supported way to extract a value from an optional is with `else`: - -```pbs -let x: int = opt else 0; -``` - -This makes fallback behavior explicit at the call site. - ---- - -## 15. `result` - -* typed errors -* no exceptions -* no implicit extraction - -```pbs -let v = f()?; -``` - -### Error handling with `handle` - -`handle` is the *only* construct that allows value extraction from a `result` while mapping errors. - -```pbs -let x: int = handle r -{ - ErrorA.not_found => ErrorB.io, - ErrorA.denied => ErrorB.permission, - _ => ErrorB.unknown, -}; -``` - -Semantics: - -* If `r` is `ok(v)`, the expression evaluates to `v` -* If `r` is `err(e)`, the first matching arm is selected and an early `return err(mapped)` is executed - -Rules: - -* Arms must be exhaustive (explicit or via `_`) -* Right-hand side must be a label of the destination error type -* No traps are permitted -* **The only possible way to extract a value is by handling the error** - ---- - -## 16. What is *deliberately missing* (v0) - -* heap allocation -* GC -* references -* inheritance -* traits / interfaces -* async -* closures -* generics (beyond containers) - ---- - -## 17. Roadmap (v1+ ideas) - -This is a non-binding list of future directions. None of these are part of v0. - -* generics for `struct` -* slice views with runtime bounds -* iterator sugar -* `defer` -* pattern matching on `result` -* ABI boundary spec (VM ↔ PBS) -* contract versioning - ---- - -**This document is now the canonical organized v0 spec.****