Handle if statements

This commit is contained in:
Deukhoofd 2018-11-13 12:48:50 +01:00
parent e5ac28bacf
commit 1aee448999
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
15 changed files with 135 additions and 17 deletions

View File

@ -33,6 +33,8 @@ namespace Upsilon.Binder
return BindAssignmentStatement((AssignmentExpressionSyntax) s); return BindAssignmentStatement((AssignmentExpressionSyntax) s);
case SyntaxKind.BlockStatement: case SyntaxKind.BlockStatement:
return BindBlockStatement((BlockStatementSyntax) s); return BindBlockStatement((BlockStatementSyntax) s);
case SyntaxKind.IfStatement:
return BindIfStatement((IfStatementSyntax) s);
} }
throw new NotImplementedException(s.Kind.ToString()); throw new NotImplementedException(s.Kind.ToString());
@ -173,5 +175,13 @@ namespace Upsilon.Binder
} }
return new BoundBlockStatement(arr.ToImmutable()); return new BoundBlockStatement(arr.ToImmutable());
} }
private BoundStatement BindIfStatement(IfStatementSyntax e)
{
var condition = BindExpressionStatement(e.Condition);
var block = BindBlockStatement(e.Block);
return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block);
}
} }
} }

View File

@ -0,0 +1,16 @@
namespace Upsilon.Binder
{
public class BoundIfStatement : BoundStatement
{
public BoundIfStatement(BoundExpressionStatement condition, BoundBlockStatement block)
{
Condition = condition;
Block = block;
}
public override BoundKind Kind => BoundKind.BoundIfStatement;
public BoundExpressionStatement Condition { get; }
public BoundBlockStatement Block { get; }
}
}

View File

@ -12,6 +12,7 @@ namespace Upsilon.Binder
// Statements // Statements
BoundAssignmentStatement, BoundAssignmentStatement,
BoundExpressionStatement, BoundExpressionStatement,
BoundBlockStatement BoundBlockStatement,
BoundIfStatement
} }
} }

View File

@ -25,9 +25,9 @@ namespace Upsilon
Log(DiagnosticLevel.Error, message, location); Log(DiagnosticLevel.Error, message, location);
} }
public void LogBadCharacter(TextSpan location) public void LogBadCharacter(TextSpan location, SyntaxKind expectedToken)
{ {
LogError($"Invalid character found", location); LogError($"Invalid character found. Expected: '{expectedToken}'", location);
} }
public void LogUnknownVariable(TextSpan span, string variable) public void LogUnknownVariable(TextSpan span, string variable)

View File

@ -40,6 +40,9 @@ namespace Upsilon.Evaluator
case BoundKind.BoundBlockStatement: case BoundKind.BoundBlockStatement:
EvaluateBoundBlockStatement((BoundBlockStatement) e); EvaluateBoundBlockStatement((BoundBlockStatement) e);
break; break;
case BoundKind.BoundIfStatement:
EvaluateBoundIfStatement((BoundIfStatement) e);
break;
default: default:
EvaluateExpressionStatement((BoundExpressionStatement) e); EvaluateExpressionStatement((BoundExpressionStatement) e);
break; break;
@ -127,5 +130,14 @@ namespace Upsilon.Evaluator
EvaluateStatement(boundStatement); EvaluateStatement(boundStatement);
} }
} }
private void EvaluateBoundIfStatement(BoundIfStatement boundBlockStatement)
{
var condition = EvaluateExpression(boundBlockStatement.Condition.Expression);
if ((bool) condition)
{
EvaluateBoundBlockStatement(boundBlockStatement.Block);
}
}
} }
} }

View File

