From 72ce4e94a08baf5d722582e2914bb2074ef7d1a7 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 2 Dec 2023 23:55:03 +0000 Subject: Type check break statements --- .../compiler/ast/typed/TypedNodeMatchers.java | 9 ++++ .../org/shedlang/compiler/ast/untyped/Untyped.java | 8 +++ .../typechecker/TypeCheckerBreakTests.java | 61 ++++++++++++++++++++++ .../typechecker/TypeCheckerResumeTests.java | 4 +- 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/shedlang/compiler/typechecker/TypeCheckerBreakTests.java (limited to 'src/test/java') diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java index bb787024..0014e4f0 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -68,6 +68,15 @@ public class TypedNodeMatchers { ); } + public static Matcher isTypedBreakNode( + Matcher expression + ) { + return instanceOf( + TypedBreakNode.class, + has("expression", x -> x.expression(), expression) + ); + } + public static TypedCallNodeMatcher isTypedCallNode() { return TypedCallNodeMatcher.empty(); } diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java index 51b6c2b6..8d105dd9 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java @@ -43,6 +43,14 @@ public class Untyped { return new UntypedBoolLiteralNode(value, Sources.NULL); } + public static UntypedBreakNode break_() { + return new UntypedBreakNode(Optional.empty(), Sources.NULL); + } + + public static UntypedBreakNode break_(UntypedExpressionNode expression) { + return new UntypedBreakNode(Optional.of(expression), Sources.NULL); + } + public static UntypedCallNode call( UntypedExpressionNode receiver, List positionalArgs diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerBreakTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerBreakTests.java new file mode 100644 index 00000000..32f7b127 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerBreakTests.java @@ -0,0 +1,61 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.typechecker.errors.CannotBreakOutsideOfEffectHandlerMethodError; +import org.shedlang.compiler.types.Effects; +import org.shedlang.compiler.types.NamespaceName; +import org.shedlang.compiler.types.Types; +import org.shedlang.compiler.util.P; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.zwobble.precisely.AssertThat.assertThat; + +public class TypeCheckerBreakTests { + @Test + public void expressionIsTypeChecked() { + var untyped = Untyped.break_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.INT)); + + var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); + + assertThat(typed.value(), isTypedBreakNode(isTypedStringLiteralNode("hello"))); + } + + @Test + public void whenExpressionIsAbsentThenUnitIsReturned() { + var untyped = Untyped.break_(); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.UNIT)); + + var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); + + assertThat(typed.value(), isTypedBreakNode(isTypedUnitLiteralNode())); + } + + @Test + public void whenInNamespaceThenResumeIsForbidden() { + var untyped = Untyped.break_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterNamespace(NamespaceName.of("Example"), P.map()); + + assertThrows( + CannotBreakOutsideOfEffectHandlerMethodError.class, + () -> TypeChecker.typeCheckFunctionStatement(untyped, context) + ); + } + + @Test + public void whenInFunctionThenResumeIsForbidden() { + var untyped = Untyped.break_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, ReturnTypeState.explicit(Types.STRING)); + + assertThrows( + CannotBreakOutsideOfEffectHandlerMethodError.class, + () -> TypeChecker.typeCheckFunctionStatement(untyped, context) + ); + } +} diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerResumeTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerResumeTests.java index f0f18839..b5bab880 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerResumeTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerResumeTests.java @@ -43,7 +43,7 @@ public class TypeCheckerResumeTests { } @Test - public void whenNotInFunctionThenResumeIsForbidden() { + public void whenInNamespaceThenResumeIsForbidden() { var untyped = Untyped.resume(Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY .enterNamespace(NamespaceName.of("Example"), P.map()); @@ -67,7 +67,7 @@ public class TypeCheckerResumeTests { } @Test - public void canResumeWIthSubtypeOfResumeType() { + public void canResumeWithSubtypeOfResumeType() { var untyped = Untyped.resume(Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.TOP)); -- cgit v1.2.3