package org.shedlang.compiler.parser; import org.junit.jupiter.api.Test; import org.shedlang.compiler.parser.errors.PositionalArgAfterNamedArgError; 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 ParserCallTests { // == Receiver == @Test public void canParseReceiver() { var source = "f()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withReceiver(isUntypedReferenceNode("f")) ); } // == Effects == @Test public void canParseCallWithoutEffects() { var source = "f()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withHasEffects(false) ); } @Test public void canParseCallWithEffects() { var source = "f!()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withHasEffects(true) ); } // == Args == @Test public void canParseCallWithNoArguments() { var source = "f()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence()) .withNamed(isSequence()) ) ); } @Test public void canParseCallWithSinglePositionalArg() { var source = "f(a)"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence( isUntypedReferenceNode("a") )) .withNamed(isSequence()) ) ); } @Test public void canParseCallWithManyPositionalArgs() { var source = "f(a, b, c)"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence( isUntypedReferenceNode("a"), isUntypedReferenceNode("b"), isUntypedReferenceNode("c") )) .withNamed(isSequence()) ) ); } @Test public void canParseCallWithSingleNamedArg() { var source = "f(.first = a)"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence()) .withNamed(isSequence( isUntypedNamedArgNode("first", isUntypedReferenceNode("a")) )) ) ); } @Test public void canParseCallWithManyNamedArgs() { var source = "f(.first = a, .second = b, .third = c)"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence()) .withNamed(isSequence( isUntypedNamedArgNode("first", isUntypedReferenceNode("a")), isUntypedNamedArgNode("second", isUntypedReferenceNode("b")), isUntypedNamedArgNode("third", isUntypedReferenceNode("c")) )) ) ); } @Test public void canParseNamedArgumentAfterPositionalArgument() { var source = "f(a, .named = b)"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence( isUntypedReferenceNode("a") )) .withNamed(isSequence( isUntypedNamedArgNode("named", isUntypedReferenceNode("b")) )) ) ); } @Test public void cannotParsePositionalArgumentAfterNamedArgument() { var source = "f(.y = false, true)"; assertThrows( PositionalArgAfterNamedArgError.class, () -> parseString(source, Parser::parseTopLevelExpression) ); } @Test public void argumentListCanHaveTrailingComma() { var source = "f(a,)"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withArgs( isUntypedArgsNode() .withPositional(isSequence( isUntypedReferenceNode("a") )) .withNamed(isSequence()) ) ); } // == Type-level args == @Test public void canParseCallWithNoTypeLevelArgs() { var source = "f()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withoutTypeLevelArgs() ); } @Test public void canParseCallWithOneTypeLevelArg() { var source = "f[A]()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withTypeLevelArgs(isSequence( isUntypedTypeLevelReferenceNode("A") )) ); } @Test public void canParseCallWithMultipleTypeLevelArgs() { var source = "f[A, B, C]()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withTypeLevelArgs(isSequence( isUntypedTypeLevelReferenceNode("A"), isUntypedTypeLevelReferenceNode("B"), isUntypedTypeLevelReferenceNode("C") )) ); } @Test public void typeLevelArgsCanHaveTrailingComma() { var source = "f[A,]()"; var node = parseString(source, Parser::parseTopLevelExpression); assertThat( node, isUntypedCallNode() .withTypeLevelArgs(isSequence( isUntypedTypeLevelReferenceNode("A") )) ); } }