diff --git a/docs/specs/pbs/1. Language Charter.md b/docs/specs/pbs/1. Language Charter.md new file mode 100644 index 00000000..49af27e9 --- /dev/null +++ b/docs/specs/pbs/1. Language Charter.md @@ -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. diff --git a/docs/specs/pbs/2. Governance and Versioning.md b/docs/specs/pbs/2. Governance and Versioning.md new file mode 100644 index 00000000..ba3fe97e --- /dev/null +++ b/docs/specs/pbs/2. Governance and Versioning.md @@ -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. diff --git a/docs/specs/pbs/3. Core Syntax Specification.md b/docs/specs/pbs/3. Core Syntax Specification.md new file mode 100644 index 00000000..3a136967 --- /dev/null +++ b/docs/specs/pbs/3. Core Syntax Specification.md @@ -0,0 +1,1077 @@ +# PBS Core Syntax Specification + +Status: Draft v1 (Normative) +Scope: Lexer + parser contract for PBS source files (`.pbs`) and `mod.barrel` + +## 1. Purpose + +This document defines the canonical syntax for PBS core language. + +It is intended to: + +- keep parsing deterministic, +- provide a stable contract for tooling, +- support game-first workflows with explicit structure, +- remain compatible with runtime and bytecode authority. + +This document defines syntax only. Runtime behavior, optimizer policy, and bytecode encoding are out of scope. + +## 2. 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. This document +4. Legacy references (`docs/specs/random/pbs_old/*`) + +If a rule here conflicts with higher authority, it is invalid. + +## 3. Source model + +- Source encoding: UTF-8. +- Valid line terminators: `\n`, `\r\n`. +- Whitespace is insignificant except as token separator. +- A `.pbs` file is a declaration unit. +- Top-level executable statements are forbidden. + +## 4. Lexical specification + +### 4.1 Required token classes + +The lexer MUST emit: + +- identifiers, +- keywords, +- literals, +- punctuation, +- operators, +- comments, +- EOF. + +Each token MUST include stable source span metadata (byte offsets). + +### 4.2 Comments + +- Line comment: `// ...` until line end. +- Block comments are not part of v1 core syntax. + +### 4.3 Identifiers + +Identifier rules: + +- first character: `_` or alphabetic, +- following characters: `_`, alphabetic, or digit. + +Keywords are not valid identifiers. + +### 4.4 Keywords + +Active keywords in `.pbs`: + +- `import`, `from`, `as` +- `service`, `host`, `fn`, `apply`, `bind`, `new`, `implements`, `using`, `ctor` +- `declare`, `struct`, `contract`, `error`, `enum`, `callback`, `Self`, `this` +- `let`, `const` +- `if`, `else`, `switch`, `default`, `for`, `from`, `until`, `step`, `while`, `break`, `continue`, `return` +- `void`, `optional`, `result` +- `some`, `none`, `ok`, `err` +- `handle` +- `true`, `false`, `and`, `or`, `not` + +Barrel-only keywords: + +- `pub`, `mod` +- `type` + +Reserved (not active syntax in v1 core): + +- `alloc`, `borrow`, `mutate`, `peek`, `take`, `weak` +- `spawn`, `yield`, `sleep` +- `match` + +Note: + +- `match` remains a reserved keyword for possible future pattern-matching constructs. +- `match` is not used for `result` handling in v1 core syntax. + +### 4.5 Literals + +Numeric literals: + +- `IntLit`: decimal integer (`0`, `42`, `1000`) +- `FloatLit`: decimal float with dot (`3.14`, `0.5`) +- `BoundedLit`: decimal integer with `b` suffix (`0b`, `255b`) + +String literals: + +- delimited by `"`. +- supported escapes: `\\`, `\"`, `\n`, `\r`, `\t`. + +Boolean literals: + +- `true`, `false`. + +## 5. Module and barrel model + +### 5.1 Required files + +A valid module contains: + +- one or more `.pbs` files, +- exactly one `mod.barrel`. + +Missing `mod.barrel` is a compile-time error. + +### 5.2 Visibility responsibility + +`mod.barrel` is the single source of module visibility. + +Visibility levels: + +- `mod`: visible across files in the same module. +- `pub`: visible to importing modules. + +Top-level declarations not listed in `mod.barrel` are file-private. +Using `mod`/`pub` as top-level modifiers inside `.pbs` files is a syntax error. +For overloaded functions, visibility is per-signature; each exported overload must appear explicitly in `mod.barrel`. + +### 5.3 Barrel grammar + +```ebnf +BarrelFile ::= BarrelItem* EOF +BarrelItem ::= BarrelFnItem | BarrelTypeItem | BarrelServiceItem | BarrelConstItem | BarrelCallbackItem +BarrelVisibility ::= 'mod' | 'pub' + +BarrelFnItem ::= BarrelVisibility 'fn' Identifier ParamList ReturnAnn? ';' + +BarrelTypeItem ::= BarrelVisibility 'type' Identifier ';' +BarrelServiceItem ::= BarrelVisibility 'service' Identifier ';' +BarrelConstItem ::= BarrelVisibility 'const' Identifier ';' +BarrelCallbackItem ::= BarrelVisibility 'callback' Identifier ';' +``` + +Rules: + +- No aliases in `mod.barrel`. +- Duplicate non-function entries are forbidden by symbol kind + identifier. +- Duplicate function entries are forbidden by full function signature. +- Every barrel item must resolve to an existing top-level declaration in module scope. +- `fn` barrel entries MUST use the same signature surface as `fn` declarations. +- Labels are part of the declaration surface, but do not participate in function identity or barrel matching. +- Only `pub` symbols may be imported from another module. +- Multiple `fn` barrel entries with the same identifier are allowed only when signatures differ (overload set). +- Barrel labels MAY differ from the implementation labels. +- A `fn` barrel entry must resolve to exactly one declaration in module scope. +- A `callback` barrel entry must resolve to exactly one top-level callback declaration in module scope. + +Example: + +```barrel +pub fn func(a: int) -> int; +pub fn bla(b: int, c: int) -> (d: float, e: float); +pub callback TickCb; +``` + +## 6. File grammar + +EBNF conventions: + +- `A?` optional, +- `A*` zero or more, +- `A+` one or more, +- terminals in single quotes. + +```ebnf +File ::= ImportDecl* TopDecl* EOF +``` + +### 6.1 Imports + +Imports target modules, never files. + +```ebnf +ImportDecl ::= 'import' ( ModuleRef | '{' ImportList '}' 'from' ModuleRef ) ';' +ImportList ::= ImportItem (',' ImportItem)* +ImportItem ::= Identifier ('as' Identifier)? +ModuleRef ::= '@' Identifier ':' ModulePath +ModulePath ::= Identifier ('/' Identifier)* +``` + +### 6.2 Top-level declarations + +```ebnf +TopDecl ::= TypeDecl | CallbackDecl | ServiceDecl | FunctionDecl | HostFnDecl | ConstDecl | ImplDecl +``` + +Top-level `let` forms and top-level executable statements are forbidden. + +## 7. Declarations + +### 7.1 Type declarations + +```ebnf +TypeDecl ::= StructDecl | ContractDecl | ErrorDecl | EnumDecl +StructDecl ::= 'declare' 'struct' Identifier '(' StructFieldList? ')' (StructMethodBody | ';') +ContractDecl ::= 'declare' 'contract' Identifier ContractBody +ErrorDecl ::= 'declare' 'error' Identifier ErrorBody +EnumDecl ::= 'declare' 'enum' Identifier '(' EnumCaseDecl (',' EnumCaseDecl)* ','? ')' ';' +StructFieldList ::= StructFieldDecl (',' StructFieldDecl)* ','? +StructFieldDecl ::= Identifier ':' TypeRef +StructMethodBody ::= '{' StructMemberDecl* '}' +StructMemberDecl ::= StructMethodDecl | StructCtorDecl +StructMethodDecl ::= 'fn' Identifier ParamList ReturnAnn? Block +StructCtorDecl ::= 'ctor' Identifier ParamList Block +ContractBody ::= '{' FnSigDecl* '}' +ErrorBody ::= '{' ErrorCaseDecl+ '}' +FieldDecl ::= Identifier ':' TypeRef ';' +FnSigDecl ::= 'fn' Identifier ParamList ReturnAnn? ';' +ErrorCaseDecl ::= Identifier ';' +EnumCaseDecl ::= Identifier ('=' IntLit)? +ImplDecl ::= 'implements' Identifier 'for' Identifier 'using' Identifier ImplBody +ImplBody ::= '{' ImplMethodDecl* '}' +ImplMethodDecl ::= 'fn' Identifier ParamList ReturnAnn? Block +``` + +Rules: + +- `declare struct Name(...);` declares a heap-backed nominal user type with fields only. +- `declare struct Name(...) { ... }` declares a heap-backed nominal user type with fields and methods. +- Struct fields are declared positionally in the header and are mutable and accessible by default in v1 core. +- Struct method and `ctor` declarations belong only in the optional struct body. +- Struct methods have an implicit `this` receiver and do not declare `this: Self` explicitly. +- `ctor` declares a named factory for the enclosing struct type. +- Struct values are constructed only through `new Type(...)`. +- `declare contract` bodies contain only function signatures. +- A contract declaration introduces a nominal runtime-dispatchable method surface. +- `declare error` bodies contain only error case labels. +- `declare enum` uses a parenthesized case list and ends with `;`. +- Error case labels in the same `declare error` body must be unique. +- Enum case labels in the same `declare enum` must be unique. +- If no enum case uses `=`, case identifiers default to ascending integer values starting at `0` in declaration order. +- If any enum case uses `=`, then every enum case in that declaration must use `=`. +- Explicit enum case identifiers must be integer literals. +- Enum values are written as `EnumName.caseName`. +- Enum values expose intrinsic method-call surfaces `value.name()` and `value.key()`. +- `implements Contract for Struct using s { ... }` declares the contract implementation block for `Struct`, with `s` as the explicit binder name inside the block. +- `implements` declarations are top-level declarations and are never exported separately through `mod.barrel`. +- Contract values are formed from struct values by explicit or contextual conversion, as defined by static semantics. +- Structs do not have static methods in v1 core. + +### 7.2 Callbacks + +```ebnf +CallbackDecl ::= 'declare' 'callback' Identifier ParamList ReturnAnn? ';' +``` + +Rules: + +- A callback declaration introduces a nominal callback type. +- A callback declaration defines exactly one callable signature. +- A callback declaration has no body. +- A callback declaration is distinct from `contract` and `fn`. +- A callback declaration may be exported through `mod.barrel` using `callback`. +- Callback values may be formed either by assigning a compatible top-level `fn` or by using `bind(context, fn_name)` under a callback-typed context. + +### 7.3 Services + +```ebnf +ServiceDecl ::= 'service' Identifier (':' Identifier)? ServiceBody +ServiceBody ::= '{' ServiceMember* '}' +ServiceMember ::= 'fn' Identifier ParamList ReturnAnn? Block +``` + +### 7.4 Functions + +```ebnf +FunctionDecl ::= 'fn' Identifier ParamList ReturnAnn? Block +ParamList ::= '(' (Param (',' Param)*)? ')' +Param ::= Identifier ':' TypeRef +ReturnAnn ::= '->' ReturnSpec +ReturnSpec ::= ReturnVoid | ReturnPlain | ReturnResult +ReturnVoid ::= 'void' | '()' +ReturnPlain ::= ReturnPayload +ReturnResult ::= 'result' '<' TypeRef '>' ReturnPayload? +ReturnPayload ::= TypeRef +``` + +Rule: + +- `ParamList` declares the named fields of the function input tuple. +- Parameter names are local binding names and are not part of overload identity. +- A named tuple return annotation declares a named output tuple surface. +- A function return surface MUST choose exactly one top-level effect form: plain payload or `result` payload. +- Omitted return annotation is equivalent to `-> void` and `-> ()`. +- `-> T` is sugar for `-> (value: T)`. +- Single-field tuple labels do not create a distinct overload shape. +- `optional T` is a valid `TypeRef` in variables, parameters, fields, and plain function returns. +- `-> optional T` is internally normalized as `-> optional (value: T)` for single-slot payloads. +- `optional` and `optional void` are invalid as type surfaces. +- `result` in function return surface wraps exactly one payload shape and one error type. +- `-> result T` is internally normalized as `result` for single-slot payloads. +- `-> result` is valid and equivalent to `-> result void`. +- A function return surface MUST NOT combine `optional` and `result`. +- Overload sets may differ by return shape; overload resolution rules are defined in static semantics. + +### 7.5 Host functions + +```ebnf +HostFnDecl ::= 'host' 'fn' Identifier ParamList ReturnAnn? ';' +``` + +Rules: + +- `host fn` has no body. +- `host fn` declares callable host surface only. +- `host fn` is not first-class. +- Service-shaped wrappers over host calls belong to stdlib/design patterns, not host syntax. + +### 7.6 Top-level constants + +```ebnf +ConstDecl ::= 'declare' 'const' Identifier ':' TypeRef '=' Expr ';' +``` + +Rules: + +- Top-level constants MUST use `declare const`. +- `declare const` is a declaration and may be exported through `mod.barrel`. + +## 8. Type syntax + +```ebnf +TypeRef ::= OptionalType | TypePrimary +TypePrimary ::= SelfType | SimpleType | GenericType | NamedTupleType | GroupType | UnitType +SelfType ::= 'Self' +SimpleType ::= Identifier +GenericType ::= Identifier '<' TypeRef (',' TypeRef)* '>' +NamedTupleType ::= '(' NamedTupleField (',' NamedTupleField){0,5} ')' +NamedTupleField ::= Identifier ':' TypeRef +GroupType ::= '(' TypeRef ')' +UnitType ::= 'void' | '()' +OptionalType ::= 'optional' TypeRef +``` + +Rules: + +- Named tuple arity is 1..6 in v1 core syntax. +- Tuple types in PBS core are always named. +- Single-slot tuple types are allowed only in type/signature surface, not as tuple literals. +- `optional T` is the canonical optional-type syntax in all type positions. +- `optional void` and `optional ()` are invalid. +- `Self` is valid only inside struct method and `ctor` declarations and refers to the enclosing struct type. + +## 9. Statements and blocks + +```ebnf +Block ::= '{' Stmt* TailExpr? '}' +TailExpr ::= Expr + +Stmt ::= BindingStmt + | AssignStmt + | ReturnStmt + | IfStmt + | ForStmt + | WhileStmt + | BreakStmt + | ContinueStmt + | ExprStmt + +BindingStmt ::= 'let' LetConstOpt Identifier (':' TypeRef)? '=' Expr ';' +LetConstOpt ::= 'const'? +AssignStmt ::= LValue AssignOp Expr ';' +AssignOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' +ReturnStmt ::= 'return' ReturnValue? ';' +ExprStmt ::= Expr ';' + +ReturnValue ::= ResultCtorExpr | Expr +ResultCtorExpr ::= OkExpr | ErrExpr +OkExpr ::= 'ok' '(' Expr ')' +ErrExpr ::= 'err' '(' ErrorPath ')' + +IfStmt ::= 'if' Expr Block ('else' (IfStmt | Block))? +ForStmt ::= 'for' Identifier ':' TypeRef 'from' Expr 'until' Expr StepClause? Block +StepClause ::= 'step' Expr +WhileStmt ::= 'while' Expr Block +BreakStmt ::= 'break' ';' +ContinueStmt ::= 'continue' ';' + +LValue ::= Identifier LValueSuffix* +LValueSuffix ::= '.' Identifier | '[' Expr ']' +``` + +Rules: + +- Assignment is a statement, not an expression. +- Local bindings MUST start with `let`. +- A local constant binding uses `let const`. +- A `Block` used in expression position evaluates to the `TailExpr` when present, otherwise to unit. +- `TailExpr` is only the final unsemicoloned expression in a block. +- A function body block does not use `TailExpr` as implicit `return`. +- `for` is the declarative counted loop form in v1 core. +- `for name: T from start until end { ... }` iterates with an implicit step of `1`. +- `for name: T from start until end step s { ... }` uses explicit step `s`. +- `while` is the general-purpose loop form in v1 core. +- `break` and `continue` are valid only inside loop contexts (`for`, `while`). +- `LValue` MUST NOT include function call segments. +- Compound assignment is syntax sugar over read/compute/write. + +## 10. Expression grammar and precedence + +```ebnf +Expr ::= HandleExpr + +HandleExpr ::= 'handle' ElseExpr HandleMap | ElseExpr +HandleMap ::= '{' HandleArm (',' HandleArm)* ','? '}' +HandleArm ::= HandlePattern '=>' ErrorPath +HandlePattern ::= ErrorPath | '_' +ErrorPath ::= Identifier ('.' Identifier)* +EnumCasePath ::= Identifier '.' Identifier + +ElseExpr ::= IfExpr ('else' ElseExpr)? +IfExpr ::= 'if' Expr Block 'else' (IfExpr | Block) | SwitchExpr +SwitchExpr ::= 'switch' Expr '{' SwitchArm (',' SwitchArm)* ','? '}' | ApplyExpr +SwitchArm ::= SwitchPattern ':' Block +SwitchPattern ::= EnumCasePath | Literal | '_' | 'default' + +ApplyExpr ::= OrExpr ('apply' ApplyExpr)? + +OrExpr ::= AndExpr (('||' | 'or') AndExpr)* +AndExpr ::= EqualityExpr (('&&' | 'and') EqualityExpr)* + +EqualityExpr ::= CompareExpr (('==' | '!=') CompareExpr)? +CompareExpr ::= CastExpr (('<' | '<=' | '>' | '>=') CastExpr)? + +CastExpr ::= AddExpr ('as' TypeRef)* + +AddExpr ::= MulExpr (('+' | '-') MulExpr)* +MulExpr ::= UnaryExpr (('*' | '/' | '%') UnaryExpr)* + +UnaryExpr ::= UnaryOp UnaryExpr | PostfixExpr +UnaryOp ::= '!' | '-' | 'not' +PostfixExpr ::= PrimaryExpr PostfixSuffix* +PostfixSuffix ::= CallSugarSuffix | MemberSuffix | IndexSuffix | PropagateSuffix +CallSugarSuffix ::= '(' ArgList? ')' +MemberSuffix ::= '.' Identifier +IndexSuffix ::= '[' Expr ']' +PropagateSuffix ::= '!' + +ArgList ::= Expr (',' Expr)* + +Literal ::= IntLit | FloatLit | BoundedLit | StringLit | BoolLit +BoolLit ::= 'true' | 'false' +PrimaryExpr ::= Literal | Identifier | ThisExpr | NewExpr | BindExpr | SomeExpr | NoneExpr | UnitExpr | TupleExpr | GroupExpr | Block +ThisExpr ::= 'this' +NewExpr ::= 'new' NewTarget '(' ArgList? ')' +NewTarget ::= Identifier ('.' Identifier)? +BindExpr ::= 'bind' '(' Expr ',' Identifier ')' +SomeExpr ::= 'some' '(' Expr ')' +NoneExpr ::= 'none' +UnitExpr ::= '(' ')' +TupleExpr ::= '(' TupleExprItem ',' TupleExprItem (',' TupleExprItem){0,4} ')' +TupleExprItem ::= Identifier ':' Expr | Expr +GroupExpr ::= '(' Expr ')' +``` + +Non-associative constraints: + +- `a < b < c` is invalid. +- `a == b == c` is invalid. + +Rules: + +- `apply` is the canonical function-application syntax. +- `opt else fallback` is the canonical extraction surface for `optional`. +- `else` extraction is right-associative: `a else b else c` parses as `a else (b else c)`. +- `if cond { a } else { b }` is the canonical conditional expression surface. +- `if` used as an expression always requires an `else` branch and uses blocks on both branches. +- `switch value { pattern: { ... }, ... }` is the canonical multi-branch selection surface. +- `switch` is an expression form; statement-style `switch` is ordinary `ExprStmt` usage whose selected arm blocks evaluate to unit. +- Each `switch` arm contains a block and may therefore use `TailExpr` when the surrounding context expects a value. +- `default` is the canonical wildcard arm spelling for `switch`; `_` is accepted as equivalent shorthand. +- `switch` patterns may use literals or enum cases; error labels are not valid switch patterns. +- `new Type(args...)` is the canonical struct construction surface. +- `new Type.ctorName(args...)` is the canonical named-constructor surface. +- Struct construction arguments are positional and follow the declared struct field order. +- `this` is an implicit receiver name available only inside struct method bodies. +- `apply` is right-associative: `f1 apply f2 apply x` parses as `f1 apply (f2 apply x)`. +- `f1 apply f2 apply f3 apply params` parses as `f1 apply (f2 apply (f3 apply params))`. +- `f()` is exact surface sugar for `f apply ()`. +- `f(e)` is exact surface sugar for `f apply e`. +- `f(e1, e2, ..., en)` for `n >= 2` is exact surface sugar for `f apply (e1, e2, ..., en)`. +- `TupleExpr` is a value expression and is distinct from `GroupExpr`. +- `TupleExpr` arity is 2..6 in v1 core syntax. +- Tuple expressions may be written with explicit labels (`(a: 1, b: 2)`) or positional sugar (`(1, 2)`). +- Positional tuple expression sugar requires an expected named tuple shape supplied by context. +- No single-slot tuple literal exists in v1 core syntax. +- `bind(context, fn_name)` is the canonical surface for explicit callback context binding. +- `bind(context, fn_name)` binds only the first parameter of `fn_name`. +- The second operand of `bind` is a function identifier, not an arbitrary expression. +- Mixing labeled and unlabeled tuple items in the same `TupleExpr` is a syntax error. + +## 11. Deterministic parser requirements + +A conformant parser MUST: + +- parse this grammar deterministically, +- preserve stable source spans in AST nodes, +- produce deterministic diagnostics for forbidden constructs, +- reject unsupported reserved-word syntax in `.pbs`. + +## 12. Required syntax diagnostics + +At minimum, deterministic diagnostics are required for: + +- missing `mod.barrel`, +- duplicate barrel entries (including duplicate `fn` signatures), +- unresolved or ambiguous `fn` barrel signatures, +- unresolved callback barrel entries, +- invalid `error` declaration shape, +- invalid `enum` declaration shape, +- invalid `struct` declaration shape, +- invalid `implements` declaration shape, +- invalid mixed implicit/explicit enum case identifiers, +- invalid return annotation shape (`->` clauses), +- invalid mixed `optional`/`result` function return surface, +- invalid `result` return surface, +- invalid `optional` type surface, +- invalid `optional void` type surface, +- invalid named tuple type shape, +- use of return-surface `result<...>` outside function return annotations, +- invalid `handle` mapping clauses (syntax form), +- invalid `some(...)` construction form, +- invalid `none` usage without optional type context, +- invalid `ok(...)` construction form, +- invalid `err(...)` construction form, +- use of `ok(...)` or `err(...)` outside `return`, +- invalid `bind(...)` construction form, +- invalid `else` extraction form, +- invalid `if` expression form, +- invalid `switch` expression form, +- invalid `for` loop form, +- invalid `new` construction form, +- invalid enum case path, +- invalid callback declaration shape, +- invalid `Self` type usage, +- invalid `this` usage, +- invalid struct method declaration shape, +- invalid `ctor` declaration shape, +- duplicate enum case label, +- duplicate enum case identifier, +- incompatible callback assignment target, +- ambiguous overloaded function assignment to callback, +- incompatible `bind(...)` target, +- ambiguous overloaded function target in `bind(...)`, +- malformed `apply` clause, +- attempted single-slot tuple literal, +- positional tuple expression without expected named tuple shape, +- mixed labeled/unlabeled tuple expression items, +- use of `?` as propagation operator syntax, +- `break`/`continue` outside loops, +- invalid assignment targets (`LValue` violations), +- top-level executable statements, +- `const` used outside `declare const` or `let const`, +- duplicate declaration names in the same namespace/scope (as defined by semantic phase), +- use of reserved syntax words as active constructs. + +## 13. `optional` + +This section defines the `optional` type. +All rules in this section are normative. + +`optional` represents the explicit presence or absence of a payload, without exceptions, traps, or implicit unwrapping. + +### 13.1 Purpose and Design Goals + +`optional` exists to model situations where a payload: + +- 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. + +### 13.2 Type Definition + +`optional` is a built-in prefix type constructor. + +Rules: + +- `optional T` may appear in any type position where `TypeRef` is allowed. +- The payload type is mandatory. +- `optional void` and `optional ()` are invalid. +- A single-slot payload is internally modeled as a one-field tuple payload. +- `optional` is a regular value type. + +### 13.3 Construction + +An `optional` value is constructed using: + +```pbs +some(value) +none +``` + +Rules: + +- `some(...)` always requires parentheses and exactly one expression argument. +- `some(...)` and `none` are built-in special forms, not ordinary identifiers. +- `some(value)` produces an `optional` value carrying the declared payload. +- `none` produces an empty `optional` value. +- `none` represents absence of value. +- `none` must be typed by context. + +### 13.4 Extraction with `else` + +The only way to extract a value from an `optional` value is using `else`. + +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 the declared payload carrier or tuple shape. + +### 13.5 Control Flow and Functions + +Functions returning `optional T` may omit an explicit `return none`. + +Rules: + +- Falling off the end of a function returning `optional T` is equivalent to `return none`. +- This behavior applies only to `optional T`. + +### 13.6 API Surface + +`optional` exposes a minimal API surface: + +```pbs +opt.hasSome(): bool +opt.hasNone(): bool +``` + +Rules: + +- These methods do not extract the contained value. +- They exist for explicit branching logic. +- `opt.hasSome()` and `opt.hasNone()` are compiler-recognized intrinsic method surfaces over `optional`. + +### 13.7 Mutability and Copying + +Rules: + +- `optional` follows normal value semantics for its declared payload. +- Assigning an `optional` value copies the container and its contents. +- Mutability of the payload follows normal binding rules. + +## 14. `result` + +This section defines the `result` return surface. +All rules in this section are normative at syntax-contract level. + +`result` represents an explicit success-or-error outcome. +In PBS core, it is the mechanism for typed error signaling and is valid only in function return annotations. + +### 14.1 Purpose and Design Goals + +`result` exists to model operations that: + +- may succeed and produce a declared payload, or +- may fail with a typed error `E`. + +Design goals: + +- no exceptions, +- no implicit error propagation, +- stack-only representation, +- explicit, typed error handling. + +### 14.2 Return-Only Definition + +`result` is not part of general `TypeRef`. +It may appear only in a function return annotation. + +Rules: + +- `E` must be an `error` type declared via `declare error`. +- The payload may be omitted; omitted payload is equivalent to `void`. +- If present, the payload must be a valid return payload shape written after `result`. +- A single-slot payload is internally modeled as a one-field tuple payload. +- `result` cannot be used for local variable annotations, parameter annotations, field types, or generic type arguments. +- In function return surface, `result` is the only effect wrapper in use for that declaration. + +### 14.3 Construction Surface + +A `result` outcome is produced only by function return forms: + +```pbs +return ok(value); +return err(errorLabel); +``` + +Rules: + +- `ok(...)` always requires parentheses and exactly one expression argument. +- `err(...)` always requires parentheses and exactly one expression argument. +- `ok(...)` and `err(...)` are built-in return special forms, not ordinary identifiers. +- `ok(value)` produces a success payload. +- `err(label)` produces a failure payload. +- `label` must match error type `E`. +- `ok(...)` and `err(...)` are not general-purpose value constructors in v1 core; they are valid only in function return flow for `result`. + +### 14.4 `!` Propagation Operator + +The `!` operator propagates errors when error types match. + +Syntax: + +```pbs +value = expr! +``` + +Rules: + +- `expr` must be a call or expression producing `result Payload`. +- The enclosing function must return `result Payload2` with the same error type. +- If `expr` is success, expression evaluates to wrapped value. +- If `expr` is error, the enclosing function returns `err(e)` immediately. + +### 14.5 Error Mapping with `handle` + +`handle` is the only active construct for extracting success values while remapping error labels. + +Syntax: + +```pbs +value = handle expr { + ErrorA.case1 => ErrorB.mapped1, + ErrorA.case2 => ErrorB.mapped2, + _ => ErrorB.default, +}; +``` + +Rules: + +- `expr` must produce `result Payload1`. +- The enclosing function must return `result Payload2`. +- Arms map `E1` labels to `E2` labels. +- Arms must be exhaustive (explicitly or with `_`). + +### 14.6 Restrictions + +Rules: + +- No implicit extraction of `result Payload`. +- `result` cannot be implicitly converted to `optional` and vice versa. +- Falling off the end of a function returning `result` is a compile-time error. +- `?` is not active as propagation syntax in v1 core; propagation uses postfix `!`. +- No pattern matching construct beyond `handle` is active in v1 core. +- `match` is reserved for future pattern-matching constructs and is not part of `result` remapping in v1 core. + +## 15. Canonical examples + +### 15.1 Host function declaration + +```pbs +host fn input_state() -> (x: int, y: int, buttons: int); +``` + +### 15.2 Top-level and local constants + +```pbs +declare const A: int = 1; + +fn demo() -> int { + let const a: int = 2; + return A + a; +} +``` + +### 15.3 Function with loops and assignment + +```pbs +fn tick(counter: int) -> int { + let x = counter; + for i: int from 0 until 60 { + x = x + 1; + if i == 10 { + continue; + } + if i == 49 { + break; + } + } + return x; +} +``` + +### 15.4 Function application with named output tuple + +```pbs +fn func(a: int, b: int) -> (c: int, d: float) { + return (c: a + b, d: 2.0); +} + +fn demo_apply() -> int { + let params: (a: int, b: int) = (1, 2); + let r = func apply params; + let c = r.c; + let d = r.d; + let r2 = func(1, 2); + let r3 = func apply (a: 1, b: 2); + let _ = d; + let _ = r2.d; + let _ = r3.c; + return c; +} + +fn plus_one(n: int) -> int { + return n + 1; +} + +fn times_two(n: int) -> int { + return n * 2; +} + +fn demo_chain() -> int { + return plus_one apply times_two apply 10; +} +``` + +### 15.5 Module import and service + +```pbs +import { Vec2 } from @core:math; + +service Physics { + fn step(pos: Vec2, vel: Vec2) -> Vec2 { + let out = pos; + out.x += vel.x; + out.y += vel.y; + return out; + } +} +``` + +### 15.6 Nominal callback + +```pbs +declare callback TickCb(dt: int) -> void; + +fn on_tick(dt: int) -> void { + let _ = dt; +} + +fn install(cb: TickCb) -> void { + cb apply 16; + cb(16); +} + +fn demo_callback() -> void { + let cb: TickCb = on_tick; + install(cb); +} +``` + +### 15.7 Bound callback + +```pbs +declare callback UpdateCb(dt: int) -> void; + +declare struct Enemy(hp: int); + +fn enemy_tick(self: Enemy, dt: int) -> void { + let _ = self.hp; + let _ = dt; +} + +fn install_update(cb: UpdateCb) -> void { + cb apply 16; +} + +fn demo_bind(enemy: Enemy) -> void { + let cb: UpdateCb = bind(enemy, enemy_tick); + install_update(cb); +} +``` + +### 15.8 Struct construction, methods, and contract implementation + +```pbs +declare contract StasisProcess { + fn stasis() -> int; +} + +declare struct Struct( + a: int, + b: str, + c: float, +) { + fn compute_something() -> int { + return this.a; + } + + ctor createWithSomethingSpecial(x: int, y: int) { + let _ = y; + return new Struct(x, "special", 3.0); + } +} + +implements StasisProcess for Struct using s { + fn stasis() -> int { + return s.compute_something(); + } +} + +fn demo_contract() -> int { + let s = new Struct(1, "2", 3.0); + let s2 = new Struct.createWithSomethingSpecial(1, 2); + let sp: StasisProcess = s; + let sp2 = s as StasisProcess; + let direct = s.compute_something(); + let via_contract = sp.stasis(); + let _ = s2.compute_something(); + let _ = sp2.stasis(); + return direct + via_contract; +} +``` + +### 15.9 Return-shape equivalence and overload by return + +```pbs +fn a(i: int) { } +fn b(i: int) -> void { } +fn c(i: int) -> () { } + +fn length(x: float, y: float) -> float { + return x * x + y * y; +} + +fn transform(a: int, b: int, c: int) -> (left: int, right: int) { + return (left: a, right: b); +} + +fn transform(a: int, b: int, c: int) -> float { + return 0.0; +} +``` + +### 15.10 Optional and result examples + +```pbs +declare error IOError { + not_found; +} + +fn maybe_number() -> optional int { + let opt = some(1); + let n: int = opt else 0; + let _ = n; + return some(1); +} + +fn maybe_pair() -> optional (left: int, right: int) { + return none; +} + +fn pair_or_error() -> result (left: int, right: int) { + return ok((left: 1, right: 2)); +} + +fn pair_or_error_fail() -> result (left: int, right: int) { + return err(IOError.not_found); +} +``` + +### 15.11 Result propagation and remapping + +```pbs +declare error ErrorA { + bad_input; +} + +declare error ErrorB { + invalid; + generic; +} + +fn inner() -> result int { + return ok(1); +} + +fn outer_propagate() -> result int { + let v: int = inner()!; + return ok(v + 1); +} + +fn outer_remap() -> result int { + let v: int = handle inner() { + ErrorA.bad_input => ErrorB.invalid, + _ => ErrorB.generic, + }; + return ok(v); +} +``` + +### 15.12 Switch expression and statement-style use + +```pbs +fn state_code(state: int) -> int { + let code = switch state { + 0: { 0 }, + 1: { 1 }, + default: { 2 }, + }; + return code; +} + +fn func(v: int) -> void { + let _ = v; +} + +fn dispatch(state: int) -> void { + switch state { + 0: { + func(0); + }, + 1: { + func(1); + }, + default: { + func(2); + }, + }; +} +``` + +### 15.13 Enum declaration and use + +```pbs +declare enum Enum(e1 = 0, e2 = 10, e3 = 100); + +fn test(e: Enum) -> str { + let current: Enum = Enum.e1; + let name: str = current.name(); + let key: int = current.key(); + let _ = key; + + let label = switch e { + Enum.e1: { "e1" }, + Enum.e2: { "e2" }, + default: { "e3" }, + }; + + let _ = name; + return label; +} +``` diff --git a/docs/specs/pbs/4. Static Semantics Specification.md b/docs/specs/pbs/4. Static Semantics Specification.md new file mode 100644 index 00000000..d57d554b --- /dev/null +++ b/docs/specs/pbs/4. Static Semantics Specification.md @@ -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 T` denotes a result return surface over payload shape `T` and error type `E`. +- `-> result T` with single-slot payload is internally normalized to `result`. +- `-> result` is valid and denotes a result return surface with unit payload. +- A function return surface may be plain or `result`. +- A function return surface must not combine `optional` and `result` in the same declaration. +- `result` 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

