From 40785bfb636320f305ead61647c2049226a6e845 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 2 Mar 2026 15:21:17 +0000 Subject: [PATCH] add specs --- docs/specs/pbs/1. Language Charter.md | 5 +- .../specs/pbs/2. Governance and Versioning.md | 2 + .../specs/pbs/3. Core Syntax Specification.md | 106 ++++- .../pbs/4. Static Semantics Specification.md | 111 +++-- ...tdlib, and SDK Resolution Specification.md | 449 ++++++++++++++++++ ...ing and Loader Resolution Specification.md | 426 +++++++++++++++++ ... and Runtime Capabilities Specification.md | 246 ++++++++++ ...ent Packaging and Loading Specification.md | 251 ++++++++++ docs/specs/pbs/TODO.md | 43 +- .../compiler/dtos/PrometeuManifestDTO.java | 3 +- .../compiler/models/DependencyContext.java | 6 +- .../compiler/models/PrometeuManifest.java | 1 + .../compiler/models/ResolvedWorkspace.java | 1 + .../utilities/PrometeuManifestUtils.java | 32 +- .../workspaces/phases/DiscoverPhase.java | 3 + .../workspaces/phases/ValidatePhase.java | 32 +- .../utilities/PrometeuManifestUtilsTest.java | 82 ++++ .../workspaces/phases/ValidatePhaseTest.java | 106 +++++ .../.workspace/repos/dep1/1.0.0/prometeu.json | 7 +- .../.workspace/repos/dep2/1.0.0/prometeu.json | 7 +- .../main/.workspace/repos/registry.json | 3 +- .../.workspace/repos/sdk/1.0.0/prometeu.json | 7 - test-projects/main/prometeu.json | 7 +- 23 files changed, 1845 insertions(+), 91 deletions(-) create mode 100644 docs/specs/pbs/5. Manifest, Stdlib, and SDK Resolution Specification.md create mode 100644 docs/specs/pbs/6. Host ABI Binding and Loader Resolution Specification.md create mode 100644 docs/specs/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md create mode 100644 docs/specs/pbs/8. Stdlib Environment Packaging and Loading Specification.md create mode 100644 prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/utilities/PrometeuManifestUtilsTest.java create mode 100644 prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/workspaces/phases/ValidatePhaseTest.java delete mode 100644 test-projects/main/.workspace/repos/sdk/1.0.0/prometeu.json diff --git a/docs/specs/pbs/1. Language Charter.md b/docs/specs/pbs/1. Language Charter.md index 49af27e9..159234a5 100644 --- a/docs/specs/pbs/1. Language Charter.md +++ b/docs/specs/pbs/1. Language Charter.md @@ -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 diff --git a/docs/specs/pbs/2. Governance and Versioning.md b/docs/specs/pbs/2. Governance and Versioning.md index ba3fe97e..9de6ee40 100644 --- a/docs/specs/pbs/2. Governance and Versioning.md +++ b/docs/specs/pbs/2. Governance and Versioning.md @@ -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 diff --git a/docs/specs/pbs/3. Core Syntax Specification.md b/docs/specs/pbs/3. Core Syntax Specification.md index 0dec603a..e674da92 100644 --- a/docs/specs/pbs/3. Core Syntax Specification.md +++ b/docs/specs/pbs/3. Core Syntax Specification.md @@ -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; diff --git a/docs/specs/pbs/4. Static Semantics Specification.md b/docs/specs/pbs/4. Static Semantics Specification.md index 65ba0278..8d0139bf 100644 --- a/docs/specs/pbs/4. Static Semantics Specification.md +++ b/docs/specs/pbs/4. Static Semantics Specification.md @@ -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. diff --git a/docs/specs/pbs/5. Manifest, Stdlib, and SDK Resolution Specification.md b/docs/specs/pbs/5. Manifest, Stdlib, and SDK Resolution Specification.md new file mode 100644 index 00000000..8151b622 --- /dev/null +++ b/docs/specs/pbs/5. Manifest, Stdlib, and SDK Resolution Specification.md @@ -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. diff --git a/docs/specs/pbs/6. Host ABI Binding and Loader Resolution Specification.md b/docs/specs/pbs/6. Host ABI Binding and Loader Resolution Specification.md new file mode 100644 index 00000000..4e910877 --- /dev/null +++ b/docs/specs/pbs/6. Host ABI Binding and Loader Resolution Specification.md @@ -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 `. +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 ` into `SYSCALL `. +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 ` 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 ` into `SYSCALL `. +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 +``` + +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 ` into `SYSCALL `. + +Verifier responsibilities: + +- run on the already-patched executable image, +- validate stack safety and control-flow safety, +- treat `SYSCALL ` 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 `. + +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 ` 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 `. +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 ` into `SYSCALL `. +12. The verifier runs after patching and validates the final numeric program. +13. The VM executes numeric syscall identifiers only. diff --git a/docs/specs/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md b/docs/specs/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md new file mode 100644 index 00000000..b78e6e1d --- /dev/null +++ b/docs/specs/pbs/7. Cartridge Manifest and Runtime Capabilities Specification.md @@ -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 `, +- 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. diff --git a/docs/specs/pbs/8. Stdlib Environment Packaging and Loading Specification.md b/docs/specs/pbs/8. Stdlib Environment Packaging and Loading Specification.md new file mode 100644 index 00000000..6fbe37ad --- /dev/null +++ b/docs/specs/pbs/8. Stdlib Environment Packaging and Loading Specification.md @@ -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//sdk/gfx` -> `@sdk:gfx` +- `stdlib//sdk/audio` -> `@sdk:audio` +- `stdlib//core/math` -> `@core:math` +- `stdlib//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. diff --git a/docs/specs/pbs/TODO.md b/docs/specs/pbs/TODO.md index 64903b80..cc90f170 100644 --- a/docs/specs/pbs/TODO.md +++ b/docs/specs/pbs/TODO.md @@ -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: diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/dtos/PrometeuManifestDTO.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/dtos/PrometeuManifestDTO.java index 33cb7b6f..53634ccb 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/dtos/PrometeuManifestDTO.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/dtos/PrometeuManifestDTO.java @@ -9,6 +9,7 @@ public record PrometeuManifestDTO( String name, String version, String language, + String stdlib, List dependencies) { @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) @@ -32,4 +33,4 @@ public record PrometeuManifestDTO( } } } -} \ No newline at end of file +} diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/DependencyContext.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/DependencyContext.java index cfc17e3c..e7e8b8c9 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/DependencyContext.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/DependencyContext.java @@ -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); } diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/PrometeuManifest.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/PrometeuManifest.java index 34c32806..382f793f 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/PrometeuManifest.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/PrometeuManifest.java @@ -7,5 +7,6 @@ public record PrometeuManifest( String name, String version, String language, + int stdlib, ReadOnlyList dependencies) { } diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java index c2bf34c6..cebc926a 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/models/ResolvedWorkspace.java @@ -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() { diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/utilities/PrometeuManifestUtils.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/utilities/PrometeuManifestUtils.java index 60439fb0..7373804d 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/utilities/PrometeuManifestUtils.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/utilities/PrometeuManifestUtils.java @@ -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); } } diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/DiscoverPhase.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/DiscoverPhase.java index c73e868a..0f2d7a81 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/DiscoverPhase.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/DiscoverPhase.java @@ -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()) { diff --git a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/ValidatePhase.java b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/ValidatePhase.java index 464eca1e..d75e1e44 100644 --- a/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/ValidatePhase.java +++ b/prometeu-compiler/prometeu-deps/src/main/java/p/studio/compiler/workspaces/phases/ValidatePhase.java @@ -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(); diff --git a/prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/utilities/PrometeuManifestUtilsTest.java b/prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/utilities/PrometeuManifestUtilsTest.java new file mode 100644 index 00000000..df0ef5e8 --- /dev/null +++ b/prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/utilities/PrometeuManifestUtilsTest.java @@ -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; + } +} diff --git a/prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/workspaces/phases/ValidatePhaseTest.java b/prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/workspaces/phases/ValidatePhaseTest.java new file mode 100644 index 00000000..033bce11 --- /dev/null +++ b/prometeu-compiler/prometeu-deps/src/test/java/p/studio/compiler/workspaces/phases/ValidatePhaseTest.java @@ -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()); + } +} diff --git a/test-projects/main/.workspace/repos/dep1/1.0.0/prometeu.json b/test-projects/main/.workspace/repos/dep1/1.0.0/prometeu.json index 84303c7d..3088e395 100644 --- a/test-projects/main/.workspace/repos/dep1/1.0.0/prometeu.json +++ b/test-projects/main/.workspace/repos/dep1/1.0.0/prometeu.json @@ -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" } ] -} \ No newline at end of file +} diff --git a/test-projects/main/.workspace/repos/dep2/1.0.0/prometeu.json b/test-projects/main/.workspace/repos/dep2/1.0.0/prometeu.json index 6dc9feb3..1056fc84 100644 --- a/test-projects/main/.workspace/repos/dep2/1.0.0/prometeu.json +++ b/test-projects/main/.workspace/repos/dep2/1.0.0/prometeu.json @@ -2,10 +2,7 @@ "name": "dep2", "version": "1.0.0", "language": "pbs", + "stdlib": "1", "dependencies": [ - { - "name": "sdk", - "version": "1.0.0" - } ] -} \ No newline at end of file +} diff --git a/test-projects/main/.workspace/repos/registry.json b/test-projects/main/.workspace/repos/registry.json index f30c506a..cf27e173 100644 --- a/test-projects/main/.workspace/repos/registry.json +++ b/test-projects/main/.workspace/repos/registry.json @@ -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" } \ No newline at end of file diff --git a/test-projects/main/.workspace/repos/sdk/1.0.0/prometeu.json b/test-projects/main/.workspace/repos/sdk/1.0.0/prometeu.json deleted file mode 100644 index 92bfd195..00000000 --- a/test-projects/main/.workspace/repos/sdk/1.0.0/prometeu.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "sdk", - "version": "1.0.0", - "language": "pbs", - "dependencies": [ - ] -} \ No newline at end of file diff --git a/test-projects/main/prometeu.json b/test-projects/main/prometeu.json index d7f84553..a7f8b342 100644 --- a/test-projects/main/prometeu.json +++ b/test-projects/main/prometeu.json @@ -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" } ] -} \ No newline at end of file +}