Compare commits
8 Commits
dd6c9dd718
...
cd82cb5cff
| Author | SHA1 | Date | |
|---|---|---|---|
| cd82cb5cff | |||
| 908cb2b1fe | |||
| 1e99553d94 | |||
| a17dfde481 | |||
| 46728a542f | |||
| ef582b6687 | |||
| 9dbc88ce35 | |||
| d9f857b9bf |
@ -1,4 +1,7 @@
|
|||||||
{"type":"meta","next_id":{"DSC":27,"AGD":29,"DEC":26,"PLN":49,"LSN":40,"CLSN":1}}
|
{"type":"meta","next_id":{"DSC":30,"AGD":32,"DEC":28,"PLN":56,"LSN":42,"CLSN":1}}
|
||||||
|
{"type":"discussion","id":"DSC-0029","status":"done","ticket":"studio-frame-composer-syscall-and-sprite-alignment","title":"Studio Alignment with Runtime FrameComposer Syscalls and Sprite Composition","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["studio","compiler","pbs","stdlib","runtime-alignment","abi","syscall","frame-composer","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0041","file":"discussion/lessons/DSC-0029-studio-frame-composer-syscall-and-sprite-alignment/LSN-0041-composer-must-own-public-sprite-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
|
||||||
|
{"type":"discussion","id":"DSC-0028","status":"open","ticket":"studio-tiled-parser-assets-scene-asset-type","title":"Tiled Parser and Scene Asset-Type Ownership in Assets Workspace","created_at":"2026-04-15","updated_at":"2026-04-15","tags":["studio","assets","scene","tiled","parser","asset-type"],"agendas":[{"id":"AGD-0030","file":"AGD-0030-tiled-parser-and-scene-asset-type.md","status":"open","created_at":"2026-04-15","updated_at":"2026-04-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
|
{"type":"discussion","id":"DSC-0027","status":"abandoned","ticket":"studio-scene-workspace","title":"Scene Workspace for SCENE Authoring","created_at":"2026-04-14","updated_at":"2026-04-15","tags":["studio","workspace","scene","tilemap","asset","runtime-alignment"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-studio-scene-workspace.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","_override_reason":"Explicit user request on 2026-04-15 to abandon the accepted agenda and its downstream work."}],"decisions":[{"id":"DEC-0026","file":"DEC-0026-studio-scene-workspace.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_agenda":"AGD-0029","_override_reason":"Explicit user request on 2026-04-15 to abandon the accepted decision and stop using it as normative guidance."}],"plans":[{"id":"PLN-0049","file":"PLN-0049-scene-workspace-spec-and-boundary-propagation.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0050","file":"PLN-0050-scene-workspace-shell-and-project-state-foundations.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0051","file":"PLN-0051-scene-artifact-and-assets-handoff-contract.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0052","file":"PLN-0052-scene-workspace-wave-1-tilemap-editor.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."}],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0026","status":"done","ticket":"glyph-bank-naming-alignment-with-runtime","title":"Glyph Bank Naming Alignment with Runtime DEC-0006","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["packer","studio","naming","asset-contract","runtime-alignment","glyph-bank"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0026-glyph-bank-naming-alignment-with-runtime/LSN-0040-glyph-bank-artifact-naming-alignment.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
{"type":"discussion","id":"DSC-0026","status":"done","ticket":"glyph-bank-naming-alignment-with-runtime","title":"Glyph Bank Naming Alignment with Runtime DEC-0006","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["packer","studio","naming","asset-contract","runtime-alignment","glyph-bank"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0026-glyph-bank-naming-alignment-with-runtime/LSN-0040-glyph-bank-artifact-naming-alignment.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
||||||
{"type":"discussion","id":"DSC-0025","status":"done","ticket":"packer-pipeline-metadata-ownership","title":"Pipeline Metadata Ownership and Runtime Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["packer","metadata","runtime-contract","tooling","studio"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0025-packer-pipeline-metadata-ownership/LSN-0039-runtime-header-boundary-and-tooling-owned-pipeline-metadata.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
{"type":"discussion","id":"DSC-0025","status":"done","ticket":"packer-pipeline-metadata-ownership","title":"Pipeline Metadata Ownership and Runtime Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["packer","metadata","runtime-contract","tooling","studio"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0025-packer-pipeline-metadata-ownership/LSN-0039-runtime-header-boundary-and-tooling-owned-pipeline-metadata.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
||||||
{"type":"discussion","id":"DSC-0024","status":"done","ticket":"jacoco-reports-consolidation","title":"JaCoCo Reports Consolidation in Gradle","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["infra","gradle","jacoco","coverage","jenkins"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0038-jacoco-reports-consolidation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
{"type":"discussion","id":"DSC-0024","status":"done","ticket":"jacoco-reports-consolidation","title":"JaCoCo Reports Consolidation in Gradle","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["infra","gradle","jacoco","coverage","jenkins"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0038-jacoco-reports-consolidation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
id: LSN-0041
|
||||||
|
ticket: studio-frame-composer-syscall-and-sprite-alignment
|
||||||
|
title: Composer Must Own the Public Sprite-Composition Surface
|
||||||
|
created: 2026-04-18
|
||||||
|
tags: [studio, compiler, pbs, stdlib, runtime-alignment, abi, syscall, frame-composer, sprites]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The runtime had already moved canonical frame orchestration to `FrameComposer` and published the corresponding public ABI under `composer.*`, but Studio still exposed sprite composition through `@sdk:gfx` and `Gfx.set_sprite`.
|
||||||
|
|
||||||
|
That left the repository teaching the wrong owner even though runtime dispatch, architectural lessons, and the public syscall domain had already changed. The migration closed that drift for the sprite path without pulling scene binding, camera control, or scene-bank work into the same ticket.
|
||||||
|
|
||||||
|
## Key Decisions
|
||||||
|
|
||||||
|
### Public Source Ownership Must Match the Runtime ABI
|
||||||
|
|
||||||
|
**What:**
|
||||||
|
Studio now exposes sprite composition through `@sdk:composer`, with `LowComposer` as the low-level host owner and `Composer` as the public service facade. The old `Gfx.set_sprite` path was removed instead of being kept as compatibility sugar.
|
||||||
|
|
||||||
|
**Why:**
|
||||||
|
Once runtime ownership moved to `FrameComposer`, keeping sprite submission under `gfx.*` would preserve a false mental model and create a dual contract between what the runtime actually owns and what PBS/stdlib teaches.
|
||||||
|
|
||||||
|
**Trade-offs:**
|
||||||
|
The migration required coordinated changes across stdlib resources, specs, frontend conformance, examples, and LSP fixtures. That was more work up front, but it avoided a longer-lived compatibility layer that would have encoded the wrong architecture.
|
||||||
|
|
||||||
|
### A Partial Domain Rollout Is Valid If the Boundary Is Explicit
|
||||||
|
|
||||||
|
**What:**
|
||||||
|
This wave shipped only `composer.emit_sprite(...)` in Studio. `bind_scene`, `unbind_scene`, and `set_camera` were explicitly deferred.
|
||||||
|
|
||||||
|
**Why:**
|
||||||
|
The current pipeline only needed sprite composition. Pulling scene/camera rollout into the same thread would have mixed ABI convergence with unfinished scene-work concerns and made the migration harder to execute cleanly.
|
||||||
|
|
||||||
|
**Trade-offs:**
|
||||||
|
The docs had to state clearly that `@sdk:composer` exists now for sprite emission while other composer members remain future work. That is slightly less symmetrical in the short term, but it keeps the implemented contract honest.
|
||||||
|
|
||||||
|
### Raw Status Returns Can Be Preserved During Ownership Migration
|
||||||
|
|
||||||
|
**What:**
|
||||||
|
`Composer.emit_sprite(...)` kept the raw `int` status return for this wave instead of introducing a new editorial status shell.
|
||||||
|
|
||||||
|
**Why:**
|
||||||
|
The architectural problem in this discussion was ownership and ABI drift, not status typing. Keeping the return shape stable allowed the migration to focus on the service boundary and public surface without coupling it to a second API redesign.
|
||||||
|
|
||||||
|
**Trade-offs:**
|
||||||
|
The API remains less expressive than a nominal status shell, but the migration stayed narrow and easier to validate end to end.
|
||||||
|
|
||||||
|
## Patterns and Algorithms
|
||||||
|
|
||||||
|
- Treat runtime service ownership changes as public-source changes, not merely internal lowering rewrites.
|
||||||
|
- When the runtime removes a legacy public syscall path, prefer removing the matching source-level facade instead of silently retargeting it.
|
||||||
|
- Split migrations by boundary: first stdlib/spec surface, then compiler/conformance, then examples and fixture cleanup.
|
||||||
|
- Keep primitive-oriented `@sdk:gfx` coverage intact while moving only frame-composition behavior to `@sdk:composer`.
|
||||||
|
- Use a negative regression test for the removed API so compatibility leakage is caught explicitly.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Retargeting `Gfx.set_sprite` internally to `composer.emit_sprite` would have looked convenient, but it would have preserved the wrong owner in the public teaching surface.
|
||||||
|
- A repository sweep that ignores examples and fixtures leaves stale guidance behind even when compiler tests are green.
|
||||||
|
- Replacing every `@sdk:gfx` example wholesale would be overcorrection; primitive and overlay operations still belong there.
|
||||||
|
- Updating docs to mention deferred composer members as if they already shipped would create a second kind of contract drift.
|
||||||
|
|
||||||
|
## Takeaways
|
||||||
|
|
||||||
|
- Public SDK ownership should mirror canonical runtime ownership, not historical implementation leftovers.
|
||||||
|
- Removing the old API is safer than keeping a compatibility facade that encodes the wrong subsystem boundary.
|
||||||
|
- A sprite-only `@sdk:composer` wave is acceptable when the deferred scene/camera scope is stated explicitly.
|
||||||
|
- Ownership migration and status-typing redesign do not need to happen in the same change.
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
id: AGD-0030
|
||||||
|
ticket: studio-tiled-parser-assets-scene-asset-type
|
||||||
|
title: Tiled Parser and Scene Asset-Type Ownership in Assets Workspace
|
||||||
|
status: open
|
||||||
|
created: 2026-04-15
|
||||||
|
resolved:
|
||||||
|
decision:
|
||||||
|
tags:
|
||||||
|
- studio
|
||||||
|
- assets
|
||||||
|
- scene
|
||||||
|
- tiled
|
||||||
|
- parser
|
||||||
|
- asset-type
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pain
|
||||||
|
|
||||||
|
The current abandoned direction assumed a dedicated `Scene Workspace` as the primary authoring owner for scenes.
|
||||||
|
|
||||||
|
That no longer matches the intended product direction.
|
||||||
|
|
||||||
|
Without a new direction:
|
||||||
|
|
||||||
|
- the Studio has no agreed path for scene authoring;
|
||||||
|
- the `Assets` workspace still lacks a defined ownership model for scene-like content;
|
||||||
|
- there is no agreed contract for importing or interpreting `Tiled` data;
|
||||||
|
- implementation risks rebuilding a custom scene editor model that the team has already decided to discard.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Domain owner: `studio`
|
||||||
|
|
||||||
|
Expected documentation owner:
|
||||||
|
|
||||||
|
- `docs/studio/specs`
|
||||||
|
|
||||||
|
Cross-domain impact:
|
||||||
|
|
||||||
|
- asset typing and creation flows in the `Assets` workspace;
|
||||||
|
- Studio-side parsing/import rules for `Tiled`;
|
||||||
|
- possible downstream alignment with runtime-facing `SCENE` publication, depending on the chosen import boundary.
|
||||||
|
|
||||||
|
User direction explicitly provided on 2026-04-15:
|
||||||
|
|
||||||
|
- abandon `DEC-0026` and its derived plans;
|
||||||
|
- do not proceed with a dedicated `Scene Workspace`;
|
||||||
|
- instead, write a parser for `Tiled`;
|
||||||
|
- create a new asset type managed from the `Assets` workspace.
|
||||||
|
|
||||||
|
This agenda exists to turn that direction into a disciplined technical recommendation rather than jumping directly into implementation with undefined ownership and file-contract assumptions.
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- [ ] Which `Tiled` surface is the primary input contract: TMX XML, JSON export, TSX, or a constrained subset of those?
|
||||||
|
- [ ] Is the new asset type a raw imported `Tiled` document, a normalized Studio-owned intermediate asset, or a published runtime-facing `SCENE` derivative?
|
||||||
|
- [ ] Should `Assets` own only creation/import and metadata management, or also the main editing/reimport flow for this new asset type?
|
||||||
|
- [ ] How should tileset references inside `Tiled` map onto existing glyph-bank assets and asset identity rules in Studio?
|
||||||
|
- [ ] What parts of `Tiled` are intentionally out of scope for the first wave: object layers, properties, flips, collisions, infinite maps, templates, Wang sets, animations?
|
||||||
|
- [ ] Does the parser need to preserve full round-trip fidelity with `Tiled`, or only import the supported subset into Studio-owned structures?
|
||||||
|
- [ ] What is the canonical failure model when a `Tiled` document references unsupported constructs or broken external assets?
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### Option A - Import `Tiled` into a new Studio-owned scene asset type in `Assets`
|
||||||
|
- **Approach:** Introduce a new asset type in `Assets`; parse supported `Tiled` files into a Studio-owned asset model managed from that workspace.
|
||||||
|
- **Pro:** Aligns with the requested ownership shift, keeps authoring/import centered in `Assets`, and allows Studio to define a strict supported subset.
|
||||||
|
- **Con:** Requires defining a clean normalized asset contract and a mapping layer from `Tiled` concepts to Studio/runtime concepts.
|
||||||
|
- **Maintainability:** Strong, if the supported subset and ownership boundary are made explicit early.
|
||||||
|
|
||||||
|
### Option B - Treat `Tiled` files as external source documents and keep Studio as a thin importer/viewer
|
||||||
|
- **Approach:** Keep `Tiled` as the primary authoring format and let Studio mostly import, validate, and publish from it.
|
||||||
|
- **Pro:** Minimizes Studio-owned scene-model invention and stays close to existing `Tiled` authoring workflows.
|
||||||
|
- **Con:** Couples Studio behavior heavily to `Tiled`'s full document model and makes partial support/error handling harder to reason about.
|
||||||
|
- **Maintainability:** Medium, because unsupported `Tiled` breadth can leak into every implementation phase.
|
||||||
|
|
||||||
|
### Option C - Create a new Studio asset type unrelated to `Tiled` and only offer one-time conversion
|
||||||
|
- **Approach:** Use `Tiled` only as an optional bootstrap/import tool, then move fully into a separate Studio-native asset format.
|
||||||
|
- **Pro:** Gives Studio maximum control over the final asset model and future editing behavior.
|
||||||
|
- **Con:** Moves away from the user's stated direction of writing a `Tiled` parser as a central path and may reintroduce the same custom-model burden the previous direction had.
|
||||||
|
- **Maintainability:** Medium, but only if the conversion boundary is very clearly justified.
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
The key architectural change here is not only "use `Tiled`".
|
||||||
|
|
||||||
|
It is also a change in ownership:
|
||||||
|
|
||||||
|
- scene-related authoring/import should no longer justify a dedicated `Scene Workspace`;
|
||||||
|
- the `Assets` workspace becomes the management surface for the relevant asset type;
|
||||||
|
- any parser choice must therefore fit an asset-first workflow instead of a separate editor-first workflow.
|
||||||
|
|
||||||
|
That creates several decisions that should be made deliberately:
|
||||||
|
|
||||||
|
1. what exact `Tiled` contract Studio supports in wave 1;
|
||||||
|
2. whether Studio stores `Tiled` data directly or through a normalized asset form;
|
||||||
|
3. how glyph banks and tilesets map without creating identity ambiguity;
|
||||||
|
4. how much of `Tiled` fidelity is preserved versus normalized away;
|
||||||
|
5. whether editing is a reimport/update flow, a direct managed-asset flow, or some hybrid.
|
||||||
|
|
||||||
|
There is already one strong constraint from the user:
|
||||||
|
|
||||||
|
- the previous `Scene Workspace` split should be considered discarded;
|
||||||
|
- the new work should be organized around `Assets` plus a `Tiled` parser.
|
||||||
|
|
||||||
|
That makes Option A the current best fit for convergence, but it still needs the agenda to narrow the supported `Tiled` subset and the resulting asset contract before a new decision can be written.
|
||||||
|
|
||||||
|
## Resolution
|
||||||
|
|
||||||
|
Recommended next step:
|
||||||
|
|
||||||
|
- converge this agenda around a first-wave `Tiled` subset;
|
||||||
|
- define whether the new asset type is raw-imported or normalized;
|
||||||
|
- then write a replacement decision for asset ownership, parser boundary, and wave-1 scope.
|
||||||
@ -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:
|
This document is the authority for how the compiler discovers and loads:
|
||||||
|
|
||||||
- ordinary project modules,
|
- 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,
|
- compile-time reserved metadata attached to those reserved modules,
|
||||||
- interface-only service facades that wrap host surfaces for source ergonomics,
|
- interface-only service facades that wrap host surfaces for source ergonomics,
|
||||||
- and backend-provided compile surfaces consumed by the frontend for symbolic authoring.
|
- and backend-provided compile surfaces consumed by the frontend for symbolic authoring.
|
||||||
@ -211,6 +211,20 @@ Rules:
|
|||||||
- `Gfx` becomes visible only through `import`.
|
- `Gfx` becomes visible only through `import`.
|
||||||
- The compiler accepts that import because the selected stdlib environment exposes `@sdk:gfx`.
|
- 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
|
## 6. Effective Stdlib of the Build
|
||||||
|
|
||||||
The root project owns the effective stdlib line 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 contributes compile-time symbols and metadata.
|
||||||
- It does not produce executable bytecode by itself.
|
- 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
|
## 10. Compiler Loading of the Stdlib Environment
|
||||||
|
|
||||||
The compiler loads the stdlib environment from the stdlib line selected by the root project.
|
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.
|
- `declare host` remains reserved to SDK/toolchain-controlled modules.
|
||||||
- Ordinary user-authored project modules do not declare canonical host bindings directly.
|
- Ordinary user-authored project modules do not declare canonical host bindings directly.
|
||||||
- User code consumes SDK exports through normal imports.
|
- 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.
|
- 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
|
## 13. Project Publication and Distribution
|
||||||
|
|||||||
@ -61,7 +61,7 @@ as the normative runtime-facing identity.
|
|||||||
|
|
||||||
The host-binding pipeline is:
|
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.
|
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)`.
|
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.
|
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)
|
("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.
|
The same rule applies to the low-level asset surface.
|
||||||
For example, the PBS-facing declaration:
|
For example, the PBS-facing declaration:
|
||||||
|
|
||||||
@ -396,7 +423,7 @@ Diagnostics should identify the failing canonical identity whenever available.
|
|||||||
|
|
||||||
Rules:
|
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,
|
- the loader only consumes canonical host-binding metadata emitted into the PBX,
|
||||||
- `stdlib` affects the loader indirectly through what the compiler emitted,
|
- `stdlib` affects the loader indirectly through what the compiler emitted,
|
||||||
- the loader must not attempt to reconstruct SDK/module semantics from the PBX.
|
- the loader must not attempt to reconstruct SDK/module semantics from the PBX.
|
||||||
|
|||||||
@ -56,6 +56,9 @@ prometeu-compiler/frontends/prometeu-frontend-pbs/
|
|||||||
stdlib/
|
stdlib/
|
||||||
1/
|
1/
|
||||||
sdk/
|
sdk/
|
||||||
|
composer/
|
||||||
|
main.pbs
|
||||||
|
mod.barrel
|
||||||
gfx/
|
gfx/
|
||||||
main.pbs
|
main.pbs
|
||||||
mod.barrel
|
mod.barrel
|
||||||
@ -75,7 +78,7 @@ Interpretation:
|
|||||||
|
|
||||||
- `stdlib/1` selects stdlib major line `1`,
|
- `stdlib/1` selects stdlib major line `1`,
|
||||||
- `sdk` and `core` are reserved project spaces,
|
- `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.
|
- `main.pbs` and `mod.barrel` form the interface module contents.
|
||||||
|
|
||||||
## 5. Logical Mapping
|
## 5. Logical Mapping
|
||||||
@ -85,6 +88,7 @@ The compiler must treat the physical layout as a logical stdlib environment.
|
|||||||
Required mapping:
|
Required mapping:
|
||||||
|
|
||||||
- `stdlib/<N>/sdk/gfx` -> `@sdk:gfx`
|
- `stdlib/<N>/sdk/gfx` -> `@sdk:gfx`
|
||||||
|
- `stdlib/<N>/sdk/composer` -> `@sdk:composer`
|
||||||
- `stdlib/<N>/sdk/asset` -> `@sdk:asset`
|
- `stdlib/<N>/sdk/asset` -> `@sdk:asset`
|
||||||
- `stdlib/<N>/sdk/audio` -> `@sdk:audio`
|
- `stdlib/<N>/sdk/audio` -> `@sdk:audio`
|
||||||
- `stdlib/<N>/core/math` -> `@core:math`
|
- `stdlib/<N>/core/math` -> `@core:math`
|
||||||
@ -94,7 +98,7 @@ Rules:
|
|||||||
|
|
||||||
- physical storage is an implementation detail,
|
- physical storage is an implementation detail,
|
||||||
- logical module identity is authoritative,
|
- 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
|
## 6. Module File Convention
|
||||||
|
|
||||||
@ -145,6 +149,7 @@ Responsibility:
|
|||||||
Minimum operations:
|
Minimum operations:
|
||||||
|
|
||||||
- `resolve(@sdk:gfx)`
|
- `resolve(@sdk:gfx)`
|
||||||
|
- `resolve(@sdk:composer)`
|
||||||
- `resolve(@core:math/vector)`
|
- `resolve(@core:math/vector)`
|
||||||
|
|
||||||
### 7.3 `StdlibModuleSource`
|
### 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:
|
Rules:
|
||||||
|
|
||||||
- the parser reads the attribute as part of the interface module source,
|
- 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 compiler stores the extracted metadata in its interface graph,
|
||||||
- the raw attribute surface is not treated as a runtime object,
|
- 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.
|
- 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`,
|
- stdlib line `1` MUST expose the low-level asset interface module at `@sdk:asset`,
|
||||||
- that module MUST declare `LowAssets`,
|
- that module MUST declare `LowAssets`,
|
||||||
- that module MUST use runtime module `asset` and capability `asset`,
|
- 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;
|
- 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.
|
- 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
|
## 13. Stdlib Contract Expectations for Builtin MVP
|
||||||
|
|
||||||
For the current builtin MVP, stdlib should be able to expose at least:
|
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")]
|
[Capability(name = "gfx")]
|
||||||
fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void;
|
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)]
|
[Host(module = "gfx", name = "draw_text", version = 1)]
|
||||||
[Capability(name = "gfx")]
|
[Capability(name = "gfx")]
|
||||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void;
|
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);
|
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
|
fn draw_text(x: int, y: int, message: str, color: Color) -> void
|
||||||
{
|
{
|
||||||
LowGfx.draw_text(x, y, message, color);
|
LowGfx.draw_text(x, y, message, color);
|
||||||
|
|||||||
@ -85,13 +85,14 @@ class PbsGateUSdkInterfaceConformanceTest {
|
|||||||
tempDir.resolve("gate-u-reserved-import-positive"),
|
tempDir.resolve("gate-u-reserved-import-positive"),
|
||||||
"""
|
"""
|
||||||
import { Color } from @core:color;
|
import { Color } from @core:color;
|
||||||
|
import { Composer } from @sdk:composer;
|
||||||
import { Gfx } from @sdk:gfx;
|
import { Gfx } from @sdk:gfx;
|
||||||
import { Input, InputPad, InputButton } from @sdk:input;
|
import { Input, InputPad, InputButton } from @sdk:input;
|
||||||
import { Assets } from @sdk:asset;
|
import { Assets } from @sdk:asset;
|
||||||
import { Log } from @sdk:log;
|
import { Log } from @sdk:log;
|
||||||
|
|
||||||
declare contract Renderer {
|
declare contract Renderer {
|
||||||
fn render(gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: Assets, log: Log) -> void;
|
fn render(composer: Composer, gfx: Gfx, color: Color, input: Input, pad: InputPad, button: InputButton, assets: Assets, log: Log) -> void;
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
"pub contract Renderer;",
|
"pub contract Renderer;",
|
||||||
@ -115,6 +116,11 @@ class PbsGateUSdkInterfaceConformanceTest {
|
|||||||
&& h.abiModule().equals("gfx")
|
&& h.abiModule().equals("gfx")
|
||||||
&& h.abiMethod().equals("clear")
|
&& h.abiMethod().equals("clear")
|
||||||
&& h.abiVersion() == 1));
|
&& h.abiVersion() == 1));
|
||||||
|
assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream()
|
||||||
|
.anyMatch(h -> h.ownerName().equals("LowComposer")
|
||||||
|
&& h.abiModule().equals("composer")
|
||||||
|
&& h.abiMethod().equals("emit_sprite")
|
||||||
|
&& h.abiVersion() == 1));
|
||||||
assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream()
|
assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream()
|
||||||
.anyMatch(h -> h.ownerName().equals("LowLog")
|
.anyMatch(h -> h.ownerName().equals("LowLog")
|
||||||
&& h.abiModule().equals("log")
|
&& h.abiModule().equals("log")
|
||||||
|
|||||||
@ -84,4 +84,71 @@ class InterfaceModuleLoaderTest {
|
|||||||
"pub host LowAssets;",
|
"pub host LowAssets;",
|
||||||
fileTable.get(module.barrelFiles().getFirst().fileId()).readUtf8().orElseThrow());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1006,8 +1006,72 @@ class PBSFrontendPhaseServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldLowerSdkGfxSetSpriteFacadeUsingBankIdContract() throws IOException {
|
void shouldLowerSdkComposerEmitSpriteFacadeUsingCanonicalComposerContract() throws IOException {
|
||||||
final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-set-sprite");
|
final var projectRoot = tempDir.resolve("project-bootstrap-sdk-composer-emit-sprite");
|
||||||
|
final var sourceRoot = projectRoot.resolve("src");
|
||||||
|
final var modulePath = sourceRoot.resolve("app");
|
||||||
|
Files.createDirectories(modulePath);
|
||||||
|
|
||||||
|
final var sourceFile = modulePath.resolve("source.pbs");
|
||||||
|
final var modBarrel = modulePath.resolve("mod.barrel");
|
||||||
|
Files.writeString(sourceFile, """
|
||||||
|
import { Composer } from @sdk:composer;
|
||||||
|
|
||||||
|
fn render() -> int
|
||||||
|
{
|
||||||
|
return Composer.emit_sprite(7, 3, 12, 18, 0, 2, false, true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Frame]
|
||||||
|
fn frame() -> void
|
||||||
|
{
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
Files.writeString(modBarrel, """
|
||||||
|
pub fn render() -> int;
|
||||||
|
pub fn frame() -> void;
|
||||||
|
""");
|
||||||
|
|
||||||
|
final var projectTable = new ProjectTable();
|
||||||
|
final var fileTable = new FileTable(1);
|
||||||
|
final var projectId = projectTable.register(ProjectDescriptor.builder()
|
||||||
|
.rootPath(projectRoot)
|
||||||
|
.name("app")
|
||||||
|
.version("1.0.0")
|
||||||
|
.sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot)))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
registerFile(projectId, projectRoot, sourceFile, fileTable);
|
||||||
|
registerFile(projectId, projectRoot, modBarrel, fileTable);
|
||||||
|
|
||||||
|
final var ctx = new FrontendPhaseContext(
|
||||||
|
projectTable,
|
||||||
|
fileTable,
|
||||||
|
new BuildStack(ReadOnlyList.wrap(List.of(projectId))),
|
||||||
|
1);
|
||||||
|
final var diagnostics = DiagnosticSink.empty();
|
||||||
|
|
||||||
|
final var irBackend = new PBSFrontendPhaseService().compile(
|
||||||
|
ctx,
|
||||||
|
diagnostics,
|
||||||
|
LogAggregator.empty(),
|
||||||
|
BuildingIssueSink.empty());
|
||||||
|
|
||||||
|
assertTrue(diagnostics.stream().noneMatch(d ->
|
||||||
|
d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name())));
|
||||||
|
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function -> "frame".equals(function.callableName())));
|
||||||
|
assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream()
|
||||||
|
.anyMatch(h -> h.ownerName().equals("LowComposer")
|
||||||
|
&& h.sourceMethodName().equals("emit_sprite")
|
||||||
|
&& h.abiModule().equals("composer")
|
||||||
|
&& h.abiMethod().equals("emit_sprite")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectLegacyGfxSetSpriteAfterComposerMigration() throws IOException {
|
||||||
|
final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-set-sprite-removed");
|
||||||
final var sourceRoot = projectRoot.resolve("src");
|
final var sourceRoot = projectRoot.resolve("src");
|
||||||
final var modulePath = sourceRoot.resolve("app");
|
final var modulePath = sourceRoot.resolve("app");
|
||||||
Files.createDirectories(modulePath);
|
Files.createDirectories(modulePath);
|
||||||
@ -1059,10 +1123,10 @@ class PBSFrontendPhaseServiceTest {
|
|||||||
LogAggregator.empty(),
|
LogAggregator.empty(),
|
||||||
BuildingIssueSink.empty());
|
BuildingIssueSink.empty());
|
||||||
|
|
||||||
assertTrue(diagnostics.stream().noneMatch(d ->
|
assertTrue(diagnostics.stream().anyMatch(d ->
|
||||||
d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name())));
|
d.getCode().equals(PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name())
|
||||||
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function -> "frame".equals(function.callableName())));
|
|| d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name())));
|
||||||
assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream()
|
assertFalse(irBackend.getReservedMetadata().hostMethodBindings().stream()
|
||||||
.anyMatch(h -> h.ownerName().equals("LowGfx") && h.sourceMethodName().equals("set_sprite")));
|
.anyMatch(h -> h.ownerName().equals("LowGfx") && h.sourceMethodName().equals("set_sprite")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,10 +55,10 @@ final class LspServiceImplTest {
|
|||||||
""";
|
""";
|
||||||
|
|
||||||
private static final String SDK_IMPORT_SOURCE = """
|
private static final String SDK_IMPORT_SOURCE = """
|
||||||
import { Gfx } from @sdk:gfx;
|
import { Composer } from @sdk:composer;
|
||||||
|
|
||||||
fn main() -> void {
|
fn main() -> void {
|
||||||
Gfx.clear();
|
Composer.emit_sprite(1, 0, 0, 0, 0, 0, false, false, 0);
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ final class LspServiceImplTest {
|
|||||||
|
|
||||||
final var analysis = service.analyzeDocument(new LspAnalyzeDocumentRequest(mainFile));
|
final var analysis = service.analyzeDocument(new LspAnalyzeDocumentRequest(mainFile));
|
||||||
|
|
||||||
assertEquals(List.of("pbs-service", "pbs-service"), semanticKeysForLexeme(analysis, SDK_IMPORT_SOURCE, "Gfx"));
|
assertEquals(List.of("pbs-service", "pbs-service"), semanticKeysForLexeme(analysis, SDK_IMPORT_SOURCE, "Composer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
36
test-projects/main/assets/scenes/primeiro mapa.tmx
Normal file
36
test-projects/main/assets/scenes/primeiro mapa.tmx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.12.1" orientation="orthogonal" renderorder="right-down" width="25" height="25" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="12">
|
||||||
|
<tileset firstgid="1" source="primeiro tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="25" height="25">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="3" name="Object Layer 1">
|
||||||
|
<object id="11" x="16" y="16" width="64" height="64"/>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
||||||
7
test-projects/main/assets/scenes/primeiro tileset.tsx
Normal file
7
test-projects/main/assets/scenes/primeiro tileset.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.10" tiledversion="1.12.1" name="primeiro tileset" tilewidth="16" tileheight="16" tilecount="1" columns="0">
|
||||||
|
<grid orientation="orthogonal" width="1" height="1"/>
|
||||||
|
<tile id="0">
|
||||||
|
<image source="../recovered/atlas2/confirm.png" width="16" height="16"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
14
test-projects/main/assets/scenes/scene1.tiled-project
Normal file
14
test-projects/main/assets/scenes/scene1.tiled-project
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"automappingRulesFile": "",
|
||||||
|
"commands": [
|
||||||
|
],
|
||||||
|
"compatibilityVersion": 1100,
|
||||||
|
"extensionsPath": "extensions",
|
||||||
|
"folders": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
],
|
||||||
|
"propertyTypes": [
|
||||||
|
]
|
||||||
|
}
|
||||||
38
test-projects/main/assets/scenes/scene1.tiled-session
Normal file
38
test-projects/main/assets/scenes/scene1.tiled-session
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"activeFile": "primeiro tileset.tsx",
|
||||||
|
"expandedProjectPaths": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"fileStates": {
|
||||||
|
"primeiro mapa.tmx": {
|
||||||
|
"scale": 2.55,
|
||||||
|
"selectedLayer": 1,
|
||||||
|
"viewCenter": {
|
||||||
|
"x": 200,
|
||||||
|
"y": 200.58823529411768
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"primeiro tileset.tsx": {
|
||||||
|
"dynamicWrapping": true,
|
||||||
|
"scaleInDock": 1,
|
||||||
|
"scaleInEditor": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"last.imagePath": "/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/studio/test-projects/main/assets/recovered/atlas2",
|
||||||
|
"map.height": 25,
|
||||||
|
"map.lastUsedFormat": "tmx",
|
||||||
|
"map.tileHeight": 16,
|
||||||
|
"map.tileWidth": 16,
|
||||||
|
"map.width": 25,
|
||||||
|
"openFiles": [
|
||||||
|
"primeiro mapa.tmx",
|
||||||
|
"primeiro tileset.tsx"
|
||||||
|
],
|
||||||
|
"project": "scene1.tiled-project",
|
||||||
|
"recentFiles": [
|
||||||
|
"primeiro mapa.tmx",
|
||||||
|
"primeiro tileset.tsx"
|
||||||
|
],
|
||||||
|
"tileset.lastUsedFormat": "tsx",
|
||||||
|
"tileset.type": 1
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import { Color } from @core:color;
|
|||||||
|
|
||||||
import { Log } from @sdk:log;
|
import { Log } from @sdk:log;
|
||||||
import { Input } from @sdk:input;
|
import { Input } from @sdk:input;
|
||||||
|
import { Composer } from @sdk:composer;
|
||||||
import { Gfx } from @sdk:gfx;
|
import { Gfx } from @sdk:gfx;
|
||||||
import { Assets } from @sdk:asset;
|
import { Assets } from @sdk:asset;
|
||||||
|
|
||||||
@ -41,9 +42,9 @@ fn frame() -> void
|
|||||||
Log.failure("commit failed");
|
Log.failure("commit failed");
|
||||||
}
|
}
|
||||||
} else if (s == 3) {
|
} else if (s == 3) {
|
||||||
let sprite_status : int = Gfx.set_sprite(3, 10, 150, 150, 0, 0, true, false, false, 1);
|
let sprite_status : int = Composer.emit_sprite(0, 0, 150, 150, 1, 3, false, false, 1);
|
||||||
if (sprite_status != 0) {
|
if (sprite_status != 0) {
|
||||||
Log.failure("set_sprite failed");
|
Log.failure("emit_sprite failed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.info("state: waiting");
|
Log.info("state: waiting");
|
||||||
@ -74,8 +75,8 @@ fn frame() -> void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx.set_sprite(0, 0, touch.x() - 16, touch.y() + 8, tile_id, 0, true, true, false, 0);
|
Composer.emit_sprite(tile_id, 0, touch.x() - 16, touch.y() + 8, 0, 0, true, false, 0);
|
||||||
Gfx.set_sprite(0, 1, touch.x() + 16, touch.y() + 8, tile_id, 0, true, false, false, 0);
|
Composer.emit_sprite(tile_id, 0, touch.x() + 16, touch.y() + 8, 0, 1, false, false, 0);
|
||||||
|
|
||||||
let a : int = 10;
|
let a : int = 10;
|
||||||
let b : int = 15;
|
let b : int = 15;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user