dev/studio-frame-composer-syscall-and-sprite-alignment #5

Merged
bquarkz merged 7 commits from dev/studio-frame-composer-syscall-and-sprite-alignment into master 2026-04-18 16:33:53 +00:00
8 changed files with 213 additions and 37 deletions
Showing only changes of commit 46728a542f - Show all commits

View File

@ -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

View File

@ -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.

View File

@ -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`,

View File

@ -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:

View File

@ -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);
}
}

View File

@ -0,0 +1,2 @@
mod host LowComposer;
pub service Composer;

View File

@ -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);

View File

@ -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());
}
}