implements PLN-0100

This commit is contained in:
bQUARKz 2026-07-15 07:18:19 +01:00
parent e7647165bc
commit 63215ac02f
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
11 changed files with 525 additions and 9 deletions

View File

@ -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"}]}

View File

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

View File

@ -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())) {

View File

@ -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(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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