diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-07-08 21:42:45 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-07-08 21:42:45 +0100 |
| commit | cf52b6e8a2ebf85b4be8b9065a755f3481eb43db (patch) | |
| tree | 553f99eee5123f7b06ae34ff657d975adf37b240 /src | |
| parent | b018be7fb57e79b62e727c1fb00bfd474888ddf8 (diff) | |
Parse if expressions
Diffstat (limited to 'src')
7 files changed, 202 insertions, 1 deletions
diff --git a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedConditionalBranchNode.java b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedConditionalBranchNode.java new file mode 100644 index 00000000..7f9e96aa --- /dev/null +++ b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedConditionalBranchNode.java @@ -0,0 +1,12 @@ +package org.shedlang.compiler.ast.untyped; + +import org.shedlang.compiler.sources.Source; + +import java.util.List; + +public record UntypedConditionalBranchNode( + UntypedExpressionNode condition, + List<UntypedFunctionStatementNode> body, + Source source +) implements UntypedNode { +} diff --git a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedExpressionNode.java b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedExpressionNode.java index cabf5876..729d8279 100644 --- a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedExpressionNode.java +++ b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedExpressionNode.java @@ -1,5 +1,5 @@ package org.shedlang.compiler.ast.untyped; public sealed interface UntypedExpressionNode extends UntypedNode - permits UntypedBinaryOperationNode, UntypedBoolLiteralNode, UntypedCallNode, UntypedIntLiteralNode, UntypedReferenceNode, UntypedStringLiteralNode { + permits UntypedBinaryOperationNode, UntypedBoolLiteralNode, UntypedCallNode, UntypedIfNode, UntypedIntLiteralNode, UntypedReferenceNode, UntypedStringLiteralNode { } diff --git a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedIfNode.java b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedIfNode.java new file mode 100644 index 00000000..6baff19c --- /dev/null +++ b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedIfNode.java @@ -0,0 +1,12 @@ +package org.shedlang.compiler.ast.untyped; + +import org.shedlang.compiler.sources.Source; + +import java.util.List; + +public record UntypedIfNode( + List<UntypedConditionalBranchNode> conditionalBranches, + List<UntypedFunctionStatementNode> elseBody, + Source source +) implements UntypedExpressionNode { +} diff --git a/src/main/java/org/shedlang/compiler/parser/Parser.java b/src/main/java/org/shedlang/compiler/parser/Parser.java index bfb756db..4dd59eb4 100644 --- a/src/main/java/org/shedlang/compiler/parser/Parser.java +++ b/src/main/java/org/shedlang/compiler/parser/Parser.java @@ -248,6 +248,54 @@ public class Parser { }; } + private static UntypedExpressionNode parseIf(TokenIterator<TokenType> tokens) { + var start = tokens.position(); + + var conditionalBranches = new ArrayList<UntypedConditionalBranchNode>(); + var elseBody = List.<UntypedFunctionStatementNode>of(); + + while (true) { + var conditionalBranch = parseConditionalBranch(tokens); + conditionalBranches.add(conditionalBranch); + + if (!tokens.trySkip(TokenType.KEYWORD_ELSE)) { + break; + } + + if (!tokens.isNext(TokenType.KEYWORD_IF)) { + elseBody = parseBlock(tokens); + break; + } + + } + + var end = tokens.position(); + var source = start.to(end); + + return new UntypedIfNode( + conditionalBranches, + elseBody, + source + ); + } + + private static UntypedConditionalBranchNode parseConditionalBranch(TokenIterator<TokenType> tokens) { + var start = tokens.position(); + + tokens.skip(TokenType.KEYWORD_IF); + + tokens.skip(TokenType.SYMBOL_PAREN_OPEN); + var condition = parseTopLevelExpression(tokens); + tokens.skip(TokenType.SYMBOL_PAREN_CLOSE); + + var body = parseBlock(tokens); + + var end = tokens.position(); + var source = start.to(end); + + return new UntypedConditionalBranchNode(condition, body, source); + } + private static UntypedExpressionNode parseInt(TokenIterator<TokenType> tokens) { var start = tokens.position(); @@ -333,6 +381,8 @@ public class Parser { case IDENTIFIER -> parseReference(tokens); case INT -> parseInt(tokens); + + case KEYWORD_IF -> parseIf(tokens); case STRING -> parseStringLiteral(tokens); diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java index 273f1898..afe7f565 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java @@ -276,6 +276,9 @@ public class TypeChecker { case UntypedCallNode untypedCall -> typeCheckCall(untypedCall, context); + case UntypedIfNode untypedIf -> + throw new UnsupportedOperationException("TODO"); + case UntypedIntLiteralNode untypedIntLiteral -> typeCheckIntLiteral(untypedIntLiteral, context); diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java index bc237a00..17675e35 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java @@ -38,6 +38,17 @@ public class UntypedNodeMatchers { return UntypedCallNodeMatcher.empty(); } + public static Matcher<UntypedConditionalBranchNode> isUntypedConditionalBranchNode( + Matcher<UntypedExpressionNode> condition, + Matcher<Iterable<? extends UntypedFunctionStatementNode>> body + ) { + return instanceOf( + UntypedConditionalBranchNode.class, + has("condition", x -> x.condition(), condition), + has("body", x -> x.body(), body) + ); + } + public static Matcher<UntypedExpressionNode> isUntypedEqualsNode( Matcher<UntypedExpressionNode> left, Matcher<UntypedExpressionNode> right @@ -58,6 +69,17 @@ public class UntypedNodeMatchers { return UntypedFunctionDefinitionNodeMatcher.empty(); } + public static Matcher<UntypedExpressionNode> isUntypedIfNode( + Matcher<Iterable<? extends UntypedConditionalBranchNode>> conditionalBranches, + Matcher<Iterable<? extends UntypedFunctionStatementNode>> elseBody + ) { + return instanceOf( + UntypedIfNode.class, + has("conditionalBranches", x -> x.conditionalBranches(), conditionalBranches), + has("elseBody", x -> x.elseBody(), elseBody) + ); + } + public static Matcher<UntypedExpressionNode> isUntypedIntLiteralNode(int value) { return isUntypedIntLiteralNode(BigInteger.valueOf(value)); } diff --git a/src/test/java/org/shedlang/compiler/parser/ParserIfTests.java b/src/test/java/org/shedlang/compiler/parser/ParserIfTests.java new file mode 100644 index 00000000..5de0988d --- /dev/null +++ b/src/test/java/org/shedlang/compiler/parser/ParserIfTests.java @@ -0,0 +1,102 @@ +package org.shedlang.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.*; +import static org.shedlang.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserIfTests { + @Test + public void canParseIfWithSingleConditionalBranchAndNoElse() { + var source = "if (a) { 1; }"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedIfNode( + isSequence( + isUntypedConditionalBranchNode( + isUntypedReferenceNode("a"), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(1)) + ) + ) + ), + isSequence() + )); + } + + @Test + public void canParseIfWithSingleConditionalBranchAndElse() { + var source = "if (a) { 1; } else { 2; }"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedIfNode( + isSequence( + isUntypedConditionalBranchNode( + isUntypedReferenceNode("a"), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(1)) + ) + ) + ), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(2)) + ) + )); + } + + @Test + public void canParseIfWithManyConditionalBranchesAndNoElse() { + var source = "if (a) { 1; } else if (b) { 2; }"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedIfNode( + isSequence( + isUntypedConditionalBranchNode( + isUntypedReferenceNode("a"), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(1)) + ) + ), + isUntypedConditionalBranchNode( + isUntypedReferenceNode("b"), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(2)) + ) + ) + ), + isSequence() + )); + } + + @Test + public void canParseIfWithManyConditionalBranchesAndElse() { + var source = "if (a) { 1; } else if (b) { 2; } else { 3; }"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedIfNode( + isSequence( + isUntypedConditionalBranchNode( + isUntypedReferenceNode("a"), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(1)) + ) + ), + isUntypedConditionalBranchNode( + isUntypedReferenceNode("b"), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(2)) + ) + ) + ), + isSequence( + isUntypedExpressionStatementNode(isUntypedIntLiteralNode(3)) + ) + )); + } +} |
