implements PLN-0100
This commit is contained in:
parent
e7647165bc
commit
63215ac02f
@ -1,7 +1,7 @@
|
||||
{"type":"meta","next_id":{"DSC":39,"AGD":42,"DEC":40,"PLN":103,"LSN":55,"CLSN":1}}
|
||||
{"type":"discussion","id":"DSC-0038","status":"done","ticket":"studio-packer-rgba8888-asset-pipeline","title":"Studio and Packer RGBA8888 Asset Pipeline Alignment","created_at":"2026-05-23","updated_at":"2026-07-14","tags":["studio","packer","assets","glyph-bank","palette","rgba8888","runtime-alignment"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0053","file":"discussion/lessons/DSC-0038-studio-packer-rgba8888-asset-pipeline/LSN-0053-rgba8888-is-the-canonical-studio-packer-palette-contract.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14"}]}
|
||||
{"type":"discussion","id":"DSC-0037","status":"done","ticket":"pbs-autocomplete-parameter-names","title":"PBS autocomplete parameter names for stdlib and method calls","created_at":"2026-05-08","updated_at":"2026-05-14","tags":["compiler-pbs","studio","lsp","autocomplete","signature-help","stdlib"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0052","file":"discussion/lessons/DSC-0037-pbs-autocomplete-parameter-names/LSN-0052-canonical-callable-parameter-names-through-pbs-editor-assistance.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14"}]}
|
||||
{"type":"discussion","id":"DSC-0036","status":"in_progress","ticket":"pbs-symbol-documentation-and-hover-markdown","title":"Modelo de documentacao de simbolos em PBS e consumo markdown no hover","created_at":"2026-05-08","updated_at":"2026-07-15","tags":["compiler","compiler-pbs","studio","lsp","vscode","editor","hover","documentation","markdown"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-pbs-symbol-documentation-and-hover-markdown.md","status":"accepted","created_at":"2026-05-08","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0039","file":"DEC-0039-pbs-symbol-documentation-with-doc-markdown-text-blocks.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0039"}],"plans":[{"id":"PLN-0092","file":"PLN-0092-specify-pbs-doc-attribute-and-markdown-text-block-syntax.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0093","file":"PLN-0093-implement-pbs-lexer-support-for-documentation-text-blocks.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0094","file":"PLN-0094-extend-pbs-attribute-parser-and-ast-for-doc-markdown-payloads.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0095","file":"PLN-0095-implement-documentation-text-block-normalization.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0096","file":"PLN-0096-validate-doc-attribute-semantics-and-diagnostics.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0097","file":"PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0098","file":"PLN-0098-expose-doc-metadata-through-pbs-editorial-and-lsp-surfaces.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0099","file":"PLN-0099-render-doc-markdown-in-hover-composition.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0100","file":"PLN-0100-author-doc-metadata-for-stdlib-sdk-and-interface-declarations.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0101","file":"PLN-0101-protect-runtime-artifacts-from-doc-metadata-lowering.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0102","file":"PLN-0102-add-end-to-end-doc-documentation-conformance-coverage.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0036","status":"in_progress","ticket":"pbs-symbol-documentation-and-hover-markdown","title":"Modelo de documentacao de simbolos em PBS e consumo markdown no hover","created_at":"2026-05-08","updated_at":"2026-07-15","tags":["compiler","compiler-pbs","studio","lsp","vscode","editor","hover","documentation","markdown"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-pbs-symbol-documentation-and-hover-markdown.md","status":"accepted","created_at":"2026-05-08","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0039","file":"DEC-0039-pbs-symbol-documentation-with-doc-markdown-text-blocks.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0039"}],"plans":[{"id":"PLN-0092","file":"PLN-0092-specify-pbs-doc-attribute-and-markdown-text-block-syntax.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0093","file":"PLN-0093-implement-pbs-lexer-support-for-documentation-text-blocks.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0094","file":"PLN-0094-extend-pbs-attribute-parser-and-ast-for-doc-markdown-payloads.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0095","file":"PLN-0095-implement-documentation-text-block-normalization.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0096","file":"PLN-0096-validate-doc-attribute-semantics-and-diagnostics.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0097","file":"PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0098","file":"PLN-0098-expose-doc-metadata-through-pbs-editorial-and-lsp-surfaces.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0099","file":"PLN-0099-render-doc-markdown-in-hover-composition.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0100","file":"PLN-0100-author-doc-metadata-for-stdlib-sdk-and-interface-declarations.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0101","file":"PLN-0101-protect-runtime-artifacts-from-doc-metadata-lowering.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0102","file":"PLN-0102-add-end-to-end-doc-documentation-conformance-coverage.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0035","status":"done","ticket":"pbs-lsp-editor-assistance-wave-1","title":"Wave 1 de assistencia editorial via LSP para PBS no VS Code","created_at":"2026-05-08","updated_at":"2026-05-08","tags":["studio","lsp","vscode","compiler","compiler-pbs","editor","completion","hover","signature-help"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0051","file":"discussion/lessons/DSC-0035-pbs-lsp-editor-assistance-wave-1/LSN-0051-compiler-backed-editor-assistance-for-pbs.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]}
|
||||
{"type":"discussion","id":"DSC-0034","status":"done","ticket":"frontend-semantic-host-projection-flexibility","title":"Frontend semantic vocabulary flexibility and declarative host projection","created_at":"2026-05-06","updated_at":"2026-05-07","tags":["compiler","compiler-general","frontend","semantics","vscode","host-projection","lsp"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0034-frontend-semantic-host-projection-flexibility/LSN-0048-frontend-owned-semantic-vocabularies-with-declarative-host-projection.md","status":"done","created_at":"2026-05-07","updated_at":"2026-05-07"}]}
|
||||
{"type":"discussion","id":"DSC-0033","status":"done","ticket":"frontend-visual-theme-spec-and-css-retirement","title":"Frontend visual theme spec and retirement of host-consumed semantic CSS","created_at":"2026-05-06","updated_at":"2026-05-08","tags":["compiler","compiler-general","frontend","presentation","theming","studio","vscode","lsp","pbs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0050","file":"discussion/lessons/DSC-0033-frontend-visual-theme-spec-and-css-retirement/LSN-0050-frontend-owned-visual-themes-with-structured-contract-and-host-adapters.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0100
|
||||
ticket: pbs-symbol-documentation-and-hover-markdown
|
||||
title: Author Doc metadata for stdlib SDK and interface declarations
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-07-15
|
||||
ref_decisions: [DEC-0039]
|
||||
tags: [compiler, compiler-pbs, studio, lsp, vscode, editor, hover, documentation, markdown]
|
||||
|
||||
@ -388,10 +388,17 @@ final class PbsDeclarationParser {
|
||||
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
||||
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||
attributes = attributeParser.parseAttributeList();
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FN)) {
|
||||
methods.add(parseFunctionLike(cursor.previous(), ReadOnlyList.empty()));
|
||||
methods.add(parseFunctionLike(cursor.previous(), attributes));
|
||||
continue;
|
||||
}
|
||||
if (!attributes.isEmpty()) {
|
||||
reportAttributesNotAllowed(attributes, "Attributes are valid only on struct methods in struct bodies");
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.CTOR)) {
|
||||
ctors.add(parseCtorDeclarationInBody(cursor.previous()));
|
||||
continue;
|
||||
@ -451,13 +458,20 @@ final class PbsDeclarationParser {
|
||||
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start service body");
|
||||
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||
var methodAttributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||
methodAttributes = attributeParser.parseAttributeList();
|
||||
}
|
||||
if (!cursor.match(PbsTokenKind.FN)) {
|
||||
if (!methodAttributes.isEmpty()) {
|
||||
reportAttributesNotAllowed(methodAttributes, "Attributes are valid only on service methods in service bodies");
|
||||
}
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||
"Service body accepts only method declarations");
|
||||
cursor.advance();
|
||||
continue;
|
||||
}
|
||||
methods.add(parseFunctionLike(cursor.previous(), ReadOnlyList.empty()));
|
||||
methods.add(parseFunctionLike(cursor.previous(), methodAttributes));
|
||||
}
|
||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end service body");
|
||||
return new PbsAst.ServiceDecl(name.lexeme(), ReadOnlyList.wrap(methods), attributes, span(declareToken.start(), rightBrace.end()));
|
||||
@ -708,17 +722,16 @@ final class PbsDeclarationParser {
|
||||
return PbsAst.LifecycleMarker.NONE;
|
||||
}
|
||||
|
||||
if (!isOrdinaryMode()) {
|
||||
reportAttributesNotAllowed(attributes, "Attributes are not allowed before top-level functions");
|
||||
return PbsAst.LifecycleMarker.NONE;
|
||||
}
|
||||
|
||||
PbsAst.LifecycleMarker marker = PbsAst.LifecycleMarker.NONE;
|
||||
for (final var attribute : attributes) {
|
||||
final PbsAst.LifecycleMarker nextMarker;
|
||||
if ("Doc".equals(attribute.name())) {
|
||||
continue;
|
||||
}
|
||||
if (!isOrdinaryMode()) {
|
||||
reportAttributesNotAllowed(attributes, "Only [Doc] is allowed before interface executable methods");
|
||||
return PbsAst.LifecycleMarker.NONE;
|
||||
}
|
||||
if ("Init".equals(attribute.name())) {
|
||||
nextMarker = PbsAst.LifecycleMarker.INIT;
|
||||
} else if ("Frame".equals(attribute.name())) {
|
||||
|
||||
@ -199,6 +199,7 @@ public final class PbsDeclarationSemanticsValidator {
|
||||
false);
|
||||
final var methodScope = PbsCallableScope.structMethods(ownerNameId);
|
||||
for (final var method : structDecl.methods()) {
|
||||
validateDocAttributes(method.attributes(), "struct methods", diagnostics);
|
||||
validateCallable(
|
||||
methodScope,
|
||||
method.name(),
|
||||
@ -238,6 +239,7 @@ public final class PbsDeclarationSemanticsValidator {
|
||||
final DiagnosticSink diagnostics) {
|
||||
final var methodScope = PbsCallableScope.serviceMethods(nameTable.register(serviceDecl.name()));
|
||||
for (final var method : serviceDecl.methods()) {
|
||||
validateDocAttributes(method.attributes(), "service methods", diagnostics);
|
||||
validateCallable(
|
||||
methodScope,
|
||||
method.name(),
|
||||
|
||||
@ -1,61 +1,136 @@
|
||||
[Doc(markdown = """
|
||||
Represents a packed RGBA color.
|
||||
""")]
|
||||
[BuiltinType(name = "color", version = 1)]
|
||||
declare builtin type Color(
|
||||
pub raw: int
|
||||
) {
|
||||
[Doc(markdown = """
|
||||
Builds a color from its packed integer representation.
|
||||
|
||||
- `raw`: packed color value.
|
||||
""")]
|
||||
[IntrinsicCall(name = "from_raw", version = 1)]
|
||||
fn from_raw(raw: int) -> Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Builds an opaque color from red, green, and blue channels.
|
||||
|
||||
- `r`: red channel.
|
||||
- `g`: green channel.
|
||||
- `b`: blue channel.
|
||||
""")]
|
||||
[IntrinsicCall(name = "rgb", version = 1)]
|
||||
fn rgb(r: int, g: int, b: int) -> Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Builds a color from red, green, blue, and alpha channels.
|
||||
|
||||
- `r`: red channel.
|
||||
- `g`: green channel.
|
||||
- `b`: blue channel.
|
||||
- `a`: alpha channel.
|
||||
""")]
|
||||
[IntrinsicCall(name = "rgba", version = 1)]
|
||||
fn rgba(r: int, g: int, b: int, a: int) -> Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Parses a CSS-style RGBA color string.
|
||||
|
||||
- `value`: color text in HTML/CSS format.
|
||||
""")]
|
||||
[IntrinsicCall(name = "html_rgba", version = 1)]
|
||||
fn html_rgba(value: str) -> Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Builds an opaque grayscale color.
|
||||
|
||||
- `value`: grayscale channel value.
|
||||
""")]
|
||||
[IntrinsicCall(name = "gray_scale", version = 1)]
|
||||
fn gray_scale(value: int) -> Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the packed RGB portion of this color.
|
||||
""")]
|
||||
[IntrinsicCall(name = "hex", version = 1)]
|
||||
fn hex() -> int;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the alpha channel of this color.
|
||||
""")]
|
||||
[IntrinsicCall(name = "alpha", version = 1)]
|
||||
fn alpha() -> int;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque black.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "black", version = 1)]
|
||||
declare const BLACK: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque white.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "white", version = 1)]
|
||||
declare const WHITE: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque red.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "red", version = 1)]
|
||||
declare const RED: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque green.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "green", version = 1)]
|
||||
declare const GREEN: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque blue.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "blue", version = 1)]
|
||||
declare const BLUE: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque yellow.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "yellow", version = 1)]
|
||||
declare const YELLOW: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque orange.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "orange", version = 1)]
|
||||
declare const ORANGE: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque indigo.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "indigo", version = 1)]
|
||||
declare const INDIGO: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque gray.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "gray", version = 1)]
|
||||
declare const GRAY: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque cyan.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "cyan", version = 1)]
|
||||
declare const CYAN: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Opaque magenta.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "magenta", version = 1)]
|
||||
declare const MAGENTA: Color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Fully transparent color.
|
||||
""")]
|
||||
[BuiltinConst(target = "color", name = "transparent", version = 1)]
|
||||
declare const TRANSPARENT: Color;
|
||||
|
||||
@ -1,35 +1,83 @@
|
||||
[Doc(markdown = """
|
||||
Low-level asset loading host bindings.
|
||||
""")]
|
||||
declare host LowAssets {
|
||||
[Doc(markdown = """
|
||||
Starts loading an addressable asset into a slot.
|
||||
|
||||
- `addressable`: asset reference.
|
||||
- `slot`: destination slot.
|
||||
""")]
|
||||
[Host(module = "asset", name = "load", version = 1)]
|
||||
[Capability(name = "asset")]
|
||||
[AssetLowering(param = 0)]
|
||||
fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int);
|
||||
|
||||
[Doc(markdown = """
|
||||
Reads the status of an asset loading operation.
|
||||
|
||||
- `loading_handle`: handle returned by `load`.
|
||||
""")]
|
||||
[Host(module = "asset", name = "status", version = 1)]
|
||||
[Capability(name = "asset")]
|
||||
fn status(loading_handle: int) -> int;
|
||||
|
||||
[Doc(markdown = """
|
||||
Commits a completed asset loading operation.
|
||||
|
||||
- `loading_handle`: handle returned by `load`.
|
||||
""")]
|
||||
[Host(module = "asset", name = "commit", version = 1)]
|
||||
[Capability(name = "asset")]
|
||||
fn commit(loading_handle: int) -> int;
|
||||
|
||||
[Doc(markdown = """
|
||||
Cancels an asset loading operation.
|
||||
|
||||
- `loading_handle`: handle returned by `load`.
|
||||
""")]
|
||||
[Host(module = "asset", name = "cancel", version = 1)]
|
||||
[Capability(name = "asset")]
|
||||
fn cancel(loading_handle: int) -> int;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
High-level asset loading commands.
|
||||
""")]
|
||||
declare service Assets {
|
||||
[Doc(markdown = """
|
||||
Starts loading an addressable asset into a slot.
|
||||
|
||||
- `addressable`: asset reference.
|
||||
- `slot`: destination slot.
|
||||
""")]
|
||||
fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int) {
|
||||
return LowAssets.load(addressable, slot);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Reads the status of an asset loading operation.
|
||||
|
||||
- `loading_handle`: handle returned by `load`.
|
||||
""")]
|
||||
fn status(loading_handle: int) -> int {
|
||||
return LowAssets.status(loading_handle);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Commits a completed asset loading operation.
|
||||
|
||||
- `loading_handle`: handle returned by `load`.
|
||||
""")]
|
||||
fn commit(loading_handle: int) -> int {
|
||||
return LowAssets.commit(loading_handle);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Cancels an asset loading operation.
|
||||
|
||||
- `loading_handle`: handle returned by `load`.
|
||||
""")]
|
||||
fn cancel(loading_handle: int) -> int {
|
||||
return LowAssets.cancel(loading_handle);
|
||||
}
|
||||
|
||||
@ -1,16 +1,46 @@
|
||||
[Doc(markdown = """
|
||||
Low-level scene composer host bindings.
|
||||
""")]
|
||||
declare host LowComposer {
|
||||
[Doc(markdown = """
|
||||
Sets the scene camera position.
|
||||
|
||||
- `x`: camera x coordinate.
|
||||
- `y`: camera y coordinate.
|
||||
""")]
|
||||
[Host(module = "composer", name = "set_camera", version = 1)]
|
||||
[Capability(name = "composer")]
|
||||
fn set_camera(x: int, y: int) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Binds a scene bank for composer emission.
|
||||
|
||||
- `scene_bank_id`: scene bank identifier.
|
||||
""")]
|
||||
[Host(module = "composer", name = "bind_scene", version = 1)]
|
||||
[Capability(name = "composer")]
|
||||
fn bind_scene(scene_bank_id: int) -> int;
|
||||
|
||||
[Doc(markdown = """
|
||||
Unbinds the active scene bank.
|
||||
""")]
|
||||
[Host(module = "composer", name = "unbind_scene", version = 1)]
|
||||
[Capability(name = "composer")]
|
||||
fn unbind_scene() -> int;
|
||||
|
||||
[Doc(markdown = """
|
||||
Emits a sprite command into the active scene.
|
||||
|
||||
- `glyph_id`: glyph identifier.
|
||||
- `palette_id`: palette identifier.
|
||||
- `x`: screen x coordinate.
|
||||
- `y`: screen y coordinate.
|
||||
- `layer`: render layer.
|
||||
- `bank_id`: asset bank identifier.
|
||||
- `flip_x`: mirror horizontally.
|
||||
- `flip_y`: mirror vertically.
|
||||
- `priority`: ordering priority.
|
||||
""")]
|
||||
[Host(module = "composer", name = "emit_sprite", version = 1)]
|
||||
[Capability(name = "composer")]
|
||||
fn emit_sprite(
|
||||
@ -26,23 +56,53 @@ declare host LowComposer {
|
||||
) -> int;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
High-level scene composition commands.
|
||||
""")]
|
||||
declare service Composer
|
||||
{
|
||||
[Doc(markdown = """
|
||||
Sets the scene camera position.
|
||||
|
||||
- `x`: camera x coordinate.
|
||||
- `y`: camera y coordinate.
|
||||
""")]
|
||||
fn set_camera(x: int, y: int) -> void
|
||||
{
|
||||
LowComposer.set_camera(x, y);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Binds a scene bank for composer emission.
|
||||
|
||||
- `scene_bank_id`: scene bank identifier.
|
||||
""")]
|
||||
fn bind_scene(scene_bank_id: int) -> int
|
||||
{
|
||||
return LowComposer.bind_scene(scene_bank_id);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Unbinds the active scene bank.
|
||||
""")]
|
||||
fn unbind_scene() -> int
|
||||
{
|
||||
return LowComposer.unbind_scene();
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Emits a sprite command into the active scene.
|
||||
|
||||
- `glyph_id`: glyph identifier.
|
||||
- `palette_id`: palette identifier.
|
||||
- `x`: screen x coordinate.
|
||||
- `y`: screen y coordinate.
|
||||
- `layer`: render layer.
|
||||
- `bank_id`: asset bank identifier.
|
||||
- `flip_x`: mirror horizontally.
|
||||
- `flip_y`: mirror vertically.
|
||||
- `priority`: ordering priority.
|
||||
""")]
|
||||
fn emit_sprite(
|
||||
glyph_id: int,
|
||||
palette_id: int,
|
||||
|
||||
@ -1,67 +1,189 @@
|
||||
import { Color } from @core:color;
|
||||
|
||||
[Doc(markdown = """
|
||||
Low-level 2D graphics host bindings.
|
||||
""")]
|
||||
declare host LowGfx {
|
||||
[Doc(markdown = """
|
||||
Clears the render target.
|
||||
|
||||
- `color`: fill color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "clear", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn clear(color: Color) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a filled rectangle.
|
||||
|
||||
- `x`: left coordinate.
|
||||
- `y`: top coordinate.
|
||||
- `w`: rectangle width.
|
||||
- `h`: rectangle height.
|
||||
- `color`: fill color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "fill_rect", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn fill_rect(x: int, y: int, w: int, h: int, color: Color) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a line segment.
|
||||
|
||||
- `x1`: start x coordinate.
|
||||
- `y1`: start y coordinate.
|
||||
- `x2`: end x coordinate.
|
||||
- `y2`: end y coordinate.
|
||||
- `color`: line color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "draw_line", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn draw_line(x1: int, y1: int, x2: int, y2: int, color: Color) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a circle outline.
|
||||
|
||||
- `x`: center x coordinate.
|
||||
- `y`: center y coordinate.
|
||||
- `r`: radius.
|
||||
- `color`: outline color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "draw_circle", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn draw_circle(x: int, y: int, r: int, color: Color) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a filled circle.
|
||||
|
||||
- `x`: center x coordinate.
|
||||
- `y`: center y coordinate.
|
||||
- `r`: radius.
|
||||
- `border_color`: outline color.
|
||||
- `fill_color`: fill color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "draw_disc", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn draw_disc(x: int, y: int, r: int, border_color: Color, fill_color: Color) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a filled rectangle with a border.
|
||||
|
||||
- `x`: left coordinate.
|
||||
- `y`: top coordinate.
|
||||
- `w`: rectangle width.
|
||||
- `h`: rectangle height.
|
||||
- `border_color`: border color.
|
||||
- `fill_color`: fill color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "draw_square", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws text at a screen position.
|
||||
|
||||
- `x`: left coordinate.
|
||||
- `y`: baseline coordinate.
|
||||
- `message`: text to draw.
|
||||
- `color`: text color.
|
||||
""")]
|
||||
[Host(module = "gfx2d", name = "draw_text", version = 1)]
|
||||
[Capability(name = "gfx2d")]
|
||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
High-level 2D drawing commands.
|
||||
""")]
|
||||
declare service Gfx
|
||||
{
|
||||
[Doc(markdown = """
|
||||
Clears the render target.
|
||||
|
||||
- `color`: fill color.
|
||||
""")]
|
||||
fn clear(color: Color) -> void
|
||||
{
|
||||
LowGfx.clear(color);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a filled rectangle.
|
||||
|
||||
- `x`: left coordinate.
|
||||
- `y`: top coordinate.
|
||||
- `w`: rectangle width.
|
||||
- `h`: rectangle height.
|
||||
- `color`: fill color.
|
||||
""")]
|
||||
fn fill_rect(x: int, y: int, w: int, h: int, color: Color) -> void
|
||||
{
|
||||
LowGfx.fill_rect(x, y, w, h, color);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a line segment.
|
||||
|
||||
- `x1`: start x coordinate.
|
||||
- `y1`: start y coordinate.
|
||||
- `x2`: end x coordinate.
|
||||
- `y2`: end y coordinate.
|
||||
- `color`: line color.
|
||||
""")]
|
||||
fn draw_line(x1: int, y1: int, x2: int, y2: int, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_line(x1, y1, x2, y2, color);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a circle outline.
|
||||
|
||||
- `x`: center x coordinate.
|
||||
- `y`: center y coordinate.
|
||||
- `r`: radius.
|
||||
- `color`: outline color.
|
||||
""")]
|
||||
fn draw_circle(x: int, y: int, r: int, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_circle(x, y, r, color);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a filled circle.
|
||||
|
||||
- `x`: center x coordinate.
|
||||
- `y`: center y coordinate.
|
||||
- `r`: radius.
|
||||
- `border_color`: outline color.
|
||||
- `fill_color`: fill color.
|
||||
""")]
|
||||
fn draw_disc(x: int, y: int, r: int, border_color: Color, fill_color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_disc(x, y, r, border_color, fill_color);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws a filled rectangle with a border.
|
||||
|
||||
- `x`: left coordinate.
|
||||
- `y`: top coordinate.
|
||||
- `w`: rectangle width.
|
||||
- `h`: rectangle height.
|
||||
- `border_color`: border color.
|
||||
- `fill_color`: fill color.
|
||||
""")]
|
||||
fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_square(x, y, w, h, border_color, fill_color);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Draws text at a screen position.
|
||||
|
||||
- `x`: left coordinate.
|
||||
- `y`: baseline coordinate.
|
||||
- `message`: text to draw.
|
||||
- `color`: text color.
|
||||
""")]
|
||||
fn draw_text(x: int, y: int, message: str, color: Color) -> void
|
||||
{
|
||||
LowGfx.draw_text(x, y, message, color);
|
||||
|
||||
@ -1,85 +1,169 @@
|
||||
[Doc(markdown = """
|
||||
Root input device provider.
|
||||
""")]
|
||||
[BuiltinType(name = "input", version = 1)]
|
||||
declare builtin type InputType() {
|
||||
[Doc(markdown = """
|
||||
Returns the gamepad input state.
|
||||
""")]
|
||||
[IntrinsicCall(name = "pad", version = 1)]
|
||||
fn pad() -> InputPad;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the touch input state.
|
||||
""")]
|
||||
[IntrinsicCall(name = "touch", version = 1)]
|
||||
fn touch() -> InputTouch;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Represents the current gamepad state.
|
||||
""")]
|
||||
[BuiltinType(name = "input.pad", version = 1)]
|
||||
declare builtin type InputPad() {
|
||||
[Doc(markdown = """
|
||||
Returns the up directional button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "up", version = 1)]
|
||||
fn up() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the down directional button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "down", version = 1)]
|
||||
fn down() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the left directional button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "left", version = 1)]
|
||||
fn left() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the right directional button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "right", version = 1)]
|
||||
fn right() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the A action button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "a", version = 1)]
|
||||
fn a() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the B action button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "b", version = 1)]
|
||||
fn b() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the X action button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "x", version = 1)]
|
||||
fn x() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the Y action button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "y", version = 1)]
|
||||
fn y() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the left shoulder button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "l", version = 1)]
|
||||
fn l() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the right shoulder button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "r", version = 1)]
|
||||
fn r() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the start button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "start", version = 1)]
|
||||
fn start() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the select button.
|
||||
""")]
|
||||
[IntrinsicCall(name = "select", version = 1)]
|
||||
fn select() -> InputButton;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Represents the current touch input state.
|
||||
""")]
|
||||
[BuiltinType(name = "input.touch", version = 1)]
|
||||
declare builtin type InputTouch() {
|
||||
[Doc(markdown = """
|
||||
Returns the touch contact as a button state.
|
||||
""")]
|
||||
[IntrinsicCall(name = "button", version = 1)]
|
||||
fn button() -> InputButton;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the current touch x coordinate.
|
||||
""")]
|
||||
[IntrinsicCall(name = "x", version = 1)]
|
||||
fn x() -> int;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the current touch y coordinate.
|
||||
""")]
|
||||
[IntrinsicCall(name = "y", version = 1)]
|
||||
fn y() -> int;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Represents one digital input button.
|
||||
""")]
|
||||
[BuiltinType(name = "input.button", version = 1)]
|
||||
declare builtin type InputButton() {
|
||||
[Doc(markdown = """
|
||||
Returns true on the frame where the button becomes down.
|
||||
""")]
|
||||
[IntrinsicCall(name = "pressed", version = 1)]
|
||||
fn pressed() -> bool;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns true on the frame where the button becomes up.
|
||||
""")]
|
||||
[IntrinsicCall(name = "released", version = 1)]
|
||||
fn released() -> bool;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns true while the button is currently down.
|
||||
""")]
|
||||
[IntrinsicCall(name = "down", version = 1)]
|
||||
fn down() -> bool;
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns how long the button has been held.
|
||||
""")]
|
||||
[IntrinsicCall(name = "hold", version = 1)]
|
||||
fn hold() -> int;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
High-level access to player input devices.
|
||||
""")]
|
||||
declare service Input
|
||||
{
|
||||
[Doc(markdown = """
|
||||
Returns the gamepad input state.
|
||||
""")]
|
||||
fn pad() -> InputPad
|
||||
{
|
||||
return InputType.pad();
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Returns the touch input state.
|
||||
""")]
|
||||
fn touch() -> InputTouch
|
||||
{
|
||||
return InputType.touch();
|
||||
|
||||
@ -1,60 +1,134 @@
|
||||
[Doc(markdown = """
|
||||
Low-level logging host bindings.
|
||||
""")]
|
||||
declare host LowLog {
|
||||
[Doc(markdown = """
|
||||
Writes a log message at a numeric level.
|
||||
|
||||
- `level`: severity level.
|
||||
- `message`: text to write.
|
||||
""")]
|
||||
[Host(module = "log", name = "write", version = 1)]
|
||||
[Capability(name = "log")]
|
||||
fn write(level: int, message: str) -> void;
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a tagged log message at a numeric level.
|
||||
|
||||
- `level`: severity level.
|
||||
- `tag`: application-defined tag.
|
||||
- `message`: text to write.
|
||||
""")]
|
||||
[Host(module = "log", name = "write_tag", version = 1)]
|
||||
[Capability(name = "log")]
|
||||
fn write_tag(level: int, tag: int, message: str) -> void;
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
High-level logging commands.
|
||||
""")]
|
||||
declare service Log
|
||||
{
|
||||
[Doc(markdown = """
|
||||
Writes a trace log message.
|
||||
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn trace(msg: str) -> void
|
||||
{
|
||||
LowLog.write(0, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a debug log message.
|
||||
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn debug(msg: str) -> void
|
||||
{
|
||||
LowLog.write(1, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes an informational log message.
|
||||
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn info(msg: str) -> void
|
||||
{
|
||||
LowLog.write(2, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a warning log message.
|
||||
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn warn(msg: str) -> void
|
||||
{
|
||||
LowLog.write(3, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a failure log message.
|
||||
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn failure(msg: str) -> void
|
||||
{
|
||||
LowLog.write(4, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a tagged trace log message.
|
||||
|
||||
- `tag`: application-defined tag.
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn trace(tag: int, msg: str) -> void
|
||||
{
|
||||
LowLog.write_tag(0, tag, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a tagged debug log message.
|
||||
|
||||
- `tag`: application-defined tag.
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn debug(tag: int, msg: str) -> void
|
||||
{
|
||||
LowLog.write_tag(1, tag, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a tagged informational log message.
|
||||
|
||||
- `tag`: application-defined tag.
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn info(tag: int, msg: str) -> void
|
||||
{
|
||||
LowLog.write_tag(2, tag, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a tagged warning log message.
|
||||
|
||||
- `tag`: application-defined tag.
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn warn(tag: int, msg: str) -> void
|
||||
{
|
||||
LowLog.write_tag(3, tag, msg);
|
||||
}
|
||||
|
||||
[Doc(markdown = """
|
||||
Writes a tagged failure log message.
|
||||
|
||||
- `tag`: application-defined tag.
|
||||
- `msg`: text to write.
|
||||
""")]
|
||||
fn failure(tag: int, msg: str) -> void
|
||||
{
|
||||
LowLog.write_tag(4, tag, msg);
|
||||
|
||||
@ -377,6 +377,44 @@ class PbsParserTest {
|
||||
assertTrue(((PbsAst.AttributeDocTextBlockValue) signatureDoc).value().endsWith("\"\"\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPreserveDocumentationAttributesOnStructAndServiceMethods() {
|
||||
final var source = """
|
||||
declare struct Sprite() {
|
||||
[Doc(markdown = \"""
|
||||
Draws this sprite.
|
||||
\""")]
|
||||
fn draw() -> void {}
|
||||
}
|
||||
|
||||
[Doc(markdown = \"""
|
||||
Provides drawing commands.
|
||||
\""")]
|
||||
declare service Gfx {
|
||||
[Doc(markdown = \"""
|
||||
Clears the screen.
|
||||
\""")]
|
||||
fn clear() -> void {}
|
||||
}
|
||||
""";
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
final var fileId = new FileId(0);
|
||||
|
||||
final PbsAst.File ast = PbsParser.parse(
|
||||
PbsLexer.lex(source, fileId, diagnostics),
|
||||
fileId,
|
||||
diagnostics,
|
||||
PbsParser.ParseMode.ORDINARY);
|
||||
|
||||
assertTrue(diagnostics.isEmpty(), "Parser should accept Doc attributes on struct and service methods");
|
||||
final var structDecl = assertInstanceOf(PbsAst.StructDecl.class, ast.topDecls().getFirst());
|
||||
assertEquals("Doc", structDecl.methods().getFirst().attributes().getFirst().name());
|
||||
|
||||
final var serviceDecl = assertInstanceOf(PbsAst.ServiceDecl.class, ast.topDecls().get(1));
|
||||
assertEquals("Doc", serviceDecl.attributes().getFirst().name());
|
||||
assertEquals("Doc", serviceDecl.methods().getFirst().attributes().getFirst().name());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRecoverFromMalformedReservedDeclarationsInInterfaceModuleMode() {
|
||||
final var source = """
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user