add specs
This commit is contained in:
parent
4c4ba1d603
commit
40785bfb63
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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:
|
||||
|
||||
@ -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(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -7,5 +7,6 @@ public record PrometeuManifest(
|
||||
String name,
|
||||
String version,
|
||||
String language,
|
||||
int stdlib,
|
||||
ReadOnlyList<DependencyReference> dependencies) {
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,7 @@
|
||||
"name": "dep2",
|
||||
"version": "1.0.0",
|
||||
"language": "pbs",
|
||||
"stdlib": "1",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.0.0",
|
||||
"language": "pbs",
|
||||
"dependencies": [
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user