Upsilon/Upsilon/Evaluator/Evaluator.cs

285 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number;
using Upsilon.Binder;
using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.Evaluator
{
internal class Evaluator
{
private readonly Diagnostics _diagnostics;
private LuaType _lastValue;
private LuaType _returnValue;
internal EvaluationScope Scope { get; }
private bool HasReturned { get; set; }
internal Evaluator(Diagnostics diagnostics, Dictionary<VariableSymbol, LuaType> variables)
{
_diagnostics = diagnostics;
Scope = new EvaluationScope(variables);
}
private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope)
{
_diagnostics = diagnostics;
Scope = new EvaluationScope(parentScope);
}
public LuaType Evaluate(BoundScript e)
{
Evaluate(e.Statement);
if (_returnValue == null)
return _lastValue;
return _returnValue;
}
public LuaType Evaluate(BoundScript e, string functionName, ImmutableArray<LuaType> parameters)
{
Evaluate(e.Statement);
if (!Scope.TryGet(functionName, out var statement) || statement.Type != Type.Function)
{
throw new ArgumentException(($"Function '{functionName}' could not be found"));
}
var function = (LuaFunction) statement;
var innerEvaluator = new Evaluator(_diagnostics, Scope);
for (var index = 0; index < parameters.Length; index++)
{
var parameter = parameters[index];
var parameterName = function.Parameters[index];
innerEvaluator.Scope.Set(parameterName, parameter);
}
var result = innerEvaluator.Evaluate(function.Block);
return result;
}
private LuaType Evaluate(BoundNode b)
{
switch (b.Kind)
{
case BoundKind.BoundScript:
EvaluateStatement(((BoundScript)b).Statement);
break;
case BoundKind.BoundLiteralExpression:
case BoundKind.BoundBinaryExpression:
case BoundKind.BoundUnaryExpression:
case BoundKind.VariableExpression:
case BoundKind.BoundFunctionCallExpression:
_lastValue = EvaluateExpression((BoundExpression) b);
break;
case BoundKind.BoundAssignmentStatement:
case BoundKind.BoundExpressionStatement:
case BoundKind.BoundBlockStatement:
case BoundKind.BoundIfStatement:
case BoundKind.BoundElseStatement:
case BoundKind.BoundFunctionStatement:
case BoundKind.BoundPromise:
EvaluateStatement((BoundStatement) b);
break;
default:
throw new ArgumentOutOfRangeException();
}
return _returnValue;
}
private void EvaluateStatement(BoundStatement e)
{
if (HasReturned)
return;
switch (e.Kind)
{
case BoundKind.BoundAssignmentStatement:
EvaluateAssignmentStatement((BoundVariableAssignment) e);
break;
case BoundKind.BoundBlockStatement:
EvaluateBoundBlockStatement((BoundBlockStatement) e);
break;
case BoundKind.BoundIfStatement:
EvaluateBoundIfStatement((BoundIfStatement) e);
break;
case BoundKind.BoundFunctionStatement:
EvaluateBoundFunctionStatement((BoundFunctionStatement) e);
break;
case BoundKind.BoundPromise:
EvaluateUnboundFunctionStatement((UnboundFunctionStatement) e);
break;
case BoundKind.BoundReturnStatement:
EvaluateReturnStatement((BoundReturnStatement) e);
break;
default:
EvaluateExpressionStatement((BoundExpressionStatement) e);
break;
}
}
private void EvaluateExpressionStatement(BoundExpressionStatement e)
{
_lastValue = EvaluateExpression(e.Expression);
}
private LuaType EvaluateExpression(BoundExpression e)
{
switch (e.Kind)
{
case BoundKind.BoundLiteralExpression:
return ((BoundLiteralExpression) e).Value;
case BoundKind.BoundBinaryExpression:
return EvaluateBinaryExpression((BoundBinaryExpression) e);
case BoundKind.BoundUnaryExpression:
return EvaluateUnaryExpression((BoundUnaryExpression) e);
case BoundKind.VariableExpression:
return EvaluateVariableExpression((BoundVariableExpression) e);
case BoundKind.BoundFunctionCallExpression:
return EvaluateBoundFunctionCallExpression((BoundFunctionCallExpression) e);
default:
throw new NotImplementedException();
}
}
private LuaType EvaluateUnaryExpression(BoundUnaryExpression e)
{
var operand = EvaluateExpression(e.InExpression);
switch (e.Operator.Kind)
{
case BoundUnaryOperator.OperatorKind.Identity:
return operand;
case BoundUnaryOperator.OperatorKind.Negation:
return -((Number)operand);
case BoundUnaryOperator.OperatorKind.LogicalNegation:
return !(LuaBoolean) operand;
default:
throw new Exception("Invalid Unary Operator: " + e.Operator.Kind);
}
}
private LuaType EvaluateBinaryExpression(BoundBinaryExpression e)
{
var left = EvaluateExpression(e.LeftExpression);
var right = EvaluateExpression(e.RightExpression);
switch (e.Operator.Kind)
{
case BoundBinaryOperator.OperatorKind.Addition:
return ((Number)left) + ((Number)right);
case BoundBinaryOperator.OperatorKind.Subtraction:
return ((Number)left) - ((Number)right);
case BoundBinaryOperator.OperatorKind.Multiplication:
return ((Number)left) * ((Number)right);
case BoundBinaryOperator.OperatorKind.Division:
return ((Number)left) / ((Number)right);
case BoundBinaryOperator.OperatorKind.Equality:
return new LuaBoolean(Equals(left, right));
case BoundBinaryOperator.OperatorKind.Inequality:
return new LuaBoolean(!Equals(left, right));
default:
throw new Exception("Invalid Binary Operator: " + e.Operator);
}
}
private void EvaluateAssignmentStatement(BoundVariableAssignment e)
{
var val = EvaluateExpression(e.BoundExpression);
if (e.Variable.Local)
{
Scope.Set(e.Variable, val);
}
else
{
Scope.SetGlobal(e.Variable, val);
}
_lastValue = val;
}
private LuaType EvaluateVariableExpression(BoundVariableExpression e)
{
if (Scope.TryGet(e.Variable, out var val))
{
return val;
}
throw new Exception($"Cannot find variable: '{e.Variable.Name}'");
}
private void EvaluateBoundBlockStatement(BoundBlockStatement boundBlockStatement)
{
foreach (var boundStatement in boundBlockStatement.Statements)
{
if (HasReturned)
return;
EvaluateStatement(boundStatement);
}
}
private void EvaluateBoundIfStatement(BoundIfStatement boundBlockStatement)
{
var innerEvaluator = new Evaluator(_diagnostics, Scope);
var condition = innerEvaluator.EvaluateExpression(boundBlockStatement.Condition.Expression);
if ((LuaBoolean) condition)
{
innerEvaluator.EvaluateStatement(boundBlockStatement.Block);
}
else if (boundBlockStatement.NextElseIf != null)
{
innerEvaluator.EvaluateStatement(boundBlockStatement.NextElseIf);
}
else if (boundBlockStatement.ElseStatement != null)
{
innerEvaluator.EvaluateStatement(boundBlockStatement.ElseStatement.Block);
}
HasReturned = innerEvaluator.HasReturned;
if (HasReturned)
_returnValue = innerEvaluator._returnValue;
_lastValue = innerEvaluator._lastValue;
}
private void EvaluateBoundFunctionStatement(BoundFunctionStatement boundFunctionStatement)
{
var func = new LuaFunction(boundFunctionStatement.Parameters, boundFunctionStatement.Block);
if (boundFunctionStatement.Identifier.Local)
Scope.Set(boundFunctionStatement.Identifier, func);
else
Scope.SetGlobal(boundFunctionStatement.Identifier, func);
}
private void EvaluateUnboundFunctionStatement(UnboundFunctionStatement unboundFunctionStatement)
{
var func = new LuaFunction(unboundFunctionStatement.Parameters, unboundFunctionStatement.Block);
if (unboundFunctionStatement.Identifier.Local)
Scope.Set(unboundFunctionStatement.Identifier, func);
else
Scope.SetGlobal(unboundFunctionStatement.Identifier, func);
}
private LuaType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression)
{
var name = boundFunctionCallExpression.Identifier;
if (!Scope.TryGet(name, out var functionObj))
{
throw new Exception("Cannot find function: " + name.Name);
}
var function = (LuaFunction) functionObj;
var innerEvaluator = new Evaluator(_diagnostics, Scope);
for (var i = 0; i < function.Parameters.Length; i++)
{
var parameterVariable = function.Parameters[i];
var parameterValue = innerEvaluator.EvaluateExpression(boundFunctionCallExpression.Parameters[i]);
innerEvaluator.Scope.Set(parameterVariable, parameterValue);
}
_lastValue = innerEvaluator.Evaluate(function.Block);
return _lastValue;
}
private void EvaluateReturnStatement(BoundReturnStatement b)
{
_returnValue = EvaluateExpression(b.Expression);
_lastValue = _returnValue;
HasReturned = true;
}
}
}