diff --git a/Upsilon/BaseTypes/Number/NumberLong.cs b/Upsilon/BaseTypes/Number/NumberLong.cs index e41686b..fab60e5 100644 --- a/Upsilon/BaseTypes/Number/NumberLong.cs +++ b/Upsilon/BaseTypes/Number/NumberLong.cs @@ -1,3 +1,4 @@ +using System; using System.Globalization; namespace Upsilon.BaseTypes.Number @@ -21,5 +22,10 @@ namespace Upsilon.BaseTypes.Number { return Value.ToString(CultureInfo.InvariantCulture); } + + public static explicit operator long(NumberLong n) + { + return n.Value; + } } } \ No newline at end of file diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index abe9e9e..30a14e2 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -180,7 +180,17 @@ namespace Upsilon.Binder { var condition = BindExpressionStatement(e.Condition); var block = BindBlockStatement(e.Block); - return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block); + if (e.ElseStatement == null) + { + return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block); + } + else + { + var elseBlock = BindBlockStatement(e.ElseStatement.Block); + var elseStatement = new BoundElseStatement((BoundBlockStatement) elseBlock); + return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block, + elseStatement); + } } } diff --git a/Upsilon/Binder/BoundIfStatement.cs b/Upsilon/Binder/BoundIfStatement.cs index 135187f..37c31e7 100644 --- a/Upsilon/Binder/BoundIfStatement.cs +++ b/Upsilon/Binder/BoundIfStatement.cs @@ -5,12 +5,31 @@ namespace Upsilon.Binder public BoundIfStatement(BoundExpressionStatement condition, BoundBlockStatement block) { Condition = condition; - Block = block; + Block = block; + } + public BoundIfStatement(BoundExpressionStatement condition, BoundBlockStatement block, BoundElseStatement elseStatement) + { + Condition = condition; + Block = block; + ElseStatement = elseStatement; } public override BoundKind Kind => BoundKind.BoundIfStatement; public BoundExpressionStatement Condition { get; } public BoundBlockStatement Block { get; } + public BoundElseStatement ElseStatement { get; } + } + + public class BoundElseStatement : BoundStatement + { + public BoundBlockStatement Block { get; } + + public BoundElseStatement(BoundBlockStatement block) + { + Block = block; + } + + public override BoundKind Kind => BoundKind.BoundElseStatement; } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundKind.cs b/Upsilon/Binder/BoundKind.cs index 53af829..89e9d9a 100644 --- a/Upsilon/Binder/BoundKind.cs +++ b/Upsilon/Binder/BoundKind.cs @@ -13,6 +13,7 @@ namespace Upsilon.Binder BoundAssignmentStatement, BoundExpressionStatement, BoundBlockStatement, - BoundIfStatement + BoundIfStatement, + BoundElseStatement } } \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 2fcf1b7..834d9b0 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -138,6 +138,10 @@ namespace Upsilon.Evaluator { EvaluateBoundBlockStatement(boundBlockStatement.Block); } + else if (boundBlockStatement.ElseStatement != null) + { + EvaluateBoundBlockStatement(boundBlockStatement.ElseStatement.Block); + } } } } \ No newline at end of file diff --git a/Upsilon/Parser/Parser.cs b/Upsilon/Parser/Parser.cs index 4a0ecc3..07516be 100644 --- a/Upsilon/Parser/Parser.cs +++ b/Upsilon/Parser/Parser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Immutable; +using System.Linq; using Upsilon.Text; namespace Upsilon.Parser @@ -48,14 +49,14 @@ namespace Upsilon.Parser return new SyntaxToken(kind, Current.Span.Start, "", null); } - public StatementSyntax ParseScriptSyntax() + private StatementSyntax ParseScriptSyntax() { - var statement = ParseBlockStatement(); + var statement = ParseBlockStatement(new []{SyntaxKind.EndOfFile}); MatchToken(SyntaxKind.EndOfFile); return statement; } - public StatementSyntax ParseStatement() + private StatementSyntax ParseStatement() { if (Current.Kind == SyntaxKind.Identifier && Next.Kind == SyntaxKind.Equals) { @@ -69,14 +70,18 @@ namespace Upsilon.Parser { return ParseIfStatement(); } + if (Current.Kind == SyntaxKind.ElseKeyword) + { + Console.WriteLine("Here"); + } return ParseExpressionStatement(); } - public StatementSyntax ParseBlockStatement() + private StatementSyntax ParseBlockStatement(SyntaxKind[] endTokens) { var statements = ImmutableArray.CreateBuilder(); - while (Current.Kind != SyntaxKind.EndKeyword && Current.Kind != SyntaxKind.EndOfFile) + while (!endTokens.Contains(Current.Kind)) { var next = ParseStatement(); statements.Add(next); @@ -84,23 +89,36 @@ namespace Upsilon.Parser return new BlockStatementSyntax(statements.ToImmutable()); } - public StatementSyntax ParseIfStatement() + private StatementSyntax ParseIfStatement() { var ifToken = MatchToken(SyntaxKind.IfKeyword); var condition = ParseExpressionStatement(); var thenToken = MatchToken(SyntaxKind.ThenKeyword); - var block = ParseBlockStatement(); - var endToken = MatchToken(SyntaxKind.EndKeyword); - return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, endToken); + var block = ParseBlockStatement(new []{SyntaxKind.EndKeyword, SyntaxKind.ElseIfKeyword, SyntaxKind.ElseKeyword}); + var endToken = NextToken(); + switch (endToken.Kind) + { + case SyntaxKind.EndKeyword: + return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, endToken); + case SyntaxKind.ElseKeyword: + { + var elseBlock = ParseBlockStatement(new[]{SyntaxKind.EndKeyword}); + var endEndToken = MatchToken(SyntaxKind.EndKeyword); + var elseStatement = new ElseStatementSyntax(endToken, (BlockStatementSyntax) elseBlock, endEndToken); + return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, elseStatement); + } + default: + return null; + } } - public ExpressionStatementSyntax ParseExpressionStatement() + private ExpressionStatementSyntax ParseExpressionStatement() { var expression = ParseExpression(); return new ExpressionStatementSyntax(expression); } - public ExpressionSyntax ParseExpression() + private ExpressionSyntax ParseExpression() { return ParseBinaryExpression(); } diff --git a/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs b/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs index 5c10b1b..0633f9e 100644 --- a/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs +++ b/Upsilon/Parser/StatementSyntax/IfStatementSyntax.cs @@ -17,11 +17,24 @@ namespace Upsilon.Parser 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; + ElseStatement = elseStatement; + + Span = new TextSpan(ifToken.Span.Start, elseStatement.Span.End); + } + public SyntaxToken IfToken { get; } public ExpressionStatementSyntax Condition { get; } public SyntaxToken ThenToken { get; } public BlockStatementSyntax Block { get; } + public ElseStatementSyntax ElseStatement { get; } public SyntaxToken EndToken { get; } public override SyntaxKind Kind => SyntaxKind.IfStatement; @@ -31,6 +44,31 @@ namespace Upsilon.Parser yield return Condition; yield return ThenToken; yield return Block; + if (EndToken != null) + yield return EndToken; + if (ElseStatement != null) + yield return ElseStatement; + } + } + + public sealed class ElseStatementSyntax : StatementSyntax + { + public SyntaxToken ElseToken { get; } + public BlockStatementSyntax Block { get; } + public SyntaxToken EndToken { get; } + + public ElseStatementSyntax(SyntaxToken elseToken, BlockStatementSyntax block, SyntaxToken endToken) + { + ElseToken = elseToken; + Block = block; + EndToken = endToken; + } + + public override SyntaxKind Kind => SyntaxKind.ElseStatement; + public override IEnumerable ChildNodes() + { + yield return ElseToken; + yield return Block; yield return EndToken; } } diff --git a/Upsilon/Parser/SyntaxKeyWords.cs b/Upsilon/Parser/SyntaxKeyWords.cs index 2f17713..850d0ae 100644 --- a/Upsilon/Parser/SyntaxKeyWords.cs +++ b/Upsilon/Parser/SyntaxKeyWords.cs @@ -24,6 +24,10 @@ namespace Upsilon.Parser return SyntaxKind.IfKeyword; case "then": return SyntaxKind.ThenKeyword; + case "elseif": + return SyntaxKind.ElseIfKeyword; + case "else": + return SyntaxKind.ElseKeyword; default: return SyntaxKind.Identifier; } diff --git a/Upsilon/Parser/SyntaxKind.cs b/Upsilon/Parser/SyntaxKind.cs index 84f9550..f84d31c 100644 --- a/Upsilon/Parser/SyntaxKind.cs +++ b/Upsilon/Parser/SyntaxKind.cs @@ -30,6 +30,8 @@ namespace Upsilon.Parser EndKeyword, IfKeyword, ThenKeyword, + ElseIfKeyword, + ElseKeyword, Identifier, @@ -48,6 +50,8 @@ namespace Upsilon.Parser // statements ExpressionStatement, BlockStatement, - IfStatement + IfStatement, + ElseIfStatement, + ElseStatement } } \ No newline at end of file diff --git a/Upsilon/Text/SourceText.cs b/Upsilon/Text/SourceText.cs index a72aae1..e6dfef6 100644 --- a/Upsilon/Text/SourceText.cs +++ b/Upsilon/Text/SourceText.cs @@ -18,6 +18,8 @@ namespace Upsilon.Text length += start; start = 0; }; + if (start >= _text.Length) + return string.Empty; if (start + length >= _text.Length) length = _text.Length - start; return _text.Substring(start, length); } diff --git a/UpsilonTests/IfTests.cs b/UpsilonTests/IfTests.cs index 0b66cd4..ce3d76d 100644 --- a/UpsilonTests/IfTests.cs +++ b/UpsilonTests/IfTests.cs @@ -1,3 +1,4 @@ +using Upsilon.BaseTypes.Number; using Upsilon.Evaluator; using Xunit; @@ -8,11 +9,30 @@ namespace UpsilonTests [Fact] public void BasicIfTest() { - var input = "if true then val = true end"; + var input = "if true then val = true end"; var script = new Script(input); Assert.Empty(script.Diagnostics.Messages); var actual = script.Evaluate(); Assert.True(actual); } + + [Theory] + [InlineData("true", 3, 8, 3)] + [InlineData("false", 3, 8, 8)] + [InlineData("5 == 5", 500, 349, 500)] + public void BasicIfElseTests(string condition, int in1, int in2, long expected) + { + var input = +$@" +if {condition} then + val = {in1} +else + val = {in2} +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