diff options
Diffstat (limited to 'src')
6 files changed, 124 insertions, 1 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedConstructedTypeNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedConstructedTypeNode.java new file mode 100644 index 0000000..6615995 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedConstructedTypeNode.java @@ -0,0 +1,12 @@ +package org.zwobble.hobgoblin.compiler.ast.untyped; + +import org.zwobble.hobgoblin.compiler.sources.Source; + +import java.util.List; + +public record UntypedConstructedTypeNode( + UntypedTypeLevelExpressionNode receiver, + List<UntypedTypeLevelExpressionNode> args, + Source source +) implements UntypedTypeLevelExpressionNode { +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java index 871393d..12414c5 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java @@ -1,4 +1,4 @@ package org.zwobble.hobgoblin.compiler.ast.untyped; -public sealed interface UntypedTypeLevelExpressionNode extends UntypedNode permits UntypedTypeLevelReferenceNode { +public sealed interface UntypedTypeLevelExpressionNode extends UntypedNode permits UntypedConstructedTypeNode, UntypedTypeLevelReferenceNode { } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java index 4cd35f8..d60b249 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -156,6 +156,28 @@ public class Parser { ) { var start = tokens.startPosition(); + UntypedTypeLevelExpressionNode expression = parsePrimaryTypeLevelExpression(tokens); + + while (tokens.trySkip(TokenType.SYMBOL_SQUARE_OPEN)) { + var args = parseMany( + () -> tokens.isNext(TokenType.SYMBOL_SQUARE_CLOSE), + () -> parseTypeLevelExpression(tokens), + () -> tokens.trySkip(TokenType.SYMBOL_COMMA) + ); + tokens.skip(TokenType.SYMBOL_SQUARE_CLOSE); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + expression = new UntypedConstructedTypeNode(expression, args, source); + } + + return expression; + } + + private static UntypedTypeLevelReferenceNode parsePrimaryTypeLevelExpression(TokenIterator tokens) { + var start = tokens.startPosition(); + var name = tokens.next(TokenType.IDENTIFIER); var end = tokens.endPosition(); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java index 0ae3d28..55edd97 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java @@ -44,6 +44,16 @@ class TokenIterator { } } + public boolean trySkip(TokenType tokenType) { + var token = peek(); + if (token.tokenType().equals(tokenType)) { + this.tokenIndex++; + return true; + } else { + return false; + } + } + private Token getAbsolute(int index) { if (index < this.tokens.size()) { return this.tokens.get(index); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java index c713c70..a535bc8 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -143,6 +143,9 @@ public class TypeChecker { TypeCheckerNamespaceContext context ) { return switch (untyped) { + case UntypedConstructedTypeNode untypedConstructedType -> + throw new UnsupportedOperationException("TODO"); + case UntypedTypeLevelReferenceNode untypedTypeLevelReference -> typeCheckTypeLevelReference(untypedTypeLevelReference, context); }; diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserConstructedTypeTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserConstructedTypeTests.java new file mode 100644 index 0000000..1444cf0 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserConstructedTypeTests.java @@ -0,0 +1,76 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedConstructedTypeNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode; +import org.zwobble.precisely.Matcher; + +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.*; + +public class ParserConstructedTypeTests { + @Test + public void canParseConstructedTypeWithOneArg() { + var source = "List[String]"; + + var node = parseString(source, Parser::parseTypeLevelExpression); + + assertThat(node, isUntypedConstructedTypeNode( + isUntypedTypeLevelReferenceNode("List"), + isSequence( + isUntypedTypeLevelReferenceNode("String") + ) + )); + } + + @Test + public void canParseConstructedTypeWithMultipleArgs() { + var source = "X[T1, T2, T3]"; + + var node = parseString(source, Parser::parseTypeLevelExpression); + + assertThat(node, isUntypedConstructedTypeNode( + isUntypedTypeLevelReferenceNode("X"), + isSequence( + isUntypedTypeLevelReferenceNode("T1"), + isUntypedTypeLevelReferenceNode("T2"), + isUntypedTypeLevelReferenceNode("T3") + ) + )); + } + + @Test + public void argsCanHaveTrailingComma() { + var source = "List[String,]"; + + var node = parseString(source, Parser::parseTypeLevelExpression); + + assertThat(node, isUntypedConstructedTypeNode( + isUntypedTypeLevelReferenceNode("List"), + isSequence( + isUntypedTypeLevelReferenceNode("String") + ) + )); + } + + private static Matcher<UntypedTypeLevelExpressionNode> isUntypedConstructedTypeNode( + Matcher<UntypedTypeLevelExpressionNode> receiver, + Matcher<Iterable<? extends UntypedTypeLevelExpressionNode>> args + ) { + return instanceOf( + UntypedConstructedTypeNode.class, + has("receiver", UntypedConstructedTypeNode::receiver, receiver), + has("args", UntypedConstructedTypeNode::args, args) + ); + } + + private static Matcher<UntypedTypeLevelExpressionNode> isUntypedTypeLevelReferenceNode(String name) { + return instanceOf( + UntypedTypeLevelReferenceNode.class, + has("name", UntypedTypeLevelReferenceNode::name, equalTo(name)) + ); + } +} + |
