291 lines
11 KiB
C#
291 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:
|
|
if (left.Type == Type.Number)
|
|
return ((Number)left) + ((Number)right);
|
|
else if (left.Type == Type.String)
|
|
{
|
|
return ((LuaString) left) + right;
|
|
}
|
|
goto default;
|
|
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;
|
|
}
|
|
|
|
}
|
|
} |