add spec docs

This commit is contained in:
bQUARKz 2026-03-01 11:26:24 +00:00
parent f3be4f7946
commit aa1e0860d2
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
17 changed files with 2412 additions and 0 deletions

View File

@ -0,0 +1,91 @@
# PBS Language Charter
Status: Draft v1
Applies to: Prometeu Base Script (PBS) language design
## 1. Mission
PBS exists to make 2D game development on Prometeu intuitive for beginners and reliable for experienced developers, without sacrificing deterministic runtime behavior.
## 2. Product Vision
PBS is a game-first language for a logical, evolving hardware platform.
It must feel fluid for frame-driven gameplay code while remaining strict enough to produce stable, verifiable bytecode for a closed VM target.
## 3. Target Users
- Beginner creators building their first playable 2D games.
- Intermediate developers shipping complete games with predictable behavior.
- Advanced developers who need explicit control of architecture and performance costs.
## 4. Core Principles
1. Game-first ergonomics: common gameplay patterns must be concise.
2. Determinism first: same program + same inputs/events => same behavior.
3. Explicit boundaries: host interaction, costs, and side effects must be visible.
4. Safe defaults: simple code paths should be hard to misuse.
5. Progressive power: advanced control is opt-in, not mandatory.
6. Compatibility discipline: language evolution must not silently break cartridges.
## 5. Scope of PBS v1
- Stable core syntax and grammar contract.
- Static and dynamic semantics for deterministic game code.
- Typed host function declarations (`host fn`) aligned with syscall/capability model.
- Module visibility and import model suitable for multi-file game projects.
- Diagnostics and conformance requirements for interoperable implementations.
## 6. Out of Scope for PBS v1
- New VM opcodes as a language requirement.
- Non-deterministic async execution models.
- Mandatory custom allocator model replacing runtime GC.
- Advanced memory syntax (`alloc/borrow/mutate/...`) before explicit profile activation.
## 7. Non-Negotiable Constraints
- Runtime and bytecode authority override language preferences.
- Closed VM compatibility is mandatory.
- `FRAME_SYNC`-based execution semantics are preserved.
- Host API usage remains capability-gated and versioned.
## 8. Evolution Commitments
- Additive-first evolution for minor releases.
- Incompatible changes only in major lines with migration guidance.
- Canonical host primitive versioning by `(module, name, version)`.
- Deterministic loader rejection when target contracts are unsupported.
## 9. Quality Bar
PBS is considered healthy only if:
- newcomers can produce a working 2D game loop quickly,
- generated behavior is deterministic and replayable,
- diagnostics are stable and actionable,
- performance risks are visible in tooling,
- older supported cartridges keep running across updates.
## 10. Required Spec Set Before Implementation
Before implementation starts, PBS must define at least:
1. Core Syntax Specification.
2. Static Semantics Specification.
3. Dynamic Semantics Specification.
4. Memory and Lifetime Specification.
5. Host ABI Binding Specification.
6. Module/Package Specification.
7. Diagnostics Specification.
8. IR and Lowering Specification.
9. Conformance Test Specification.
10. Compatibility and Evolution Policy.
## 11. Decision Rule
When trade-offs appear, decisions must prioritize, in order:
1. Determinism and compatibility.
2. Clarity for beginners.
3. Fluency for real game workflows.
4. Performance transparency for advanced users.

View File

