Handle variables
This commit is contained in:
parent
0693698f28
commit
ab61a01573
|
@ -3,19 +3,28 @@ using Upsilon.Parser;
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
public static object Evaluate(this ExpressionSyntax e)
|
||||
public object Evaluate(ExpressionSyntax e)
|
||||
{
|
||||
return EvaluateExpression(e);
|
||||
}
|
||||
|
||||
private static object EvaluateExpression(ExpressionSyntax e)
|
||||
private object EvaluateExpression(ExpressionSyntax e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
|
@ -26,13 +35,17 @@ namespace Upsilon.Evaluator
|
|||
case SyntaxKind.LiteralExpression:
|
||||
return ((LiteralExpressionSyntax) e).Value;
|
||||
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:
|
||||
throw new Exception("Invalid expression: " + e.Kind);
|
||||
}
|
||||
}
|
||||
|
||||
private static object EvaluateUnaryExpression(UnaryExpressionSyntax e)
|
||||
private object EvaluateUnaryExpression(UnaryExpressionSyntax e)
|
||||
{
|
||||
var operand = EvaluateExpression(e.Expression);
|
||||
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 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -127,16 +127,21 @@ namespace Upsilon.Parser
|
|||
private SyntaxToken LexIdentifierOrKeyword()
|
||||
{
|
||||
var start = _position;
|
||||
var numStr = new StringBuilder();
|
||||
numStr.Append(Current);
|
||||
var stringBuilder = new StringBuilder();
|
||||
stringBuilder.Append(Current);
|
||||
while (char.IsLetterOrDigit(Next) || Next == '_')
|
||||
{
|
||||
numStr.Append(Next);
|
||||
stringBuilder.Append(Next);
|
||||
_position++;
|
||||
}
|
||||
|
||||
var kind = SyntaxKeyWords.GetSyntaxKind(numStr.ToString());
|
||||
return new SyntaxToken(kind, start, numStr.ToString(), null);
|
||||
var kind = SyntaxKeyWords.GetSyntaxKind(stringBuilder.ToString());
|
||||
var str = stringBuilder.ToString();
|
||||
if (kind == SyntaxKind.Identifier)
|
||||
{
|
||||
return new IdentifierToken(str, start, str.Length);
|
||||
}
|
||||
return new SyntaxToken(kind, start, str, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,9 +53,30 @@ namespace Upsilon.Parser
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ExpressionSyntax left;
|
||||
|
@ -96,6 +117,9 @@ namespace Upsilon.Parser
|
|||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
return ParseBoolean();
|
||||
case SyntaxKind.Identifier:
|
||||
var token = MatchToken(SyntaxKind.Identifier);
|
||||
return new VariableExpressionSyntax((IdentifierToken) token);
|
||||
default:
|
||||
throw new Exception("Unknown primary expression type: " + Current.Kind);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Upsilon.Parser
|
|||
return SyntaxKind.AndKeyword;
|
||||
case "or":
|
||||
return SyntaxKind.OrKeyword;
|
||||
case "local":
|
||||
return SyntaxKind.LocalKeyword;
|
||||
default:
|
||||
return SyntaxKind.Identifier;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Upsilon.Parser
|
|||
NotKeyword,
|
||||
AndKeyword,
|
||||
OrKeyword,
|
||||
LocalKeyword,
|
||||
|
||||
Identifier,
|
||||
|
||||
|
@ -32,6 +33,8 @@ namespace Upsilon.Parser
|
|||
BinaryExpression,
|
||||
LiteralExpression,
|
||||
ParenthesizedExpression,
|
||||
AssignmentExpression,
|
||||
VariableExpression,
|
||||
|
||||
// script unit
|
||||
ScriptUnit,
|
||||
|
|
|
@ -30,17 +30,23 @@ namespace Upsilon.Parser
|
|||
{
|
||||
switch (kind)
|
||||
{
|
||||
// equality operators
|
||||
case SyntaxKind.EqualsEquals:
|
||||
return Precedence.Equality;
|
||||
case SyntaxKind.TildeEquals:
|
||||
return Precedence.Equality;
|
||||
|
||||
// logical operators
|
||||
case SyntaxKind.AndKeyword:
|
||||
return Precedence.And;
|
||||
case SyntaxKind.OrKeyword:
|
||||
return Precedence.Or;
|
||||
|
||||
// math operators
|
||||
case SyntaxKind.Plus:
|
||||
case SyntaxKind.Minus:
|
||||
return Precedence.PlusMinus;
|
||||
|
||||
case SyntaxKind.Star:
|
||||
case SyntaxKind.Slash:
|
||||
return Precedence.StarSlash;
|
||||
|
|
|
@ -3,7 +3,7 @@ using Upsilon.Text;
|
|||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public sealed class SyntaxToken : SyntaxNode
|
||||
public class SyntaxToken : SyntaxNode
|
||||
{
|
||||
public SyntaxToken(SyntaxKind kind, int position, string text, object value)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,10 @@ namespace Upsilon.Utilities
|
|||
sb.Append("|-- ");
|
||||
}
|
||||
sb.Append(token.Kind);
|
||||
if (token is SyntaxToken node && node.Value != null)
|
||||
{
|
||||
sb.Append($" - {node.Value}");
|
||||
}
|
||||
foreach (var syntaxNode in token.ChildNodes())
|
||||
{
|
||||
sb.Append("\n");
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace UpsilonTests
|
|||
[InlineData("0.005 + 2.2", 2.205)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace UpsilonTests
|
|||
[InlineData("10.256-2.8546", 7.4014)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ namespace UpsilonTests
|
|||
[InlineData("21312 * 41684", 888369408)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ namespace UpsilonTests
|
|||
[InlineData("656486 / 5146", 127.57209483)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace UpsilonTests
|
|||
[InlineData("(10 + 5) * 5", 75)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace UpsilonTests
|
|||
[InlineData("5 + 10 * 5", 55)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Parser;
|
||||
using Upsilon.Utilities;
|
||||
|
||||
namespace Yc
|
||||
{
|
||||
|
@ -9,6 +10,7 @@ namespace Yc
|
|||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Upsilon REPL");
|
||||
Dictionary<string, object> variables = new Dictionary<string, object>();
|
||||
while (true)
|
||||
{
|
||||
Console.Write("» ");
|
||||
|
@ -18,9 +20,10 @@ namespace Yc
|
|||
return;
|
||||
}
|
||||
|
||||
var parser = Parser.Parse(input);
|
||||
//Console.WriteLine(parser.Print());
|
||||
Console.WriteLine(parser.Evaluate());
|
||||
var parsed = new Script(input, variables);
|
||||
//Console.WriteLine(parsed.Parsed.Print());
|
||||
Console.WriteLine(parsed.Evaluate());
|
||||
variables = parsed.Variables;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue