diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBranchExpressionAnalyzer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBranchExpressionAnalyzer.java new file mode 100644 index 00000000..e5a47e74 --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowBranchExpressionAnalyzer.java @@ -0,0 +1,118 @@ +package p.studio.compiler.pbs.semantics; + +import p.studio.compiler.pbs.ast.PbsAst; +import p.studio.compiler.source.diagnostics.Diagnostics; +import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprResult; +import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse; +import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Kind; +import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView; + +final class PbsFlowBranchExpressionAnalyzer { + @FunctionalInterface + interface ExpressionAnalyzerDelegate { + ExprResult analyze(PbsAst.Expression expression, PbsFlowExpressionContext context); + } + + @FunctionalInterface + interface BlockAnalysisDelegate { + TypeView analyze(PbsAst.Block block, PbsFlowExpressionContext context, boolean valueContext); + } + + private final PbsFlowTypeOps typeOps; + private final ExpressionAnalyzerDelegate expressionAnalyzerDelegate; + private final BlockAnalysisDelegate blockAnalysisDelegate; + + PbsFlowBranchExpressionAnalyzer( + final PbsFlowTypeOps typeOps, + final ExpressionAnalyzerDelegate expressionAnalyzerDelegate, + final BlockAnalysisDelegate blockAnalysisDelegate) { + this.typeOps = typeOps; + this.expressionAnalyzerDelegate = expressionAnalyzerDelegate; + this.blockAnalysisDelegate = blockAnalysisDelegate; + } + + ExprResult analyzeIfExpression( + final PbsAst.IfExpr ifExpr, + final PbsFlowExpressionContext context) { + final var diagnostics = context.diagnostics(); + final var expectedType = context.expectedType(); + final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type(); + if (!typeOps.isBool(condition)) { + Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name(), + "If expression condition must have bool type", + ifExpr.condition().span()); + } + + final var thenType = blockAnalysisDelegate.analyze(ifExpr.thenBlock(), context, true); + final var elseType = analyzeValueExpression(ifExpr.elseExpression(), context, expectedType).type(); + + if (!typeOps.compatible(thenType, elseType)) { + Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_IF_BRANCH_TYPE_MISMATCH.name(), + "If expression branches must have compatible types", + ifExpr.span()); + return ExprResult.type(TypeView.unknown()); + } + return ExprResult.type(thenType); + } + + ExprResult analyzeElseExpression( + final PbsAst.ElseExpr elseExpr, + final PbsFlowExpressionContext context) { + final var diagnostics = context.diagnostics(); + final var optional = analyzeValueExpression(elseExpr.optionalExpression(), context, null).type(); + if (optional.kind() != Kind.OPTIONAL) { + Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_ELSE_NON_OPTIONAL_LEFT.name(), + "Left operand of 'else' must have optional type", + elseExpr.optionalExpression().span()); + analyzeValueExpression(elseExpr.fallbackExpression(), context, null); + return ExprResult.type(TypeView.unknown()); + } + + final var payload = optional.inner(); + final var fallback = analyzeValueExpression(elseExpr.fallbackExpression(), context, payload).type(); + if (!typeOps.compatible(fallback, payload)) { + Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_ELSE_FALLBACK_TYPE_MISMATCH.name(), + "Fallback expression in 'else' must match optional payload type", + elseExpr.fallbackExpression().span()); + } + return ExprResult.type(payload); + } + + ExprResult analyzePropagateExpression( + final PbsAst.PropagateExpr propagateExpr, + final PbsFlowExpressionContext context) { + final var diagnostics = context.diagnostics(); + final var resultErrorName = context.resultErrorName(); + final var source = analyzeValueExpression(propagateExpr.expression(), context, null).type(); + if (source.kind() != Kind.RESULT) { + Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_NON_RESULT.name(), + "Propagation operator '!' requires result type", + propagateExpr.expression().span()); + return ExprResult.type(TypeView.unknown()); + } + final var sourceError = source.errorType(); + if (resultErrorName == null || sourceError == null || !resultErrorName.equals(sourceError.name())) { + Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_ERROR_MISMATCH.name(), + "Propagation operator '!' requires matching result error type in enclosing callable", + propagateExpr.span()); + } + return ExprResult.type(source.inner()); + } + + private ExprResult analyzeValueExpression( + final PbsAst.Expression expression, + final PbsFlowExpressionContext context, + final TypeView expectedType) { + return expressionAnalyzerDelegate.analyze( + expression, + context.withExpectedType(expectedType) + .withUse(ExprUse.VALUE) + .withValueContext(true)); + } +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java index 6508ae6d..07554f4b 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowExpressionAnalyzer.java @@ -1,22 +1,13 @@ package p.studio.compiler.pbs.semantics; import p.studio.compiler.pbs.ast.PbsAst; -import p.studio.compiler.source.Span; import p.studio.compiler.source.diagnostics.DiagnosticSink; -import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.CallableSymbol; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprResult; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse; -import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Kind; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Model; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Scope; -import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TupleField; import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - final class PbsFlowExpressionAnalyzer { @FunctionalInterface interface BlockAnalyzer { @@ -35,6 +26,7 @@ final class PbsFlowExpressionAnalyzer { private final PbsFlowStructuralExpressionAnalyzer structuralExpressionAnalyzer; private final PbsFlowCallableResolutionAnalyzer callableResolutionAnalyzer; private final PbsFlowControlExpressionAnalyzer controlExpressionAnalyzer; + private final PbsFlowBranchExpressionAnalyzer branchExpressionAnalyzer; PbsFlowExpressionAnalyzer(final PbsFlowTypeOps typeOps) { this.typeOps = typeOps; @@ -49,6 +41,10 @@ final class PbsFlowExpressionAnalyzer { typeOps, this::analyzeExpressionInternal, this::analyzeBlock); + this.branchExpressionAnalyzer = new PbsFlowBranchExpressionAnalyzer( + typeOps, + this::analyzeExpressionInternal, + this::analyzeBlock); } ExprResult analyzeExpression( @@ -81,14 +77,6 @@ final class PbsFlowExpressionAnalyzer { private ExprResult analyzeExpressionInternal( final PbsAst.Expression expression, final PbsFlowExpressionContext context) { - final var scope = context.scope(); - final var expectedType = context.expectedType(); - final var returnType = context.returnType(); - final var resultErrorName = context.resultErrorName(); - final var receiverType = context.receiverType(); - final var model = context.model(); - final var diagnostics = context.diagnostics(); - final var use = context.use(); final var structural = structuralExpressionAnalyzer.analyze(expression, context); if (structural != null) { return structural; @@ -103,67 +91,16 @@ final class PbsFlowExpressionAnalyzer { return callableResolutionAnalyzer.analyzeApplyExpression(applyExpr, context); } if (expression instanceof PbsAst.IfExpr ifExpr) { - final var condition = analyzeValueExpression(ifExpr.condition(), context, TypeView.bool()).type(); - if (!typeOps.isBool(condition)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, - PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name(), - "If expression condition must have bool type", - ifExpr.condition().span()); - } - - final var thenType = analyzeBlock(ifExpr.thenBlock(), context, true); - final var elseType = analyzeValueExpression(ifExpr.elseExpression(), context, expectedType).type(); - - if (!typeOps.compatible(thenType, elseType)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, - PbsSemanticsErrors.E_SEM_IF_BRANCH_TYPE_MISMATCH.name(), - "If expression branches must have compatible types", - ifExpr.span()); - return ExprResult.type(TypeView.unknown()); - } - return ExprResult.type(thenType); + return branchExpressionAnalyzer.analyzeIfExpression(ifExpr, context); } if (expression instanceof PbsAst.SwitchExpr switchExpr) { return ExprResult.type(controlExpressionAnalyzer.analyzeSwitchExpression(switchExpr, context)); } if (expression instanceof PbsAst.ElseExpr elseExpr) { - final var optional = analyzeValueExpression(elseExpr.optionalExpression(), context, null).type(); - if (optional.kind() != Kind.OPTIONAL) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, - PbsSemanticsErrors.E_SEM_ELSE_NON_OPTIONAL_LEFT.name(), - "Left operand of 'else' must have optional type", - elseExpr.optionalExpression().span()); - analyzeValueExpression(elseExpr.fallbackExpression(), context, null); - return ExprResult.type(TypeView.unknown()); - } - - final var payload = optional.inner(); - final var fallback = analyzeValueExpression(elseExpr.fallbackExpression(), context, payload).type(); - if (!typeOps.compatible(fallback, payload)) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, - PbsSemanticsErrors.E_SEM_ELSE_FALLBACK_TYPE_MISMATCH.name(), - "Fallback expression in 'else' must match optional payload type", - elseExpr.fallbackExpression().span()); - } - return ExprResult.type(payload); + return branchExpressionAnalyzer.analyzeElseExpression(elseExpr, context); } if (expression instanceof PbsAst.PropagateExpr propagateExpr) { - final var source = analyzeValueExpression(propagateExpr.expression(), context, null).type(); - if (source.kind() != Kind.RESULT) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, - PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_NON_RESULT.name(), - "Propagation operator '!' requires result type", - propagateExpr.expression().span()); - return ExprResult.type(TypeView.unknown()); - } - final var sourceError = source.errorType(); - if (resultErrorName == null || sourceError == null || !resultErrorName.equals(sourceError.name())) { - p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, - PbsSemanticsErrors.E_SEM_RESULT_PROPAGATE_ERROR_MISMATCH.name(), - "Propagation operator '!' requires matching result error type in enclosing callable", - propagateExpr.span()); - } - return ExprResult.type(source.inner()); + return branchExpressionAnalyzer.analyzePropagateExpression(propagateExpr, context); } if (expression instanceof PbsAst.HandleExpr handleExpr) { return ExprResult.type(controlExpressionAnalyzer.analyzeHandleExpression(handleExpr, context)); @@ -175,28 +112,6 @@ final class PbsFlowExpressionAnalyzer { return ExprResult.type(TypeView.unknown()); } - private ExprResult analyzeValueExpression( - final PbsAst.Expression expression, - final PbsFlowExpressionContext context, - final TypeView expectedType) { - return analyzeExpressionInternal( - expression, - context.withExpectedType(expectedType) - .withUse(ExprUse.VALUE) - .withValueContext(true)); - } - - private ExprResult analyzeTargetExpression( - final PbsAst.Expression expression, - final PbsFlowExpressionContext context, - final ExprUse use) { - return analyzeExpressionInternal( - expression, - context.withExpectedType(null) - .withUse(use) - .withValueContext(true)); - } - private TypeView analyzeBlock( final PbsAst.Block block, final PbsFlowExpressionContext context,