summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-10-25 09:28:03 +0000
committerMichael Williamson <mike@zwobble.org>2026-10-25 09:28:03 +0000
commit6d6db81f76f350244ab58b795bccbd95afc95c56 (patch)
treebaa46085fe3824c58175bc253d6b07b09fe451dd /src/main/java
parentfeb7c87f0cbdc8b119fa315084025e2101c516a0 (diff)
Parse member access expressions
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java2
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNode.java48
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java15
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java5
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java65
5 files changed, 133 insertions, 2 deletions
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 c15736d..b9c14e9 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.UntypedReferenceNode {
+public sealed interface UntypedExpressionNode permits 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/ast/untyped/UntypedMemberAccessNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNode.java
new file mode 100644
index 0000000..6a6e406
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNode.java
@@ -0,0 +1,48 @@
+// Generated by hobgoblin.
+
+package org.zwobble.hobgoblin.compiler.ast.untyped;
+
+// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode imports
+// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode imports
+
+public record UntypedMemberAccessNode(
+ org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver,
+ String fieldName,
+ org.zwobble.hobgoblin.compiler.sources.Source source
+) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode {
+ public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder arbitrary() {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), "", org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource());
+ }
+
+ public record Builder(
+ org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver,
+ String fieldName,
+ org.zwobble.hobgoblin.compiler.sources.Source source
+ ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder {
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode build() {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode(receiver, fieldName, source);
+ }
+
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver) {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver, fieldName, source);
+ }
+
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder receiver) {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver.build(), fieldName, source);
+ }
+
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withFieldName(String fieldName) {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver, fieldName, source);
+ }
+
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver, fieldName, source);
+ }
+
+ // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder body
+ // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder body
+ }
+
+ // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode body
+ // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode body
+}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java
new file mode 100644
index 0000000..27c2740
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java
@@ -0,0 +1,15 @@
+package org.zwobble.hobgoblin.compiler.parser;
+
+import org.zwobble.sourcetext.SourcePosition;
+
+import java.util.List;
+
+interface OperationParser<TOperatorPrecedence, TNode> {
+ TOperatorPrecedence precedence();
+ List<TokenType> operatorTokenTypes();
+ TNode parse(
+ SourcePosition start,
+ TNode left,
+ TokenIterator tokens
+ );
+}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java
new file mode 100644
index 0000000..b63ca86
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java
@@ -0,0 +1,5 @@
+package org.zwobble.hobgoblin.compiler.parser;
+
+enum OperatorPrecedence {
+ CALL,
+}
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 5a77616..4620aaf 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java
@@ -11,9 +11,12 @@ import org.zwobble.sourcetext.SourceText;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class Parser {
private Parser() {
@@ -457,7 +460,29 @@ public class Parser {
// == Expressions ==
- static UntypedExpressionNode parseExpression(TokenIterator tokens) {
+ static UntypedExpressionNode parseTopLevelExpression(TokenIterator tokens) {
+ return parseExpression(tokens, Optional.empty());
+ }
+
+ private static UntypedExpressionNode parseExpression(
+ TokenIterator tokens,
+ Optional<OperatorPrecedence> parentPrecedence
+ ) {
+ var start = tokens.startPosition();
+ var left = parsePrimaryExpression(tokens);
+
+ while (true) {
+ var operationParser = OPERATION_PARSERS.get(tokens.peek().tokenType());
+ if (operationParser == null || parentPrecedence.isPresent() && operationParser.precedence().ordinal() <= parentPrecedence.get().ordinal()) {
+ return left;
+ }
+ left = operationParser.parse(start, left, tokens);
+ }
+ }
+
+ private static UntypedExpressionNode parsePrimaryExpression(
+ TokenIterator tokens
+ ) {
var start = tokens.startPosition();
var name = parseIdentifier(tokens);
@@ -468,6 +493,44 @@ public class Parser {
return new UntypedReferenceNode(name, source);
}
+ private static final OperationParser<OperatorPrecedence, UntypedExpressionNode> MEMBER_ACCESS_PARSER = new OperationParser<>() {
+ @Override
+ public OperatorPrecedence precedence() {
+ return OperatorPrecedence.CALL;
+ }
+
+ @Override
+ public List<TokenType> operatorTokenTypes() {
+ return List.of(TokenType.SYMBOL_DOT);
+ }
+
+ @Override
+ public UntypedExpressionNode parse(
+ SourcePosition start,
+ UntypedExpressionNode left,
+ TokenIterator tokens
+ ) {
+ tokens.skip(TokenType.SYMBOL_DOT);
+ var fieldName = parseIdentifier(tokens);
+
+ var end = tokens.endPosition();
+ var source = createSource(start, end);
+
+ return new UntypedMemberAccessNode(left, fieldName, source);
+ }
+ };
+
+ private static final Map<TokenType, OperationParser<OperatorPrecedence, UntypedExpressionNode>> OPERATION_PARSERS = Stream.of(
+ MEMBER_ACCESS_PARSER
+ )
+ .flatMap(parser ->
+ parser.operatorTokenTypes().stream().map(tokenType -> Map.entry(tokenType, parser))
+ )
+ .collect(Collectors.toMap(
+ x -> x.getKey(),
+ x -> x.getValue()
+ ));
+
// == Qualified type names ==
public static QualifiedTypeName parseQualifiedTypeName(SourceText sourceText) {