@ -0,0 +1,150 @@
# PBS Governance and Versioning
Status: Draft v1
Applies to: PBS language, profiles, and related specifications
## 1. Purpose
This document defines how PBS decisions are made and how versions evolve without breaking the ecosystem unpredictably.
## 2. Scope
This policy governs:
- language specification changes,
- profile changes (for example, Core/Game),
- standard library specification changes,
- compatibility policy updates,
- release and deprecation decisions.
## 3. Governance Model
PBS uses a spec-first governance model.
No implementation change is considered normative until the relevant spec change is accepted.
### 3.1 Roles
- Maintainers: approve/reject RFCs and cut releases.
- Editors: keep specs coherent, complete, and cross-referenced.
- Implementers: provide feasibility feedback and conformance evidence.
- Community contributors: propose RFCs and review drafts.
### 3.2 Authority Rule
If a governance decision conflicts with runtime/bytecode authority, runtime/bytecode authority wins.
## 4. Decision Process (RFC)
All non-trivial changes MUST go through RFC.
### 4.1 RFC Lifecycle
1. Draft: problem, proposal, alternatives, migration impact.
2. Review: open feedback period.
3. Decision: `Accepted`, `Rejected`, or `Deferred`.
4. Spec Merge: normative documents updated.
5. Implementation: only after spec merge.
6. Conformance: tests and diagnostics updated before release.
### 4.2 Fast-Track
Fast-track is allowed only for:
- critical correctness fixes,
- security/capability violations,
- deterministic behavior regressions.
Fast-track changes still require post-merge RFC backfill.
## 5. Version Domains
PBS versioning is separated by domain to avoid ambiguity.
1. Language Version (`pbs_lang`): syntax + semantics.
2. Profile Version (`pbs_profile`): profile-specific surface (Core/Game).
3. Stdlib Version (`pbs_stdlib`): standard library contract.
4. Compatibility Policy Version (`pbs_compat`): evolution/deprecation policy text.
Host primitives remain versioned by canonical identity `(module, name, version)`.
## 6. Versioning Scheme
Each domain uses semantic versioning:
- `MAJOR`: incompatible changes.
- `MINOR`: backward-compatible additions.
- `PATCH`: clarifications/fixes with no intended behavior break.
## 7. Change Classification
### 7.1 Patch Changes
- wording fixes,
- unambiguous typo corrections,
- diagnostics wording adjustments without code/behavior contract changes.
### 7.2 Minor Changes
- additive syntax behind explicit profile gates,
- new stdlib APIs,
- new host primitive versions added without removing old supported versions.
### 7.3 Major Changes
- behavior changes that can alter valid program outcomes,
- removal of previously supported constructs,
- incompatible type or resolution rule changes.
## 8. Compatibility Rules
- Supported old versions MUST continue to compile/link/run as declared by the compatibility window.
- A release MUST publish a compatibility matrix for language/profile/stdlib domains.
- Toolchains MUST reject unsupported targets deterministically with explicit diagnostics.
## 9. Deprecation and Removal Policy
Deprecation stages:
1. Introduce deprecation notice in specs and tooling.
2. Keep feature functional during declared support window.
3. Remove only in a subsequent major version.
Every deprecation MUST include:
- replacement path,
- migration notes,
- timeline (first deprecated version, last supported version, removal version).
## 10. Release Requirements
Before any version release:
1. Specs for changed domains are updated.
2. Changelog is published with change classification.
3. Conformance suite is updated and passing.
4. Compatibility matrix is published.
5. Migration guide is provided for any major-impact changes.
## 11. Documentation Contract
Every accepted change MUST update, when relevant:
- normative spec sections,
- examples,
- diagnostics documentation,
- conformance tests,
- compatibility matrix/changelog.
## 12. Conflict Resolution
When maintainers disagree, the tie-breaker is:
1. determinism and compatibility,
2. clarity for beginners,
3. implementation feasibility and tooling stability.
## 13. Non-Goals
- Replacing runtime authority with governance-only decisions.
- Defining subsystem-specific API semantics in this document.
- Granting implementation behavior override over normative specs.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,565 @@
# PBS Static Semantics Specification
Status: Draft v1 (Normative)
Scope: binding, callable formation, type checking, and deterministic function application
## 1. Purpose
This document defines the static semantics of PBS core language.
It is intended to:
- give deterministic meaning to declarations accepted by the syntax specification,
- define callable identity and application rules,
- make type checking stable across implementations,
- preserve beginner-facing clarity without hidden callable behavior.
## 2. Core Semantic Model
### 2.1 Authority and precedence
Normative precedence:
1. Runtime authority (`docs/specs/hardware/topics/chapter-2.md`, `chapter-3.md`, `chapter-9.md`, `chapter-12.md`, `chapter-16.md`)
2. Bytecode authority (`docs/specs/bytecode/ISA_CORE.md`)
3. `3. Core Syntax Specification.md`
4. This document
If a rule here conflicts with higher authority, it is invalid.
### 2.2 Semantic namespaces
PBS distinguishes at least:
- type namespace,
- value namespace,
- callable namespace.
Rules:
- `declare struct`, `declare contract`, `declare error`, `declare enum`, and `declare callback` introduce names in the type namespace.
- `let`, parameters, and `declare const` introduce names in the value namespace.
- Top-level `fn`, service methods, and `host fn` participate in the callable namespace.
- `fn` declarations are not first-class values in v1 core and therefore do not become ordinary value expressions.
- `bind(...)` is a callback-formation construct, not a general function-value construct.
### 2.3 Tuple shapes
Static checking uses tuple shapes as semantic objects.
Definitions:
- Input tuple shape: ordered sequence of zero or more input slots.
- Output tuple shape: ordered sequence of zero or more output slots.
- Each slot has a type and a stable label.
Rules:
- Tuple shapes in PBS core are always named, except for unit `()`.
- Input slot labels come from parameter names and are local frame names.
- Output slot labels come from return annotations and are visible for multi-slot member projection.
- Tuple-shape identity for overload and resolution ignores labels.
- Tuple-shape compatibility requires matching arity and positional slot types.
- Labels must still be preserved as part of declaration surface and projection metadata.
- A single-slot tuple shape has a distinguished carrier value type equal to its slot type.
- In value positions, single-slot tuple values collapse to their carrier value type.
- Single-slot tuple shapes have no dedicated tuple-literal surface in v1 core.
## 3. Functions and `apply`
### 3.1 Canonical function model
Every callable `fn` in PBS is modeled statically as:
```text
Fn : InputTuple -> OutputTuple
```
Rules:
- `fn f(a: A, b: B)` has input tuple shape `(a: A, b: B)`.
- `fn f()` has empty input tuple shape `()`.
- `-> void`, `-> ()`, and omitted return annotation all denote empty output tuple shape `()`.
- `-> T` denotes a single-slot output tuple shape `(value: T)`.
- `-> (x: T)` denotes a single-slot output tuple shape `(x: T)`.
- `-> (x: T, y: U)` denotes a two-slot output tuple shape `(x: T, y: U)`.
- `-> T` and `-> (x: T)` are identical for overload identity and differ only in preserved label metadata.
- `optional T` is a regular value type and may appear in parameters, locals, fields, and plain returns.
- `optional T` with single-slot payload is internally normalized to `optional<(value: T)>`.
- `optional` and `optional void` are invalid type surfaces.
- `-> result<E> T` denotes a result return surface over payload shape `T` and error type `E`.
- `-> result<E> T` with single-slot payload is internally normalized to `result<E, (value: T)>`.
- `-> result<E>` is valid and denotes a result return surface with unit payload.
- A function return surface may be plain or `result<E>`.
- A function return surface must not combine `optional` and `result` in the same declaration.
- `result<E>` wraps the declared success payload; the payload itself may be scalar or a named tuple shape.
### 3.2 Callable categories
The following constructs are callable in v1 core:
- top-level `fn`,
- struct methods on concrete receivers,
- contract values,
- callback values,
- service methods,
- `host fn`.
Rules:
- All callable categories use the same input/output tuple model.
- A struct method is identified by `(struct type, method name, input tuple shape excluding the receiver slot, output tuple shape)`.
- A contract value is identified at the type level by its contract type and at runtime by an underlying concrete struct instance plus its selected implementation.
- A callback declaration defines a nominal callable type with exactly one input/output tuple shape.
- A service method is identified by `(service name, method name, input tuple shape, output tuple shape)`.
- A top-level `fn` is identified by `(name, input tuple shape, output tuple shape)`.
- A callback value is identified at the type level by its declared callback type and signature.
- A `host fn` is identified by `(name, input tuple shape, output tuple shape)` and has no PBS body.
- Contract signatures and barrel signatures must preserve the same callable shape.
### 3.3 Declaration validity
Rules:
- Parameter names in a single `ParamList` must be unique.
- Output labels in a named tuple return annotation must be unique.
- Error case labels in a single `declare error` body must be unique.
- Enum case labels in a single `declare enum` must be unique.
- Enum case integer identifiers in a single `declare enum` must be unique.
- Two declarations with the same callable name and the same input/output tuple shape are duplicates.
- Changing only parameter names does not create a new callable.
- Changing only input labels in `ParamList` does not create a new callable.
- Changing only output labels does not create a new callable.
- `mod.barrel` function entries are matched by callable identity, ignoring labels.
- Barrel labels may differ from implementation labels.
- Any return surface that combines `optional` and `result` is statically invalid.
- Any payload-less `optional` type surface is statically invalid.
- Any `optional void` type surface is statically invalid.
### 3.4 Enum declarations and values
Rules:
- `declare enum Name(case1, case2, ...);` introduces a nominal enum type `Name`.
- `declare enum Name(case1 = n1, case2 = n2, ...);` introduces explicit integer identifiers for enum cases.
- If no enum case uses an explicit identifier, identifiers are assigned as `0..n-1` in declaration order.
- If any enum case uses an explicit identifier, then every enum case in that declaration must use an explicit identifier.
- `Name.case` is a valid enum value surface only when `Name` resolves to a declared enum type and `case` resolves to a declared case of that enum.
- The static type of `Name.case` is the enum type `Name`.
- Error labels are not enum cases and must not be used where enum case values are required.
### 3.5 Structs, methods, and contracts
Rules:
- Struct values are heap-backed reference values in v1 core.
- `new Struct(args...)` allocates a new struct instance of the declared struct type.
- `new Struct.ctorName(args...)` invokes a named constructor declared in the enclosing struct body and produces a new struct instance of that type.
- Struct construction arguments are matched positionally to the declared struct field order.
- Assigning a struct value copies the reference, not the underlying field storage.
- Struct fields are mutable and accessible by default in v1 core.
- A struct method declared in a struct body has an implicit receiver binding `this` of type `Self`.
- `this` is valid only inside struct method bodies.
- `Self` is valid only in struct method and `ctor` declarations and denotes the enclosing struct type.
- A named `ctor` declared in a struct body is not a method and does not introduce `this`.
- `ctor` bodies return the enclosing struct type and may use explicit `return`.
- Methods declared in a struct body belong to the direct method namespace of that concrete struct type.
- `receiver.method(...)` on a concrete struct receiver resolves only against methods declared directly in that struct body.
- `receiver.ctorName(...)` is never valid on a concrete struct value.
- Contract implementations do not inject their methods into the direct method namespace of the concrete struct.
- `implements Contract for Struct using s { ... }` is unique per `(Contract, Struct)` pair.
- A contract for a struct may be implemented only in the module where the struct itself is declared.
- Contracts may be declared in other modules and implemented for the local struct in its owner module.
- An `implements` block has no independent visibility; its observability follows the visibility of the struct, and the contract must also be nameable at the use site.
- A struct value may be coerced to a contract value when exactly one visible `implements Contract for Struct ...` applies.
- Contract coercion may happen by explicit `expr as ContractType` or by contextual typing in assignments, parameter passing, and returns.
- A contract value carries the underlying struct reference together with the selected contract implementation for runtime dispatch.
- Calling `contractValue.method(...)` uses dynamic dispatch through the contract value.
- Calling `structValue.contractMethod(...)` is not valid unless that method is also declared directly on the struct.
- Structs do not have static methods in v1 core; named constructors are their only type-qualified callable members.
Example:
```pbs
declare contract StasisProcess {
fn stasis() -> int;
}
declare struct Struct(a: int) {
fn compute() -> int {
return this.a;
}
ctor createWithSomethingSpecial(x: int) {
return new Struct(x);
}
}
implements StasisProcess for Struct using s {
fn stasis() -> int {
return s.compute();
}
}
fn demo() -> int {
let s = new Struct(1);
let s2 = new Struct.createWithSomethingSpecial(2);
let sp: StasisProcess = s;
let sp2 = s as StasisProcess;
let a = s.compute();
let _ = s2.compute();
let b = sp.stasis();
let c = sp2.stasis();
return a + b + c;
}
```
### 3.6 Nominal callbacks
Rules:
- `declare callback Name(params...) -> R;` introduces a nominal callback type `Name`.
- A callback type is not a `fn`, and a `fn` declaration does not implicitly define a callback type.
- A value of callback type may be formed only from a compatible top-level `fn` in v1 core.
- A value of callback type may also be formed by `bind(context, fn_name)` when the surrounding static type context expects a callback type.
- Service methods are not assignable to callback values in v1 core.
- `host fn` declarations are not assignable to callback values in v1 core.
- Callback values do not capture local environment implicitly.
- Assignment of a top-level `fn` to a callback type succeeds only when exactly one visible overload matches the callback signature.
- `bind(context, fn_name)` succeeds only when exactly one visible top-level `fn` overload matches after consuming the first parameter with `context`.
- Valid expected callback contexts for `bind(context, fn_name)` include variable initialization with an explicit callback type, assignment to an explicitly typed callback location, argument passing to a callback-typed parameter, and return from a callback-typed function.
- The context expression in `bind(context, fn_name)` is evaluated once at bind time and stored explicitly in the resulting callback value.
- The stored context follows normal value semantics for its type.
- `bind(context, fn_name)` does not introduce general closures; it is explicit partial binding of the first parameter only.
- Callback compatibility ignores labels and compares ordered input/output slot types plus the return-surface kind.
Example:
```pbs
declare callback TickCb(dt: int) -> void;
fn on_tick(dt: int) -> void {
}
let cb: TickCb = on_tick;
```
Bound example:
```pbs
declare callback UpdateCb(dt: int) -> void;
declare struct Enemy(hp: int);
fn update_enemy(self: Enemy, dt: int) -> void {
}
let cb: UpdateCb = bind(enemy, update_enemy);
```
Ambiguous overload example:
```pbs
declare callback TickCb(dt: int) -> void;
fn tick(dt: int) -> void {
}
fn tick(time: int) -> void {
}
let cb: TickCb = tick; // error: ambiguous overloaded fn assignment
```
### 3.7 Canonical application
The canonical surface form for function application is:
```pbs
callee apply arg
```
Rules:
- `apply` is an infix operator that is right-associative.
- `f1 apply f2 apply x` is defined as `f1 apply (f2 apply x)`.
- `f1 apply f2 apply f3 apply params` is defined as `f1 apply (f2 apply (f3 apply params))`.
- The left operand of `apply` must resolve to one of: a callable candidate set, a bound struct method target, a contract-method target, or a callback value.
- The right operand of `apply` is an argument expression.
- If the callable input arity is `0`, the argument expression must be `()`.
- If the callable input arity is `1`, the argument expression is checked against the carrier type of the single input slot.
- If the callable input arity is `2..6`, the argument expression must have a named tuple shape with matching arity and positional types.
- Input labels are not used for compatibility.
- No single-slot tuple argument literal is required or recognized in surface syntax.
- There is no partial application in v1 core.
- There is no currying in v1 core.
- Chained `apply` is nested ordinary application, not callable composition as a first-class value.
- Callback values, `host fn`, service methods, struct methods, and contract-method targets use the same `apply` rules as top-level `fn`.
### 3.8 Call sugar
The surface form:
```pbs
callee(arg1, arg2, ..., argN)
```
desugars to the canonical `apply` surface according to arity.
Rules:
- The desugaring is purely syntactic.
- `f()` is exact sugar for `f apply ()`.
- `f(x)` is exact sugar for `f apply x`.
- `f(x1, x2, ..., xn)` for `n >= 2` is exact sugar for `f apply (x1, x2, ..., xn)`.
- No semantic distinction exists between direct call sugar and its canonical `apply` form.
- When `(arg1, ..., argN)` is used as positional tuple sugar, the callee input tuple shape supplies the effective labels.
- All overload resolution, diagnostics, and type checking are defined on the canonical `apply` form.
### 3.9 Result type of application
Rules:
- If the selected callable output arity is `0`, the result of `apply` is unit.
- If the selected callable output arity is `1`, the result of `apply` is the carrier type of the single output slot.
- If the selected callable output arity is `2..6`, the result of `apply` has the named output tuple shape of the selected callable.
- Multi-slot output tuple results expose their labels for member projection.
- If the selected callable is a callback value, the visible multi-slot output labels come from the callback type rather than from the original source `fn`.
- If the selected callable returns `()`, the application result is unit.
Example:
```pbs
fn func(a: int, b: int) -> (c: int, d: float) {
return (c: a + b, d: 2.0);
}
let params: (a: int, b: int) = (1, 2);
let r = func apply params;
let c = r.c;
let d = r.d;
```
In the example above:
- `func` has input tuple shape `(a: int, b: int)`,
- `func` has output tuple shape `(c: int, d: float)`,
- `r` has named output tuple type `(c: int, d: float)`,
- `r.c` has type `int`,
- `r.d` has type `float`.
Single-slot example:
```pbs
fn plus_one(x: int) -> int {
return x + 1;
}
let r = plus_one apply 1;
if r == 2 { }
```
In the example above:
- `plus_one` has input tuple shape `(x: int)`,
- the argument `1` is accepted directly as the carrier value for the single input slot,
- `plus_one` has output tuple shape `(value: int)`,
- the value produced by `plus_one apply 1` has carrier type `int`,
- `r.value` is not required and should not be used.
### 3.10 Overload resolution
Rules:
- Overload resolution starts from the visible callable candidates with the selected name.
- Candidate filtering first uses callable kind and input tuple arity.
- Remaining candidates are filtered by positional input type compatibility.
- If exactly one candidate remains, resolution succeeds.
- If multiple candidates remain, expected output types or expected multi-slot output tuple shapes may be used as a tie-breaker when statically available.
- If multiple candidates still remain, the `apply` is ambiguous and compilation fails.
- If no candidate remains, the `apply` is unresolved and compilation fails.
- Overloads that differ only by labels are duplicates and are forbidden.
- Overloads that differ only by output positional types are permitted, but uncontextualized `apply` sites may become ambiguous.
### 3.11 Member access and projection
Rules:
- `TypeName.case` is valid when `TypeName` resolves to a declared enum type and `case` resolves to one of its declared enum cases.
- `TypeName.case` has static type `TypeName`.
- `expr.name` is valid when the static type of `expr` is a declared `struct` containing field `name`.
- For `struct` field access, the projected member type is the declared field type.
- `expr.method` is a valid method-call target when the static type of `expr` is a concrete struct type declaring method `method`.
- `expr.method` over a concrete struct receiver does not search visible contract implementations.
- `TypeName.ctorName` is not a general member access surface; named constructors are entered only through `new TypeName.ctorName(...)`.
- `expr.method` is a valid method-call target when the static type of `expr` is a contract value whose contract declares method `method`.
- `expr.name` is valid when the static type of `expr` is a multi-slot named output tuple containing label `name`.
- For named output tuples, the projected member type is the type of the corresponding output slot.
- Projection is label-based, not position-based.
- `expr.hasSome()` and `expr.hasNone()` are valid intrinsic method-call surfaces when the static type of `expr` is `optional<P>`.
- `expr.hasSome()` and `expr.hasNone()` both have static type `bool`.
- `expr.name()` and `expr.key()` are valid intrinsic method-call surfaces when the static type of `expr` is an enum type.
- `expr.name()` has static type `str`.
- `expr.key()` has static type `int`.
- Access to a missing output label is a compile-time error.
- Access to a missing struct field is a compile-time error.
- Access to a missing struct method is a compile-time error.
- Access to a missing contract method is a compile-time error.
- Member projection is not defined for single-slot collapsed carrier values.
### 3.12 Block expressions and function fallthrough
Rules:
- A block expression with a `TailExpr` has the static type of that final expression.
- A block expression without a `TailExpr` has static type unit.
- Only the final unsemicoloned expression in a block contributes to the block result.
- A function body does not implicitly return its trailing `TailExpr`; explicit `return` and ordinary control-flow rules still apply.
- Reaching the end of a plain non-unit function is a compile-time error unless all control-flow paths are proven to return explicitly.
- Reaching the end of a `void` or `()` function is valid and yields unit.
- Reaching the end of an `optional P` function yields `none`.
- Reaching the end of a `result<E>` function is a compile-time error.
### 3.13 Loop forms
Rules:
- `while cond { ... }` requires `cond` to have static type `bool`.
- `for i: T from start until end { ... }` is the declarative counted loop form in v1 core.
- `for i: T from start until end step s { ... }` is the explicit-step counted loop form in v1 core.
- In `for`, the declared iteration variable `i` introduces a loop-local binding of type `T`.
- `start`, `end`, and `step` expressions in `for` must be type-compatible with the declared iteration type `T`.
- If `step` is omitted, the loop uses the integer value `1` of the iteration type domain.
- `for` iteration proceeds by evaluating `start`, `end`, and optional `step` once before loop entry.
- `for ... until ...` uses an exclusive upper bound.
- `continue` inside `while` transfers control to the next condition check.
- `continue` inside `for` transfers control to the step update and then to the next bound check.
- `break` exits the nearest enclosing loop.
### 3.14 `if` and `switch` conditional expressions
Rules:
- In `if cond { a } else { b }`, `cond` must have static type `bool`.
- Only the selected branch is evaluated at runtime.
- The branch expressions `a` and `b` must have the same static type, subject only to ordinary contextual typing already defined elsewhere in the language.
- The static result type of the `if` expression is that shared static type.
- In `switch selector { pattern: { ... }, ... }`, the selector expression is evaluated once.
- `switch` accepts enum selectors and literal-comparable scalar selectors; `error` selectors are invalid.
- Each non-wildcard `switch` arm pattern must be unique within the same `switch`.
- Each non-wildcard `switch` arm pattern must be type-compatible with the selector expression.
- Enum-case switch patterns must resolve to declared cases of the selector's enum type.
- Error labels are not valid switch patterns.
- `default` and `_` are equivalent wildcard spellings for `switch`.
- At most one wildcard arm may appear in a `switch`.
- A `switch` must not mix `default` and `_` in the same arm set.
- The blocks of all `switch` arms must have the same static type, subject only to ordinary contextual typing already defined elsewhere in the language.
- The static result type of the `switch` expression is that shared static type.
- A `switch` used as an `ExprStmt` is valid when the selected arm blocks evaluate to unit.
- A `switch` expression used in a value-required position must be exhaustive, either by a wildcard arm or by another statically proven exhaustive case set.
### 3.15 `optional` construction and extraction
Rules:
- `some(expr)` forms an `optional<P>` value where `P` is the static type of `expr`, unless an expected `optional<Q>` context is present and constrains `expr` to be compatible with `Q`.
- `some(expr)` is invalid when the payload normalizes to `void`.
- `none` is valid only when an expected `optional<P>` type is available from context.
- `opt else fallback` requires the left operand to have static type `optional<P>`.
- In `opt else fallback`, the `fallback` expression must be compatible with payload shape `P`.
- The static result type of `opt else fallback` is the carrier value type for single-slot payloads and the named tuple payload shape for multi-slot payloads.
- `opt else fallback` does not introduce implicit mutation or unwrapping side effects beyond normal expression evaluation.
### 3.16 `result<E>` return flow and handling
Rules:
- `return ok(expr);` is valid only inside a function whose declared return surface is `result<E> P`.
- In `return ok(expr);`, `expr` must be compatible with the declared success payload shape `P`.
- `return err(E.label);` is valid only inside a function whose declared return surface is `result<E> P`.
- In `return err(E.label);`, the error path must resolve to a declared case of the same error type `E`.
- `ok(...)` and `err(...)` do not denote ordinary value expressions in v1 core.
- `expr!` requires `expr` to have static type `result<E> P`.
- `expr!` is valid only when the enclosing function returns `result<E> Q` for the same error type `E`.
- The static result type of `expr!` is the extracted success payload shape `P`, collapsed to its carrier value when single-slot.
- `handle expr { ... }` requires `expr` to have static type `result<E1> P`.
- `handle expr { ... }` is valid only when the enclosing function returns `result<E2> Q` for some payload `Q`.
- Each non-wildcard `handle` arm must match a distinct declared case of `E1`.
- Each mapped error path on the right side of a `handle` arm must resolve to a declared case of `E2`.
- `handle` arms must be exhaustive over `E1`, either by naming all cases exactly once or by providing `_`.
- The static result type of `handle expr { ... }` is the extracted success payload shape `P`, collapsed to its carrier value when single-slot.
- On error, `handle` performs an immediate enclosing-function return with the mapped `err(...)`.
### 3.17 Required static diagnostics
At minimum, deterministic static diagnostics are required for:
- duplicate parameter names in `fn` declarations,
- duplicate output labels in named tuple returns,
- duplicate error case labels in `declare error`,
- duplicate enum case labels in `declare enum`,
- duplicate enum case identifiers in `declare enum`,
- invalid mixed implicit/explicit enum case identifiers,
- invalid `this` usage outside struct method body,
- invalid `Self` type usage outside struct method or `ctor` declarations,
- invalid struct method declaration shape,
- invalid `ctor` declaration shape,
- duplicate `implements Contract for Struct` pair,
- `implements` declared outside the owner module of the struct,
- incompatible or missing contract method implementation,
- invalid contract coercion from concrete struct value,
- unresolved barrel function signature after label-insensitive matching,
- unresolved callback barrel entry,
- invalid enum case path,
- invalid enum intrinsic member call,
- invalid named constructor target,
- incompatible top-level `fn` assignment to callback type,
- ambiguous overloaded top-level `fn` assignment to callback type,
- attempted assignment of service method to callback type,
- attempted assignment of `host fn` to callback type,
- use of `bind(...)` without an expected callback type,
- incompatible `bind(...)` context type for callback target,
- ambiguous overloaded top-level `fn` target in `bind(...)`,
- `apply` on a non-callable target,
- arity mismatch in `apply`,
- argument type mismatch in `apply`,
- `apply` chain with incompatible intermediate carrier/tuple shape,
- ambiguous overload resolution,
- unresolved callable application,
- possible fallthrough of plain non-unit function,
- non-boolean `while` condition,
- invalid `for` iteration type,
- incompatible `for` bound or step type,
- non-boolean `if` expression condition,
- incompatible `if` expression branch types,
- duplicate `switch` arm pattern,
- mixed `default` and `_` wildcard arms in `switch`,
- invalid `switch` selector type,
- selector-pattern type mismatch in `switch`,
- invalid enum case switch pattern,
- incompatible `switch` arm block types,
- non-exhaustive `switch` expression in value position,
- invalid mixed `optional`/`result` return surface,
- invalid payload-less `optional` type surface,
- invalid `optional void` type surface,
- invalid `none` usage without expected optional type,
- invalid `else` extraction over non-optional operand,
- fallback type mismatch in `else` extraction,
- invalid `some(...)` construction form,
- use of `ok(...)` or `err(...)` outside `return`,
- invalid `ok(...)` construction form,
- invalid `err(...)` construction form,
- invalid error label in `err(...)`,
- invalid `!` propagation on non-result expression,
- mismatched error type in `!` propagation,
- invalid `handle` on non-result expression,
- non-exhaustive `handle` mapping,
- duplicate `handle` mapping arm,
- invalid mapped error label in `handle`,
- attempted use of a single-slot tuple literal where no such surface form exists,
- member access on a missing struct field,
- member access on a missing struct method,
- member access on a missing contract method,
- member projection on a single-slot carrier value,
- invalid optional intrinsic member call,
- member projection on a missing named output label.

