Handle variables

This commit is contained in:
Deukhoofd 2018-11-10 17:00:39 +01:00
parent 0693698f28
commit ab61a01573
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
16 changed files with 266 additions and 23 deletions

View File

@ -3,19 +3,28 @@ using Upsilon.Parser;
namespace Upsilon.Evaluator namespace Upsilon.Evaluator
{ {
public static class Evaluator public class Evaluator
{ {
public static object Evaluate(this ScriptSyntax e) public Script Script { get; }
public VariableScope Scope { get; }
public Evaluator(Script script, VariableScope scope)
{
Script = script;
Scope = scope;
}
public object Evaluate(ScriptSyntax e)
{ {
return EvaluateExpression(e.Statement); return EvaluateExpression(e.Statement);
} }
public static object Evaluate(this ExpressionSyntax e) public object Evaluate(ExpressionSyntax e)
{ {
return EvaluateExpression(e); return EvaluateExpression(e);
} }
private static object EvaluateExpression(ExpressionSyntax e) private object EvaluateExpression(ExpressionSyntax e)
{ {
switch (e.Kind) switch (e.Kind)
{ {
@ -26,13 +35,17 @@ namespace Upsilon.Evaluator
case SyntaxKind.LiteralExpression: case SyntaxKind.LiteralExpression:
return ((LiteralExpressionSyntax) e).Value; return ((LiteralExpressionSyntax) e).Value;
case SyntaxKind.ParenthesizedExpression: case SyntaxKind.ParenthesizedExpression:
return ((ParenthesizedExpressionSyntax) e).Expression.Evaluate(); return Evaluate(((ParenthesizedExpressionSyntax)e).Expression);
case SyntaxKind.AssignmentExpression:
return EvaluateAssignmentExpression((AssignmentExpressionSyntax)e);
case SyntaxKind.VariableExpression:
return EvaluateVariableExpression((VariableExpressionSyntax) e);
default: default:
throw new Exception("Invalid expression: " + e.Kind); throw new Exception("Invalid expression: " + e.Kind);
} }
} }
private static object EvaluateUnaryExpression(UnaryExpressionSyntax e) private object EvaluateUnaryExpression(UnaryExpressionSyntax e)
{ {
var operand = EvaluateExpression(e.Expression); var operand = EvaluateExpression(e.Expression);
switch (e.Operator.Kind) switch (e.Operator.Kind)
@ -48,7 +61,7 @@ namespace Upsilon.Evaluator
} }
} }
private static object EvaluateBinaryExpression(BinaryExpressionSyntax e) private object EvaluateBinaryExpression(BinaryExpressionSyntax e)
{ {
var left = EvaluateExpression(e.Left); var left = EvaluateExpression(e.Left);
var right = EvaluateExpression(e.Right); var right = EvaluateExpression(e.Right);
@ -75,5 +88,30 @@ namespace Upsilon.Evaluator
} }
} }
private object EvaluateAssignmentExpression(AssignmentExpressionSyntax e)
{
var variableName = e.Identifier.Name;
var val = EvaluateExpression(e.Expression);
if (e.LocalToken == null)
{
Scope.SetGlobalVariable(variableName, val);
}
else
{
Scope.SetVariable(variableName, val);
}
return val;
}
private object EvaluateVariableExpression(VariableExpressionSyntax e)
{
var varName = e.Identifier.Name;
if (Scope.TryGetVariable(varName, out var value))
{
return value;
}
throw new Exception("Unknown variable: " + varName);
}
} }
} }

View File

