implements PLN-0053
This commit is contained in:
parent
ef582b6687
commit
46728a542f
@ -18,7 +18,7 @@ This document defines the normative compile-time model for:
|
||||
This document is the authority for how the compiler discovers and loads:
|
||||
|
||||
- ordinary project modules,
|
||||
- reserved stdlib modules such as `@sdk:gfx`,
|
||||
- reserved stdlib modules such as `@sdk:gfx` and `@sdk:composer`,
|
||||
- compile-time reserved metadata attached to those reserved modules,
|
||||
- interface-only service facades that wrap host surfaces for source ergonomics,
|
||||
- and backend-provided compile surfaces consumed by the frontend for symbolic authoring.
|
||||
@ -211,6 +211,20 @@ Rules:
|
||||
- `Gfx` becomes visible only through `import`.
|
||||
- The compiler accepts that import because the selected stdlib environment exposes `@sdk:gfx`.
|
||||
|
||||
For sprite composition in stdlib line `1`, the canonical reserved SDK surface is:
|
||||
|
||||
```pbs
|
||||
import { Composer } from @sdk:composer;
|
||||
|
||||
let status: int = Composer.emit_sprite(7, 0, 12, 18, 0, 2, false, false, 1);
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `@sdk:composer` is the canonical sprite-composition module in this wave.
|
||||
- `@sdk:gfx` remains valid for primitive and overlay-oriented graphics operations.
|
||||
- `Gfx.set_sprite` is not part of the active stdlib line `1` contract.
|
||||
|
||||
## 6. Effective Stdlib of the Build
|
||||
|
||||
The root project owns the effective stdlib line of the build.
|
||||
@ -359,6 +373,26 @@ Rules:
|
||||
- It contributes compile-time symbols and metadata.
|
||||
- It does not produce executable bytecode by itself.
|
||||
|
||||
Another valid reserved stdlib interface module in stdlib line `1` is the sprite-composition surface:
|
||||
|
||||
```pbs
|
||||
declare host LowComposer {
|
||||
[Host(module = "composer", name = "emit_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
}
|
||||
```
|
||||
|
||||
## 10. Compiler Loading of the Stdlib Environment
|
||||
|
||||
The compiler loads the stdlib environment from the stdlib line selected by the root project.
|
||||
@ -433,7 +467,7 @@ 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.
|
||||
- SDK-facing surfaces such as `Gfx.draw_pixel(...)` and `Composer.emit_sprite(...)` 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
|
||||
|
||||
@ -61,7 +61,7 @@ as the normative runtime-facing identity.
|
||||
|
||||
The host-binding pipeline is:
|
||||
|
||||
1. User code imports SDK surfaces such as `Gfx` from `@sdk:gfx`.
|
||||
1. User code imports SDK surfaces such as `Gfx` from `@sdk:gfx` and `Composer` from `@sdk:composer`.
|
||||
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.
|
||||
@ -136,6 +136,33 @@ but the PBX-facing declaration is canonical, for example:
|
||||
("gfx", "draw_pixel", 1)
|
||||
```
|
||||
|
||||
The same rule applies to sprite composition through the composer domain.
|
||||
For example, the PBS-facing declaration:
|
||||
|
||||
```pbs
|
||||
declare host LowComposer {
|
||||
[Host(module = "composer", name = "emit_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
}
|
||||
```
|
||||
|
||||
maps to the canonical runtime identity:
|
||||
|
||||
```text
|
||||
("composer", "emit_sprite", 1)
|
||||
```
|
||||
|
||||
The same rule applies to the low-level asset surface.
|
||||
For example, the PBS-facing declaration:
|
||||
|
||||
@ -396,7 +423,7 @@ Diagnostics should identify the failing canonical identity whenever available.
|
||||
|
||||
Rules:
|
||||
|
||||
- the loader does not resolve source imports such as `@sdk:gfx`,
|
||||
- the loader does not resolve source imports such as `@sdk:gfx` or `@sdk:composer`,
|
||||
- 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.
|
||||
|
||||
@ -56,6 +56,9 @@ prometeu-compiler/frontends/prometeu-frontend-pbs/
|
||||
stdlib/
|
||||
1/
|
||||
sdk/
|
||||
composer/
|
||||
main.pbs
|
||||
mod.barrel
|
||||
gfx/
|
||||
main.pbs
|
||||
mod.barrel
|
||||
@ -75,7 +78,7 @@ 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,
|
||||
- `composer`, `gfx`, `audio`, `math` are module paths within those reserved spaces,
|
||||
- `main.pbs` and `mod.barrel` form the interface module contents.
|
||||
|
||||
## 5. Logical Mapping
|
||||
@ -85,6 +88,7 @@ The compiler must treat the physical layout as a logical stdlib environment.
|
||||
Required mapping:
|
||||
|
||||
- `stdlib/<N>/sdk/gfx` -> `@sdk:gfx`
|
||||
- `stdlib/<N>/sdk/composer` -> `@sdk:composer`
|
||||
- `stdlib/<N>/sdk/asset` -> `@sdk:asset`
|
||||
- `stdlib/<N>/sdk/audio` -> `@sdk:audio`
|
||||
- `stdlib/<N>/core/math` -> `@core:math`
|
||||
@ -94,7 +98,7 @@ 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.
|
||||
- callers of the resolver should work with logical addresses such as `@sdk:gfx` and `@sdk:composer`, not resource paths.
|
||||
|
||||
## 6. Module File Convention
|
||||
|
||||
@ -145,6 +149,7 @@ Responsibility:
|
||||
Minimum operations:
|
||||
|
||||
- `resolve(@sdk:gfx)`
|
||||
- `resolve(@sdk:composer)`
|
||||
- `resolve(@core:math/vector)`
|
||||
|
||||
### 7.3 `StdlibModuleSource`
|
||||
@ -219,6 +224,26 @@ declare host LowAssets {
|
||||
}
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```pbs
|
||||
declare host LowComposer {
|
||||
[Host(module = "composer", name = "emit_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- the parser reads the attribute as part of the interface module source,
|
||||
@ -226,6 +251,11 @@ Rules:
|
||||
- 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.
|
||||
- stdlib line `1` MUST expose the sprite-composition interface module at `@sdk:composer`,
|
||||
- that module MUST declare `LowComposer` and public service `Composer`,
|
||||
- that module MUST use runtime module `composer` and capability `gfx`,
|
||||
- that module MUST expose `emit_sprite(glyph_id: int, palette_id: int, x: int, y: int, layer: int, bank_id: int, flip_x: bool, flip_y: bool, priority: int) -> int`,
|
||||
- stdlib line `1` MUST NOT expose `Gfx.set_sprite`,
|
||||
- stdlib line `1` MUST expose the low-level asset interface module at `@sdk:asset`,
|
||||
- that module MUST declare `LowAssets`,
|
||||
- that module MUST use runtime module `asset` and capability `asset`,
|
||||
|
||||
@ -164,6 +164,20 @@ Rules:
|
||||
- PBS symbolic asset references (`Addressable`) are not themselves required to be stdlib-imported modules;
|
||||
- backend-owned frontend surface contracts MAY supply symbolic values that later lower into stdlib host-backed APIs.
|
||||
|
||||
### 12.1 Current line `1` sprite-composition rule
|
||||
|
||||
For stdlib line `1`, sprite composition is owned by `@sdk:composer`, not by `@sdk:gfx`.
|
||||
|
||||
Rules:
|
||||
|
||||
- `@sdk:composer` MUST expose a low-level host owner `LowComposer`.
|
||||
- `@sdk:composer` MUST expose a public service facade `Composer`.
|
||||
- `Composer.emit_sprite(...)` MUST lower through canonical host identity `("composer", "emit_sprite", 1)`.
|
||||
- `Composer.emit_sprite(...)` MUST return a raw `int` in this wave.
|
||||
- `@sdk:gfx` MAY continue to expose primitive and overlay-oriented operations.
|
||||
- `@sdk:gfx` MUST NOT expose `set_sprite`.
|
||||
- `bind_scene`, `unbind_scene`, and `set_camera` are deferred and are not required members of `@sdk:composer` in this wave.
|
||||
|
||||
## 13. Stdlib Contract Expectations for Builtin MVP
|
||||
|
||||
For the current builtin MVP, stdlib should be able to expose at least:
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
declare host LowComposer {
|
||||
[Host(module = "composer", name = "emit_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
}
|
||||
|
||||
declare service Composer
|
||||
{
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int
|
||||
{
|
||||
return LowComposer.emit_sprite(glyph_id, palette_id, x, y, layer, bank_id, flip_x, flip_y, priority);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
mod host LowComposer;
|
||||
pub service Composer;
|
||||
@ -25,21 +25,6 @@ declare host LowGfx {
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void;
|
||||
|
||||
[Host(module = "gfx", name = "set_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn set_sprite(
|
||||
bank_id: int,
|
||||
index: int,
|
||||
x: int,
|
||||
y: int,
|
||||
tile_id: int,
|
||||
palette_id: int,
|
||||
active: bool,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
|
||||
[Host(module = "gfx", name = "draw_text", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void;
|
||||
@ -81,22 +66,6 @@ declare service Gfx
|
||||
LowGfx.draw_square(x, y, w, h, border_color, fill_color);
|
||||
}
|
||||
|
||||
fn set_sprite(
|
||||
bank_id: int,
|
||||
index: int,
|
||||
x: int,
|
||||
y: int,
|
||||
tile_id: int,
|
||||
palette_id: int,
|
||||
active: bool,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int
|
||||
{
|
||||
return LowGfx.set_sprite(bank_id, index, x, y, tile_id, palette_id, active, flip_x, flip_y, priority);
|
||||
}
|
||||
|
||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_text(x, y, message, color);
|
||||
|
||||
@ -84,4 +84,71 @@ class InterfaceModuleLoaderTest {
|
||||
"pub host LowAssets;",
|
||||
fileTable.get(module.barrelFiles().getFirst().fileId()).readUtf8().orElseThrow());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadComposerStdlibModuleWithPublicServiceBarrel() {
|
||||
final var projectTable = new ProjectTable();
|
||||
final var projectId = projectTable.register(ProjectDescriptor.builder()
|
||||
.name("app")
|
||||
.version("1.0.0")
|
||||
.rootPath(Path.of("/tmp/app"))
|
||||
.sourceRoots(ReadOnlyList.wrap(java.util.List.of(Path.of("/tmp/app/src"))))
|
||||
.build());
|
||||
final var fileTable = new FileTable(1);
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
final var moduleSource = new StdlibModuleSource(
|
||||
"sdk",
|
||||
ReadOnlyList.wrap(java.util.List.of("composer")),
|
||||
ReadOnlyList.wrap(java.util.List.of(new StdlibModuleSource.SourceFile(
|
||||
"main.pbs",
|
||||
"""
|
||||
declare host LowComposer {
|
||||
[Host(module = "composer", name = "emit_sprite", version = 1)]
|
||||
[Capability(name = "gfx")]
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int;
|
||||
}
|
||||
|
||||
declare service Composer {
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
x: int,
|
||||
y: int,
|
||||
layer: int,
|
||||
bank_id: int,
|
||||
flip_x: bool,
|
||||
flip_y: bool,
|
||||
priority: int
|
||||
) -> int {
|
||||
return LowComposer.emit_sprite(glyph_id, palette_id, x, y, layer, bank_id, flip_x, flip_y, priority);
|
||||
}
|
||||
}
|
||||
"""))),
|
||||
"""
|
||||
mod host LowComposer;
|
||||
pub service Composer;
|
||||
""");
|
||||
|
||||
final var module = new InterfaceModuleLoader().load(moduleSource, projectId, fileTable, diagnostics);
|
||||
|
||||
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||
assertEquals(1, module.sourceFiles().size());
|
||||
assertEquals(1, module.barrelFiles().size());
|
||||
assertEquals(
|
||||
"""
|
||||
mod host LowComposer;
|
||||
pub service Composer;
|
||||
""".strip(),
|
||||
fileTable.get(module.barrelFiles().getFirst().fileId()).readUtf8().orElseThrow().strip());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user