View File

@ -0,0 +1,90 @@
# PBS Heap Model - Discussion Agenda
Status: Working agenda (pre-spec)
Purpose: define how PBS will expose heap usage after the RC/HIP era
## 1. Context
Legacy PBS experiments used explicit RC/HIP syntax (`alloc`, `borrow`, `mutate`, `peek`, `take`, `weak`).
Current runtime authority is GC-based with deterministic safepoints.
We need a clear PBS heap story that:
- stays compatible with closed VM/runtime authority,
- remains simple for beginners,
- gives advanced developers explicit performance control,
- keeps frame-time behavior predictable.
## 2. Decisions to Produce
This agenda must end with decisions for:
1. Heap surface in core language (what is in/out of core syntax).
2. Heap control surface in stdlib/tooling (what is explicit but optional).
3. Cost visibility contract (what diagnostics/profiling must report).
4. Migration policy from legacy RC/HIP concepts.
## 3. Core Questions
### Q1. Baseline model
- Is GC always the default execution model for PBS core? (expected: yes)
- Are there any mandatory explicit lifetime constructs in core syntax? (expected: no)
### Q2. Language vs library boundary
- Should explicit memory workflows live in syntax or only in stdlib APIs?
- Which old RC/HIP concepts become API patterns instead of keywords?
### Q3. Reserved keywords strategy
- Keep `alloc/borrow/mutate/peek/take/weak` reserved only?
- Or activate a subset in a future opt-in profile?
- What hard criteria must be met before activation?
### Q4. Determinism and frame budget
- How do we guarantee predictable frame behavior under GC pressure?
- What static/dynamic tooling must warn on hot-path allocations?
### Q5. Heap collections and identity semantics
- What collection semantics are required in v1 stdlib for games?
- Which operations must be explicit due to allocation/copy costs?
### Q6. Host memory boundary
- How does PBS communicate "VM heap vs host-owned memory" to users?
- Which host calls may allocate, and how should that appear in diagnostics?
### Q7. Advanced performance patterns
- Should pools/reuse patterns be standardized as library primitives?
- If yes, what minimum API contract is required (without changing VM model)?
### Q8. Error and safety model
- Which heap-related failures become traps vs status values?
- What compile-time checks are mandatory for safer defaults?
## 4. Proposed Output Documents
After discussion, produce:
1. `4. Static Semantics Specification.md` (binding/mutability/type rules impacted by heap model).
2. `5. Dynamic Semantics Specification.md` (evaluation and runtime-visible behavior).
3. `6. Memory and Lifetime Specification.md` (authoritative PBS heap model).
4. `9. Diagnostics Specification.md` (heap/cost diagnostics contract).
## 5. Non-Goals for This Agenda
- Defining new VM opcodes.
- Reintroducing RC as mandatory runtime strategy.
- Designing full stdlib APIs in this discussion pass.
## 6. Working Assumptions (to validate)
1. GC remains baseline for PBS core.
2. Explicit heap control is opt-in and likely library/tooling-driven.
3. Deterministic behavior and frame-sync constraints are non-negotiable.
4. Language should expose cost signals without forcing expert-only syntax.