@ -18,6 +18,8 @@ namespace Upsilon.Evaluator
ScriptString = new SourceText(scriptString); ScriptString = new SourceText(scriptString);
Diagnostics = new Diagnostics(ScriptString); Diagnostics = new Diagnostics(ScriptString);
_parsed = Parser.Parser.Parse(scriptString, Diagnostics); _parsed = Parser.Parser.Parse(scriptString, Diagnostics);
if (variables == null)
variables = new Dictionary<VariableSymbol, object>();
Binder = new Binder.Binder(new BoundScope(variables, null), Diagnostics); Binder = new Binder.Binder(new BoundScope(variables, null), Diagnostics);
Evaluator = new Evaluator(this, Diagnostics, variables); Evaluator = new Evaluator(this, Diagnostics, variables);
} }

View File

@ -99,7 +99,7 @@ namespace Upsilon.Parser
default: default:
if (char.IsLetter(Current)) if (char.IsLetter(Current))
return LexIdentifierOrKeyword(); return LexIdentifierOrKeyword();
_diagnostics.LogBadCharacter(new TextSpan(_position, 1)); _diagnostics.LogBadCharacter(new TextSpan(_position, 1), SyntaxKind.Identifier);
return new SyntaxToken(SyntaxKind.BadToken, _position, "", null); return new SyntaxToken(SyntaxKind.BadToken, _position, "", null);
} }
} }
@ -116,7 +116,7 @@ namespace Upsilon.Parser
{ {
if (hasDecimalPoint) if (hasDecimalPoint)
{ {
_diagnostics.LogBadCharacter(new TextSpan(_position, 1)); _diagnostics.LogBadCharacter(new TextSpan(_position, 1), SyntaxKind.Number);
return new SyntaxToken(SyntaxKind.BadToken, _position, "", null); return new SyntaxToken(SyntaxKind.BadToken, _position, "", null);
} }
hasDecimalPoint = true; hasDecimalPoint = true;

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using Upsilon.Text; using Upsilon.Text;
@ -43,13 +44,14 @@ namespace Upsilon.Parser
if (Current.Kind == kind) if (Current.Kind == kind)
return NextToken(); return NextToken();
_diagnostics.LogBadCharacter(Current.Span); _diagnostics.LogBadCharacter(Current.Span, kind);
return new SyntaxToken(kind, Current.Span.Start, "", null); return new SyntaxToken(kind, Current.Span.Start, "", null);
} }
public StatementSyntax ParseScriptSyntax() public StatementSyntax ParseScriptSyntax()
{ {
var statement = ParseBlockStatement(); var statement = ParseBlockStatement();
MatchToken(SyntaxKind.EndOfFile);
return statement; return statement;
} }
@ -63,6 +65,10 @@ namespace Upsilon.Parser
{ {
return ParseAssignmentExpression(); return ParseAssignmentExpression();
} }
if (Current.Kind == SyntaxKind.IfKeyword)
{
return ParseIfStatement();
}
return ParseExpressionStatement(); return ParseExpressionStatement();
} }
@ -75,8 +81,17 @@ namespace Upsilon.Parser
var next = ParseStatement(); var next = ParseStatement();
statements.Add(next); statements.Add(next);
} }
var endToken = NextToken(); return new BlockStatementSyntax(statements.ToImmutable());
return new BlockStatementSyntax(statements.ToImmutable(), endToken); }
public 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);
} }
public ExpressionStatementSyntax ParseExpressionStatement() public ExpressionStatementSyntax ParseExpressionStatement()
@ -147,7 +162,7 @@ namespace Upsilon.Parser
var token = MatchToken(SyntaxKind.Identifier); var token = MatchToken(SyntaxKind.Identifier);
return new VariableExpressionSyntax((IdentifierToken) token); return new VariableExpressionSyntax((IdentifierToken) token);
default: default:
_diagnostics.LogBadCharacter(new TextSpan(_position, 1)); _diagnostics.LogBadCharacter(new TextSpan(_position, 1), SyntaxKind.Identifier);
NextToken(); NextToken();
return new BadExpressionSyntax(); return new BadExpressionSyntax();
} }