`. +- `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` 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

` value where `P` is the static type of `expr`, unless an expected `optional` 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

` type is available from context. +- `opt else fallback` requires the left operand to have static type `optional

`. +- 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` return flow and handling + +Rules: + +- `return ok(expr);` is valid only inside a function whose declared return surface is `result 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 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 P`. +- `expr!` is valid only when the enclosing function returns `result 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 P`. +- `handle expr { ... }` is valid only when the enclosing function returns `result 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. diff --git a/docs/specs/pbs/Heap Model - Discussion Agenda.md b/docs/specs/pbs/Heap Model - Discussion Agenda.md new file mode 100644 index 00000000..3eb0a2e2 --- /dev/null +++ b/docs/specs/pbs/Heap Model - Discussion Agenda.md @@ -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. diff --git a/docs/specs/random/pbs/PBS - Compatibility and Evolution Policy v1 (Draft).md b/docs/specs/random/pbs/PBS - Compatibility and Evolution Policy v1 (Draft).md new file mode 100644 index 00000000..12d9f792 --- /dev/null +++ b/docs/specs/random/pbs/PBS - Compatibility and Evolution Policy v1 (Draft).md @@ -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. diff --git a/docs/specs/random/pbs/PBS - Game Language Profile v1 (Draft).md b/docs/specs/random/pbs/PBS - Game Language Profile v1 (Draft).md new file mode 100644 index 00000000..5321ad6a --- /dev/null +++ b/docs/specs/random/pbs/PBS - Game Language Profile v1 (Draft).md @@ -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. diff --git a/docs/specs/random/pbs/PBS - Game Profile Syntax Specification v1 (Draft).md b/docs/specs/random/pbs/PBS - Game Profile Syntax Specification v1 (Draft).md new file mode 100644 index 00000000..8f385dcb --- /dev/null +++ b/docs/specs/random/pbs/PBS - Game Profile Syntax Specification v1 (Draft).md @@ -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. diff --git a/docs/specs/pbs/PBS - Language Syntax Specification v0.md b/docs/specs/random/pbs/PBS - Language Syntax Specification v0.md similarity index 100% rename from docs/specs/pbs/PBS - Language Syntax Specification v0.md rename to docs/specs/random/pbs/PBS - Language Syntax Specification v0.md diff --git a/docs/specs/pbs/README.md b/docs/specs/random/pbs/README.md similarity index 56% rename from docs/specs/pbs/README.md rename to docs/specs/random/pbs/README.md index 2a3dfafb..a912418d 100644 --- a/docs/specs/pbs/README.md +++ b/docs/specs/random/pbs/README.md @@ -19,3 +19,6 @@ When a rule conflicts with runtime or bytecode authority, runtime and bytecode a ## Files - `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). diff --git a/docs/specs/pbs_old/PBS - Canonical Addenda.md b/docs/specs/random/pbs_old/PBS - Canonical Addenda.md similarity index 100% rename from docs/specs/pbs_old/PBS - Canonical Addenda.md rename to docs/specs/random/pbs_old/PBS - Canonical Addenda.md diff --git a/docs/specs/pbs_old/PBS - Module and Linking Model.md b/docs/specs/random/pbs_old/PBS - Module and Linking Model.md similarity index 100% rename from docs/specs/pbs_old/PBS - Module and Linking Model.md rename to docs/specs/random/pbs_old/PBS - Module and Linking Model.md diff --git a/docs/specs/pbs_old/PBS - prometeu.json specs.md b/docs/specs/random/pbs_old/PBS - prometeu.json specs.md similarity index 100% rename from docs/specs/pbs_old/PBS - prometeu.json specs.md rename to docs/specs/random/pbs_old/PBS - prometeu.json specs.md diff --git a/docs/specs/pbs_old/Prometeu Base Script (PBS) - Implementation Spec.md b/docs/specs/random/pbs_old/Prometeu Base Script (PBS) - Implementation Spec.md similarity index 100% rename from docs/specs/pbs_old/Prometeu Base Script (PBS) - Implementation Spec.md rename to docs/specs/random/pbs_old/Prometeu Base Script (PBS) - Implementation Spec.md diff --git a/docs/specs/pbs_old/Prometeu Runtime Traps.md b/docs/specs/random/pbs_old/Prometeu Runtime Traps.md similarity index 100% rename from docs/specs/pbs_old/Prometeu Runtime Traps.md rename to docs/specs/random/pbs_old/Prometeu Runtime Traps.md diff --git a/docs/specs/pbs_old/Prometeu Scripting - Prometeu Bytecode Script (PBS).md b/docs/specs/random/pbs_old/Prometeu Scripting - Prometeu Bytecode Script (PBS).md similarity index 100% rename from docs/specs/pbs_old/Prometeu Scripting - Prometeu Bytecode Script (PBS).md rename to docs/specs/random/pbs_old/Prometeu Scripting - Prometeu Bytecode Script (PBS).md diff --git a/docs/specs/pbs_old/Prometeu VM Memory model.md b/docs/specs/random/pbs_old/Prometeu VM Memory model.md similarity index 100% rename from docs/specs/pbs_old/Prometeu VM Memory model.md rename to docs/specs/random/pbs_old/Prometeu VM Memory model.md