package org.shedlang.compiler.parser; import org.junit.jupiter.api.Test; import org.shedlang.compiler.tokeniser.UnexpectedTokenException; import org.shedlang.compiler.types.Visibility; 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 ParserFunctionDefinitionTests { @Test public void canParseMinimalFunction() { var source = """ fun f() -> Unit { }"""; var node = parseString(source, Parser::parseNamespaceStatement); assertThat( node, isUntypedFunctionDefinitionNode() .withName("f") .withSignature( isUntypedFunctionSignatureNode() .withParams(isUntypedParamsNode(isSequence(), isSequence())) .withEffect(isOptionalEmpty()) .withReturnType(isUntypedTypeLevelReferenceNode("Unit")) ) .withBody(isUntypedBlockNode(isSequence())) ); } @Test public void defaultVisibilityIsPrivate() { var source = """ fun f() -> Unit { }"""; var node = parseString(source, Parser::parseNamespaceStatement); assertThat( node, isUntypedFunctionDefinitionNode().withVisibility(Visibility.PRIVATE) ); } @Test public void whenFunIsPrefixedWithPubThenVisibilityIsPublic() { var source = """ pub fun f() -> Unit { }"""; var node = parseString(source, Parser::parseNamespaceStatement); assertThat( node, isUntypedFunctionDefinitionNode().withVisibility(Visibility.PUBLIC) ); } @Test public void canParseFunctionWithBodyWithoutReturn() { var source = """ fun f() -> Unit { a; b; c; }"""; var node = parseString(source, Parser::parseNamespaceStatement); assertThat( node, isUntypedFunctionDefinitionNode() .withBody(isUntypedBlockNode(isSequence( isUntypedExpressionStatementNode(isUntypedReferenceNode("a")), isUntypedExpressionStatementNode(isUntypedReferenceNode("b")), isUntypedExpressionStatementNode(isUntypedReferenceNode("c")) ))) ); } @Test public void canParseFunctionWithBodyWithBlockReturn() { var source = """ fun f() -> Unit { a; b; c }"""; var node = parseString(source, Parser::parseNamespaceStatement); assertThat( node, isUntypedFunctionDefinitionNode() .withBody(isUntypedBlockNode(isSequence( isUntypedExpressionStatementNode(isUntypedReferenceNode("a")), isUntypedExpressionStatementNode(isUntypedReferenceNode("b")), isUntypedBlockReturnNode(isUntypedReferenceNode("c")) ))) ); } @Test public void blockReturnEndsFunction() { var source = """ fun f() -> Unit { a; b c }"""; var error = assertThrows( UnexpectedTokenException.class, () -> parseString(source, Parser::parseNamespaceStatement) ); assertThat(error.getActual().value().toString(), equalTo("c")); assertThat(error.getExpected(), equalTo(TokenType.SYMBOL_BRACE_CLOSE.toString())); } }