diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-10-25 09:28:03 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-10-25 09:28:03 +0000 |
| commit | 6d6db81f76f350244ab58b795bccbd95afc95c56 (patch) | |
| tree | baa46085fe3824c58175bc253d6b07b09fe451dd /src/main/java | |
| parent | feb7c87f0cbdc8b119fa315084025e2101c516a0 (diff) | |
Parse member access expressions
Diffstat (limited to 'src/main/java')
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) { |
