From b9aac5247668d722e21bb91ffc9ab5dbfd8bc519 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Tue, 13 Nov 2018 15:15:44 +0100 Subject: [PATCH] Handle if else if (else) statements --- Upsilon/Binder/Binder.cs | 8 +- Upsilon/Binder/BoundIfStatement.cs | 12 ++- Upsilon/Evaluator/Evaluator.cs | 4 + Upsilon/Parser/Parser.cs | 28 +++---- .../StatementSyntax/IfStatementSyntax.cs | 73 ++++++++++++++++--- UpsilonTests/IfTests.cs | 21 ++++++ 6 files changed, 120 insertions(+), 26 deletions(-) diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 30a14e2..f3e9c2e 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -105,7 +105,7 @@ namespace Upsilon.Binder type = Type.Number; outValue = new NumberLong(l); break; - case bool b: + case bool _: type = Type.Boolean; outValue = value; break; @@ -180,6 +180,12 @@ namespace Upsilon.Binder { var condition = BindExpressionStatement(e.Condition); var block = BindBlockStatement(e.Block); + if (e.NextElseIfStatement != null) + { + var nextElseIf = BindIfStatement(e.NextElseIfStatement); + return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block, + (BoundIfStatement) nextElseIf); + } if (e.ElseStatement == null) { return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block); diff --git a/Upsilon/Binder/BoundIfStatement.cs b/Upsilon/Binder/BoundIfStatement.cs index 37c31e7..1a80bfd 100644 --- a/Upsilon/Binder/BoundIfStatement.cs +++ b/Upsilon/Binder/BoundIfStatement.cs @@ -7,17 +7,25 @@ namespace Upsilon.Binder Condition = condition; Block = block; } + public BoundIfStatement(BoundExpressionStatement condition, BoundBlockStatement block, BoundElseStatement elseStatement) { - Condition = condition; - Block = block; + Condition = condition; + Block = block; ElseStatement = elseStatement; } + public BoundIfStatement(BoundExpressionStatement condition, BoundBlockStatement block, BoundIfStatement nextElseIf) + { + Condition = condition; + Block = block; + NextElseIf = nextElseIf; + } public override BoundKind Kind => BoundKind.BoundIfStatement; public BoundExpressionStatement Condition { get; } public BoundBlockStatement Block { get; } + public BoundIfStatement NextElseIf { get; } public BoundElseStatement ElseStatement { get; } } diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 834d9b0..6fc3c12 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -138,6 +138,10 @@ namespace Upsilon.Evaluator { EvaluateBoundBlockStatement(boundBlockStatement.Block); } + else if (boundBlockStatement.NextElseIf != null) + { + EvaluateBoundIfStatement(boundBlockStatement.NextElseIf); + } else if (boundBlockStatement.ElseStatement != null) { EvaluateBoundBlockStatement(boundBlockStatement.ElseStatement.Block); diff --git a/Upsilon/Parser/Parser.cs b/Upsilon/Parser/Parser.cs index 07516be..3c3bf64 100644 --- a/Upsilon/Parser/Parser.cs +++ b/Upsilon/Parser/Parser.cs @@ -68,11 +68,7 @@ namespace Upsilon.Parser } if (Current.Kind == SyntaxKind.IfKeyword) { - return ParseIfStatement(); - } - if (Current.Kind == SyntaxKind.ElseKeyword) - { - Console.WriteLine("Here"); + return ParseIfStatement(SyntaxKind.IfKeyword); } return ParseExpressionStatement(); @@ -89,26 +85,32 @@ namespace Upsilon.Parser return new BlockStatementSyntax(statements.ToImmutable()); } - private StatementSyntax ParseIfStatement() + private StatementSyntax ParseIfStatement(SyntaxKind requiredToken) { - var ifToken = MatchToken(SyntaxKind.IfKeyword); + var ifToken = MatchToken(requiredToken); var condition = ParseExpressionStatement(); var thenToken = MatchToken(SyntaxKind.ThenKeyword); var block = ParseBlockStatement(new []{SyntaxKind.EndKeyword, SyntaxKind.ElseIfKeyword, SyntaxKind.ElseKeyword}); - var endToken = NextToken(); - switch (endToken.Kind) + switch (Current.Kind) { - case SyntaxKind.EndKeyword: - return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, endToken); + case SyntaxKind.ElseIfKeyword: + var nextElseIf = + new ElseIfStatementSyntax((IfStatementSyntax) ParseIfStatement(SyntaxKind.ElseIfKeyword)); + return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, + nextElseIf); case SyntaxKind.ElseKeyword: { + var elseToken = MatchToken(SyntaxKind.ElseKeyword); var elseBlock = ParseBlockStatement(new[]{SyntaxKind.EndKeyword}); var endEndToken = MatchToken(SyntaxKind.EndKeyword); - var elseStatement = new ElseStatementSyntax(endToken, (BlockStatementSyntax) elseBlock, endEndToken); + var elseStatement = new ElseStatementSyntax(elseToken, (BlockStatementSyntax) elseBlock, endEndToken); return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, elseStatement); } + case SyntaxKind.EndKeyword: + var endToken = MatchToken(SyntaxKind.EndKeyword); + return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, endToken); default: - return null; + throw new ArgumentOutOfRangeException(); } } diff --git a/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs b/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs index 0633f9e..9d08e14 100644 --- a/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs +++ b/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs @@ -1,9 +1,12 @@ +using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using Upsilon.Text; namespace Upsilon.Parser { - public sealed class IfStatementSyntax : StatementSyntax + public class IfStatementSyntax : StatementSyntax { public IfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, BlockStatementSyntax block, SyntaxToken endToken) @@ -14,19 +17,27 @@ namespace Upsilon.Parser Block = block; EndToken = endToken; - Span = new TextSpan(ifToken.Span.Start, endToken.Span.End); } public IfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, BlockStatementSyntax block, ElseStatementSyntax elseStatement) { - IfToken = ifToken; - Condition = condition; - ThenToken = thenToken; - Block = block; + IfToken = ifToken; + Condition = condition; + ThenToken = thenToken; + Block = block; ElseStatement = elseStatement; - Span = new TextSpan(ifToken.Span.Start, elseStatement.Span.End); + } + + public IfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, + BlockStatementSyntax block, ElseIfStatementSyntax nextElseIfStatement) + { + IfToken = ifToken; + Condition = condition; + ThenToken = thenToken; + Block = block; + NextElseIfStatement = nextElseIfStatement; } @@ -34,7 +45,8 @@ namespace Upsilon.Parser public ExpressionStatementSyntax Condition { get; } public SyntaxToken ThenToken { get; } public BlockStatementSyntax Block { get; } - public ElseStatementSyntax ElseStatement { get; } + public ElseIfStatementSyntax NextElseIfStatement { get; protected set; } + public ElseStatementSyntax ElseStatement { get; protected set; } public SyntaxToken EndToken { get; } public override SyntaxKind Kind => SyntaxKind.IfStatement; @@ -44,13 +56,54 @@ namespace Upsilon.Parser yield return Condition; yield return ThenToken; yield return Block; - if (EndToken != null) - yield return EndToken; + if (NextElseIfStatement != null) + yield return NextElseIfStatement; if (ElseStatement != null) yield return ElseStatement; + if (EndToken != null) + yield return EndToken; } } + public sealed class ElseIfStatementSyntax : IfStatementSyntax + { + public SyntaxToken ElseIfToken => IfToken; + + public override SyntaxKind Kind => SyntaxKind.ElseIfStatement; + public override IEnumerable ChildNodes() + { + yield return ElseIfToken; + yield return Condition; + yield return ThenToken; + yield return Block; + } + + public ElseIfStatementSyntax(IfStatementSyntax s) + :base(s.IfToken, s.Condition, s.ThenToken, s.Block, s.EndToken) + { + NextElseIfStatement = s.NextElseIfStatement; + ElseStatement = s.ElseStatement; + } + + public ElseIfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, + BlockStatementSyntax block, SyntaxToken endToken) : base(ifToken, condition, thenToken, block, endToken) + { + } + + public ElseIfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, + BlockStatementSyntax block, ElseStatementSyntax elseStatement) : base(ifToken, condition, thenToken, block, + elseStatement) + { + } + + public ElseIfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken, + BlockStatementSyntax block, ElseIfStatementSyntax nextElseIfStatement) : base(ifToken, condition, thenToken, + block, nextElseIfStatement) + { + } + } + + public sealed class ElseStatementSyntax : StatementSyntax { public SyntaxToken ElseToken { get; } diff --git a/UpsilonTests/IfTests.cs b/UpsilonTests/IfTests.cs index ce3d76d..5d87909 100644 --- a/UpsilonTests/IfTests.cs +++ b/UpsilonTests/IfTests.cs @@ -34,5 +34,26 @@ end"; var actual = (long)(NumberLong)script.Evaluate(); Assert.Equal(expected, actual); } + + [Theory] + [InlineData("true", "false", 3, 8, 5, 3)] + [InlineData("false", "true", 3, 8, 5, 8)] + public void BasicIfElseIfElseTests(string condition1, string condition2, int in1, int in2, int in3, long expected) + { + var input = + $@" +if {condition1} then + val = {in1} +elseif {condition2} then + val = {in2} +else + val = {in3} +end"; + var script = new Script(input); + Assert.Empty(script.Diagnostics.Messages); + var actual = (long)(NumberLong)script.Evaluate(); + Assert.Equal(expected, actual); + } + } } \ No newline at end of file