add specs

This commit is contained in:
bQUARKz 2026-03-02 15:21:17 +00:00
parent 4c4ba1d603
commit 40785bfb63
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
23 changed files with 1845 additions and 91 deletions

View File

@ -31,7 +31,7 @@ It must feel fluid for frame-driven gameplay code while remaining strict enough
- 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.
- Toolchain-managed host binding model aligned with syscall/capability model.
- Module visibility and import model suitable for multi-file game projects.
- Diagnostics and conformance requirements for interoperable implementations.
@ -41,6 +41,7 @@ It must feel fluid for frame-driven gameplay code while remaining strict enough
- Non-deterministic async execution models.
- Mandatory custom allocator model replacing runtime GC.
- Advanced memory syntax (`alloc/borrow/mutate/...`) before explicit profile activation.
- User-authored declaration of canonical host primitive bindings outside reserved SDK/toolchain `declare host` surfaces.
## 7. Non-Negotiable Constraints
@ -48,12 +49,14 @@ It must feel fluid for frame-driven gameplay code while remaining strict enough
- Closed VM compatibility is mandatory.
- `FRAME_SYNC`-based execution semantics are preserved.
- Host API usage remains capability-gated and versioned.
- Canonical host primitive bindings remain reserved to SDK/toolchain-controlled `declare host` surfaces.
## 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)`.
- User-facing host-backed APIs evolve through SDK/stdlib surfaces, not direct user-authored host-binding declarations outside reserved `declare host` modules.
- Deterministic loader rejection when target contracts are unsupported.
## 9. Quality Bar

View File

@ -66,6 +66,8 @@ PBS versioning is separated by domain to avoid ambiguity.
4. Compatibility Policy Version (`pbs_compat`): evolution/deprecation policy text.
Host primitives remain versioned by canonical identity `(module, name, version)`.
Direct declaration of canonical host primitive bindings is reserved to SDK/toolchain-managed `declare host` surfaces, not ordinary user-authored PBS modules.
User-facing PBS and stdlib APIs over host primitives MAY evolve independently from those bindings as long as canonical host identity, capability policy, and compatibility commitments remain preserved.
## 6. Versioning Scheme

View File

