From 59e33d663979c7a0a5aea5b7d69bdddb8dea5d0f Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 10 Mar 2026 11:51:21 +0000 Subject: [PATCH] a little bit of gfx --- .../pbs/PbsReservedMetadataExtractor.java | 4 + .../lowering/PbsExecutableBodyLowerer.java | 41 +++++++ .../PbsExecutableCallsiteEmitter.java | 72 +++++++++-- .../PbsExecutableLoweringContext.java | 28 +++++ .../lowering/PbsExecutableLoweringModels.java | 1 + .../PbsExecutableMetadataIndexFactory.java | 1 + .../resources/stdlib/1/core/color/main.pbs | 7 +- .../main/resources/stdlib/1/sdk/gfx/main.pbs | 108 +++++++++++++++- .../resources/stdlib/1/sdk/gfx/mod.barrel | 3 +- .../compiler/pbs/PbsFrontendCompilerTest.java | 13 +- .../PbsGateUSdkInterfaceConformanceTest.java | 5 +- .../services/PBSFrontendPhaseServiceTest.java | 116 +++++++++++++++++- .../compiler/models/IRReservedMetadata.java | 4 + test-projects/main/cartridge/manifest.json | 2 +- test-projects/main/cartridge/program.pbx | Bin 2734 -> 4895 bytes test-projects/main/src/main.pbs | 8 ++ 16 files changed, 378 insertions(+), 35 deletions(-) diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java index 34bed80f..46bd9ac5 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java @@ -79,6 +79,10 @@ public final class PbsReservedMetadataExtractor { abiModule, abiMethod, abiVersion, + switch (signature.returnKind()) { + case INFERRED_UNIT, EXPLICIT_UNIT -> 0; + case PLAIN, RESULT -> 1; + }, capabilityAttribute.isPresent(), capability, signature.span())); diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java index e586f142..6e3c0c65 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java @@ -28,6 +28,7 @@ final class PbsExecutableBodyLowerer { final PbsExecutableCallableRegistry callableRegistry, final IntrinsicTable intrinsicIdTable) { final var localSlotByNameId = initialLocalSlots(functionDecl, nameTable); + final var localOwnerByNameId = initialLocalOwners(functionDecl, nameTable, metadataIndex); final var context = new PbsExecutableLoweringContext( diagnostics, nameTable, @@ -35,6 +36,7 @@ final class PbsExecutableBodyLowerer { callableRegistry, intrinsicIdTable, localSlotByNameId, + localOwnerByNameId, functionDecl.parameters().size()); final var terminated = lowerBlock(functionDecl.body(), context); finalizeFunctionInstructions(functionDecl, context, terminated); @@ -70,6 +72,20 @@ final class PbsExecutableBodyLowerer { return localSlotByNameId; } + private Map initialLocalOwners( + final PbsAst.FunctionDecl functionDecl, + final NameTable nameTable, + final PbsExecutableMetadataIndex metadataIndex) { + final var localOwnerByNameId = new HashMap(); + for (final var parameter : functionDecl.parameters()) { + final var ownerCanonical = resolveBuiltinOwnerCanonical(parameter.typeRef(), metadataIndex); + if (!ownerCanonical.isBlank()) { + localOwnerByNameId.put(nameTable.register(parameter.name()), ownerCanonical); + } + } + return localOwnerByNameId; + } + private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) { return switch (functionDecl.returnKind()) { case INFERRED_UNIT, EXPLICIT_UNIT -> 0; @@ -137,8 +153,10 @@ final class PbsExecutableBodyLowerer { private boolean lowerLetStatement( final PbsAst.LetStatement letStatement, final PbsExecutableLoweringContext context) { + final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(letStatement.initializer(), context); lowerExpression(letStatement.initializer(), context); final var localSlot = context.declareLocalSlot(letStatement.name()); + context.bindLocalOwner(letStatement.name(), localOwner); emitSetLocal(localSlot, letStatement.span(), context); return false; } @@ -161,17 +179,40 @@ final class PbsExecutableBodyLowerer { return false; } if (assignStatement.operator() == PbsAst.AssignOperator.ASSIGN) { + final var localOwner = callsiteEmitter.resolveExpressionOwnerForLowering(assignStatement.value(), context); lowerExpression(assignStatement.value(), context); + context.bindLocalOwner(target.rootName(), localOwner); emitSetLocal(targetSlot, assignStatement.span(), context); return false; } emitGetLocal(targetSlot, assignStatement.span(), context); lowerExpression(assignStatement.value(), context); emitBinaryOperatorInstruction(compoundAssignBinaryOperator(assignStatement.operator()), assignStatement.span(), context); + context.bindLocalOwner(target.rootName(), ""); emitSetLocal(targetSlot, assignStatement.span(), context); return false; } + private String resolveBuiltinOwnerCanonical( + final PbsAst.TypeRef typeRef, + final PbsExecutableMetadataIndex metadataIndex) { + final var unwrapped = unwrapGroup(typeRef); + if (unwrapped == null || unwrapped.kind() != PbsAst.TypeRefKind.SIMPLE) { + return ""; + } + return metadataIndex.builtinCanonicalBySourceType().getOrDefault(unwrapped.name(), ""); + } + + private PbsAst.TypeRef unwrapGroup(final PbsAst.TypeRef typeRef) { + if (typeRef == null) { + return null; + } + if (typeRef.kind() != PbsAst.TypeRefKind.GROUP) { + return typeRef; + } + return unwrapGroup(typeRef.inner()); + } + private boolean lowerReturnStatement( final PbsAst.ReturnStatement returnStatement, final PbsExecutableLoweringContext context) { diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java index 80584cf0..c5b40327 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java @@ -43,10 +43,28 @@ final class PbsExecutableCallsiteEmitter { final PbsExecutableLoweringContext context) { return new PbsResolvedCallsiteCandidates( resolveCallableCandidates(callExpr, calleeIdentity, context), - context.hostByMethodName().getOrDefault(calleeIdentity.memberNameId(), List.of()), + resolveHostCandidates(callExpr, calleeIdentity, context), resolveIntrinsicCandidates(callExpr, calleeIdentity, context)); } + private List resolveHostCandidates( + final PbsAst.CallExpr callExpr, + final PbsCalleeIdentity calleeIdentity, + final PbsExecutableLoweringContext context) { + final var hostCandidates = context.hostByMethodName().getOrDefault(calleeIdentity.memberNameId(), List.of()); + if (hostCandidates.isEmpty()) { + return List.of(); + } + final var ownerHint = hostOwnerHint(callExpr.callee()); + if (ownerHint.isBlank()) { + return hostCandidates; + } + final var filtered = hostCandidates.stream() + .filter(candidate -> ownerHint.equals(candidate.ownerName())) + .toList(); + return filtered; + } + private List resolveCallableCandidates( final PbsAst.CallExpr callExpr, final PbsCalleeIdentity calleeIdentity, @@ -126,7 +144,7 @@ final class PbsExecutableCallsiteEmitter { final PbsAst.CallExpr callExpr, final PbsExecutableLoweringContext context, final IRReservedMetadata.HostMethodBinding host) { - final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee()); + final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee(), context); context.instructions().add(new IRBackendExecutableFunction.Instruction( IRBackendExecutableFunction.InstructionKind.CALL_HOST, "", @@ -135,7 +153,7 @@ final class PbsExecutableCallsiteEmitter { host.abiMethod(), host.abiVersion(), effectiveArgSlots, - 0), + host.retSlots()), null, callExpr.span())); } @@ -144,7 +162,7 @@ final class PbsExecutableCallsiteEmitter { final PbsAst.CallExpr callExpr, final PbsExecutableLoweringContext context, final IRReservedMetadata.IntrinsicSurface intrinsic) { - final var effectiveArgSlots = intrinsic.argSlots() + implicitReceiverArgSlots(callExpr.callee()); + final var effectiveArgSlots = intrinsic.argSlots() + implicitReceiverArgSlots(callExpr.callee(), context); context.instructions().add(new IRBackendExecutableFunction.Instruction( IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC, "", @@ -176,7 +194,7 @@ final class PbsExecutableCallsiteEmitter { reportUnsupportedLowering("executable lowering resolved callable without signature metadata", callExpr.span(), context); return; } - final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee()); + final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee(), context); context.instructions().add(new IRBackendExecutableFunction.Instruction( IRBackendExecutableFunction.InstructionKind.CALL_FUNC, calleeSignature.moduleId(), @@ -211,15 +229,32 @@ final class PbsExecutableCallsiteEmitter { }; } + String resolveExpressionOwnerForLowering( + final PbsAst.Expression expression, + final PbsExecutableLoweringContext context) { + return resolveExpressionOwner(expression, context); + } + + private String hostOwnerHint(final PbsAst.Expression callee) { + return switch (callee) { + case PbsAst.MemberExpr memberExpr -> { + final var rootName = memberRootName(memberExpr.receiver()); + yield rootName == null ? "" : rootName; + } + case PbsAst.GroupExpr groupExpr -> hostOwnerHint(groupExpr.expression()); + default -> ""; + }; + } + private String resolveExpressionOwner( final PbsAst.Expression expression, final PbsExecutableLoweringContext context) { if (expression == null) { return ""; } - return switch (expression) { + return switch (expression) { case PbsAst.IdentifierExpr identifierExpr -> - context.builtinConstOwnerByNameId().getOrDefault(context.nameTable().register(identifierExpr.name()), ""); + resolveIdentifierOwner(identifierExpr.name(), context); case PbsAst.CallExpr callExpr -> resolveCallReturnOwner(callExpr, context); case PbsAst.MemberExpr memberExpr -> resolveExpressionOwner(memberExpr.receiver(), context); case PbsAst.GroupExpr groupExpr -> resolveExpressionOwner(groupExpr.expression(), context); @@ -227,6 +262,16 @@ final class PbsExecutableCallsiteEmitter { }; } + private String resolveIdentifierOwner( + final String identifier, + final PbsExecutableLoweringContext context) { + final var localOwner = context.resolveLocalOwner(identifier); + if (!localOwner.isBlank()) { + return localOwner; + } + return context.builtinConstOwnerByNameId().getOrDefault(context.nameTable().register(identifier), ""); + } + private String resolveCallReturnOwner( final PbsAst.CallExpr callExpr, final PbsExecutableLoweringContext context) { @@ -302,17 +347,22 @@ final class PbsExecutableCallsiteEmitter { }; } - private int implicitReceiverArgSlots(final PbsAst.Expression callee) { + private int implicitReceiverArgSlots( + final PbsAst.Expression callee, + final PbsExecutableLoweringContext context) { if (!(callee instanceof PbsAst.MemberExpr memberExpr)) { return 0; } - return receiverProducesRuntimeValue(memberExpr.receiver()) ? 1 : 0; + return receiverProducesRuntimeValue(memberExpr.receiver(), context) ? 1 : 0; } - private boolean receiverProducesRuntimeValue(final PbsAst.Expression receiver) { + private boolean receiverProducesRuntimeValue( + final PbsAst.Expression receiver, + final PbsExecutableLoweringContext context) { return switch (receiver) { case PbsAst.CallExpr ignored -> true; - case PbsAst.GroupExpr groupExpr -> receiverProducesRuntimeValue(groupExpr.expression()); + case PbsAst.IdentifierExpr identifierExpr -> context.resolveLocalSlot(identifierExpr.name()) != null; + case PbsAst.GroupExpr groupExpr -> receiverProducesRuntimeValue(groupExpr.expression(), context); default -> false; }; } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringContext.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringContext.java index a23e28fb..3551029d 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringContext.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringContext.java @@ -17,6 +17,7 @@ final class PbsExecutableLoweringContext { private final PbsExecutableCallableRegistry callableRegistry; private final IntrinsicTable intrinsicIdTable; private final Map localSlotByNameId; + private final Map localOwnerByNameId; private final ArrayList instructions = new ArrayList<>(); private final ArrayDeque loopTargets = new ArrayDeque<>(); private int nextLabelId = 0; @@ -29,6 +30,7 @@ final class PbsExecutableLoweringContext { final PbsExecutableCallableRegistry callableRegistry, final IntrinsicTable intrinsicIdTable, final Map localSlotByNameId, + final Map localOwnerByNameId, final int initialLocalSlot) { this.diagnostics = diagnostics; this.nameTable = nameTable; @@ -36,6 +38,7 @@ final class PbsExecutableLoweringContext { this.callableRegistry = callableRegistry; this.intrinsicIdTable = intrinsicIdTable; this.localSlotByNameId = localSlotByNameId == null ? new HashMap<>() : localSlotByNameId; + this.localOwnerByNameId = localOwnerByNameId == null ? new HashMap<>() : localOwnerByNameId; this.nextLocalSlot = Math.max(0, initialLocalSlot); } @@ -55,6 +58,10 @@ final class PbsExecutableLoweringContext { return metadataIndex.builtinConstOwnerByNameId(); } + Map builtinCanonicalBySourceType() { + return metadataIndex.builtinCanonicalBySourceType(); + } + Map> intrinsicByOwnerAndMethod() { return metadataIndex.intrinsicByOwnerAndMethod(); } @@ -83,6 +90,27 @@ final class PbsExecutableLoweringContext { return localSlotByNameId; } + String resolveLocalOwner(final String localName) { + if (localName == null || localName.isBlank()) { + return ""; + } + return localOwnerByNameId.getOrDefault(nameTable.register(localName), ""); + } + + void bindLocalOwner( + final String localName, + final String ownerCanonicalName) { + if (localName == null || localName.isBlank()) { + return; + } + final var nameId = nameTable.register(localName); + if (ownerCanonicalName == null || ownerCanonicalName.isBlank()) { + localOwnerByNameId.remove(nameId); + return; + } + localOwnerByNameId.put(nameId, ownerCanonicalName); + } + Integer resolveLocalSlot(final String localName) { if (localName == null || localName.isBlank()) { return null; diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringModels.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringModels.java index 32ce1fb3..e75b7828 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringModels.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringModels.java @@ -42,6 +42,7 @@ public class PbsExecutableLoweringModels { record PbsExecutableMetadataIndex( Map> hostByMethodName, + Map builtinCanonicalBySourceType, Map builtinConstOwnerByNameId, Map> intrinsicByOwnerAndMethod, Map intrinsicReturnOwnerByCanonical) { diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableMetadataIndexFactory.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableMetadataIndexFactory.java index 05c6a315..b5ee5e92 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableMetadataIndexFactory.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableMetadataIndexFactory.java @@ -42,6 +42,7 @@ final class PbsExecutableMetadataIndexFactory { diagnostics); return new PbsExecutableMetadataIndex( hostByMethodName, + builtinCanonicalBySourceType, builtinConstOwnerByNameId, intrinsicByOwnerAndMethod, intrinsicReturnOwnerByCanonical); diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/core/color/main.pbs b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/core/color/main.pbs index 9bc1c8ac..70c87c0d 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/core/color/main.pbs +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/core/color/main.pbs @@ -1,10 +1,5 @@ [BuiltinType(name = "Color", version = 1)] declare builtin type Color( - pub r: int, - pub g: int, - pub b: int, - pub a: int + pub raw: int ) { - [IntrinsicCall(name = "core.color.pack", version = 1)] - fn pack() -> int; } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/main.pbs b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/main.pbs index eb8dc359..a3f6edd9 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/main.pbs +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/main.pbs @@ -1,7 +1,109 @@ import { Color } from @core:color; -declare host Gfx { - [Host(module = "gfx", name = "draw_pixel", version = 1)] +declare host LowGfx { + [Host(module = "gfx", name = "clear", version = 1)] [Capability(name = "gfx")] - fn draw_pixel(x: int, y: int, color: Color) -> void; + fn clear(color: Color) -> void; + + [Host(module = "gfx", name = "fill_rect", version = 1)] + [Capability(name = "gfx")] + fn fill_rect(x: int, y: int, w: int, h: int, color: Color) -> void; + + [Host(module = "gfx", name = "draw_line", version = 1)] + [Capability(name = "gfx")] + fn draw_line(x1: int, y1: int, x2: int, y2: int, color: Color) -> void; + + [Host(module = "gfx", name = "draw_circle", version = 1)] + [Capability(name = "gfx")] + fn draw_circle(x: int, y: int, r: int, color: Color) -> void; + + [Host(module = "gfx", name = "draw_disc", version = 1)] + [Capability(name = "gfx")] + fn draw_disc(x: int, y: int, r: int, border_color: Color, fill_color: Color) -> void; + + [Host(module = "gfx", name = "draw_square", version = 1)] + [Capability(name = "gfx")] + fn draw_square(x: int, y: int, w: int, h: int, border_color: Color, fill_color: Color) -> void; + + [Host(module = "gfx", name = "set_sprite", version = 1)] + [Capability(name = "gfx")] + fn set_sprite( + asset_name: str, + index: int, + x: int, + y: int, + tile_id: int, + palette_id: int, + active: bool, + flip_x: bool, + flip_y: bool, + priority: int + ) -> int; + + [Host(module = "gfx", name = "draw_text", version = 1)] + [Capability(name = "gfx")] + fn draw_text(x: int, y: int, message: str, color: Color) -> void; + + [Host(module = "gfx", name = "clear_565", version = 1)] + [Capability(name = "gfx")] + fn clear_565(color_565: int) -> void; +} + +declare service Gfx +{ + fn clear(color: Color) -> void + { + LowGfx.clear(color); + } + + fn fill_rect(x: int, y: int, w: int, h: int, color: Color) -> void + { + LowGfx.fill_rect(x, y, w, h, color); + } + + fn draw_line(x1: int, y1: int, x2: int, y2: int, color: Color) -> void + { + LowGfx.draw_line(x1, y1, x2, y2, color); + } + + fn draw_circle(x: int, y: int, r: int, color: Color) -> void + { + LowGfx.draw_circle(x, y, r, 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); + } + + 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); + } + + fn set_sprite( + asset_name: str, + 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(asset_name, index, x, y, tile_id, palette_id, active, flip_x, flip_y, priority); + } + + fn draw_text(x: int, y: int, message: str, color: Color) -> void + { + LowGfx.draw_text(x, y, message, color); + } + + fn clear_565(color_565: int) -> void + { + LowGfx.clear_565(color_565); + } } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/mod.barrel b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/mod.barrel index b08ba680..01344c23 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/mod.barrel +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/gfx/mod.barrel @@ -1 +1,2 @@ -pub host Gfx; +mod host LowGfx; +pub service Gfx; diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java index 74e1c175..e2351072 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java @@ -304,12 +304,8 @@ class PbsFrontendCompilerTest { final var source = """ [BuiltinType(name = "Color", version = 1)] declare builtin type Color( - pub r: int, - pub g: int, - pub b: int + pub raw: int ) { - [IntrinsicCall(name = "core.color.pack")] - fn pack() -> int; } declare host Gfx { [Host(module = "gfx", name = "draw_pixel", version = 1)] @@ -332,7 +328,7 @@ class PbsFrontendCompilerTest { assertEquals(1, fileBackend.reservedMetadata().requiredCapabilities().size()); assertEquals("gfx", fileBackend.reservedMetadata().requiredCapabilities().getFirst()); assertEquals("Color", fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().canonicalTypeName()); - assertEquals(3, fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().fields().size()); + assertEquals(1, fileBackend.reservedMetadata().builtinTypeSurfaces().getFirst().fields().size()); } @Test @@ -453,10 +449,7 @@ class PbsFrontendCompilerTest { final var source = """ [BuiltinType(name = "Color", version = 1)] declare builtin type Color( - pub r: int, - pub g: int, - pub b: int, - pub a: int + pub raw: int ) { } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java index 42e4e53d..f736f3b9 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java @@ -116,7 +116,10 @@ class PbsGateUSdkInterfaceConformanceTest { .anyMatch(t -> t.sourceTypeName().equals("InputButton") && t.intrinsics().stream().anyMatch(i -> i.canonicalName().equals("input.button.hold")))); assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream() - .anyMatch(h -> h.ownerName().equals("Gfx"))); + .anyMatch(h -> h.ownerName().equals("LowGfx") + && h.abiModule().equals("gfx") + && h.abiMethod().equals("clear") + && h.abiVersion() == 1)); assertTrue(positive.irBackend().getReservedMetadata().hostMethodBindings().stream() .anyMatch(h -> h.ownerName().equals("LowLog") && h.abiModule().equals("log") diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java index c0ccf6a9..45a9401b 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java @@ -525,7 +525,7 @@ class PBSFrontendPhaseServiceTest { d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name()))); assertEquals(0, irBackend.getFunctions().size()); assertTrue(irBackend.getReservedMetadata().builtinTypeSurfaces().stream() - .anyMatch(t -> t.sourceTypeName().equals("Color") && t.fields().size() == 4)); + .anyMatch(t -> t.sourceTypeName().equals("Color") && t.fields().size() == 1)); } @Test @@ -578,7 +578,56 @@ class PBSFrontendPhaseServiceTest { d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name()))); assertEquals(0, irBackend.getFunctions().size()); assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream() - .anyMatch(h -> h.ownerName().equals("Gfx") && h.sourceMethodName().equals("draw_pixel"))); + .anyMatch(h -> h.ownerName().equals("LowGfx") && h.sourceMethodName().equals("clear"))); + } + + @Test + void shouldLowerSdkGfxServiceFacadeCallsWithoutHostCallableAmbiguity() throws IOException { + final var projectRoot = tempDir.resolve("project-bootstrap-sdk-gfx-facade-call"); + 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 { Gfx } from @sdk:gfx; + + fn frame() -> void + { + Gfx.clear_565(6577); + } + """); + Files.writeString(modBarrel, "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()))); } @Test @@ -1008,6 +1057,69 @@ class PBSFrontendPhaseServiceTest { assertTrue(intrinsicCalls.contains("input.touch.x")); } + @Test + void shouldResolveIntrinsicCallsOnBuiltinValuesStoredInLocals() throws IOException { + final var projectRoot = tempDir.resolve("project-intrinsic-local-builtin-receiver"); + final var sourceRoot = projectRoot.resolve("src"); + Files.createDirectories(sourceRoot); + + final var sourceFile = sourceRoot.resolve("main.pbs"); + final var modBarrel = sourceRoot.resolve("mod.barrel"); + Files.writeString(sourceFile, """ + import { Input } from @sdk:input; + + fn frame() -> void + { + let touch = Input.touch(); + touch.x(); + touch.y(); + } + """); + Files.writeString(modBarrel, "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("main") + .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())), + diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString()); + final var frameExecutable = irBackend.getExecutableFunctions().stream() + .filter(function -> "frame".equals(function.callableName())) + .findFirst() + .orElseThrow(); + final var intrinsicCalls = frameExecutable.instructions().stream() + .filter(instruction -> + instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC + && instruction.intrinsicCall() != null) + .map(instruction -> instruction.intrinsicCall().canonicalName()) + .toList(); + assertTrue(intrinsicCalls.contains("input.touch")); + assertTrue(intrinsicCalls.contains("input.touch.x")); + assertTrue(intrinsicCalls.contains("input.touch.y")); + } + private void registerFile( final ProjectId projectId, final Path projectRoot, diff --git a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java index 832deae0..e262a8a3 100644 --- a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java +++ b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java @@ -32,6 +32,7 @@ public record IRReservedMetadata( String abiModule, String abiMethod, long abiVersion, + int retSlots, boolean capabilityDeclared, String requiredCapability, Span span) { @@ -40,6 +41,9 @@ public record IRReservedMetadata( sourceMethodName = Objects.requireNonNull(sourceMethodName, "sourceMethodName"); abiModule = Objects.requireNonNull(abiModule, "abiModule"); abiMethod = Objects.requireNonNull(abiMethod, "abiMethod"); + if (retSlots < 0) { + throw new IllegalArgumentException("host retSlots must be non-negative"); + } requiredCapability = requiredCapability == null ? "" : requiredCapability; span = Objects.requireNonNull(span, "span"); } diff --git a/test-projects/main/cartridge/manifest.json b/test-projects/main/cartridge/manifest.json index f9301734..4f93d84a 100644 --- a/test-projects/main/cartridge/manifest.json +++ b/test-projects/main/cartridge/manifest.json @@ -6,5 +6,5 @@ "app_version": "0.1.0", "app_mode": "Game", "entrypoint": "0", - "capabilities": ["log"] + "capabilities": ["log", "gfx"] } diff --git a/test-projects/main/cartridge/program.pbx b/test-projects/main/cartridge/program.pbx index 32ba652d81e5bd503d4f3c23cde70b43d72462a4..a090a1490b7ba8e3b58a69f40cf4bbaee939538a 100644 GIT binary patch literal 4895 zcma)=dyEy;9mmhz7rU^o3tnA7s&|PRV#o%CHO5jyFH{g0x++1%h4r#~ce!@i<<4Fn zQlGe`gi@r0f>~#NZ?NXrqSG)c8kZTVtw1(^6ad`JVfm-Puv1Cw$NO ze$MZA&dfb?W_E_=^tDc<+}Gb;wb0#(66iHF4O2opglc!GbNqA9F~Kd>#d*e$aPBMm z)L&LdHE}{J*PALV%BB`)I&*EQ?xl+tW`lYkYdl5l0>;gWjnFLJ*&GuEDdz0%2~v#{ z>XT%utkL=gG8`5_be1?VY&P9gD>)S-WU_EF2wwNi5zRi?_$(x5wfeWAVTx zQqE1~T%4O<`KJWe2>@%%YaewB0WN=?`?ecy%Dx%*S&mQQDXKRd!R3#&F%P&?18?)*-vX4E057WkBZMF=eFVZAniGochMh^c9)9oqz}+Y z0p&CF1=8+T5g$kA2`K+U|3()ID2j{63n>3l#iDE9tBA|sOFi)?0$+(PH&$Fc#ds*b z8eL&rgRezR#$)jH=o;e&{6uuU@lo1Fc!9D0G+lw(jVI%$qnnNA;OC+x#!dL^&@$si z_#C>`xC_4+t%}6Oea0>LZnV~T1^#ZdA;Q0eH%It4u&$Fz0Y8mR^k5O)4)_VA>#DK~ zzZ*SS#Qu1=2f}<^bCqyDU1ya8jC~uuT13z9!iOS!1U_v15?<${J)@$(gU8X4BD$~e z$I!>dVf>RAe`*}gr{}Rs6>}%iu_AgN37?4YSlHjlH05}mPX)(Rxfp*rsy6;1{zqs; zES`+9p6)8M$-Ne(jIYDrfG&u{#p8@;;;-aJ)V@;*;9sFh#yR{tbXhEait!F&k09+y zm0kGVs3{T`&o;gvuj9=z)_cRB(LCe*_>a*|vHT9>Bc+W2m!y9!jP(74}GZ9^XzZ^!RKM~!#m_n=RV`|vNIPmSyGFC*P2G_H^1uSWkc zUV~qY{%O1pzaD*UyaB%vm2jOjeiMExsx~uRz_#mG~-D zFdl}lMJtUzXTIU^oyPaWBjHuXzvlQ`&>G{d`0Z%D@dNlBXp`|n_?_rJ<45pMpa+bf z!oP_0jL`4dhd+SyOc1|@e*-;h{1*N_wAc9e_&=hTjem|miVhkV@P9&jj_A$i5c}#4 z^tSO^_`~RZ<9+yd&_~7_@!I=GjBnuHpNI4e(tIuWR`eI+3|`+JpBc}H_&%g{S3HmZ1zKo41HTUG8YuoX=k{N8v++rM34V!j z8SN0X%=qubEAh7)YoDEt*Y~65n+M;B`i$q}Z$fL0XW=twgYo%z-HZC()cE04BhbCZ zNqimJZafM<25DVreDwp&F5|8tU(8 zZM>wB(f;#Oo$Wzpd23rPAX`TYbA5MvuD4B759;j7b!YXs(brr51iksCnIOv(VsLID zyP}}`H=Lj&*VWY;WZMcn8~taaCg{x;T6>oSxk8pZ)SrD*PiI4WcHz=a9&v*^sv{lQ zAm|A+vnJCJ-=U9D9a0^+?v5UvW|V8;a6lo*v}K3KJM@tdj#!=vy3dHQPO8*(cC09O zeu)!KtL}TMialE6NbfKgS>%MjpqAag$_al7qrF<@NUzONT<3)U(1~Vip$E;}ztWL8 lhPr{J*8j#NIO(pQ&T_Y0i=S}XE54_y*wb3bbSlTW{{x>O?`i-5 literal 2734 zcmZ{mU5J!b7{`yZV~!T?hohsh*=$*fgbaq&MM!O4h!jj8h_0*atUJ!mvOCkh&AKH~ zDCUBBAzoBO7ZuuvMS^T1S!f@`reIx!7bO%+7g8Wy#1gbtzyF!%b!QLu;QyTG_dL%z z?|ILecaQnbr<`oAZ*Sam6E2}S=qogi5@?h_3r-XM7|q`|)#ur_z`1*gDW1&G+?UIi zpUu|#i`k(GbQ)lGl18=dxLU&}jL>1xpB_iRD@!V8na${ zF<+CnF4`ixrH(pXS3_;a1N0+ksqq2&F{Ja69Yn976#`6ovA!_b9&`xlla;-S-bOk( znfMrbP=JX~pmhT52m46KgHT-net-oxvU?*lyL6* zI=aK~2k4UV82)i|**LDBG(Jo2YjnkU0{=YH8OW3u|4~PG24BycGX535hNg{w#Qz5+ zkSwlG882e&Qnb*x9e+7WH{``jjGOWGymsRU@O#i5#;fsnpq0jt@XRu-`-0BteZECG z;|uf`k}?*yU=>$Zu}m!(YS!$hji|$-+*64n~WFZ4*Z6b%bLb=E zqxfH-lg1JL1k#9zKA#`-p1M&B53px4@U&Kf^Wa}DW?#sAVInEwak zB>p_4->LGy)1=^Ej8DVO@T74UycJzB-iE&eT{V6jzkvQUz6-w}O&Pbc*KKIpIE|k{ z32OM~mf^Rfl(DY3+t5Pei>ze@9REK^xBITBFjQnde~CU;Ev@b=_KxTynH^x{^*}L- zsu9!74rsCz(j4W=uBtAjMxNI&pcWN&7M0Y^tpPqDh)0YTqRN7RwWuaHSS`(UqfxoW Ss;@`p*CRc(LTL^)&iw~;W&%Y3 diff --git a/test-projects/main/src/main.pbs b/test-projects/main/src/main.pbs index f3fea3a7..cd6c7fb9 100644 --- a/test-projects/main/src/main.pbs +++ b/test-projects/main/src/main.pbs @@ -1,8 +1,16 @@ +import { Color } from @core:color; + import { Log } from @sdk:log; import { Input } from @sdk:input; +import { Gfx } from @sdk:gfx; fn frame() -> void { + let touch = Input.touch(); + + Gfx.clear(new Color(6577)); + Gfx.draw_square(touch.x() - 8, touch.y() - 8, 16, 16, new Color(65535), new Color(13271)); + let a = 10; let b = 15; let total = a + b;