View File

@ -0,0 +1,144 @@
# PBS - Compatibility and Evolution Policy v1 (Draft)
Status: Draft (Normative policy for language/runtime evolution)
Scope: Compatibility contract for PBS features across hardware/runtime updates
Language: English
## 1. Purpose
This document defines how PBS remains stable while the logical hardware evolves.
Goals:
- preserve existing cartridge behavior across updates,
- allow additive platform growth without silent regressions,
- keep evolution deterministic and verifier-friendly,
- define explicit upgrade/rejection rules for toolchains and firmware.
## 2. Authority and precedence
Normative precedence order:
1. Runtime authority (`docs/specs/hardware/topics/chapter-2.md`, `chapter-3.md`, `chapter-9.md`, `chapter-12.md`, `chapter-16.md`)
2. Bytecode authority (`docs/specs/bytecode/ISA_CORE.md`)
3. `PBS - Language Syntax Specification v0.md`
4. `PBS - Game Language Profile v1 (Draft).md`
5. `PBS - Game Profile Syntax Specification v1 (Draft).md`
6. This document
7. Legacy references (`docs/specs/pbs_old/*`)
If any rule here conflicts with higher authority, this document is invalid for that rule.
## 3. Core stability model
### 3.1 Frozen core contract
The following contracts MUST remain stable once released in a major line:
- frame synchronization semantics (`FRAME_SYNC` as deterministic safepoint),
- trap and verifier invariants,
- stack/call/return discipline for valid bytecode,
- deterministic scheduling and host-call contract rules.
Behavioral changes to frozen core contracts require a new major line.
### 3.2 Additive extension rule
New features SHOULD be additive by default.
Existing valid programs MUST keep their previous behavior unless they explicitly target a newer incompatible major.
## 4. Primitive and host API versioning
### 4.1 Canonical identity
Host-call primitives MUST be versioned by canonical identity:
`(module, name, version)`
An incompatible change MUST create a new `version`.
The old version MUST remain resolvable during its supported compatibility window.
### 4.2 No semantic retcon
Implementations MUST NOT silently change semantics of an existing canonical identity.
If semantics must change incompatibly, publish a new identity version and deprecate the old one by policy.
## 5. Cartridge target contract
Each cartridge/app manifest MUST declare, at minimum:
- target VM/runtime major (or compatible range),
- language profile and profile version,
- required canonical host-call identities and versions,
- required capabilities.
Loader behavior MUST be deterministic:
- load and run when all requirements resolve,
- reject with explicit diagnostic when requirements are missing or incompatible.
Implicit fallback to a different major/version is forbidden.
## 6. Evolution policy (major/minor)
### 6.1 Minor updates
Minor updates MUST be backward compatible.
Allowed examples:
- adding new host-call identities,
- adding optional metadata/diagnostics,
- adding syntax/features gated behind explicit profile selection.
### 6.2 Major updates
Major updates MAY introduce incompatible changes.
When this happens, maintainers MUST provide:
- explicit migration notes,
- compatibility window policy for prior majors,
- deterministic loader diagnostics for unsupported targets.
## 7. Compatibility layer policy
Runtime/firmware SHOULD provide compatibility shims for older identity versions for a defined window.
If shims are provided:
- shim behavior MUST be deterministic,
- shim scope and support horizon MUST be documented,
- shim removal MUST occur only in a new major line.
## 8. Toolchain requirements
Compiler/linker/tooling MUST:
- resolve host calls by canonical identity and declared version,
- fail deterministically on unresolved identities,
- emit target/profile metadata in output artifacts,
- avoid emitting instructions/features not supported by selected target contracts.
## 9. Conformance and replay guarantees
Each release MUST pass compatibility conformance suites that include:
- bytecode verifier invariants,
- host-call resolution checks by version,
- deterministic replay checks under fixed input/event traces.
For supported target contracts, the same cartridge plus the same input/event sequence MUST produce the same observable behavior.
## 10. Deprecation policy
Deprecation MUST be explicit and non-breaking during its support window.
Required stages:
1. Mark as deprecated (tooling warning + documentation).
2. Maintain compatibility through declared window.
3. Remove only in a subsequent major line.
## 11. Non-goals
- Defining subsystem-specific syscall tables.
- Defining game-stdlib API surfaces.
- Replacing runtime authority with PBS-layer policy.