View File

@ -7,17 +7,16 @@ namespace Upsilon.Parser
{ {
public sealed class BlockStatementSyntax : StatementSyntax public sealed class BlockStatementSyntax : StatementSyntax
{ {
public BlockStatementSyntax(ImmutableArray<StatementSyntax> statements, SyntaxToken endToken) public BlockStatementSyntax(ImmutableArray<StatementSyntax> statements)
{ {
Statements = statements; Statements = statements;
EndToken = endToken;
var first = statements.FirstOrDefault(); var first = statements.FirstOrDefault();
if (first != null) var last = statements.LastOrDefault();
Span = new TextSpan(first.Span.Start, endToken.Span.End); if (first != null && last != null)
Span = new TextSpan(first.Span.Start, last.Span.End);
} }
public ImmutableArray<StatementSyntax> Statements { get; } public ImmutableArray<StatementSyntax> Statements { get; }
public SyntaxToken EndToken { get; }
public override SyntaxKind Kind => SyntaxKind.BlockStatement; public override SyntaxKind Kind => SyntaxKind.BlockStatement;
public override IEnumerable<SyntaxNode> ChildNodes() public override IEnumerable<SyntaxNode> ChildNodes()
{ {

View File

@ -0,0 +1,37 @@
using System.Collections.Generic;
using Upsilon.Text;
namespace Upsilon.Parser
{
public sealed class IfStatementSyntax : StatementSyntax
{
public IfStatementSyntax(SyntaxToken ifToken, ExpressionStatementSyntax condition, SyntaxToken thenToken,
BlockStatementSyntax block, SyntaxToken endToken)
{
IfToken = ifToken;
Condition = condition;
ThenToken = thenToken;
Block = block;
EndToken = endToken;
Span = new TextSpan(ifToken.Span.Start, endToken.Span.End);
}
public SyntaxToken IfToken { get; }
public ExpressionStatementSyntax Condition { get; }
public SyntaxToken ThenToken { get; }
public BlockStatementSyntax Block { get; }
public SyntaxToken EndToken { get; }
public override SyntaxKind Kind => SyntaxKind.IfStatement;
public override IEnumerable<SyntaxNode> ChildNodes()
{
yield return IfToken;
yield return Condition;
yield return ThenToken;
yield return Block;
yield return EndToken;
}
}
}

View File

@ -20,6 +20,10 @@ namespace Upsilon.Parser
return SyntaxKind.LocalKeyword; return SyntaxKind.LocalKeyword;
case "end": case "end":
return SyntaxKind.EndKeyword; return SyntaxKind.EndKeyword;
case "if":
return SyntaxKind.IfKeyword;
case "then":
return SyntaxKind.ThenKeyword;
default: default:
return SyntaxKind.Identifier; return SyntaxKind.Identifier;
} }

View File

@ -28,6 +28,8 @@ namespace Upsilon.Parser
OrKeyword, OrKeyword,
LocalKeyword, LocalKeyword,
EndKeyword, EndKeyword,
IfKeyword,
ThenKeyword,
Identifier, Identifier,
@ -45,6 +47,7 @@ namespace Upsilon.Parser
// statements // statements
ExpressionStatement, ExpressionStatement,
BlockStatement BlockStatement,
IfStatement
} }
} }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>

18
UpsilonTests/IfTests.cs Normal file
View File

@ -0,0 +1,18 @@
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
{
public class IfTests
{
[Fact]
public void BasicIfTest()
{
var input = "if true then val = true end";
var script = new Script(input);
Assert.Empty(script.Diagnostics.Messages);
var actual = script.Evaluate<bool>();
Assert.True(actual);
}
}
}

View File

@ -47,8 +47,9 @@ namespace Yc
} }
else else
{ {
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(evaluate); Console.WriteLine(evaluate);
//variables = parsed.Variables; Console.ResetColor();
} }
} }
} }