package org.zwobble.hobgoblin.compiler.parser; import org.junit.jupiter.api.Test; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode; import org.zwobble.hobgoblin.compiler.types.NamespaceName; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedImportNodeMatcher.isUntypedImportNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceNodeMatcher.isUntypedNamespaceNode; import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; public class ParserNamespaceTests { @Test public void emptyNamespaceHasNoImports() { var source = ""; var node = parseString( source, tokens -> Parser.parseNamespace(tokens, NamespaceName.of("Example", "Project")) ); assertThat(node, isUntypedNamespaceNode() .withImports(isSequence()) ); } @Test public void importsAreParsed() { var source = """ import {Point} from point; import {Square, Rectangle} from ./shapes; import {Line} from ../../line; """; var node = parseString( source, tokens -> Parser.parseNamespace(tokens, NamespaceName.of("Example", "Project")) ); assertThat(node, isUntypedNamespaceNode() .withImports(isSequence( isUntypedImportNode() .withImportedNames(isSequence(equalTo("Point"))) .withAncestorDepth(isOptionalEmpty()) .withNamespaceName(equalTo(NamespaceName.of("point"))), isUntypedImportNode() .withImportedNames(isSequence(equalTo("Square"), equalTo("Rectangle"))) .withAncestorDepth(isOptionalOf(equalTo(0))) .withNamespaceName(equalTo(NamespaceName.of("shapes"))), isUntypedImportNode() .withImportedNames(isSequence(equalTo("Line"))) .withAncestorDepth(isOptionalOf(equalTo(2))) .withNamespaceName(equalTo(NamespaceName.of("line"))) )) ); } @Test public void emptyNamespaceHasEmptyBody() { var source = ""; var node = parseString( source, tokens -> Parser.parseNamespace(tokens, NamespaceName.of("Example", "Project")) ); assertThat(node, isUntypedNamespaceNode() .withNamespaceName(equalTo(NamespaceName.of("Example", "Project"))) .withBody(isSequence()) ); } @Test public void bodyContainsStatements() { var source = """ struct One {} struct Two {}"""; var node = parseString( source, tokens -> Parser.parseNamespace(tokens, NamespaceName.of("Example", "Project")) ); assertThat(node, isUntypedNamespaceNode() .withBody(isSequence( instanceOf( UntypedStructDefinitionNode.class, has("name", UntypedStructDefinitionNode::name, equalTo("One")) ), instanceOf( UntypedStructDefinitionNode.class, has("name", UntypedStructDefinitionNode::name, equalTo("Two")) ) )) ); } @Test public void whenNamespaceStatementIsNotRecognisedThenErrorIsThrown() { var source = "blah"; var error = assertThrows( UnexpectedTokenError.class, () -> parseString( source, tokens -> Parser.parseNamespace(tokens, NamespaceName.of("Example", "Project")) ) ); assertThat(error.getExpected(), equalTo("namespace statement")); assertThat(error.getActual().charSequence().toString(), equalTo("blah")); } }