View File

@ -0,0 +1,106 @@
# PBS - Game Language Profile v1 (Draft)
Status: Draft (Language/Product direction)
Scope: PBS game-first language contract for 2D workloads
Language: English
## 1. Purpose
This document defines the language-level direction for PBS as a game-first scripting language.
The profile targets:
- intuitive onboarding for beginners,
- predictable frame behavior for real-time 2D games,
- explicit performance/cost surfaces for advanced developers,
- strict compatibility with a closed VM and its bytecode/runtime authority.
This document is language-contract oriented. It does not define bytecode encoding details.
## 2. Authority and compatibility
Normative precedence order:
1. Runtime authority (`docs/specs/hardware/topics/chapter-2.md`, `chapter-3.md`, `chapter-9.md`, `chapter-12.md`, `chapter-16.md`)
2. Bytecode authority (`docs/specs/bytecode/ISA_CORE.md`)
3. `PBS - Language Syntax Specification v0.md`
4. This document (Game Profile direction and requirements)
5. Legacy references (`docs/specs/pbs_old/*`)
If any rule here conflicts with runtime or bytecode authority, this document is invalid for that rule.
## 3. Design constraints
- The VM is a closed execution target.
- Language features must lower to supported VM/bytecode behavior.
- Determinism and frame synchronization are non-negotiable.
- GC is the baseline memory strategy.
## 4. Ten normative rules
### Rule 1 - Frame-first semantics
PBS game-facing language features MUST preserve deterministic frame execution.
Language constructs MUST NOT imply hidden asynchronous callbacks or preemptive execution.
### Rule 2 - Closed-VM compatibility
PBS MUST only define features that can be represented by the closed VM contract.
Language design MUST NOT require custom allocators, hidden host threads, or runtime behaviors outside VM authority.
### Rule 3 - GC-first memory model
PBS MUST treat GC-managed heap semantics as the default and beginner path.
Any future lifetime tools (such as pools/arena-like workflows) MUST be additive at API/library level unless runtime authority changes.
### Rule 4 - Explicit cost surfaces
PBS SHOULD make high-cost operations visible to users through language diagnostics and tooling contracts.
At minimum, allocation pressure, frequent host calls, and hot-loop risk points SHOULD be reportable in deterministic diagnostics.
### Rule 5 - 2D game-native type surface
PBS SHOULD provide a first-class game-oriented type surface through core language + standard library alignment.
The profile MUST support fluent expression of common 2D tasks (positions, velocities, bounds, colors, sprite/tile identifiers, frame counters).
### Rule 6 - Service boundary as gameplay architecture
`service` MUST remain the primary executable boundary for gameplay systems.
Language guidance SHOULD promote service-oriented decomposition for input, simulation, rendering orchestration, audio, and UI flows.
### Rule 7 - Typed host contract boundary
Host interactions MUST remain typed and explicit at language level.
Language constructs for host-facing APIs MUST preserve capability checks and syscall determinism from runtime authority.
### Rule 8 - Frame-based event and timer model
Game-facing event/timer language patterns MUST be frame-based and deterministic.
Language evolution MUST NOT reintroduce interrupt-like user execution semantics.
### Rule 9 - Progressive complexity in one language
PBS MUST support both beginner and advanced developers without forking into separate languages.
The beginner path MUST keep defaults simple and safe; advanced control MUST be explicit and opt-in.
### Rule 10 - Fluency for game loops
PBS syntax and standard patterns SHOULD optimize for high-frequency game-loop code:
- concise state updates,
- clear system boundaries,
- predictable data flow across update/render stages,
- minimal boilerplate for frame-driven logic.
## 5. Non-goals for this draft
- Defining new bytecode opcodes.
- Replacing runtime GC with a new mandatory memory subsystem.
- Adding language-level async callbacks or non-deterministic host behavior.
## 6. Planned follow-up specs
1. Game Profile syntax additions (if needed), layered on top of v0 Core.
2. Game-oriented standard library contract (collections, timers, ECS-adjacent utilities).
3. Tooling diagnostics spec for frame-cost visibility.
4. Host API declaration surface aligned with capability and syscall metadata.

