From 9097b3ac138a60f6e7c708d2b452f95eb75601cb Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 23 Apr 2026 11:24:44 +0100 Subject: Parse namespace body --- .../zwobble/hobgoblin/compiler/parser/Parser.java | 36 +++++++++++++++++++++- .../hobgoblin/compiler/parser/TokenIterator.java | 4 +++ .../compiler/parser/ParserNamespaceTests.java | 27 ++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) (limited to 'src') 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 53a852e..106a101 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -6,7 +6,10 @@ import org.zwobble.hobgoblin.compiler.sources.Source; import org.zwobble.hobgoblin.compiler.types.NamespaceName; import org.zwobble.sourcetext.SourcePosition; +import java.util.ArrayList; import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; public class Parser { private Parser() { @@ -18,12 +21,18 @@ public class Parser { ) { var start = tokens.startPosition(); + var body = parseMany( + () -> tokens.isNext(TokenType.END), + () -> parseNamespaceStatement(tokens), + () -> true + ); + var end = tokens.endPosition(); var source = createSource(start, end); return new UntypedNamespaceNode( namespaceName, - List.of(), + body, source ); } @@ -63,6 +72,31 @@ public class Parser { ); } + /** + * Parse zero, one, or many instances of {@code T} using {@code parseOne}. + * Each instance is expected to be separated by tokens consumed by + * {@code parseSeparator}. Parsing stops either if {@code parseSeparator} + * returns false, or if {@code stop} returns false. Trailing separators + * are permitted. + */ + private static List parseMany( + BooleanSupplier stop, + Supplier parseOne, + BooleanSupplier parseSeparator + ) { + var values = new ArrayList(); + + while (!stop.getAsBoolean()) { + var value = parseOne.get(); + values.add(value); + if (!parseSeparator.getAsBoolean()) { + return values; + } + } + + return values; + } + private static Source createSource(SourcePosition start, SourcePosition end) { return new FileFragmentSource(start.to(end)); } 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 4479043..a66ec35 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java @@ -19,6 +19,10 @@ public class TokenIterator { return this.getAbsolute(this.tokenIndex); } + public boolean isNext(TokenType tokenType) { + return this.peek().tokenType().equals(tokenType); + } + public Token next(TokenType tokenType) { var token = peek(); if (token.tokenType().equals(tokenType)) { diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserNamespaceTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserNamespaceTests.java index e5796e4..22cddca 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserNamespaceTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserNamespaceTests.java @@ -2,6 +2,7 @@ package org.zwobble.hobgoblin.compiler.parser; import org.junit.jupiter.api.Test; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode; import org.zwobble.hobgoblin.compiler.types.NamespaceName; import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; @@ -24,4 +25,30 @@ public class ParserNamespaceTests { has("body", x -> x.body(), 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, instanceOf( + UntypedNamespaceNode.class, + has("body", UntypedNamespaceNode::body, isSequence( + instanceOf( + UntypedStructDefinitionNode.class, + has("name", UntypedStructDefinitionNode::name, equalTo("One")) + ), + instanceOf( + UntypedStructDefinitionNode.class, + has("name", UntypedStructDefinitionNode::name, equalTo("Two")) + ) + )) + )); + } } -- cgit v1.2.3