@ -0,0 +1,37 @@
using System.Collections.Generic;
using Upsilon.Parser;
namespace Upsilon.Evaluator
{
public class Script : VariableScope
{
private string ScriptString { get; }
private Evaluator Evaluator { get; }
public readonly ScriptSyntax Parsed;
public Script(string scriptString)
{
ScriptString = scriptString;
Evaluator = new Evaluator(this, this);
Parsed = Parser.Parser.Parse(scriptString);
}
public Script(string scriptString, Dictionary<string, object> variables = null)
:base(variables: variables)
{
ScriptString = scriptString;
Evaluator = new Evaluator(this, this);
Parsed = Parser.Parser.Parse(scriptString);
}
public object Evaluate()
{
return Evaluator.Evaluate(Parsed);
}
public T Evaluate<T>()
{
return (T)Evaluator.Evaluate(Parsed);
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
namespace Upsilon.Evaluator
{
public class VariableScope
{
private VariableScope _parentScope;
public readonly Dictionary<string, object> Variables = new Dictionary<string, object>();
public VariableScope(VariableScope parentScope = null, Dictionary<string, object> variables = null)
{
_parentScope = parentScope;
if (variables != null)
Variables = variables;
}
public void SetVariable(string key, object value)
{
if (Variables.ContainsKey(key))
Variables[key] = value;
else
Variables.Add(key, value);
}
public void SetGlobalVariable(string key, object value)
{
if (_parentScope == null)
{
SetVariable(key, value);
}
else
{
_parentScope.SetGlobalVariable(key, value);
}
}
public bool TryGetVariable(string key, out object result)
{
if (Variables.TryGetValue(key, out result))
{
return true;
}
if (_parentScope != null)
{
return _parentScope.TryGetVariable(key, out result);
}
throw new Exception("Variable not found: " + key);
}
}
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Upsilon.Parser
{
public class AssignmentExpressionSyntax : ExpressionSyntax
{
public AssignmentExpressionSyntax(SyntaxToken localToken, IdentifierToken identifier, SyntaxToken equalsToken,
ExpressionSyntax expression)
{
LocalToken = localToken;
Identifier = identifier;
EqualsToken = equalsToken;
Expression = expression;
}
public override SyntaxKind Kind => SyntaxKind.AssignmentExpression;
public SyntaxToken LocalToken { get; }
public IdentifierToken Identifier { get; }
public SyntaxToken EqualsToken { get; }
public ExpressionSyntax Expression { get; }
public override IEnumerable<SyntaxNode> ChildNodes()
{
yield return Identifier;
yield return Expression;
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace Upsilon.Parser
{
public class VariableExpressionSyntax : ExpressionSyntax
{
public IdentifierToken Identifier { get; }
public VariableExpressionSyntax(IdentifierToken identifier)
{
Identifier = identifier;
}
public override SyntaxKind Kind => SyntaxKind.VariableExpression;
public override IEnumerable<SyntaxNode> ChildNodes()
{
yield return Identifier;
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace Upsilon.Parser
{
public class IdentifierToken : SyntaxToken
{
public IdentifierToken(string name, int position, int length) : base(SyntaxKind.Identifier, position, name, null)
{
Name = name;
}
public string Name { get; }
public override SyntaxKind Kind => SyntaxKind.Identifier;
public override IEnumerable<SyntaxNode> ChildNodes()
{
yield break;
}
}
}

View File

@ -127,16 +127,21 @@ namespace Upsilon.Parser
private SyntaxToken LexIdentifierOrKeyword() private SyntaxToken LexIdentifierOrKeyword()
{ {
var start = _position; var start = _position;
var numStr = new StringBuilder(); var stringBuilder = new StringBuilder();
numStr.Append(Current); stringBuilder.Append(Current);
while (char.IsLetterOrDigit(Next) || Next == '_') while (char.IsLetterOrDigit(Next) || Next == '_')
{ {
numStr.Append(Next); stringBuilder.Append(Next);
_position++; _position++;
} }
var kind = SyntaxKeyWords.GetSyntaxKind(numStr.ToString()); var kind = SyntaxKeyWords.GetSyntaxKind(stringBuilder.ToString());
return new SyntaxToken(kind, start, numStr.ToString(), null); var str = stringBuilder.ToString();
if (kind == SyntaxKind.Identifier)
{
return new IdentifierToken(str, start, str.Length);
}
return new SyntaxToken(kind, start, str, null);
} }
} }
} }

View File

@ -53,9 +53,30 @@ namespace Upsilon.Parser
public ExpressionSyntax ParseExpression() public ExpressionSyntax ParseExpression()
{ {
if (Current.Kind == SyntaxKind.Identifier && Next.Kind == SyntaxKind.Equals)
{
return AssignmentExpression();
}
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.Identifier)
{
return AssignmentExpression();
}
return ParseBinaryExpression(); return ParseBinaryExpression();
} }
private AssignmentExpressionSyntax AssignmentExpression()
{
SyntaxToken localKeyword = null;
if (Current.Kind == SyntaxKind.LocalKeyword)
{
localKeyword = MatchToken(SyntaxKind.LocalKeyword);
}
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
var assignmentToken = MatchToken(SyntaxKind.Equals);
var expression = ParseExpression();
return new AssignmentExpressionSyntax(localKeyword, identifier, assignmentToken, expression);
}
private ExpressionSyntax ParseBinaryExpression(SyntaxKindPrecedence.Precedence parentPrecedence = SyntaxKindPrecedence.Precedence.None) private ExpressionSyntax ParseBinaryExpression(SyntaxKindPrecedence.Precedence parentPrecedence = SyntaxKindPrecedence.Precedence.None)
{ {
ExpressionSyntax left; ExpressionSyntax left;
@ -96,6 +117,9 @@ namespace Upsilon.Parser
case SyntaxKind.TrueKeyword: case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword: case SyntaxKind.FalseKeyword:
return ParseBoolean(); return ParseBoolean();
case SyntaxKind.Identifier:
var token = MatchToken(SyntaxKind.Identifier);
return new VariableExpressionSyntax((IdentifierToken) token);
default: default:
throw new Exception("Unknown primary expression type: " + Current.Kind); throw new Exception("Unknown primary expression type: " + Current.Kind);
} }

View File

@ -16,6 +16,8 @@ namespace Upsilon.Parser
return SyntaxKind.AndKeyword; return SyntaxKind.AndKeyword;
case "or": case "or":
return SyntaxKind.OrKeyword; return SyntaxKind.OrKeyword;
case "local":
return SyntaxKind.LocalKeyword;
default: default:
return SyntaxKind.Identifier; return SyntaxKind.Identifier;
} }

View File

@ -24,6 +24,7 @@ namespace Upsilon.Parser
NotKeyword, NotKeyword,
AndKeyword, AndKeyword,
OrKeyword, OrKeyword,
LocalKeyword,
Identifier, Identifier,
@ -32,6 +33,8 @@ namespace Upsilon.Parser
BinaryExpression, BinaryExpression,
LiteralExpression, LiteralExpression,
ParenthesizedExpression, ParenthesizedExpression,
AssignmentExpression,
VariableExpression,
// script unit // script unit
ScriptUnit, ScriptUnit,

View File

@ -30,17 +30,23 @@ namespace Upsilon.Parser
{ {
switch (kind) switch (kind)
{ {
// equality operators
case SyntaxKind.EqualsEquals: case SyntaxKind.EqualsEquals:
return Precedence.Equality; return Precedence.Equality;
case SyntaxKind.TildeEquals: case SyntaxKind.TildeEquals:
return Precedence.Equality; return Precedence.Equality;
// logical operators
case SyntaxKind.AndKeyword: case SyntaxKind.AndKeyword:
return Precedence.And; return Precedence.And;
case SyntaxKind.OrKeyword: case SyntaxKind.OrKeyword:
return Precedence.Or; return Precedence.Or;
// math operators
case SyntaxKind.Plus: case SyntaxKind.Plus:
case SyntaxKind.Minus: case SyntaxKind.Minus:
return Precedence.PlusMinus; return Precedence.PlusMinus;
case SyntaxKind.Star: case SyntaxKind.Star:
case SyntaxKind.Slash: case SyntaxKind.Slash:
return Precedence.StarSlash; return Precedence.StarSlash;

View File

@ -3,7 +3,7 @@ using Upsilon.Text;
namespace Upsilon.Parser namespace Upsilon.Parser
{ {
public sealed class SyntaxToken : SyntaxNode public class SyntaxToken : SyntaxNode
{ {
public SyntaxToken(SyntaxKind kind, int position, string text, object value) public SyntaxToken(SyntaxKind kind, int position, string text, object value)
{ {

View File

@ -16,6 +16,10 @@ namespace Upsilon.Utilities
sb.Append("|-- "); sb.Append("|-- ");
} }
sb.Append(token.Kind); sb.Append(token.Kind);
if (token is SyntaxToken node && node.Value != null)
{
sb.Append($" - {node.Value}");
}
foreach (var syntaxNode in token.ChildNodes()) foreach (var syntaxNode in token.ChildNodes())
{ {
sb.Append("\n"); sb.Append("\n");

View File

@ -16,7 +16,7 @@ namespace UpsilonTests
[InlineData("0.005 + 2.2", 2.205)] [InlineData("0.005 + 2.2", 2.205)]
public void Addition(string input, double expectedOutput) public void Addition(string input, double expectedOutput)
{ {
var actual = (double)Parser.Parse(input).Evaluate(); var actual = new Script(input).Evaluate<double>();
Assert.Equal(expectedOutput, actual, 8); Assert.Equal(expectedOutput, actual, 8);
} }
@ -29,7 +29,7 @@ namespace UpsilonTests
[InlineData("10.256-2.8546", 7.4014)] [InlineData("10.256-2.8546", 7.4014)]
public void Subtraction(string input, double expectedOutput) public void Subtraction(string input, double expectedOutput)
{ {
var actual = (double)Parser.Parse(input).Evaluate(); var actual = new Script(input).Evaluate<double>();
Assert.Equal(expectedOutput, actual, 8); Assert.Equal(expectedOutput, actual, 8);
} }
@ -39,7 +39,7 @@ namespace UpsilonTests
[InlineData("21312 * 41684", 888369408)] [InlineData("21312 * 41684", 888369408)]
public void Multiplication(string input, double expectedOutput) public void Multiplication(string input, double expectedOutput)
{ {
var actual = (double)Parser.Parse(input).Evaluate(); var actual = new Script(input).Evaluate<double>();
Assert.Equal(expectedOutput, actual, 8); Assert.Equal(expectedOutput, actual, 8);
} }
@ -49,7 +49,7 @@ namespace UpsilonTests
[InlineData("656486 / 5146", 127.57209483)] [InlineData("656486 / 5146", 127.57209483)]
public void Divison(string input, double expectedOutput) public void Divison(string input, double expectedOutput)
{ {
var actual = (double)Parser.Parse(input).Evaluate(); var actual = new Script(input).Evaluate<double>();
Assert.Equal(expectedOutput, actual, 8); Assert.Equal(expectedOutput, actual, 8);
} }

View File

@ -11,7 +11,7 @@ namespace UpsilonTests
[InlineData("(10 + 5) * 5", 75)] [InlineData("(10 + 5) * 5", 75)]
public void Parenthesis(string input, double expectedOutput) public void Parenthesis(string input, double expectedOutput)
{ {
var actual = (double)Parser.Parse(input).Evaluate(); var actual = new Script(input).Evaluate<double>();
Assert.Equal(expectedOutput, actual, 8); Assert.Equal(expectedOutput, actual, 8);
} }
@ -20,7 +20,7 @@ namespace UpsilonTests
[InlineData("5 + 10 * 5", 55)] [InlineData("5 + 10 * 5", 55)]
public void MultiplicationBeforeAddition(string input, double expectedOutput) public void MultiplicationBeforeAddition(string input, double expectedOutput)
{ {
var actual = (double)Parser.Parse(input).Evaluate(); var actual = new Script(input).Evaluate<double>();
Assert.Equal(expectedOutput, actual, 8); Assert.Equal(expectedOutput, actual, 8);
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic;
using Upsilon.Evaluator; using Upsilon.Evaluator;
using Upsilon.Parser; using Upsilon.Utilities;
namespace Yc namespace Yc
{ {
@ -9,6 +10,7 @@ namespace Yc
static void Main(string[] args) static void Main(string[] args)
{ {
Console.WriteLine("Upsilon REPL"); Console.WriteLine("Upsilon REPL");
Dictionary<string, object> variables = new Dictionary<string, object>();
while (true) while (true)
{ {
Console.Write("» "); Console.Write("» ");
@ -18,9 +20,10 @@ namespace Yc
return; return;
} }
var parser = Parser.Parse(input); var parsed = new Script(input, variables);
//Console.WriteLine(parser.Print()); //Console.WriteLine(parsed.Parsed.Print());
Console.WriteLine(parser.Evaluate()); Console.WriteLine(parsed.Evaluate());
variables = parsed.Variables;
} }
} }
} }