@ -33,8 +33,14 @@ If a rule here conflicts with higher authority, it is invalid.
- Valid line terminators: `\n`, `\r\n`.
- Whitespace is insignificant except as token separator.
- A `.pbs` file is a declaration unit.
- A `.pbs` file may be loaded either as an ordinary project module or as a reserved stdlib interface module.
- Top-level executable statements are forbidden.
Interface-module note:
- Reserved stdlib interface modules use the same `.pbs` syntax surface as ordinary modules.
- Whether a file is interpreted in ordinary mode or interface-module mode is determined by its containing reserved stdlib project space, not by a distinct file extension.
## 4. Lexical specification
### 4.1 Required token classes
@ -92,6 +98,7 @@ Reserved (not active syntax in v1 core):
Note:
- `host` is used only by reserved SDK/toolchain `declare host` surfaces in v1 core; ordinary user-authored modules MUST reject those declarations.
- `match` remains a reserved keyword for possible future pattern-matching constructs.
- `match` is not used for `result` handling in v1 core syntax.
@ -142,6 +149,7 @@ BarrelFile ::= BarrelItem* EOF
BarrelItem ::= BarrelFnItem
| BarrelStructItem
| BarrelContractItem
| BarrelHostItem
| BarrelErrorItem
| BarrelEnumItem
| BarrelServiceItem
@ -152,6 +160,7 @@ BarrelVisibility ::= 'mod' | 'pub'
BarrelFnItem ::= BarrelVisibility 'fn' Identifier ParamList ReturnAnn? ';'
BarrelStructItem ::= BarrelVisibility 'struct' Identifier ';'
BarrelContractItem ::= BarrelVisibility 'contract' Identifier ';'
BarrelHostItem ::= BarrelVisibility 'host' Identifier ';'
BarrelErrorItem ::= BarrelVisibility 'error' Identifier ';'
BarrelEnumItem ::= BarrelVisibility 'enum' Identifier ';'
BarrelServiceItem ::= BarrelVisibility 'service' Identifier ';'
@ -166,13 +175,18 @@ Rules:
- 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.
- Type declarations in `mod.barrel` MUST use their specific declaration kind: `struct`, `contract`, `error`, or `enum`.
- Type declarations in `mod.barrel` MUST use their specific declaration kind: `struct`, `contract`, `host`, `error`, `enum`, or `service`.
- 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.
- A `host` barrel entry must resolve to exactly one top-level `declare host` declaration in module scope.
- A `struct` barrel entry governs the visibility of the struct declaration and of all methods declared directly in that struct body.
- A `host` barrel entry governs the visibility of the host declaration and of all method signatures declared directly in that host body.
- A `service` barrel entry governs the visibility of the service declaration, its canonical singleton value, and all methods declared directly in that service body.
- Methods declared inside a `struct`, `host`, or `service` are never exported independently through `mod.barrel`.
Example:
@ -181,7 +195,9 @@ pub fn func(a: int) -> int;
pub fn bla(b: int, c: int) -> (d: float, e: float);
pub struct Struct;
pub contract TickLike;
pub host Gfx;
pub enum GameState;
pub service Physics;
pub callback TickCb;
```
@ -210,22 +226,46 @@ ModuleRef ::= '@' Identifier ':' ModulePath
ModulePath ::= Identifier ('/' Identifier)*
```
### 6.1.1 Attributes
Attributes are bracketed compile-time metadata forms.
```ebnf
AttrList ::= Attribute+
Attribute ::= '[' Identifier AttrArgs? ']'
AttrArgs ::= '(' AttrArgList? ')'
AttrArgList ::= AttrArg (',' AttrArg)*
AttrArg ::= Identifier '=' AttrValue
AttrValue ::= StringLit | IntLit | BoolLit
```
Rules:
- An attribute attaches syntactic metadata to the declaration surface that immediately follows it.
- Attributes do not introduce values, types, callables, or modules by themselves.
- In v1 core, ordinary user-authored modules MUST reject attributes unless another specification explicitly permits them.
- Reserved stdlib/toolchain interface modules MAY use attributes where explicitly allowed by syntax and semantics.
- Attribute interpretation is compile-time only unless another specification explicitly lowers its effect into runtime-facing metadata.
### 6.2 Top-level declarations
```ebnf
TopDecl ::= TypeDecl | CallbackDecl | ServiceDecl | FunctionDecl | HostFnDecl | ConstDecl | ImplDecl
TopDecl ::= TypeDecl | CallbackDecl | FunctionDecl | ConstDecl | ImplDecl
```
Top-level `let` forms and top-level executable statements are forbidden.
`declare host` is reserved to SDK/toolchain-controlled modules; ordinary user-authored modules MUST reject it.
## 7. Declarations
### 7.1 Type declarations
```ebnf
TypeDecl ::= StructDecl | ContractDecl | ErrorDecl | EnumDecl
TypeDecl ::= StructDecl | ServiceDecl | ContractDecl | HostDecl | ErrorDecl | EnumDecl
StructDecl ::= 'declare' 'struct' Identifier '(' StructFieldList? ')' (StructMethodBody | ';')
ServiceDecl ::= 'declare' 'service' Identifier ServiceBody
ContractDecl ::= 'declare' 'contract' Identifier ContractBody
HostDecl ::= 'declare' 'host' Identifier HostBody
ErrorDecl ::= 'declare' 'error' Identifier ErrorBody
EnumDecl ::= 'declare' 'enum' Identifier '(' EnumCaseDecl (',' EnumCaseDecl)* ','? ')' ';'
StructFieldList ::= StructFieldDecl (',' StructFieldDecl)* ','?
@ -237,7 +277,8 @@ StructCtorDecl ::= 'ctor' Identifier ParamList Block
ContractBody ::= '{' FnSigDecl* '}'
ErrorBody ::= '{' ErrorCaseDecl+ '}'
FieldDecl ::= Identifier ':' TypeRef ';'
FnSigDecl ::= 'fn' Identifier ParamList ReturnAnn? ';'
FnSigDecl ::= AttrList? 'fn' Identifier ParamList ReturnAnn? ';'
HostBody ::= '{' FnSigDecl* '}'
ErrorCaseDecl ::= Identifier ';'
EnumCaseDecl ::= Identifier ('=' IntLit)?
ImplDecl ::= 'implements' Identifier 'for' Identifier 'using' Identifier ImplBody
@ -259,6 +300,10 @@ Rules:
- 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 host Name { ... }` declares a reserved SDK/toolchain host-binding owner with host-backed method signatures only.
- A host declaration body contains only function signatures and no bodies.
- Host declarations are reserved to SDK/toolchain-controlled modules and MUST be rejected in ordinary user-authored modules.
- Host declarations do not declare fields, `ctor`, or `implements` bodies.
- `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.
@ -268,9 +313,9 @@ Rules:
- 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 Contract for Owner using s { ... }` declares the contract implementation block for a declared `struct` or `service` named `Owner`, 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.
- Contract values are formed from struct values or service singleton values by explicit or contextual conversion, as defined by static semantics.
- Structs do not have static methods in v1 core.
### 7.2 Callbacks
@ -291,11 +336,21 @@ Rules:
### 7.3 Services
```ebnf
ServiceDecl ::= 'service' Identifier (':' Identifier)? ServiceBody
ServiceDecl ::= 'declare' 'service' Identifier ServiceBody
ServiceBody ::= '{' ServiceMember* '}'
ServiceMember ::= 'fn' Identifier ParamList ReturnAnn? Block
```
Rules:
- `declare service Name { ... }` declares a nominal service type together with its canonical module-owned singleton value `Name`.
- A service body contains only method declarations.
- Service methods have an implicit receiver binding `this` of type `Self` and do not declare `this: Self` explicitly.
- Services do not declare fields.
- Services do not declare `ctor`.
- Services are never constructed with `new`.
- Service method visibility follows the `service` entry for that declaration in `mod.barrel`; service methods are never exported independently.
### 7.4 Functions
```ebnf
@ -328,18 +383,17 @@ Rule:
- 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? ';'
```
### 7.5 Reserved host binding surface
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.
- There is no top-level `host fn` declaration syntax in v1 core.
- Reserved host bindings are expressed only through top-level `declare host` declarations.
- `declare host` is reserved to SDK/toolchain-controlled modules and is not available to ordinary user-authored modules.
- Methods declared inside a `declare host` body are signature-only host-backed call surfaces.
- A host method signature in a reserved stdlib/interface module MAY carry attributes.
- `[Host(module = "...", name = "...", version = N)]` is the canonical reserved attribute surface for host-binding metadata in v1 core.
- Service-shaped wrappers over host calls MAY exist as SDK/stdlib design patterns, but they do not replace canonical `declare host` bindings.
### 7.6 Top-level constants
@ -512,7 +566,8 @@ Rules:
- Struct construction arguments are positional and follow the declared struct field order.
- `as` is reserved in expressions for explicit contract-view conversion only; it does not denote a general cast surface in v1 core.
- The right operand of expression-form `as` is a simple contract identifier already visible in scope, never a qualified module path.
- `this` is an implicit receiver name available only inside struct method and `ctor` bodies.
- `this` is an implicit receiver name available only inside struct method, service method, and `ctor` bodies.
- `Self` is valid only inside struct method declarations, service method declarations, and `ctor` declarations.
- `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 ()`.
@ -527,7 +582,7 @@ Rules:
- `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.
- Member selection of a struct or contract method is valid only as an immediate call target; bare method extraction such as `let m = s.compute;` is invalid.
- Member selection of a struct, service, host, or contract method is valid only as an immediate call target; bare method extraction such as `let m = s.compute;` is invalid.
## 11. Deterministic parser requirements
@ -545,10 +600,13 @@ At minimum, deterministic diagnostics are required for:
- missing `mod.barrel`,
- duplicate barrel entries (including duplicate `fn` signatures),
- unresolved or ambiguous `fn` barrel signatures,
- unresolved host barrel entries,
- unresolved callback barrel entries,
- invalid `error` declaration shape,
- invalid `enum` declaration shape,
- invalid `struct` declaration shape,
- invalid `host` declaration shape,
- invalid `service` declaration shape,
- invalid `implements` declaration shape,
- invalid mixed implicit/explicit enum case identifiers,
- invalid return annotation shape (`->` clauses),
@ -575,6 +633,7 @@ At minimum, deterministic diagnostics are required for:
- invalid `Self` type usage,
- invalid `this` usage,
- invalid struct method declaration shape,
- invalid service method declaration shape,
- invalid `ctor` declaration shape,
- duplicate enum case label,
- duplicate enum case identifier,
@ -816,12 +875,17 @@ It consolidates the primary surface forms used by tooling and conformance for:
Examples in this section are intended to stay mutually consistent with the syntax and static-semantics documents.
### 15.1 Host function declaration
### 15.1 Reserved host declaration
```pbs
host fn input_state() -> (x: int, y: int, buttons: int);
declare host Gfx {
[Host(module = "gfx", name = "draw_pixel", version = 1)]
fn draw_pixel(x: int, y: int, c: color);
}
```
This surface is reserved to SDK/toolchain-controlled modules and is not available to ordinary user-authored PBS v1 core source.
### 15.2 Top-level and local constants
```pbs
@ -889,7 +953,7 @@ fn demo_chain() -> int {
```pbs
import { Vec2 } from @core:math;
service Physics {
declare service Physics {
fn step(pos: Vec2, vel: Vec2) -> Vec2 {
let out = pos;
out.x += vel.x;

View File

@ -34,16 +34,37 @@ PBS distinguishes at least:
- type namespace,
- value namespace,
- callable namespace.
- host-owner namespace.
Rules:
- `declare struct`, `declare contract`, `declare error`, `declare enum`, and `declare callback` introduce names in the type namespace.
- `declare struct`, `declare service`, `declare contract`, `declare error`, `declare enum`, and `declare callback` introduce names in the type namespace.
- `declare host` introduces names in the host-owner 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.
- `declare service Name` also introduces the canonical singleton value `Name` in the value namespace.
- Top-level `fn`, service methods, and host methods 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.
- `declare host` declarations are reserved to SDK/toolchain-controlled modules and are not formable in ordinary user-authored PBS source.
- A host owner name is not an ordinary value expression in v1 core; it is used only as a qualified host-call owner.
### 2.3 Tuple shapes
### 2.3 Attribute metadata
Attributes are compile-time metadata attached to declaration surfaces.
Rules:
- An attribute has no value-level or type-level meaning by itself.
- Attributes are not first-class values and are not reflectable in v1 core.
- Attributes do not automatically survive into runtime or bytecode artifacts.
- An attribute affects runtime artifacts only when another specification defines an explicit lowering for its semantic effect.
- In v1 core, the only normative reserved attribute is `Host`.
- `Host` is valid only on a host method signature declared directly inside a reserved stdlib/interface-module `declare host` body.
- `Host` is invalid on ordinary user-authored modules, top-level `fn`, struct methods, service methods, callbacks, contracts, and constants.
- `Host` metadata is consumed by the compiler during host-binding lowering.
- The `Host` attribute syntax itself is not exported as runtime metadata; instead, its canonical identity participates in PBX host-binding emission as defined by the Host ABI Binding specification.
### 2.4 Tuple shapes
Static checking uses tuple shapes as semantic objects.
@ -103,19 +124,20 @@ The following constructs are callable in v1 core:
- contract values,
- callback values,
- service methods,
- `host fn`.
- host methods on declared host owners.
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 contract value is identified at the type level by its contract type and at runtime by an underlying concrete struct instance or service singleton value 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 host method is identified by `(host owner name, method name, input tuple shape, output tuple shape)` within the PBS declaration surface.
- 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.
- Canonical host primitive identity and binding are governed by the Host ABI Binding specification rather than by ordinary v1 core declaration identity alone.
### 3.3 Declaration validity
@ -135,6 +157,11 @@ Rules:
- 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.
- A host method signature in a reserved stdlib/interface module MUST carry exactly one `Host` attribute.
- A `Host` attribute MUST declare exactly the named arguments `module`, `name`, and `version`.
- `Host.module` and `Host.name` MUST be non-empty string literals.
- `Host.version` MUST be a positive integer literal.
- Two host methods in the same resolved stdlib environment MUST NOT lower to the same canonical `(module, name, version)` unless they denote the same host method declaration after project/module resolution.
- A `declare const` declaration must include an explicit type annotation.
- A `declare const` initializer must be a constant expression.
- `declare const` dependency resolution is module-wide and must not depend on source-file processing order.
@ -161,7 +188,6 @@ Rules:
- binary boolean operators over boolean constant expressions.
- A `declare const` initializer must not use:
- callable application or call sugar,
- `host fn`,
- `new`,
- `bind(...)`,
- `some(...)` or `none`,
@ -185,19 +211,22 @@ Rules:
- 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.6 Structs, methods, and contracts
### 3.6 Structs, services, methods, and contracts
Rules:
- Struct values are heap-backed reference values in v1 core.
- Service values are canonical module-owned singleton 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.
- Assigning a service value copies the same canonical singleton identity; it does not create a new instance.
- 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 and `ctor` bodies.
- `Self` is valid only in struct method and `ctor` declarations and denotes the enclosing struct type.
- A service method declared in a service body has an implicit receiver binding `this` of type `Self`.
- `this` is valid only inside struct method, service method, and `ctor` bodies.
- `Self` is valid only in struct method, service method, and `ctor` declarations and denotes the enclosing owner type.
- The enclosing struct name is in scope inside its own field declarations, enabling self-referential field types.
- A named `ctor` declared in a struct body is not a method, but it does introduce an implicit under-construction `this`.
- A `ctor` has no declared return annotation and is not a `fn`.
@ -206,21 +235,29 @@ Rules:
- Named constructors participate in overload resolution by name plus parameter signature.
- `return;` and `return expr;` are both invalid inside `ctor` bodies.
- Methods declared in a struct body belong to the direct method namespace of that concrete struct type.
- Methods declared in a service body belong to the direct method namespace of that concrete service type.
- `receiver.method(...)` on a concrete struct receiver resolves only against methods declared directly in that struct body.
- `receiver.method(...)` on a service receiver resolves only against methods declared directly in that service 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.
- Service values are ordinary value expressions and may be assigned, passed, and returned under their declared service type.
- Services do not have fields.
- Services do not declare named constructors and are never targets of `new`.
- Contract implementations do not inject their methods into the direct method namespace of the concrete struct or service.
- `implements Contract for Owner using s { ... }` is unique per `(Contract, Owner)` pair, where `Owner` is a declared struct or service.
- A contract for a struct or service may be implemented only in the module where that owner itself is declared.
- Contracts may be declared in other modules and implemented for the local struct or service in its owner module.
- An `implements` block has no independent visibility; its observability follows the visibility of the owner declaration, and the contract must also be nameable at the use site.
- Methods declared directly inside a struct or service follow the visibility of their enclosing owner declaration; they do not have independent visibility.
- A struct value or service singleton value may be coerced to a contract value when exactly one visible `implements Contract for Owner ...` applies.
- Contract coercion may happen by explicit `expr as ContractType` or by contextual typing in assignments, parameter passing, and returns.
- `as` in expression position is reserved for explicit contract coercion only; no other cast semantics exist in v1 core.
- The contract name in `expr as ContractName` must be a simple visible identifier resolved through ordinary scope and import rules.
- A contract value carries the underlying struct reference together with the selected contract implementation for runtime dispatch.
- A contract value carries the underlying struct reference or service singleton value 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.
- Calling `serviceValue.contractMethod(...)` is not valid unless that method is also declared directly on the service.
- Structs do not have static methods in v1 core; named constructors are their only type-qualified callable members.
- Services are accessed through their canonical singleton value, not through `new` or static method declarations.
Example:
@ -267,7 +304,7 @@ Rules:
- 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.
- Host methods 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`.
@ -336,7 +373,7 @@ 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 left operand of `apply` must resolve to one of: a callable candidate set, a bound struct method target, a bound service 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.
@ -346,7 +383,7 @@ Rules:
- 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`.
- Callback values, service methods, host methods, struct methods, and contract-method targets use the same `apply` rules as top-level `fn`.
### 3.9 Call sugar
@ -450,9 +487,12 @@ Rules:
- 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.
- `expr.method` is a valid method-call target when the static type of `expr` is a concrete service type declaring method `method`.
- `expr.method` over a concrete service receiver does not search visible contract implementations.
- `HostName.method` is a valid method-call target when `HostName` resolves to a visible declared host owner whose body declares method `method`.
- `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`.
- Bare extraction of a struct or contract method as a value is invalid in v1 core because methods are not first-class.
- Bare extraction of a struct, service, host, or contract method as a value is invalid in v1 core because methods are not first-class.
- `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.
@ -464,6 +504,8 @@ Rules:
- 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 service method is a compile-time error.
- Access to a missing host 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.
@ -560,17 +602,28 @@ At minimum, deterministic static diagnostics are required for:
- 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 or `ctor` body,
- invalid `Self` type usage outside struct method or `ctor` declarations,
- invalid `this` usage outside struct method, service method, or `ctor` body,
- invalid `Self` type usage outside struct method, service method, or `ctor` declarations,
- invalid struct method declaration shape,
- invalid service method declaration shape,
- invalid host declaration shape,
- invalid host method declaration shape,
- invalid attribute surface,
- invalid `Host` attribute target,
- duplicate `Host` attribute on one host method,
- missing required `Host` attribute on a reserved host method,
- malformed `Host(module=..., name=..., version=...)` argument set,
- invalid empty `Host.module` or `Host.name`,
- invalid non-positive `Host.version`,
- invalid `ctor` declaration shape,
- invalid `return` inside `ctor`,
- incomplete field initialization on a completing `ctor` path,
- duplicate `implements Contract for Struct` pair,
- `implements` declared outside the owner module of the struct,
- duplicate `implements Contract for Owner` pair,
- `implements` declared outside the owner module of the struct or service,
- incompatible or missing contract method implementation,
- invalid contract coercion from concrete struct value,
- invalid contract coercion from concrete struct or service value,
- unresolved barrel function signature after label-insensitive matching,
- unresolved host barrel entry,
- unresolved callback barrel entry,
- invalid enum case path,
- invalid enum intrinsic member call,
@ -580,7 +633,7 @@ At minimum, deterministic static diagnostics are required for:
- 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,
- attempted assignment of host method 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(...)`,
@ -628,8 +681,10 @@ At minimum, deterministic static diagnostics are required for:
- 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 service method,
- member access on a missing host method,
- member access on a missing contract method,
- bare method extraction from a struct or contract value,
- bare method extraction from a struct, service, host, or contract value,
- member projection on a single-slot carrier value,
- invalid optional intrinsic member call,
- member projection on a missing named output label.

View File

@ -0,0 +1,449 @@
# PBS Manifest, Module, Stdlib, and SDK Resolution Specification
Status: Draft v2 (Normative)
Applies to: `prometeu.json`, project and module terminology, workspace dependency resolution, stdlib selection, reserved stdlib projects, interface-module loading, and compile-time SDK metadata
## 1. Purpose
This document defines the normative compile-time model for:
- project manifests,
- project and module identity,
- import resolution,
- stdlib selection,
- reserved stdlib project spaces,
- stdlib interface modules,
- and the compile-time lifecycle of SDK host-binding metadata.
This document is the authority for how the compiler discovers and loads:
- ordinary project modules,
- reserved stdlib modules such as `@sdk:gfx`,
- and compile-time-only host-binding metadata attached to those reserved modules.
This document does not define runtime load behavior.
PBX host-binding emission and loader-side syscall resolution are defined by the Host ABI Binding specifications.
## 2. Scope
This document defines:
- the role of `language` in `prometeu.json`,
- the role of `stdlib` in `prometeu.json`,
- the distinction between projects, modules, and imports,
- the distinction between ordinary project dependencies and the stdlib environment,
- root-project ownership of the effective stdlib line of a build,
- reserved project spaces such as `@sdk:*` and `@core:*`,
- the source model for stdlib interface modules,
- the compile-time role of reserved attributes such as `[Host(...)]`.
This document does not define:
- PBX binary layout,
- loader-side syscall resolution,
- cartridge capability policy,
- package registry protocol details,
- runtime reflection over attributes.
## 3. Core Terms
### 3.1 Project
A project is the unit described by `prometeu.json`.
Rules:
- A project has a `name`, `version`, `language`, `stdlib`, and `dependencies`.
- A project is the unit resolved by the workspace dependency graph.
- A project is the unit selected by a dependency declaration.
- A project is the unit published and versioned by ordinary dependency resolution.
- A project contains one or more modules.
### 3.2 Module
A module is the unit of source import inside a project.
Rules:
- Every `.pbs` source file belongs to exactly one module.
- Every module belongs to exactly one project.
- Imports select modules, not whole projects.
- A module is addressed by a project name plus a module path.
### 3.3 Canonical module address
The canonical PBS module address format is:
```text
@project:path/to/module
```
Rules:
- `project` identifies the owning project space.
- `path/to/module` identifies the module path inside that project.
- Module path segments are directory-style segments separated by `/`.
- The syntax is Java-like in organization and JavaScript-like in import surface.
Examples:
- `@game:player/state`
- `@physics:collision/shapes`
- `@sdk:gfx`
- `@core:math/vector`
### 3.4 Reserved project spaces
The following project spaces are reserved:
- `@sdk:*`
- `@core:*`
Rules:
- Reserved project spaces belong to the selected stdlib environment.
- Ordinary user-authored projects MUST NOT publish modules into reserved project spaces.
- Ordinary dependency resolution MUST NOT satisfy `@sdk:*` or `@core:*` imports from user projects.
- Reserved project spaces are resolved only from the selected stdlib environment.
## 4. `prometeu.json`
### 4.1 Required fields
The manifest format relevant to this document is:
```json
{
"name": "main",
"version": "1.0.0",
"language": "pbs",
"stdlib": "1",
"dependencies": [
{ "name": "dep1", "version": "1.0.0" }
]
}
```
Rules:
- `name` identifies the project.
- `version` identifies the project version.
- `language` identifies the frontend language.
- `stdlib` identifies the selected stdlib major line.
- `dependencies` declare ordinary project dependencies only.
### 4.2 `language`
Rules:
- `language` selects the compiler frontend.
- All projects in one resolved workspace MUST use the same `language`.
- Unknown `language` values are compile-time errors.
### 4.3 `stdlib`
Rules:
- `stdlib` is required in every manifest.
- `stdlib` uses major-only version syntax in v1.
- The manifest surface for `stdlib` is a string containing a positive decimal integer.
- Examples of valid values: `"1"`, `"2"`.
- Examples of invalid values: `"0"`, `"01"`, `"1.0"`, `"v1"`, `""`.
- Implementations SHOULD normalize `stdlib` internally to an integer major version.
Diagnostics:
- Missing `stdlib` is a manifest error.
- Invalid major-only format is a manifest error.
## 5. Ordinary Dependencies vs Stdlib Environment
### 5.1 Ordinary dependencies
Rules:
- `dependencies` model ordinary project relationships only.
- A dependency may be local or registry-based according to the compiler's dependency resolver.
- Dependencies contribute source projects, modules, and project-graph edges.
- `dependencies` do not declare stdlib projects.
### 5.2 Stdlib environment
Rules:
- The stdlib environment is not declared through `dependencies`.
- The stdlib environment is selected through `stdlib`.
- `@sdk:*` and `@core:*` imports resolve against the stdlib environment selected for the build.
- The presence of stdlib modules does not imply automatic visibility in source.
- User code MUST still import stdlib modules explicitly through ordinary import syntax.
Example:
```pbs
import { Gfx, color } from @sdk:gfx;
let c: color = color.red;
Gfx.draw_pixel(1, 1, c);
```
Rules:
- `Gfx` is not implicitly in scope.
- `Gfx` becomes visible only through `import`.
- The compiler accepts that import because the selected stdlib environment exposes `@sdk:gfx`.
## 6. Effective Stdlib of the Build
The root project owns the effective stdlib line of the build.
Rules:
- The resolved workspace has exactly one effective stdlib major version.
- The effective stdlib major version is taken from the root project manifest.
- All projects in the workspace compile under that same effective stdlib line.
- Dependency projects do not activate separate stdlib environments for themselves.
This means:
- `stdlib` on a dependency is a compatibility declaration,
- not an instruction to switch the compiler into another stdlib mode for that dependency.
## 7. Stdlib Compatibility Across Dependency Projects
Let:
- `R` be the root project's `stdlib` major,
- `D` be a dependency project's `stdlib` major.
Rules:
- If `D > R`, dependency resolution MUST fail with a deterministic compile-time error.
- If `D <= R`, dependency resolution MAY proceed.
- `D <= R` does not guarantee semantic compatibility of all APIs used by the dependency; it only means the dependency is not asking for a newer stdlib line than the root selected.
Rationale:
- the build target is chosen by the root project,
- dependency projects may target the same or an older stdlib line,
- dependency projects must not force the build to silently adopt a newer stdlib than the root requested.
Example:
- root `stdlib = "1"`, dependency `stdlib = "2"`: error.
- root `stdlib = "2"`, dependency `stdlib = "1"`: allowed.
- root `stdlib = "1"`, dependency `stdlib = "1"`: allowed.
## 8. Import Resolution
### 8.1 Resolution sources
The compiler resolves imports against exactly two sources:
1. the selected stdlib environment,
2. the resolved ordinary project dependency graph.
Rules:
- `@sdk:*` and `@core:*` are resolved only against the stdlib environment,
- all other `@project:*` imports are resolved only against the ordinary dependency graph,
- there is no fallback from reserved stdlib resolution to ordinary dependencies,
- there is no fallback from ordinary dependency resolution to reserved stdlib spaces.
### 8.2 Resolution algorithm
For an import:
```pbs
import { X } from @project:path/to/module;
```
the compiler MUST:
1. parse the project space and module path,
2. determine whether the project space is reserved,
3. if reserved, resolve the module from the selected stdlib environment,
4. otherwise, resolve the module from the root project or its dependency graph,
5. load the target module,
6. resolve the requested exported names from that module.
Failure at any step is a deterministic compile-time error.
## 9. Stdlib Interface Modules
### 9.1 Interface-module model
The selected stdlib environment MUST provide its modules as PBS-like interface modules.
Rules:
- Stdlib modules are `.pbs` modules loaded by the compiler.
- Stdlib interface modules use the same parser surface as ordinary PBS modules.
- Stdlib interface modules are interpreted in interface-module mode.
- Interface-module mode is non-executable and compile-time only.
- Interface modules participate in import resolution and type checking.
- Interface modules do not emit executable bytecode by themselves.
### 9.2 Allowed role
An interface module exists to provide:
- exported names,
- types,
- enums,
- host owners,
- method signatures,
- compile-time metadata such as canonical host-binding attributes.
An interface module is not runtime code.
### 9.3 Interface-module restrictions
At minimum, an interface module MUST remain declarative.
Rules:
- Interface modules MUST NOT contain top-level executable statements.
- Interface modules MUST NOT require runtime initialization.
- Interface modules MUST NOT introduce executable method bodies that would emit bytecode.
- `declare host` is permitted only in reserved stdlib/toolchain interface modules.
- Reserved attributes such as `[Host(...)]` are permitted only where another specification explicitly allows them.
### 9.4 Example interface module
Illustrative stdlib interface module:
```pbs
declare enum color(red, green, blue);
declare host Gfx {
[Host(module = "gfx", name = "draw_pixel", version = 1)]
fn draw_pixel(x: int, y: int, c: color);
}
```
Rules:
- This module is valid only in a reserved stdlib/interface-module context.
- It contributes compile-time symbols and metadata.
- It does not produce executable bytecode by itself.
## 10. Compiler Loading of the Stdlib Environment
The compiler loads the stdlib environment from the stdlib line selected by the root project.
Required model:
1. Read the root `prometeu.json`.
2. Determine the effective stdlib major line.
3. Mount or select the matching stdlib environment.
4. Expose its reserved project spaces such as `@sdk:*` and `@core:*`.
5. Load interface modules from that environment on demand during import resolution.
6. Parse those modules with the ordinary PBS parser.
7. Validate them under interface-module semantic rules.
8. Populate the compiler's symbol graph from their exported declarations and attributes.
Rules:
- The compiler MUST behave as if it were loading real modules, not hardcoded symbol tables.
- The physical storage format of the stdlib environment is an implementation detail.
- The compiler MAY load the stdlib environment from disk, embedded resources, a virtual filesystem, or another implementation-defined source with equivalent behavior.
- For the Studio PBS frontend, embedding stdlib interface modules under frontend `resources` is a valid implementation strategy.
## 11. Reserved Attributes and Metadata Lifecycle
### 11.1 General rule
Attributes are compile-time metadata, not runtime objects.
Rules:
- Attributes are parsed as part of the source module.
- Attributes participate in declaration validation and compiler metadata extraction.
- Attributes are not exported as ordinary values, types, or runtime-reflectable objects.
- Attributes do not automatically survive into bytecode, PBX, or runtime metadata.
### 11.2 `Host` attribute
The canonical reserved host-binding attribute is:
```pbs
[Host(module = "gfx", name = "draw_pixel", version = 1)]
```
Rules:
- `Host` is valid only on a host method signature declared directly inside a reserved stdlib/interface-module `declare host` body.
- `Host` provides the canonical host primitive identity used by compiler lowering.
- `module` and `name` are string literals.
- `version` is a positive integer literal.
- The attribute surface itself is compile-time only.
### 11.3 Lowering effect
Although the attribute syntax itself is compile-time only, its semantic effect may be lowered.
Rules:
- The compiler consumes `Host` metadata while resolving host-backed SDK members.
- The compiler uses that metadata to emit runtime-facing host-binding declarations as defined by the Host ABI Binding specification.
- The compiler does not export the raw `[Host(...)]` syntax into runtime as a source-level attribute object.
In other words:
- the attribute is compile-time metadata,
- the lowered host-binding declaration is runtime-facing artifact metadata,
- they are not the same thing.
## 12. Relationship to `declare host`
Rules:
- `declare host` remains reserved to SDK/toolchain-controlled modules.
- Ordinary user-authored project modules do not declare canonical host bindings directly.
- User code consumes SDK exports through normal imports.
- SDK-facing surfaces such as `Gfx.draw_pixel(...)` are resolved by the compiler against the selected stdlib environment.
- Canonical host identity comes from reserved host-binding metadata such as `[Host(...)]`, not from the source spelling of the imported owner.
## 13. Project Publication and Distribution
Rules:
- Ordinary publication and dependency resolution operate at project granularity.
- A published project version contains one or more modules.
- Imports consume modules from a resolved project.
- The stdlib environment is distributed separately from ordinary project dependencies.
- The stdlib environment is selected by `stdlib`, not by `dependencies`.
## 14. Non-Goals
- Making stdlib modules implicitly visible without `import`.
- Treating the stdlib as an ordinary dependency in the workspace graph.
- Allowing each dependency project to activate its own stdlib line.
- Hardcoding stdlib symbols directly into the compiler without a module model.
- Runtime reflection over source-level attributes.
## 15. Open Items Deferred
The following remain intentionally deferred:
1. Exact on-disk distribution format of stdlib environment packs.
2. Registry/publication protocol details for ordinary projects.
3. Final diagnostics catalog for all import-resolution failures.
4. Full list of declarations allowed inside interface modules beyond the already-fixed reserved host surfaces.
## 16. Current Decision Summary
The current PBS design direction is:
1. `prometeu.json` declares `language` and `stdlib` separately.
2. A project is the unit described by `prometeu.json` and selected by `dependencies`.
3. A module is the unit selected by `import`.
4. Canonical module addresses use `@project:path/to/module`.
5. `@sdk:*` and `@core:*` are reserved stdlib project spaces.
6. The root project selects the effective stdlib line for the whole build.
7. Dependency projects with a higher stdlib requirement than the root are rejected.
8. Stdlib modules are loaded as PBS-like interface modules, not as ordinary dependencies.
9. Interface modules are compile-time-only and do not emit executable bytecode.
10. `declare host` remains reserved to stdlib/toolchain interface modules.
11. Reserved attributes such as `[Host(...)]` are compile-time metadata, not runtime objects.
12. The compiler consumes `Host` metadata to produce runtime-facing host-binding artifacts defined elsewhere.

View File

@ -0,0 +1,426 @@
# Host ABI Binding and Loader Resolution Specification
Status: Draft v1 (Temporary)
Applies to: PBX host-binding metadata, cartridge load, canonical syscall resolution, and loader-side capability gating
## 1. Purpose
This document defines the current contract between:
- the PBS compiler/toolchain,
- the PBX program artifact,
- the cartridge loader,
- and the host syscall registry.
Its purpose is to let compiler and runtime work in parallel without diverging on the most critical boundary:
how host-backed SDK calls become load-time-resolved numeric syscalls.
This document is intentionally narrower than a full PBX or VM specification.
It focuses on loader behavior and the minimum artifact contract the loader must consume.
## 2. Scope
This document defines:
- the canonical identity of a host binding,
- the relationship between `declare host` and canonical host bindings,
- the minimum PBX metadata required for load-time host resolution,
- the pre-load opcode form used for host-backed call sites,
- the required loader algorithm,
- the required loader patching step from symbolic host calls to numeric syscalls,
- the division of responsibility between loader-side ABI checks and verifier-side stack checks,
- capability checks required during load,
- deterministic loader failures for malformed or unauthorized host usage,
- the requirement that VM execution remains numeric-only.
This document does not define:
- the full PBX module format,
- the exact in-memory patching strategy used by the loader,
- the publication format of stdlib SDK packs,
- the semantics of individual subsystems such as GFX or AUDIO.
## 3. Normative Inputs
This document follows two prior decisions:
1. Canonical host identity is defined by the hardware ABI as `(module, name, version)`.
2. User code does not declare host bindings directly; SDK/toolchain-controlled modules expose reserved `declare host` surfaces and the compiler lowers those usages into PBX host-binding metadata.
This document therefore treats:
- `declare host Gfx { fn draw_pixel(...); }`
as compile-time surface only, and treats:
- `("gfx", "draw_pixel", 1)`
as the normative runtime-facing identity.
## 4. Core Model
The host-binding pipeline is:
1. User code imports SDK surfaces such as `Gfx` from `@sdk:gfx`.
2. The compiler resolves those surfaces against the selected stdlib line.
3. The compiler maps each host-backed SDK member to a canonical identity `(module, name, version)`.
4. The compiler emits those required canonical identities into the PBX `SYSC` table.
5. The compiler emits pre-load host call sites as `HOSTCALL <sysc_index>`.
6. At cartridge load time, the loader resolves `SYSC` entries against the host syscall registry.
7. The loader verifies ABI compatibility and capabilities before execution starts.
8. The loader rewrites every `HOSTCALL <sysc_index>` into `SYSCALL <id>`.
9. The VM executes only numeric syscall identifiers.
The loader never resolves host bindings from source-level names such as `Gfx.draw_pixel`.
It resolves only canonical identities declared in the PBX.
## 5. Canonical Host Binding Identity
Every host binding is identified by the triple:
```text
(module, name, version)
```
Rules:
- `module` is the canonical host subsystem namespace, such as `gfx` or `audio`.
- `name` is the canonical host operation name within that module.
- `version` is the canonical ABI version of that host operation.
- This identity is stable across source languages and SDK naming styles.
- This identity is the only identity used by the loader for host resolution.
Source-level owners such as `Gfx` are SDK-facing aliases.
They are not loader-facing identities.
## 6. Relationship to `declare host`
`declare host` is a reserved SDK/toolchain surface used to expose host-backed APIs to source programs.
Rules:
- User-authored project modules do not declare canonical host bindings directly.
- `declare host` exists to define compile-time interface shape, not executable PBS bodies.
- The compiler is responsible for mapping each host-backed member to its canonical host identity.
- The PBX must not depend on source spellings such as `Gfx` for runtime resolution.
Example:
```pbs
import { Gfx, color } from @sdk:gfx;
let c: color = color.red;
Gfx.draw_pixel(1, 1, c);
```
The source-level call may originate from an SDK declaration such as:
```pbs
declare host Gfx {
[Host(module = "gfx", name = "draw_pixel", version = 1)]
fn draw_pixel(x: int, y: int, c: color);
}
```
but the PBX-facing declaration is canonical, for example:
```text
("gfx", "draw_pixel", 1)
```
## 7. PBX Host Binding Section
### 7.1 Temporary section contract
Until the full PBX host-binding format is stabilized elsewhere, the loader contract uses a dedicated PBX section for declared host bindings.
Temporary section identifier:
- `SYSC`
Meaning:
- the list of canonical host bindings required by the program.
This section is metadata only.
It does not define the executable opcode stream itself.
### 7.2 Temporary binary layout
The current temporary layout is:
```text
u32 count
repeat count times:
u16 module_len
[module_len bytes UTF-8]
u16 name_len
[name_len bytes UTF-8]
u16 version
u16 arg_slots
u16 ret_slots
```
Rules:
- strings are UTF-8,
- `module_len` and `name_len` are byte lengths,
- `arg_slots` is the slot count the compiler expects the host binding to consume,
- `ret_slots` is the slot count the compiler expects the host binding to produce,
- duplicate `(module, name, version)` entries are invalid,
- malformed lengths or invalid UTF-8 are load errors.
The `SYSC` table is:
- unique per program,
- deduplicated by canonical identity,
- ordered by first occurrence in the compiled program.
The loader MUST preserve the table order as emitted.
### 7.3 Presence requirements
The `SYSC` section is mandatory for every valid PBX artifact.
Rules:
- every valid `program.pbx` MUST contain a `SYSC` section,
- the loader MUST inspect `SYSC` before execution begins,
- an empty `SYSC` section is valid and means the program requires no host bindings,
- absence of `SYSC` is a deterministic load error,
- malformed or duplicate `SYSC` declarations are deterministic load errors.
Rationale:
- the Prometeu runtime is always host-aware,
- PBX structural uniformity is preferred over optional host-binding metadata,
- loader simplicity and deterministic testability take priority over omitting an empty section.
## 8. Loader Resolution Algorithm
The loader must perform host-binding resolution before VM execution starts.
Required algorithm:
1. Read the PBX header and section table.
2. Locate and parse the `SYSC` section before launching the VM.
3. Decode the declared canonical identities in source order.
4. Reject duplicate identities.
5. Resolve each identity against the host syscall registry.
6. For each resolved entry, verify that declared `arg_slots` and `ret_slots` match the authoritative host metadata.
7. Verify that the granted capability set covers every resolved binding.
8. Scan the executable bytecode for every `HOSTCALL <sysc_index>` instruction.
9. Reject any `HOSTCALL` whose `sysc_index` is out of bounds.
10. Reject any `SYSC` entry that is never referenced by a `HOSTCALL`.
11. Rewrite every `HOSTCALL <sysc_index>` into `SYSCALL <syscall_id>`.
12. Reject the loaded image if any `HOSTCALL` remains after the rewrite pass.
13. Produce the final executable program image.
14. Abort load deterministically if any step fails.
The loader must not defer canonical-name resolution until the first call site executes.
## 9. Host Registry Resolution
The host maintains a canonical syscall registry:
```text
(module, name, version) -> syscall_id + metadata
```
That metadata includes, at minimum:
- numeric syscall identifier,
- canonical identity,
- argument slot count,
- return slot count,
- required capabilities.
Rules:
- the loader resolves only against this canonical registry,
- unknown identities are load errors,
- successful resolution yields numeric syscall identifiers and associated metadata,
- the resolved metadata is authoritative for capability gating and later runtime verification.
## 10. Pre-Load Callsite Form
The compiler emits host-backed call sites in pre-load form as:
```text
HOSTCALL <sysc_index>
```
Rules:
- `sysc_index` is a zero-based index into the program's `SYSC` table,
- every host-backed call site in PBX code must use `HOSTCALL`, not `SYSCALL`,
- `HOSTCALL` exists only in the pre-load artifact form,
- `HOSTCALL` must not remain in the final executable image passed to the VM.
Rationale:
- the compiler does not know final numeric syscall identifiers,
- the loader resolves by canonical identity and materializes the numeric form,
- the verifier and VM can stay numeric-only after the patch step.
## 11. Loader-Side ABI Validation
Load-time ABI validation is mandatory.
Rules:
- every `SYSC` entry declares `arg_slots` and `ret_slots`,
- the loader resolves the entry to authoritative host metadata,
- the loader MUST reject the image if declared `arg_slots` differs from host metadata,
- the loader MUST reject the image if declared `ret_slots` differs from host metadata.
This validation is not a replacement for bytecode verification.
It exists to detect mismatches between:
- the compiler's emitted host-binding contract,
- and the host registry contract actually present in the runtime.
## 12. Capability Gating
Capability gating is mandatory during load.
Rules:
- every resolved host binding declares required capabilities,
- the cartridge manifest declares requested capabilities using a nominal capability list,
- the loader derives or receives the granted capability set from the cartridge/platform policy environment,
- if a required capability is not granted, load fails,
- the loader must fail before the program begins execution.
The compiler may infer which capabilities are required by the emitted `SYSC` table, but it does not grant authority.
Authority belongs to the cartridge/platform loading environment.
Runtime capability checks may still exist defensively in the VM, but they do not replace mandatory load-time validation.
## 13. Verifier Interaction
The loader and the verifier validate different layers of the contract.
Loader responsibilities:
- validate canonical host identity resolution,
- validate `SYSC`-declared ABI shape against host metadata,
- validate capabilities,
- patch `HOSTCALL <sysc_index>` into `SYSCALL <id>`.
Verifier responsibilities:
- run on the already-patched executable image,
- validate stack safety and control-flow safety,
- treat `SYSCALL <id>` exactly like any other final numeric instruction,
- derive stack effects from authoritative runtime syscall metadata.
This is intentional and not considered harmful duplication:
- the loader checks interface compatibility,
- the verifier checks structural correctness of the final program.
## 14. Execution Model After Load
After successful load:
- the VM executes numeric syscall identifiers only,
- runtime dispatch must not depend on canonical strings,
- canonical names exist only in PBX metadata and loader-side resolution,
- the runtime-facing execution form remains `SYSCALL <id>`.
This preserves the hardware contract:
- canonical identity is used for declaration and linking,
- numeric identity is used for execution.
## 15. Deterministic Loader Errors
The loader must fail deterministically for at least the following cases:
1. missing `SYSC` section,
2. malformed `SYSC` payload,
3. invalid UTF-8 in canonical names,
4. duplicate `(module, name, version)` declarations,
5. unknown canonical identity,
6. ABI mismatch between `SYSC` declaration and host registry metadata,
7. capability mismatch,
8. `HOSTCALL` references an out-of-bounds `sysc_index`,
9. a declared `SYSC` entry is unused by all `HOSTCALL` instructions,
10. a `HOSTCALL` remains in the executable image after loader patching,
11. any internal inconsistency between parsed host-binding metadata and the loader's host registry contract.
Diagnostics should identify the failing canonical identity whenever available.
## 16. Relationship to `stdlib`
`stdlib` selection is a compile-time concern that determines which SDK surfaces and host-backed declarations the compiler may use.
Rules:
- the loader does not resolve source imports such as `@sdk:gfx`,
- the loader only consumes canonical host-binding metadata emitted into the PBX,
- `stdlib` affects the loader indirectly through what the compiler emitted,
- the loader must not attempt to reconstruct SDK/module semantics from the PBX.
In other words:
- `stdlib` selects the compile-time SDK contract,
- `SYSC` carries the runtime-facing host-binding contract.
## 17. Relationship to Cartridge Manifest Capabilities
The cartridge manifest is the current declarative source for requested runtime capabilities.
Rules:
- cartridge capability declarations are external to `program.pbx`,
- capability declarations are serializable nominal identifiers such as `gfx` or `audio`,
- the loader converts that nominal declaration into the internal capability flag set used by the runtime,
- the loader MUST NOT infer granted authority solely from the presence of `SYSC` entries.
This keeps authority separate from code generation:
- PBX declares what the program needs,
- cartridge packaging declares what the cartridge requests,
- the loader/runtime environment decides what is granted.
## 18. Current Implementation Direction
This document aligns with the current Prometeu direction:
- SDK/toolchain-controlled `declare host` surfaces define host-backed APIs,
- the compiler emits canonical host-binding declarations into PBX `SYSC`,
- the compiler emits `HOSTCALL <sysc_index>` in pre-load code,
- the loader resolves those declarations at cartridge load,
- the loader patch-rewrites host calls to numeric syscalls,
- the VM continues to execute numeric syscalls only.
## 19. Open Items Deferred
The following remain deferred:
1. final PBX section numbering and chunk registry policy,
2. exact binary opcode allocation for `HOSTCALL`,
3. whether the loader patches bytecode in place or rebuilds a fresh executable image buffer,
4. final cartridge policy model when platform-level grants differ from manifest requests,
5. final chunk numbering and binary compatibility policy for PBX versions that predate mandatory `SYSC`,
6. final integration shape with `ProgramImage` or another loaded-program container.
## 20. Current Decision Summary
The current temporary contract is:
1. Host bindings are identified canonically by `(module, name, version)`.
2. `declare host` is reserved to SDK/toolchain modules and is compile-time surface only.
3. The compiler emits a unique, deduplicated `SYSC` table ordered by first occurrence.
4. Each `SYSC` entry carries `module`, `name`, `version`, `arg_slots`, and `ret_slots`.
5. The compiler emits host-backed call sites as `HOSTCALL <sysc_index>`.
6. The loader parses `SYSC` before execution.
7. The loader resolves canonical identities against the host registry during load.
8. The loader validates `arg_slots` and `ret_slots` against authoritative host metadata.
9. The loader enforces capability gating during load.
10. The loader rejects out-of-bounds and unused `SYSC` references.
11. The loader rewrites every `HOSTCALL <sysc_index>` into `SYSCALL <id>`.
12. The verifier runs after patching and validates the final numeric program.
13. The VM executes numeric syscall identifiers only.

View File

@ -0,0 +1,246 @@
# Cartridge Manifest and Runtime Capabilities Specification
Status: Draft v1 (Temporary)
Applies to: `manifest.json` in Prometeu cartridges, runtime capability declaration, and loader capability input
## 1. Purpose
This document defines the current cartridge manifest contract relevant to runtime loading.
Its purpose is to stabilize:
- the JSON surface consumed by the cartridge loader,
- the declaration of cartridge-requested runtime capabilities,
- the boundary between cartridge packaging and loader authority,
- the conversion from manifest-facing capability names to runtime capability flags.
This document is intentionally temporary and limited.
It does not define cartridge publishing, signing, or store policy.
## 2. Scope
This document defines:
- the role of `manifest.json` in cartridge loading,
- the current required manifest fields,
- the new `capabilities` field,
- the nominal capability enumeration used in JSON,
- the mapping from nominal capabilities to runtime `CapFlags`,
- the role of the loader when combining manifest capabilities with PBX `SYSC`.
This document does not define:
- the full package format beyond the current directory cartridge contract,
- policy UI or permission prompts,
- cartridge signing or trust chains,
- final platform grant policy when the host wants to reduce requested capabilities.
## 3. Current Cartridge Manifest Role
The cartridge manifest is an external load-time descriptor for a cartridge.
It is not compiled from PBS source directly.
It is produced by cartridge packaging tooling and consumed by the runtime loader.
The manifest currently carries:
- cartridge identity and metadata,
- app entry information,
- asset metadata,
- preload metadata.
This document adds runtime capability declaration to that same manifest.
## 4. Manifest Shape
The current manifest shape relevant to this document is:
```json
{
"magic": "PMTU",
"cartridge_version": 1,
"app_id": 1001,
"title": "Example",
"app_version": "1.0.0",
"app_mode": "game",
"entrypoint": "main",
"capabilities": ["gfx", "input"],
"asset_table": [],
"preload": []
}
```
Rules:
- `magic` identifies a Prometeu cartridge manifest,
- `cartridge_version` identifies the cartridge manifest format line,
- `entrypoint` identifies the runtime entrypoint,
- `capabilities` declares the cartridge's requested runtime capabilities,
- `asset_table` and `preload` remain separate concerns.
## 5. `capabilities`
### 5.1 General rules
`capabilities` is a manifest-facing nominal list.
Rules:
- `capabilities` is optional,
- when omitted, it behaves as an empty list,
- capability names are canonical lowercase identifiers,
- duplicate entries are invalid,
- unknown capability names are manifest errors.
### 5.2 Nominal capability set
The current nominal capability set is:
- `system`
- `gfx`
- `input`
- `audio`
- `fs`
- `log`
- `asset`
- `bank`
These names are the normative manifest-facing representation.
## 6. Relationship to Runtime `CapFlags`
The runtime may continue to represent capabilities internally as a bitflag set.
That internal representation is an implementation detail.
The normative external model is:
- a nominal capability enum in the manifest,
- deterministic conversion by the loader into runtime flags.
Canonical mapping:
- `system` -> `SYSTEM`
- `gfx` -> `GFX`
- `input` -> `INPUT`
- `audio` -> `AUDIO`
- `fs` -> `FS`
- `log` -> `LOG`
- `asset` -> `ASSET`
- `bank` -> `BANK`
This means the recommended runtime design is:
1. deserialize `capabilities` into a nominal enum or equivalent validated representation,
2. convert that validated set into internal `CapFlags`,
3. use those flags during host-binding resolution.
## 7. Loader Semantics
The loader consumes both:
- the cartridge manifest,
- and the PBX `SYSC` table.
Required behavior:
1. parse `manifest.json`,
2. parse `capabilities`,
3. convert the nominal capability list to internal capability flags,
4. parse `program.pbx`,
5. resolve each `SYSC` entry against the host registry,
6. obtain required capabilities from resolved syscall metadata,
7. fail if required capabilities are not granted by the manifest/policy environment.
The manifest alone does not make a program valid.
The manifest must be consistent with the actual syscalls required by the PBX.
## 8. Requested vs Granted Authority
The manifest declares requested cartridge capabilities.
The loader/runtime environment is the authority that decides what is granted.
Current direction:
- in the initial runtime model, the manifest may act as the immediate source of granted capabilities,
- a future platform policy layer may reduce or deny requested capabilities before final load.
Normative rule:
- the compiler does not grant capabilities,
- the PBX does not grant capabilities,
- cartridge/runtime loading is the authority boundary.
## 9. Relationship to Compiler and Packer
Responsibilities are split as follows:
Compiler:
- compiles source to PBX,
- emits `SYSC`,
- emits `HOSTCALL <sysc_index>`,
- does not grant authority.
Packer:
- produces the cartridge manifest,
- declares requested runtime capabilities,
- packages `manifest.json`, `program.pbx`, and assets.
Loader:
- reads the manifest,
- derives granted capability flags,
- validates those capabilities against PBX host requirements.
## 10. Validation Rules
The cartridge manifest must fail validation if:
1. `capabilities` contains an unknown name,
2. `capabilities` contains duplicates,
3. `capabilities` is not an array of strings or nominal enum values under the chosen serialization form.
Cartridge load must fail if:
1. the manifest omits a capability required by a resolved `SYSC` entry,
2. a future platform policy denies a capability required by a resolved `SYSC` entry.
## 11. Serialization Guidance
The recommended manifest-facing model is a nominal enum list, not numeric masks.
Recommended JSON:
```json
{
"capabilities": ["gfx", "input", "audio"]
}
```
Rejected as normative surface:
- raw integer bitmasks,
- hex masks,
- unnamed positional arrays.
Rationale:
- nominal capability names are readable,
- manifest diffs remain understandable,
- packer and tooling validation is straightforward,
- runtime may still use bitflags internally after conversion.
## 12. Current Decision Summary
The current temporary contract is:
1. `manifest.json` is the cartridge-facing load descriptor.
2. `capabilities` is declared in `manifest.json`, not in `prometeu.json`.
3. `capabilities` uses nominal canonical names such as `gfx` and `audio`.
4. The runtime may keep using internal `CapFlags`.
5. The loader converts nominal manifest capabilities into internal flags.
6. PBX `SYSC` declares required host bindings, not authority.
7. Loader validation fails if PBX host requirements exceed granted cartridge capabilities.

View File

@ -0,0 +1,251 @@
# PBS Stdlib Environment Packaging and Loading Specification
Status: Draft v1 (Temporary)
Applies to: Studio PBS frontend stdlib packaging, stdlib environment discovery, reserved stdlib project spaces, and compiler-side stdlib loading abstractions
## 1. Purpose
This document defines a practical packaging and loading model for the PBS stdlib environment inside the Studio compiler frontend.
Its purpose is to make the stdlib:
- available to the compiler without hardcoded symbol tables,
- structured as real PBS interface modules,
- versioned by stdlib major line,
- and loadable through explicit compiler abstractions.
This document complements the normative language-level model in:
- [5. Manifest, Stdlib, and SDK Resolution Specification.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/studio/docs/specs/pbs/5.%20Manifest,%20Stdlib,%20and%20SDK%20Resolution%20Specification.md)
## 2. Scope
This document defines:
- the recommended Studio packaging layout for stdlib interface modules,
- the logical mapping between stdlib line, reserved project space, and module path,
- the minimum compiler abstractions required to load stdlib modules,
- the relationship between `resources` packaging and the logical stdlib environment.
This document does not define:
- runtime loader behavior,
- PBX emission,
- registry publication of stdlib packs,
- final external distribution of stdlib outside Studio.
## 3. Core Model
The compiler must load stdlib modules as real interface modules, not as hardcoded built-ins.
Rules:
- the stdlib is selected by the root project's `stdlib` major,
- each stdlib line defines one stdlib environment,
- a stdlib environment exposes reserved project spaces such as `sdk` and `core`,
- each reserved project space contains one or more modules,
- each module is represented as PBS source plus `mod.barrel`.
## 4. Recommended Studio Packaging Layout
The recommended Studio layout is:
```text
prometeu-compiler/frontends/prometeu-frontend-pbs/
src/main/resources/
stdlib/
1/
sdk/
gfx/
main.pbs
mod.barrel
audio/
main.pbs
mod.barrel
core/
math/
main.pbs
mod.barrel
```
Interpretation:
- `stdlib/1` selects stdlib major line `1`,
- `sdk` and `core` are reserved project spaces,
- `gfx`, `audio`, `math` are module paths within those reserved spaces,
- `main.pbs` and `mod.barrel` form the interface module contents.
## 5. Logical Mapping
The compiler must treat the physical layout as a logical stdlib environment.
Required mapping:
- `stdlib/<N>/sdk/gfx` -> `@sdk:gfx`
- `stdlib/<N>/sdk/audio` -> `@sdk:audio`
- `stdlib/<N>/core/math` -> `@core:math`
- `stdlib/<N>/core/math/vector` -> `@core:math/vector`
Rules:
- physical storage is an implementation detail,
- logical module identity is authoritative,
- callers of the resolver should work with logical addresses such as `@sdk:gfx`, not resource paths.
## 6. Module File Convention
For the Studio implementation, each stdlib module should follow the same structural convention as ordinary PBS modules:
- one or more `.pbs` files,
- exactly one `mod.barrel`.
Recommended minimum convention:
- `main.pbs`
- `mod.barrel`
Rules:
- `main.pbs` is a convention, not a language-level requirement,
- the module remains an ordinary PBS module from the parser's perspective,
- it is interpreted under interface-module semantics because it lives in a reserved stdlib project space.
## 7. Required Compiler Abstractions
The compiler should not reach directly into `resources` paths throughout the frontend.
At minimum, the PBS frontend should define abstractions equivalent to:
### 7.1 `StdlibEnvironment`
Responsibility:
- represent the selected stdlib line for one compilation,
- expose reserved project spaces,
- provide access to interface modules by logical module address.
Minimum operations:
- select stdlib major line,
- answer whether a reserved project space exists,
- open a module by logical address.
### 7.2 `StdlibModuleResolver`
Responsibility:
- resolve logical imports in reserved project spaces,
- map `@sdk:*` and `@core:*` imports to concrete interface module sources,
- reject unknown stdlib modules deterministically.
Minimum operations:
- `resolve(@sdk:gfx)`
- `resolve(@core:math/vector)`
### 7.3 `StdlibModuleSource`
Responsibility:
- represent the source files of one stdlib module,
- provide access to the module's `.pbs` files and `mod.barrel`,
- hide whether content came from disk, `resources`, or another source.
Minimum contents:
- module address
- list of `.pbs` source texts or streams
- `mod.barrel` source text or stream
### 7.4 `InterfaceModuleLoader`
Responsibility:
- parse a stdlib module source using the normal PBS parser,
- validate it in interface-module mode,
- return compiler-facing symbols and metadata.
Minimum outputs:
- exported symbols
- declared types
- host owners
- host method signatures
- reserved attribute metadata such as `[Host(...)]`
## 8. Loading Algorithm
The recommended Studio compiler flow is:
1. read the root `prometeu.json`,
2. parse and normalize `stdlib`,
3. construct `StdlibEnvironment` for that stdlib major,
4. create a `StdlibModuleResolver`,
5. when an import targets a reserved project space, resolve it through the stdlib resolver,
6. obtain a `StdlibModuleSource`,
7. parse the module with the standard PBS parser,
8. validate it under interface-module semantic rules,
9. cache the resulting interface module graph for the duration of the compilation.
Rules:
- reserved stdlib modules must not be handled through ordinary dependency resolution,
- ordinary dependency modules must not be handled through the stdlib resolver,
- a failed stdlib import is a deterministic compile-time error.
## 9. Attributes and Interface Metadata
Stdlib interface modules may contain reserved compile-time attributes such as:
```pbs
declare host Gfx {
[Host(module = "gfx", name = "draw_pixel", version = 1)]
fn draw_pixel(x: int, y: int, c: color);
}
```
Rules:
- the parser reads the attribute as part of the interface module source,
- the interface-module loader validates that the attribute is legal in that position,
- the compiler stores the extracted metadata in its interface graph,
- the raw attribute surface is not treated as a runtime object,
- later lowering stages may consume the extracted metadata to produce PBX host-binding declarations.
## 10. Resources as an Implementation Strategy
For the Studio PBS frontend, packaging stdlib interface modules in frontend `resources` is a valid implementation strategy.
Rules:
- `resources` packaging is implementation-level, not language-level,
- the compiler should still access stdlib modules through `StdlibEnvironment` and related abstractions,
- code outside the stdlib loader should not depend on resource path layout directly.
Rationale:
- `resources` gives a simple bootstrap path,
- stdlib evolves as real source artifacts,
- the frontend avoids hardcoded built-ins,
- the implementation remains replaceable later.
## 11. Caching and Reuse
The compiler may cache stdlib interface modules within one compilation or across compilations.
Rules:
- caching must not change observable semantics,
- cache keys should include at least the stdlib major line and logical module address,
- cached modules must preserve the same parsed declarations and attribute metadata as uncached loads.
## 12. Current Decision Summary
The current Studio direction is:
1. the stdlib environment is loaded as real PBS interface modules,
2. the initial implementation may live under frontend `resources`,
3. the logical model remains `stdlib line -> reserved project space -> module path`,
4. the compiler should use explicit abstractions such as `StdlibEnvironment` and `StdlibModuleResolver`,
5. stdlib interface modules are parsed with the normal PBS parser and validated in interface-module mode,
6. reserved attributes such as `[Host(...)]` are extracted as compile-time metadata for later lowering.

View File

@ -5,17 +5,23 @@ Purpose: checklist curto para decidir se a spec da PBS pode ser aprovada para se
## Blockers
- [ ] Fechar o gate de prontidao para implementacao definido no charter.
Hoje o charter exige specs adicionais antes de iniciar implementacao: dynamic semantics, memory and lifetime, host ABI binding, module/package, diagnostics, IR/lowering e conformance.
- [X] Fechar o gate de prontidao para implementacao definido no charter para o escopo atual.
Decisao: a aprovacao atual cobre apenas implementacao faseada de frontend (`parser`, `AST` e `binding` inicial).
Isso fecha o gate apenas para esse escopo limitado e nao reclassifica a PBS como pronta para implementacao completa.
Dynamic semantics, memory and lifetime, diagnostics, IR/lowering e conformance permanecem como trabalho aberto para fases posteriores.
Referencia: `1. Language Charter.md`, secao "Required Spec Set Before Implementation".
- [ ] Definir a identidade normativa de `host fn`.
A governanca exige versionamento por `(module, name, version)`, mas a sintaxe e a semantica atual tratam `host fn` apenas por nome e assinatura.
Isso precisa de contrato claro de binding, capability gating e linking.
Referencias: `2. Governance and Versioning.md`, `3. Core Syntax Specification.md`, `4. Static Semantics Specification.md`.
- [X] Substituir o antigo modelo publico de `host fn` por `declare host` reservado a SDK/toolchain.
Resolvido no nivel de charter, sintaxe e semantica estatica: bindings canonicos de host nao sao authorados pelo usuario comum, e superficies SDK usam `declare host` reservado.
O contrato de PBX/load/runtime continua pendente para a futura Host ABI Binding specification.
Referencias: `1. Language Charter.md`, `2. Governance and Versioning.md`, `3. Core Syntax Specification.md`, `4. Static Semantics Specification.md`, `5. Manifest, Stdlib, and SDK Resolution Specification.md`.
- [ ] Fechar o modelo semantico de `service` ou remover `service` do core v1.
A gramatica aceita `service Identifier (':' Identifier)?`, mas a spec nao define o significado do `: Identifier`, o ciclo de vida do servico, como ele e resolvido nem como participa do runtime.
- [X] Fechar a spec normativa de manifest/module/package/stdlib.
Resolvido com o modelo normativo de `project` e `module`, enderecamento canonico `@project:path/to/module`, `stdlib` como ambiente efetivo do build, `@sdk:*` e `@core:*` como project spaces reservados, interface modules PBS-like e atributos reservados de compile time como `[Host(...)]`.
Referencias: `5. Manifest, Stdlib, and SDK Resolution Specification.md`, `6. Host ABI Binding and Loader Resolution Specification.md`.
- [X] Fechar o modelo semantico de `service` no core v1.
Resolvido com `declare service` como singleton nominal por modulo, exportado via barrel, com metodos sem visibilidade independente e suporte a `implements Contract for Service using s`.
Referencias: `3. Core Syntax Specification.md`, secao de `ServiceDecl`; `4. Static Semantics Specification.md`, callable categories.
- [X] Remover do core v1 ou especificar completamente as superficies que ja existem na gramatica mas ainda nao tem contrato suficiente.
@ -33,10 +39,27 @@ Purpose: checklist curto para decidir se a spec da PBS pode ser aprovada para se
- [x] Consolidar exemplos canonicos para overload por retorno, `optional`, `result`, `bind`, `apply` e `switch`.
A base conceitual esta boa; o ajuste aqui e editorial e de conformance, nao arquitetural.
- [ ] Decidir se a implementacao pode comecar em modo faseado.
Recomendacao: permitir apenas parser/AST/binding inicial enquanto os blockers acima nao forem fechados.
- [X] Decidir se a implementacao pode comecar em modo faseado.
Decisao tomada: permitir apenas parser/AST/binding inicial nesta etapa.
Nao liberar ainda lowering, linker, host integration ou runtime behavior final.
## Pautas Dedicadas
- [ ] Abrir pauta dedicada para dynamic semantics + memory and lifetime.
Objetivo: fechar o modelo de execucao observavel, ordem de avaliacao, traps, efeitos sobre `optional`/`result`/`apply`/`bind`/`switch`, baseline GC, fronteira heap VM vs memoria do host e visibilidade de custo/alocacao.
Resultado esperado: material para futura `Dynamic Semantics Specification` e `Memory and Lifetime Specification`.
Referencia: `Heap Model - Discussion Agenda.md`.
- [ ] Abrir pauta dedicada para validar se a `Host ABI Binding and Loader Resolution Specification` atual ja satisfaz o gate da fase seguinte ou se ainda precisa deixar de ser "Temporary".
Objetivo: confirmar se o contrato atual de `declare host` -> metadata PBX -> loader -> syscall numerico ja esta suficientemente estavel para nao bloquear a etapa posterior.
Resultado esperado: decisao explicita de "suficiente para a proxima fase" ou lista curta de endurecimentos pendentes.
Referencia: `6. Host ABI Binding and Loader Resolution Specification.md`.
- [ ] FUTURO: abrir pauta de backend.
Objetivo: tratar IR/lowering, linker, host integration, runtime behavior final e conformance somente depois de fechar o frontend e aparar as arestas da linguagem/spec.
Resultado esperado: backlog da etapa de backend com precondicoes claras.
Esta pauta nao bloqueia o frontend faseado atual.
## Decision Rule
Pode aprovar a PBS para seguir agora somente se a aprovacao significar:

View File

@ -9,6 +9,7 @@ public record PrometeuManifestDTO(
String name,
String version,
String language,
String stdlib,
List<DependencyDeclaration> dependencies) {
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@ -32,4 +33,4 @@ public record PrometeuManifestDTO(
}
}
}
}
}

View File

@ -22,6 +22,7 @@ public final class DependencyContext {
public Path mainProjectRootPathCanon;
public FrontendSpec frontendSpec;
public Integer rootStdlib;
public ProjectId rootProjectId;
public BuildStack stack;
@ -45,9 +46,12 @@ public final class DependencyContext {
if (rootProjectId == null) {
throw new BuildException("dependenciesByProjectId: internal error: rootProjectId ProjectId not set");
}
if (rootStdlib == null) {
throw new BuildException("dependenciesByProjectId: internal error: rootStdlibMajor not set");
}
final var dependenciesByProject = buildDependenciesByProject();
final var workspaceGraph = new WorkspaceGraph(projectTable, dependenciesByProject);
return new ResolvedWorkspace(rootProjectId, frontendSpec, workspaceGraph, stack);
return new ResolvedWorkspace(rootProjectId, frontendSpec, rootStdlib, workspaceGraph, stack);
}

View File

@ -7,5 +7,6 @@ public record PrometeuManifest(
String name,
String version,
String language,
int stdlib,
ReadOnlyList<DependencyReference> dependencies) {
}

View File

@ -5,6 +5,7 @@ import p.studio.compiler.source.identifiers.ProjectId;
public record ResolvedWorkspace(
ProjectId mainProjectId,
FrontendSpec frontendSpec,
int stdlibMajor,
WorkspaceGraph graph,
BuildStack stack) {
public ProjectDescriptor mainProject() {

View File

@ -14,6 +14,7 @@ import p.studio.utilities.structures.ReadOnlyList;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.OptionalInt;
@UtilityClass
public final class PrometeuManifestUtils {
@ -44,6 +45,7 @@ public final class PrometeuManifestUtils {
.error(true)
.message("[DEPS]: manifest missing 'version': " + manifestPathCanon));
}
final var stdlibMajorMaybe = parseStdlibMajor(dto.stdlib(), manifestPathCanon, issuesLocal);
final var language = StringUtils.isBlank(dto.language())
? FrontendRegistryService.getDefaultFrontendSpec().getLanguageId()
: dto.language();
@ -52,6 +54,34 @@ public final class PrometeuManifestUtils {
issues.merge(issuesLocal);
return Optional.empty();
}
return Optional.of(new PrometeuManifest(dto.name(), dto.version(), language, ReadOnlyList.wrap(dependencies)));
return Optional.of(new PrometeuManifest(dto.name(), dto.version(), language, stdlibMajorMaybe.getAsInt(), ReadOnlyList.wrap(dependencies)));
}
private static OptionalInt parseStdlibMajor(
final String stdlib,
final Path manifestPathCanon,
final BuildingIssueSink issues) {
if (StringUtils.isBlank(stdlib)) {
issues.report(builder -> builder
.error(true)
.message("[DEPS]: manifest missing 'stdlib': " + manifestPathCanon));
return OptionalInt.empty();
}
final int stdlibMajor;
try {
stdlibMajor = Integer.parseInt(stdlib);
} catch (NumberFormatException e) {
issues.report(builder -> builder
.error(true)
.message("[DEPS]: invalid 'stdlib' major version \"" + stdlib + "\": " + manifestPathCanon));
return OptionalInt.empty();
}
if (stdlibMajor <= 0 || !stdlib.equals(Integer.toString(stdlibMajor))) {
issues.report(builder -> builder
.error(true)
.message("[DEPS]: invalid 'stdlib' major version \"" + stdlib + "\": " + manifestPathCanon));
return OptionalInt.empty();
}
return OptionalInt.of(stdlibMajor);
}
}

View File

@ -51,6 +51,9 @@ public class DiscoverPhase implements DependencyPhase {
if (manifest.isEmpty()) {
return Optional.empty();
}
if (rootPathCanon.equals(ctx.mainProjectRootPathCanon)) {
ctx.rootStdlib = manifest.get().stdlib();
}
final var frontendSpec = FrontendRegistryService.getFrontendSpec(manifest.get().language());
// Returns empty when language is unknown
if (frontendSpec.isEmpty()) {

View File

@ -14,6 +14,12 @@ public final class ValidatePhase implements DependencyPhase {
.error(true)
.message("[DEPS]: rootProjectId ProjectId not set"));
}
if (ctx.rootStdlib == null) {
return BuildingIssueSink.empty()
.report(builder -> builder
.error(true)
.message("[DEPS]: root stdlib major not set"));
}
// ensure the dependenciesByProject matches the number of projectDescriptors
if (ctx.dependenciesByProject.size() != ctx.projectTable.size()) {
@ -23,18 +29,36 @@ public final class ValidatePhase implements DependencyPhase {
.message("[DEPS]: internal error: dependenciesByProject and projectDescriptors size mismatch"));
}
final BuildingIssueSink depIssues = BuildingIssueSink.empty();
// ensure compatibility across stdlibs
for (final var projectInfo : ctx.projectInfoTable.values()) {
if (projectInfo.manifest.stdlib() > ctx.rootStdlib) {
depIssues.report(builder -> builder
.error(true)
.message(String.format(
"[DEPS]: incompatible stdlib: [%s] requires stdlib \"%d\" but root project uses stdlib \"%d\"",
projectInfo.manifest.name(),
projectInfo.manifest.stdlib(),
ctx.rootStdlib)));
}
}
// ensure uniformity to version across the same projectIds (associated by name)
for (final var entry : ctx.projectNameAndVersions.entrySet()) {
final var name = entry.getKey();
final var versions = entry.getValue();
if (versions.size() > 1) {
return BuildingIssueSink.empty()
.report(builder -> builder
.error(true)
.message("[DEPS]: inconsistent version for project: " + name + " (" + versions + ")"));
depIssues.report(builder -> builder
.error(true)
.message("[DEPS]: inconsistent version for project: " + name + " (" + versions + ")"));
}
}
if (!depIssues.isEmpty()) {
return depIssues;
}
// run check over source policy (if any) from here (FrontedSpec)
return BuildingIssueSink.empty();

View File

@ -0,0 +1,82 @@
package p.studio.compiler.utilities;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.studio.compiler.messages.BuildingIssueSink;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.*;
class PrometeuManifestUtilsTest {
@TempDir
Path tempDir;
@Test
void buildManifestParsesStdlibMajor() throws IOException {
final var manifestPath = writeManifest("""
{
"name": "main",
"version": "1.0.0",
"language": "pbs",
"stdlib": "1",
"dependencies": []
}
""");
final var issues = BuildingIssueSink.empty();
final var manifest = PrometeuManifestUtils.buildManifest(tempDir, manifestPath, issues);
assertTrue(manifest.isPresent());
assertFalse(issues.hasErrors());
assertEquals(1, manifest.get().stdlib());
}
@Test
void buildManifestRejectsMissingStdlib() throws IOException {
final var manifestPath = writeManifest("""
{
"name": "main",
"version": "1.0.0",
"language": "pbs",
"dependencies": []
}
""");
final var issues = BuildingIssueSink.empty();
final var manifest = PrometeuManifestUtils.buildManifest(tempDir, manifestPath, issues);
assertTrue(manifest.isEmpty());
assertTrue(issues.hasErrors());
assertTrue(issues.asCollection().stream().anyMatch(issue -> issue.getMessage().contains("missing 'stdlib'")));
}
@Test
void buildManifestRejectsNonMajorStdlib() throws IOException {
final var manifestPath = writeManifest("""
{
"name": "main",
"version": "1.0.0",
"language": "pbs",
"stdlib": "1.0",
"dependencies": []
}
""");
final var issues = BuildingIssueSink.empty();
final var manifest = PrometeuManifestUtils.buildManifest(tempDir, manifestPath, issues);
assertTrue(manifest.isEmpty());
assertTrue(issues.hasErrors());
assertTrue(issues.asCollection().stream().anyMatch(issue -> issue.getMessage().contains("invalid 'stdlib'")));
}
private Path writeManifest(final String content) throws IOException {
final var manifestPath = tempDir.resolve("prometeu.json");
Files.writeString(manifestPath, content);
return manifestPath;
}
}

View File

@ -0,0 +1,106 @@
package p.studio.compiler.workspaces.phases;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.studio.compiler.messages.DependencyConfig;
import p.studio.compiler.models.DependencyContext;
import p.studio.compiler.models.ProjectDescriptor;
import p.studio.compiler.models.ProjectInfo;
import p.studio.compiler.models.PrometeuManifest;
import p.studio.utilities.structures.ReadOnlyList;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class ValidatePhaseTest {
@TempDir
Path tempDir;
@Test
void rejectsDependencyWithHigherStdlibThanRoot() {
final var ctx = DependencyContext.basedOn(new DependencyConfig(false, tempDir));
final var rootPath = tempDir.resolve("root");
final var depPath = tempDir.resolve("dep");
final var rootProjectId = ctx.projectTable.register(ProjectDescriptor.builder()
.rootPath(rootPath)
.name("root")
.version("1.0.0")
.sourceRoots(ReadOnlyList.wrap(List.of()))
.build());
ctx.projectTable.register(ProjectDescriptor.builder()
.rootPath(depPath)
.name("dep")
.version("1.0.0")
.sourceRoots(ReadOnlyList.wrap(List.of()))
.build());
ctx.projectInfoTable.register(ProjectInfo.builder()
.rootDirectory(rootPath)
.manifestPath(rootPath.resolve("prometeu.json"))
.manifest(new PrometeuManifest("root", "1.0.0", "pbs", 1, ReadOnlyList.wrap(List.of())))
.build());
ctx.projectInfoTable.register(ProjectInfo.builder()
.rootDirectory(depPath)
.manifestPath(depPath.resolve("prometeu.json"))
.manifest(new PrometeuManifest("dep", "1.0.0", "pbs", 2, ReadOnlyList.wrap(List.of())))
.build());
ctx.rootProjectId = rootProjectId;
ctx.rootStdlib = 1;
ctx.dependenciesByProject.add(List.of());
ctx.dependenciesByProject.add(List.of());
ctx.projectNameAndVersions.put("root", new java.util.HashSet<>(List.of("1.0.0")));
ctx.projectNameAndVersions.put("dep", new java.util.HashSet<>(List.of("1.0.0")));
final var issues = new ValidatePhase().run(ctx);
assertTrue(issues.hasErrors());
assertTrue(issues.asCollection().stream().anyMatch(issue -> issue.getMessage().contains("incompatible stdlib")));
}
@Test
void acceptsDependencyWithLowerOrEqualStdlibThanRoot() {
final var ctx = DependencyContext.basedOn(new DependencyConfig(false, tempDir));
final var rootPath = tempDir.resolve("root");
final var depPath = tempDir.resolve("dep");
final var rootProjectId = ctx.projectTable.register(ProjectDescriptor.builder()
.rootPath(rootPath)
.name("root")
.version("1.0.0")
.sourceRoots(ReadOnlyList.wrap(List.of()))
.build());
ctx.projectTable.register(ProjectDescriptor.builder()
.rootPath(depPath)
.name("dep")
.version("1.0.0")
.sourceRoots(ReadOnlyList.wrap(List.of()))
.build());
ctx.projectInfoTable.register(ProjectInfo.builder()
.rootDirectory(rootPath)
.manifestPath(rootPath.resolve("prometeu.json"))
.manifest(new PrometeuManifest("root", "1.0.0", "pbs", 2, ReadOnlyList.wrap(List.of())))
.build());
ctx.projectInfoTable.register(ProjectInfo.builder()
.rootDirectory(depPath)
.manifestPath(depPath.resolve("prometeu.json"))
.manifest(new PrometeuManifest("dep", "1.0.0", "pbs", 1, ReadOnlyList.wrap(List.of())))
.build());
ctx.rootProjectId = rootProjectId;
ctx.rootStdlib = 2;
ctx.dependenciesByProject.add(List.of());
ctx.dependenciesByProject.add(List.of());
ctx.projectNameAndVersions.put("root", new java.util.HashSet<>(List.of("1.0.0")));
ctx.projectNameAndVersions.put("dep", new java.util.HashSet<>(List.of("1.0.0")));
final var issues = new ValidatePhase().run(ctx);
assertFalse(issues.hasErrors());
}
}

View File

@ -2,14 +2,11 @@
"name": "dep1",
"version": "1.0.0",
"language": "pbs",
"stdlib": "1",
"dependencies": [
{
"name": "dep2",
"version": "1.0.0"
},
{
"name": "sdk",
"version": "1.0.0"
}
]
}
}

View File

@ -2,10 +2,7 @@
"name": "dep2",
"version": "1.0.0",
"language": "pbs",
"stdlib": "1",
"dependencies": [
{
"name": "sdk",
"version": "1.0.0"
}
]
}
}

View File

@ -1,5 +1,4 @@
{
"dep1@1.0.0": ".workspace/repos/dep1/1.0.0",
"dep2@1.0.0": ".workspace/repos/dep2/1.0.0",
"sdk@1.0.0": ".workspace/repos/sdk/1.0.0"
"dep2@1.0.0": ".workspace/repos/dep2/1.0.0"
}

View File

@ -1,7 +0,0 @@
{
"name": "sdk",
"version": "1.0.0",
"language": "pbs",
"dependencies": [
]
}

View File

@ -2,14 +2,11 @@
"name": "main",
"version": "1.0.0",
"language": "pbs",
"stdlib": "1",
"dependencies": [
{
"name": "dep1",
"version": "1.0.0"
},
{
"name": "sdk",
"version": "1.0.0"
}
]
}
}