View File

@ -0,0 +1,186 @@
# PBS - Game Profile Syntax Specification v1 (Draft)
Status: Draft (Normative for Game Profile parser/lowering work)
Scope: Syntax delta over `PBS - Language Syntax Specification v0.md`
Language: English
## 1. Purpose
This document defines the parser-facing syntax additions for the PBS Game Profile.
Goals:
- keep beginner usage fluid for 2D game construction,
- preserve deterministic/frame-oriented execution,
- remain compatible with closed VM + bytecode/runtime authority,
- avoid introducing hidden runtime behavior in language syntax.
## 2. Authority and precedence
Normative precedence order:
1. Runtime authority (`docs/specs/hardware/topics/chapter-2.md`, `chapter-3.md`, `chapter-9.md`, `chapter-12.md`, `chapter-16.md`)
2. Bytecode authority (`docs/specs/bytecode/ISA_CORE.md`)
3. `PBS - Language Syntax Specification v0.md`
4. This document
5. Legacy references (`docs/specs/pbs_old/*`)
If a rule here conflicts with higher authority, the rule is invalid.
## 3. Profile activation
Game Profile syntax is enabled by toolchain profile selection (`game` profile).
Without this profile, v0 Core syntax rules remain in effect.
## 4. Lexical delta (keywords)
### 4.1 Newly active keywords in Game Profile
- `host`
- `while`
- `break`
- `continue`
### 4.2 Still reserved in this draft
- `alloc`, `borrow`, `mutate`, `peek`, `take`, `weak`
- `spawn`, `yield`, `sleep`
- `handle`
This draft does not activate heap-specialized syntax or coroutine syntax.
## 5. Top-level grammar delta
Game Profile extends top-level declarations with host function declarations.
```ebnf
TopDecl ::= TypeDecl | ServiceDecl | FunctionDecl | HostFnDecl
HostFnDecl ::= 'host' 'fn' Identifier ParamList ReturnType? ';'
```
Rules:
- `host fn` declares a host-provided call surface only (no body).
- Calling `host fn` lowers to syscall-linked call sites.
- Host functions are callable entities, never first-class function values.
- Service-shaped facades over host functions belong to stdlib design, not to host syntax.
Examples:
```pbs
host fn input_state(): (int, int, int);
host fn gfx_present();
```
## 6. Statement grammar delta
Game Profile adds imperative loop/control and assignment statements.
```ebnf
Stmt ::= LetStmt
| AssignStmt
| ReturnStmt
| IfStmt
| ForStmt
| WhileStmt
| BreakStmt
| ContinueStmt
| ExprStmt
WhileStmt ::= 'while' Expr Block
BreakStmt ::= 'break' ';'
ContinueStmt ::= 'continue' ';'
AssignStmt ::= LValue AssignOp Expr ';'
AssignOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%='
LValue ::= Identifier LValueSuffix*
LValueSuffix ::= '.' Identifier | '[' Expr ']'
```
Rules:
- `break` and `continue` are valid only inside `for`/`while`.
- Assignment is a statement, not an expression.
- `LValue` cannot contain function calls.
- Compound assignments are semantic sugar over read/compute/write.
## 7. Expression grammar delta
Game Profile adds postfix member/index access chains for fluent gameplay code.
```ebnf
UnaryExpr ::= ('!' | '-') UnaryExpr | PostfixExpr
PostfixExpr ::= PrimaryExpr PostfixSuffix*
PostfixSuffix ::= CallSuffix | MemberSuffix | IndexSuffix
CallSuffix ::= '(' ArgList? ')'
MemberSuffix ::= '.' Identifier
IndexSuffix ::= '[' Expr ']'
```
This replaces the v0 Core `CallExpr` chain and enables expressions such as:
```pbs
gfx_present();
player.transform.position.x;
tiles[i].id;
```
## 8. Determinism and lowering constraints
Game Profile syntax additions MUST preserve:
- deterministic control flow,
- verifier-friendly stack effects,
- explicit host call boundaries.
Lowering requirements:
- `host` call sites map to resolved syscall identities.
- `while`/`break`/`continue` lower to structured jumps compatible with verifier rules.
- Member/index read-write operations must lower to deterministic, type-checked access paths.
## 9. Diagnostics requirements (parser + early semantic phase)
Implementations MUST produce deterministic diagnostics for:
- `break`/`continue` outside loops,
- invalid `LValue` targets,
- duplicate host signatures in the same scope,
- unresolved host declarations at link/resolve stage,
- use of still-reserved keywords as active syntax.
## 10. Minimal canonical examples
### 10.1 Frame loop style with while
```pbs
host fn gfx_present();
fn run(): int {
let running = true;
while running {
update_world();
render_world();
gfx_present();
}
return 0;
}
```
### 10.2 Assignment and member/index access
```pbs
fn integrate(p: Vec2, v: Vec2): Vec2 {
let out = p;
out.x += v.x;
out.y += v.y;
return out;
}
```
## 11. Non-goals of this syntax draft
- Defining pool/arena memory syntax.
- Enabling `alloc/borrow/mutate` in surface syntax.
- Defining coroutine syntax (`spawn/yield/sleep`) before bytecode/runtime profile activation.

View File

@ -19,3 +19,6 @@ When a rule conflicts with runtime or bytecode authority, runtime and bytecode a
## Files ## Files
- `PBS - Language Syntax Specification v0.md`: normative syntax and grammar for PBS v0 Core profile, including mandatory `mod.barrel` visibility rules. - `PBS - Language Syntax Specification v0.md`: normative syntax and grammar for PBS v0 Core profile, including mandatory `mod.barrel` visibility rules.
- `PBS - Game Language Profile v1 (Draft).md`: game-first language direction for 2D workloads, defining ten normative design rules aligned with the closed VM and GC-first baseline.
- `PBS - Game Profile Syntax Specification v1 (Draft).md`: parser/lowering syntax delta for Game Profile over v0 Core (host `fn` declarations, assignment, while/break/continue, member/index postfix access).
- `PBS - Compatibility and Evolution Policy v1 (Draft).md`: compatibility contract for PBS across logical hardware updates (frozen core rules, canonical versioning, target manifests, major/minor policy, replay/conformance guarantees).