Upsilon/Upsilon/Evaluator/Evaluator.cs

129 lines
4.4 KiB
C#

using System;
using Upsilon.Parser;
namespace Upsilon.Evaluator
{
public class Evaluator
{
private readonly Diagnostics _diagnostics;
public Script Script { get; }
public VariableScope Scope { get; }
public Evaluator(Script script, VariableScope scope, Diagnostics diagnostics)
{
_diagnostics = diagnostics;
Script = script;
Scope = scope;
}
public object Evaluate(ScriptSyntax e)
{
return EvaluateExpression(e.Statement);
}
public object Evaluate(ExpressionSyntax e)
{
return EvaluateExpression(e);
}
private object EvaluateExpression(ExpressionSyntax e)
{
switch (e.Kind)
{
case SyntaxKind.UnaryExpression:
return EvaluateUnaryExpression((UnaryExpressionSyntax) e);
case SyntaxKind.BinaryExpression:
return EvaluateBinaryExpression((BinaryExpressionSyntax) e);
case SyntaxKind.LiteralExpression:
return ((LiteralExpressionSyntax) e).Value;
case SyntaxKind.ParenthesizedExpression:
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 object EvaluateUnaryExpression(UnaryExpressionSyntax e)
{
var operand = EvaluateExpression(e.Expression);
switch (e.Operator.Kind)
{
case SyntaxKind.Plus:
return operand;
case SyntaxKind.Minus:
return -(double) operand;
case SyntaxKind.NotKeyword:
return !(bool)operand;
default:
throw new Exception("Invalid Unary Operator: " + e.Operator.Kind);
}
}
private object EvaluateBinaryExpression(BinaryExpressionSyntax e)
{
var left = EvaluateExpression(e.Left);
var right = EvaluateExpression(e.Right);
try
{
switch (e.Operator.Kind)
{
case SyntaxKind.Plus:
return (double) left + (double) right;
case SyntaxKind.Minus:
return (double) left - (double) right;
case SyntaxKind.Star:
return (double) left * (double) right;
case SyntaxKind.Slash:
return (double) left / (double) right;
case SyntaxKind.AndKeyword:
return (bool) left && (bool) right;
case SyntaxKind.OrKeyword:
return (bool) left || (bool) right;
case SyntaxKind.EqualsEquals:
return Equals(left, right);
case SyntaxKind.TildeEquals:
return !Equals(left, right);
default:
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
}
}
catch (NullReferenceException)
{
_diagnostics.LogNullReferenceError(e.Span);
return null;
}
}
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;
}
_diagnostics.LogUnknownVariable(e.Span, varName);
return null;
}
}
}