diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-10-25 09:34:00 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-10-25 09:34:00 +0000 |
| commit | a8ad67dc75904eab189706ff3875d03ffe347ac8 (patch) | |
| tree | d38cb29f81f41b0ba0d2b11a0eea5e7db2bde64b /src | |
| parent | e1e44540ecb5a140b43a877be8fd49566b0d9bc4 (diff) | |
Parse calls
Diffstat (limited to 'src')
5 files changed, 210 insertions, 1 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNode.java new file mode 100644 index 0000000..2c19adb --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNode.java @@ -0,0 +1,60 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode imports + +public record UntypedCallNode( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver, + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> args, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), java.util.List.of(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver, + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> args, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder receiver) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver.build(), args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withArgs(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> args) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder addArg(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode arg) { + var args = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode>(this.args); + args.add(arg); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder addArg(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder arg) { + var args = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode>(this.args); + args.add(arg.build()); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java index b9c14e9..11f6b67 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java @@ -5,7 +5,7 @@ package org.zwobble.hobgoblin.compiler.ast.untyped; // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode imports // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode imports -public sealed interface UntypedExpressionNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode { +public sealed interface UntypedExpressionNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode { public interface Builder { public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode build(); 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 4620aaf..e15e97a 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -493,6 +493,40 @@ public class Parser { return new UntypedReferenceNode(name, source); } + private static final OperationParser<OperatorPrecedence, UntypedExpressionNode> CALL_PARSER = new OperationParser<>() { + @Override + public OperatorPrecedence precedence() { + return OperatorPrecedence.CALL; + } + + @Override + public List<TokenType> operatorTokenTypes() { + return List.of(TokenType.SYMBOL_PAREN_OPEN); + } + + @Override + public UntypedExpressionNode parse( + SourcePosition start, + UntypedExpressionNode left, + TokenIterator tokens + ) { + tokens.skip(TokenType.SYMBOL_PAREN_OPEN); + + var args = parseMany( + () -> tokens.isNext(TokenType.SYMBOL_PAREN_CLOSE), + () -> parseTopLevelExpression(tokens), + () -> tokens.trySkip(TokenType.SYMBOL_COMMA) + ); + + tokens.skip(TokenType.SYMBOL_PAREN_CLOSE); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedCallNode(left, args, source); + } + }; + private static final OperationParser<OperatorPrecedence, UntypedExpressionNode> MEMBER_ACCESS_PARSER = new OperationParser<>() { @Override public OperatorPrecedence precedence() { @@ -521,6 +555,7 @@ public class Parser { }; private static final Map<TokenType, OperationParser<OperatorPrecedence, UntypedExpressionNode>> OPERATION_PARSERS = Stream.of( + CALL_PARSER, MEMBER_ACCESS_PARSER ) .flatMap(parser -> diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNodeMatcher.java new file mode 100644 index 0000000..a9ce6a5 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNodeMatcher.java @@ -0,0 +1,53 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher imports + +public final class UntypedCallNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher isUntypedCallNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>> submatchers; + + private UntypedCallNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>> submatchers + ) { + this.submatchers = submatchers; + } + + public org.zwobble.precisely.MatchResult match(java.lang.Object actual) { + return this.toMatcher().match(actual); + } + + public org.zwobble.precisely.TextTree describe() { + return this.toMatcher().describe(); + } + + private org.zwobble.precisely.Matcher<java.lang.Object> toMatcher() { + return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher withReceiver(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> receiver) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("receiver", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode::receiver, (org.zwobble.precisely.Matcher) receiver)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher withArgs(org.zwobble.precisely.Matcher<? super java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode>> args) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("args", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode::args, (org.zwobble.precisely.Matcher) args)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserCallTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserCallTests.java new file mode 100644 index 0000000..64e84de --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserCallTests.java @@ -0,0 +1,61 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher.isUntypedCallNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserCallTests { + @Test + public void canParseCallWithNoArguments() { + var source = "f()"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedCallNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("f")) + ) + .withArgs(isSequence()) + ); + } + + @Test + public void canParseCallWithOnePositionalArgument() { + var source = "f(a)"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedCallNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("f")) + ) + .withArgs(isSequence( + isUntypedReferenceNode().withName(equalTo("a")) + )) + ); + } + + @Test + public void canParseCallWithMultiplePositionalArguments() { + var source = "f(a, b, c)"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedCallNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("f")) + ) + .withArgs(isSequence( + isUntypedReferenceNode().withName(equalTo("a")), + isUntypedReferenceNode().withName(equalTo("b")), + isUntypedReferenceNode().withName(equalTo("c")) + )) + ); + } +} + |
