implements PR008

This commit is contained in:
bQUARKz 2026-03-05 14:15:48 +00:00
parent b4887afacb
commit b84d4c29f6
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
6 changed files with 1979 additions and 0 deletions

View File

@ -6,6 +6,7 @@ import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.pbs.semantics.PbsDeclarationSemanticsValidator;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticsValidator;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList;
@ -14,6 +15,7 @@ import java.util.ArrayList;
public final class PbsFrontendCompiler {
private final PbsDeclarationSemanticsValidator declarationSemanticsValidator = new PbsDeclarationSemanticsValidator();
private final PbsFlowSemanticsValidator flowSemanticsValidator = new PbsFlowSemanticsValidator();
public IRBackendFile compileFile(
final FileId fileId,
@ -29,6 +31,7 @@ public final class PbsFrontendCompiler {
final PbsAst.File ast,
final DiagnosticSink diagnostics) {
declarationSemanticsValidator.validate(ast, diagnostics);
flowSemanticsValidator.validate(ast, diagnostics);
return new IRBackendFile(fileId, lowerFunctions(fileId, ast));
}

View File

@ -13,4 +13,25 @@ public enum PbsSemanticsErrors {
E_SEM_MISSING_CONST_TYPE_ANNOTATION,
E_SEM_MISSING_CONST_INITIALIZER,
E_SEM_INVALID_RETURN_INSIDE_CTOR,
E_SEM_APPLY_NON_CALLABLE_TARGET,
E_SEM_APPLY_UNRESOLVED_OVERLOAD,
E_SEM_APPLY_AMBIGUOUS_OVERLOAD,
E_SEM_BARE_METHOD_EXTRACTION,
E_SEM_INVALID_MEMBER_ACCESS,
E_SEM_IF_NON_BOOL_CONDITION,
E_SEM_IF_BRANCH_TYPE_MISMATCH,
E_SEM_SWITCH_SELECTOR_INVALID,
E_SEM_SWITCH_PATTERN_TYPE_MISMATCH,
E_SEM_SWITCH_DUPLICATE_PATTERN,
E_SEM_SWITCH_ARM_TYPE_MISMATCH,
E_SEM_SWITCH_NON_EXHAUSTIVE,
E_SEM_WHILE_NON_BOOL_CONDITION,
E_SEM_FOR_TYPE_MISMATCH,
E_SEM_NONE_WITHOUT_EXPECTED_OPTIONAL,
E_SEM_ELSE_NON_OPTIONAL_LEFT,
E_SEM_ELSE_FALLBACK_TYPE_MISMATCH,
E_SEM_RESULT_PROPAGATE_NON_RESULT,
E_SEM_RESULT_PROPAGATE_ERROR_MISMATCH,
E_SEM_HANDLE_NON_RESULT,
E_SEM_HANDLE_ERROR_MISMATCH,
}

View File

@ -0,0 +1,37 @@
package p.studio.compiler.pbs.semantics;
import org.junit.jupiter.api.Test;
import p.studio.compiler.pbs.PbsFrontendCompiler;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PbsSemanticsApplyResolutionTest {
@Test
void shouldDifferentiateNonCallableUnresolvedAndAmbiguousApply() {
final var source = """
fn over(x: int) -> int { return x; }
fn over(x: float) -> int { return 0; }
fn pair(a: int, b: int) -> int { return a + b; }
fn demo() -> int {
let a: int = 1 apply ();
let b: int = pair apply 1;
let c: int = over apply missing;
return 0;
}
""";
final var diagnostics = DiagnosticSink.empty();
new PbsFrontendCompiler().compileFile(new FileId(0), source, diagnostics);
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_APPLY_NON_CALLABLE_TARGET.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_APPLY_UNRESOLVED_OVERLOAD.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_APPLY_AMBIGUOUS_OVERLOAD.name())));
}
}

View File

@ -0,0 +1,42 @@
package p.studio.compiler.pbs.semantics;
import org.junit.jupiter.api.Test;
import p.studio.compiler.pbs.PbsFrontendCompiler;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PbsSemanticsControlFlowTest {
@Test
void shouldValidateIfSwitchAndProjectionRules() {
final var source = """
declare enum Mode(Idle, Run);
declare struct State(v: int) {
fn value() -> int { return this.v; }
}
fn flow(m: Mode, s: State) -> int {
let a: int = if 1 { 1 } else { 2 };
let b: int = switch m { Mode.Idle: { 1 } };
let c: int = s.missing;
let d = s.value;
return a + b + c + d;
}
""";
final var diagnostics = DiagnosticSink.empty();
new PbsFrontendCompiler().compileFile(new FileId(0), source, diagnostics);
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_SWITCH_NON_EXHAUSTIVE.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_INVALID_MEMBER_ACCESS.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name())));
}
}

View File

@ -0,0 +1,45 @@
package p.studio.compiler.pbs.semantics;
import org.junit.jupiter.api.Test;
import p.studio.compiler.pbs.PbsFrontendCompiler;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PbsSemanticsOptionalResultTest {
@Test
void shouldValidateOptionalAndResultFlowRules() {
final var source = """
declare error ErrA { Fail; }
declare error ErrB { Oops; }
fn make() -> result<ErrA> int { return ok(1); }
fn badOptional() -> int {
let x = none;
let y: int = 1 else 2;
return x + y;
}
fn badResult() -> result<ErrB> int {
let a: int = make()!;
let b: int = handle make() { ErrA.Fail -> ErrA.Fail };
return ok(a + b);
}
""";
final var diagnostics = DiagnosticSink.empty();
new PbsFrontendCompiler().compileFile(new FileId(0), source, diagnostics);
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_NONE_WITHOUT_EXPECTED_OPTIONAL.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_ELSE_NON_OPTIONAL_LEFT.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_ERROR_MISMATCH.name())));
assertTrue(diagnostics.stream().anyMatch(d ->
d.getCode().equals(PbsSemanticsErrors.E_SEM_HANDLE_ERROR_MISMATCH.name())));
}
}