aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/org/shedlang/compiler/parser
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-10-24 21:02:07 +0100
committerMichael Williamson <mike@zwobble.org>2023-10-24 21:02:07 +0100
commit0dccc0cdf7813c3c5571a79186a73bfd239a66d6 (patch)
treebc313d4336b83fe774ea1d8f14064bef2d09a97b /src/test/java/org/shedlang/compiler/parser
parent192cb27a8d8bc78211897c9f9325eb898390dbc4 (diff)
Parse function expressions
Diffstat (limited to 'src/test/java/org/shedlang/compiler/parser')
-rw-r--r--src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java b/src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java
new file mode 100644
index 00000000..d86776be
--- /dev/null
+++ b/src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java
@@ -0,0 +1,246 @@
+package org.shedlang.compiler.parser;
+
+import org.junit.jupiter.api.Test;
+import org.shedlang.compiler.parser.errors.PositionalParamAfterNamedParamError;
+import org.shedlang.compiler.tokeniser.UnexpectedTokenException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.*;
+import static org.shedlang.compiler.parser.ParserTesting.parseString;
+import static org.zwobble.precisely.AssertThat.assertThat;
+import static org.zwobble.precisely.Matchers.*;
+
+public class ParserFunctionExpressionTests {
+ @Test
+ public void canParseMinimalFunction() {
+ var source = """
+ fn () -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(isSequence(), isSequence()))
+ .withEffect(isOptionalEmpty())
+ .withReturnType(isUntypedTypeLevelReferenceNode("Unit"))
+ .withBody(isUntypedBlockNode(isSequence()))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithSinglePositionalParam() {
+ var source = """
+ fn (a: Int) -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(
+ isSequence(
+ isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int"))
+ ),
+ isSequence()
+ ))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithManyPositionalParams() {
+ var source = """
+ fn (a: Int, b: String, c: Bool) -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(
+ isSequence(
+ isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")),
+ isUntypedParamNode("b", isUntypedTypeLevelReferenceNode("String")),
+ isUntypedParamNode("c", isUntypedTypeLevelReferenceNode("Bool"))
+ ),
+ isSequence()
+ ))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithSingleNamedParam() {
+ var source = """
+ fn (.a: Int) -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(
+ isSequence(),
+ isSequence(
+ isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int"))
+ )
+ ))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithManyNamedParams() {
+ var source = """
+ fn (.a: Int, .b: String, .c: Bool) -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(
+ isSequence(),
+ isSequence(
+ isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")),
+ isUntypedParamNode("b", isUntypedTypeLevelReferenceNode("String")),
+ isUntypedParamNode("c", isUntypedTypeLevelReferenceNode("Bool"))
+ )
+ ))
+ );
+ }
+
+ @Test
+ public void canParseNamedParamAfterPositionalParam() {
+ var source = """
+ fn (a: Int, .b: String) -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(
+ isSequence(
+ isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int"))
+ ),
+ isSequence(
+ isUntypedParamNode("b", isUntypedTypeLevelReferenceNode("String"))
+ )
+ ))
+ );
+ }
+
+ @Test
+ public void cannotParsePositionalParamAfterNamedParam() {
+ var source = """
+ fn (.a: Int, b: String) -> Unit {
+ }""";
+
+ assertThrows(
+ PositionalParamAfterNamedParamError.class,
+ () -> parseString(source, Parser::parseTopLevelExpression)
+ );
+ }
+
+ @Test
+ public void paramsCanHaveTrailingComma() {
+ var source = """
+ fn (a: Int, ) -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withParams(isUntypedParamsNode(
+ isSequence(
+ isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int"))
+ ),
+ isSequence()
+ ))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithEffect() {
+ var source = """
+ fn () ! Io -> Unit {
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withEffect(isOptionalOf(isUntypedTypeLevelReferenceNode("Io")))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithBodyWithoutReturn() {
+ var source = """
+ fn () -> Unit {
+ a;
+ b;
+ c;
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withBody(isUntypedBlockNode(isSequence(
+ isUntypedExpressionStatementNode(isUntypedReferenceNode("a")),
+ isUntypedExpressionStatementNode(isUntypedReferenceNode("b")),
+ isUntypedExpressionStatementNode(isUntypedReferenceNode("c"))
+ )))
+ );
+ }
+
+ @Test
+ public void canParseFunctionWithBodyWithBlockReturn() {
+ var source = """
+ fn () -> Unit {
+ a;
+ b;
+ c
+ }""";
+
+ var node = parseString(source, Parser::parseTopLevelExpression);
+
+ assertThat(
+ node,
+ isUntypedFunctionExpressionNode()
+ .withBody(isUntypedBlockNode(isSequence(
+ isUntypedExpressionStatementNode(isUntypedReferenceNode("a")),
+ isUntypedExpressionStatementNode(isUntypedReferenceNode("b")),
+ isUntypedBlockReturnNode(isUntypedReferenceNode("c"))
+ )))
+ );
+ }
+
+ @Test
+ public void blockReturnEndsFunction() {
+ var source = """
+ fn () -> Unit {
+ a;
+ b
+ c
+ }""";
+
+ var error = assertThrows(
+ UnexpectedTokenException.class,
+ () -> parseString(source, Parser::parseTopLevelExpression)
+ );
+
+ assertThat(error.getActual().value(), equalTo("c"));
+ assertThat(error.getExpected(), equalTo(TokenType.SYMBOL_BRACE_CLOSE.toString()));
+ }
+}