774 lines
32 KiB
C#
774 lines
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using Upsilon.BaseTypes;
|
|
using Upsilon.BaseTypes.Number;
|
|
using Upsilon.BaseTypes.ScriptFunction;
|
|
using Upsilon.BaseTypes.ScriptTable;
|
|
using Upsilon.BaseTypes.ScriptTypeInterfaces;
|
|
using Upsilon.BaseTypes.UserData;
|
|
using Upsilon.Binder;
|
|
using Upsilon.Binder.VariableSymbols;
|
|
using Upsilon.Evaluator.Debugging;
|
|
using Upsilon.Exceptions;
|
|
using Upsilon.Text;
|
|
using Upsilon.Utilities;
|
|
using Type = Upsilon.BaseTypes.Type;
|
|
|
|
namespace Upsilon.Evaluator
|
|
{
|
|
internal class Evaluator : IDisposable
|
|
{
|
|
private readonly Script _script;
|
|
private Diagnostics _diagnostics;
|
|
private ScriptType _lastValue;
|
|
private ScriptType _returnValue;
|
|
internal EvaluationScope Scope { get; private set; }
|
|
private bool HasReturned { get; set; }
|
|
private bool HasBroken { get; set; }
|
|
|
|
internal Evaluator(Diagnostics diagnostics, Dictionary<string, ScriptType> variables, Script script)
|
|
{
|
|
_diagnostics = diagnostics;
|
|
_script = script;
|
|
Scope = new EvaluationScope(variables);
|
|
}
|
|
|
|
internal Evaluator(Diagnostics diagnostics, EvaluationScope parentScope, Script script)
|
|
{
|
|
_diagnostics = diagnostics;
|
|
_script = script;
|
|
Scope = new EvaluationScope(parentScope);
|
|
}
|
|
|
|
private Evaluator(Script script)
|
|
{
|
|
_script = script;
|
|
}
|
|
|
|
internal static Evaluator CreateWithSetScope(Diagnostics diagnostics, EvaluationScope scope, Script script)
|
|
{
|
|
return new Evaluator(script) {_diagnostics = diagnostics, Scope = scope};
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Scope.Dispose();
|
|
_lastValue = null;
|
|
_returnValue = null;
|
|
}
|
|
|
|
public ScriptType Evaluate(BoundScript e)
|
|
{
|
|
if (DebugSession.DebuggerAttached && !string.IsNullOrWhiteSpace(e.FileName))
|
|
{
|
|
var file = DebugSession.GetFileInfo(e.FileName);
|
|
if (file != null)
|
|
{
|
|
foreach (var breakpoint in file.Breakpoints)
|
|
{
|
|
var debugStatement = e.GetBottomStatementAtPosition(breakpoint.Line, breakpoint.Position);
|
|
debugStatement.HasBreakpoint = true;
|
|
}
|
|
}
|
|
}
|
|
EvaluateNode(e.Statement);
|
|
if (_returnValue == null)
|
|
return _lastValue;
|
|
return _returnValue;
|
|
}
|
|
|
|
public ScriptType Evaluate(BoundScript e, string functionName, object[] parameters)
|
|
{
|
|
if (!Scope.TryGet(functionName, out var statement) || statement.Type != Type.Function)
|
|
{
|
|
throw new ArgumentException(($"Function '{functionName}' could not be found"));
|
|
}
|
|
var function = (ScriptRuntimeFunction) statement;
|
|
var innerEvaluator = new Evaluator(_diagnostics, Scope, _script);
|
|
if (parameters != null)
|
|
{
|
|
for (var index = 0; index < parameters.Length; index++)
|
|
{
|
|
object parameter;
|
|
if (index < parameters.Length)
|
|
{
|
|
parameter = parameters[index];
|
|
}
|
|
else
|
|
{
|
|
parameter = null;
|
|
}
|
|
|
|
UserDataVariableSymbol parameterSymbol;
|
|
if (index < function.Parameters.Length)
|
|
{
|
|
parameterSymbol = (UserDataVariableSymbol)function.Parameters[index].VariableSymbol;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
if (parameterSymbol.BoundTypeDefinition != null && parameter != null)
|
|
{
|
|
bool isCompatible = false;
|
|
var parameterType = parameter.GetType();
|
|
foreach (var validType in parameterSymbol.BoundTypeDefinition.ValidInternalTypes)
|
|
{
|
|
if (validType.IsAssignableFrom(parameterType))
|
|
{
|
|
isCompatible = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isCompatible)
|
|
{
|
|
throw new EvaluationException(
|
|
$"Parameter '{parameterSymbol.Name}' of function '{functionName}' can't handle the given object with type '{parameterType}'");
|
|
}
|
|
}
|
|
|
|
var parameterConverted = parameter == null ? new ScriptNull() : parameter.ToScriptType();
|
|
innerEvaluator.Scope.CreateLocal(parameterSymbol, parameterConverted);
|
|
}
|
|
}
|
|
|
|
var result = innerEvaluator.EvaluateNode(function.Block);
|
|
return result;
|
|
}
|
|
|
|
internal ScriptType EvaluateNode(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:
|
|
case BoundKind.BoundFunctionExpression:
|
|
case BoundKind.BoundTableExpression:
|
|
case BoundKind.BoundIndexExpression:
|
|
case BoundKind.BoundFullstopIndexExpression:
|
|
_lastValue = EvaluateExpression((BoundExpression) b);
|
|
break;
|
|
case BoundKind.BoundAssignmentStatement:
|
|
case BoundKind.BoundExpressionStatement:
|
|
case BoundKind.BoundBlockStatement:
|
|
case BoundKind.BoundIfStatement:
|
|
case BoundKind.BoundElseStatement:
|
|
case BoundKind.BoundFunctionAssignmentStatement:
|
|
case BoundKind.BoundPromise:
|
|
case BoundKind.BoundReturnStatement:
|
|
case BoundKind.BoundTableAssigmentStatement:
|
|
case BoundKind.BoundMultiAssignmentStatement:
|
|
case BoundKind.BoundNumericForStatement:
|
|
case BoundKind.BoundGenericForStatement:
|
|
case BoundKind.BoundBreakStatement:
|
|
case BoundKind.BoundWhileStatement:
|
|
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.BoundReturnStatement:
|
|
EvaluateReturnStatement((BoundReturnStatement) e);
|
|
break;
|
|
case BoundKind.BoundFunctionAssignmentStatement:
|
|
EvaluateBoundFunctionAssigmentStatement((BoundFunctionAssignmentStatement) e);
|
|
break;
|
|
case BoundKind.BoundTableAssigmentStatement:
|
|
EvaluateTableAssignmentStatement((BoundTableAssigmentStatement) e);
|
|
break;
|
|
case BoundKind.BoundMultiAssignmentStatement:
|
|
EvaluateMultiAssignmentStatement((BoundMultiAssignmentStatement) e);
|
|
break;
|
|
case BoundKind.BoundNumericForStatement:
|
|
EvaluateNumericForStatement((BoundNumericForStatement) e);
|
|
break;
|
|
case BoundKind.BoundBreakStatement:
|
|
HasBroken = true;
|
|
return;
|
|
case BoundKind.BoundGenericForStatement:
|
|
EvaluateGenericForStatement((BoundGenericForStatement) e);
|
|
break;
|
|
case BoundKind.BoundWhileStatement:
|
|
EvaluateWhileStatement((BoundWhileStatement) e);
|
|
break;
|
|
default:
|
|
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
private void EvaluateExpressionStatement(BoundExpressionStatement e)
|
|
{
|
|
_lastValue = EvaluateExpression(e.Expression);
|
|
}
|
|
|
|
internal ScriptType 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);
|
|
case BoundKind.BoundTableExpression:
|
|
return EvaluateTableExpression((BoundTableExpression) e);
|
|
case BoundKind.BoundIndexExpression:
|
|
return EvaluateIndexExpression((BoundIndexExpression) e);
|
|
case BoundKind.BoundFunctionExpression:
|
|
return EvaluateBoundFunctionStatement((BoundFunctionExpression) e);
|
|
case BoundKind.BoundPromise:
|
|
return EvaluateUnboundFunctionStatement((UnboundFunctionExpression) e);
|
|
case BoundKind.BoundFullstopIndexExpression:
|
|
return EvaluateFullStopIndexExpression((BoundFullStopIndexExpression) e);
|
|
default:
|
|
throw new NotImplementedException(e.Kind.ToString());
|
|
}
|
|
}
|
|
|
|
private ScriptType EvaluateUnaryExpression(BoundUnaryExpression e)
|
|
{
|
|
var operand = EvaluateExpression(e.InExpression);
|
|
switch (e.Operator.Kind)
|
|
{
|
|
case BoundUnaryOperator.OperatorKind.Identity:
|
|
return operand;
|
|
case BoundUnaryOperator.OperatorKind.Negation:
|
|
if (operand.Type == Type.Number)
|
|
return -((ScriptNumber)operand);
|
|
else if (operand.Type == Type.UserData)
|
|
{
|
|
var ud = (GenericUserData) operand;
|
|
var (type, failed) = ud.UnaryOperator(operand, OperatorType.UnaryNegation);
|
|
if (failed) goto default;
|
|
return type;
|
|
}
|
|
goto default;
|
|
case BoundUnaryOperator.OperatorKind.LogicalNegation:
|
|
if (operand.Type == Type.Boolean)
|
|
return !(ScriptBoolean) operand;
|
|
else if (operand.Type == Type.UserData)
|
|
{
|
|
var ud = (GenericUserData) operand;
|
|
var (type, failed) = ud.UnaryOperator(operand, OperatorType.LogicalNot);
|
|
if (failed) goto default;
|
|
return type;
|
|
}
|
|
goto default;
|
|
case BoundUnaryOperator.OperatorKind.Length:
|
|
if (operand is ILengthType length)
|
|
{
|
|
return length.Length();
|
|
}
|
|
goto default;
|
|
default:
|
|
throw new Exception("Invalid Unary Operator: " + e.Operator.Kind);
|
|
}
|
|
}
|
|
|
|
private ScriptType EvaluateBinaryExpression(BoundBinaryExpression e)
|
|
{
|
|
if (e.Operator.Kind == BoundBinaryOperator.OperatorKind.Or)
|
|
{
|
|
var l = EvaluateExpression(e.LeftExpression);
|
|
if (l.Type == Type.Boolean && ((ScriptBoolean)l).Value)
|
|
return new ScriptBoolean(true);
|
|
var r = EvaluateExpression(e.RightExpression);
|
|
if (r.Type == Type.Boolean && ((ScriptBoolean)r).Value)
|
|
return new ScriptBoolean(true);
|
|
return new ScriptBoolean(false);
|
|
}
|
|
if (e.Operator.Kind == BoundBinaryOperator.OperatorKind.And)
|
|
{
|
|
var l = EvaluateExpression(e.LeftExpression);
|
|
if (l.Type != Type.Boolean || !((ScriptBoolean)l).Value)
|
|
return new ScriptBoolean(false);
|
|
var r = EvaluateExpression(e.RightExpression);
|
|
if (r.Type != Type.Boolean || !((ScriptBoolean)r).Value)
|
|
return new ScriptBoolean(false);
|
|
return new ScriptBoolean(true);
|
|
}
|
|
|
|
var left = EvaluateExpression(e.LeftExpression);
|
|
var right = EvaluateExpression(e.RightExpression);
|
|
switch (e.Operator.Kind)
|
|
{
|
|
case BoundBinaryOperator.OperatorKind.Addition:
|
|
if (left.Type == Type.Number)
|
|
{
|
|
if (right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) + ((ScriptNumber)right);
|
|
}
|
|
}
|
|
else if (left.Type == Type.String)
|
|
{
|
|
return ((ScriptString) left) + right;
|
|
}
|
|
else if (left.Type == Type.UserData)
|
|
{
|
|
var ud = (GenericUserData) left;
|
|
var (type, failed) = ud.BinaryOperator(left, OperatorType.Addition, right);
|
|
if (failed) goto default;
|
|
return type;
|
|
}
|
|
goto default;
|
|
case BoundBinaryOperator.OperatorKind.Subtraction:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) - ((ScriptNumber)right);
|
|
}
|
|
else if (left.Type == Type.UserData)
|
|
{
|
|
var ud = (GenericUserData) left;
|
|
var (type, failed) = ud.BinaryOperator(left, OperatorType.Subtraction, right);
|
|
if (failed) goto default;
|
|
return type;
|
|
}
|
|
goto default;
|
|
case BoundBinaryOperator.OperatorKind.Multiplication:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) * ((ScriptNumber)right);
|
|
}
|
|
else if (left.Type == Type.UserData)
|
|
{
|
|
var ud = (GenericUserData) left;
|
|
var (type, failed) = ud.BinaryOperator(left, OperatorType.Multiplication, right);
|
|
if (failed) goto default;
|
|
return type;
|
|
}
|
|
goto default;
|
|
case BoundBinaryOperator.OperatorKind.Division:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) / ((ScriptNumber)right);
|
|
}
|
|
else if (left.Type == Type.UserData)
|
|
{
|
|
var ud = (GenericUserData) left;
|
|
var (type, failed) = ud.BinaryOperator(left, OperatorType.Division, right);
|
|
if (failed) goto default;
|
|
return type;
|
|
}
|
|
goto default;
|
|
case BoundBinaryOperator.OperatorKind.Exponent:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ScriptNumber.Exponent((ScriptNumber) left, (ScriptNumber) right);
|
|
}
|
|
goto default;
|
|
case BoundBinaryOperator.OperatorKind.Remainder:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) % ((ScriptNumber)right);
|
|
}
|
|
goto default;
|
|
case BoundBinaryOperator.OperatorKind.Equality:
|
|
return new ScriptBoolean(left.Equals(right));
|
|
case BoundBinaryOperator.OperatorKind.Inequality:
|
|
return new ScriptBoolean(!left.Equals(right));
|
|
case BoundBinaryOperator.OperatorKind.Less:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) < ((ScriptNumber)right);
|
|
}
|
|
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
|
|
return new ScriptNull();
|
|
case BoundBinaryOperator.OperatorKind.LessEquals:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) <= ((ScriptNumber)right);
|
|
}
|
|
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
|
|
return new ScriptNull();
|
|
case BoundBinaryOperator.OperatorKind.Greater:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) > ((ScriptNumber)right);
|
|
}
|
|
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
|
|
return new ScriptNull();
|
|
case BoundBinaryOperator.OperatorKind.GreaterEquals:
|
|
if (left.Type == Type.Number && right.Type == Type.Number)
|
|
{
|
|
return ((ScriptNumber)left) >= ((ScriptNumber)right);
|
|
}
|
|
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
|
|
return new ScriptNull();
|
|
default:
|
|
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
|
|
}
|
|
}
|
|
|
|
private void ThrowException(string message, TextSpan location)
|
|
{
|
|
throw new ScriptRuntimeException(message, location, _diagnostics.ScriptString.GetSpan(location));
|
|
}
|
|
|
|
private void EvaluateAssignmentStatement(BoundVariableAssignment e)
|
|
{
|
|
var val = EvaluateExpression(e.BoundExpression);
|
|
if (e.IsLocalDefinition)
|
|
{
|
|
Scope.CreateLocal(e.Variable.VariableSymbol, val);
|
|
}
|
|
else
|
|
{
|
|
Scope.AssignToNearest(e.Variable.VariableSymbol, val);
|
|
}
|
|
_lastValue = val;
|
|
}
|
|
|
|
private void EvaluateMultiAssignmentStatement(BoundMultiAssignmentStatement e)
|
|
{
|
|
var val = EvaluateExpression(e.Assignment);
|
|
if (val is IIndexable table)
|
|
{
|
|
for (var i = 0; i < e.Variables.Length; i++)
|
|
{
|
|
var variableSymbol = e.Variables[i];
|
|
if (variableSymbol == null)
|
|
continue;
|
|
if (variableSymbol.Name == "_")
|
|
continue;
|
|
var value = table.Get(_diagnostics, e.Span, new ScriptNumberLong(i + 1), Scope);
|
|
if (variableSymbol.Local)
|
|
Scope.CreateLocal(variableSymbol, value);
|
|
else
|
|
Scope.AssignToNearest(variableSymbol, value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_diagnostics.LogError($"Can't assign type '{val.Type}' to multiple variables.", e.Span);
|
|
}
|
|
_lastValue = val;
|
|
}
|
|
|
|
private ScriptType EvaluateVariableExpression(BoundVariableExpression e)
|
|
{
|
|
if (Scope.TryGet(e.Variable.VariableSymbol, out var val))
|
|
{
|
|
return val;
|
|
}
|
|
throw new Exception($"Cannot find variable: '{e.Variable.VariableSymbol.Name}'");
|
|
}
|
|
|
|
private readonly Stack<BoundStatement> _todoStatements = new Stack<BoundStatement>();
|
|
|
|
private void EvaluateBoundBlockStatement(BoundBlockStatement boundBlockStatement)
|
|
{
|
|
for (var index = boundBlockStatement.Statements.Length - 1; index >= 0; index--)
|
|
{
|
|
var s = boundBlockStatement.Statements[index];
|
|
_todoStatements.Push(s);
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
if (DebugSession.Debugging)
|
|
{
|
|
Thread.Sleep(10);
|
|
continue;
|
|
}
|
|
if (HasReturned)
|
|
return;
|
|
if (HasBroken)
|
|
return;
|
|
if (_todoStatements.Count == 0)
|
|
return;
|
|
var boundStatement = _todoStatements.Pop();
|
|
if (DebugSession.DebuggerAttached && boundStatement.HasBreakpoint)
|
|
{
|
|
DebugSession.TriggerBreakpoint(Scope);
|
|
continue;
|
|
}
|
|
EvaluateStatement(boundStatement);
|
|
}
|
|
}
|
|
|
|
private void EvaluateBoundIfStatement(BoundIfStatement boundBlockStatement)
|
|
{
|
|
var condition = EvaluateExpression(boundBlockStatement.Condition.Expression);
|
|
if ((ScriptBoolean) condition)
|
|
{
|
|
EvaluateStatement(boundBlockStatement.Block);
|
|
}
|
|
else if (boundBlockStatement.NextElseIf != null)
|
|
{
|
|
EvaluateStatement(boundBlockStatement.NextElseIf);
|
|
}
|
|
else if (boundBlockStatement.ElseStatement != null)
|
|
{
|
|
EvaluateStatement(boundBlockStatement.ElseStatement.Block);
|
|
}
|
|
}
|
|
|
|
private void EvaluateBoundFunctionAssigmentStatement(BoundFunctionAssignmentStatement e)
|
|
{
|
|
var func = EvaluateBoundFunctionStatement(e.Func);
|
|
if (e.Variable.Local)
|
|
Scope.CreateLocal(e.Variable, func);
|
|
else
|
|
Scope.AssignToNearest(e.Variable, func);
|
|
}
|
|
|
|
private ScriptType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression)
|
|
{
|
|
var func = new ScriptRuntimeFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope);
|
|
return func;
|
|
}
|
|
|
|
private ScriptType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression)
|
|
{
|
|
var func = new ScriptRuntimeFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope);
|
|
return func;
|
|
}
|
|
|
|
private ScriptType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression)
|
|
{
|
|
var variable = EvaluateExpression(boundFunctionCallExpression.Identifier);
|
|
if (!(variable is ScriptFunction function))
|
|
{
|
|
throw new Exception($"Variable is not a function.");
|
|
}
|
|
var ls = new List<ScriptType>();
|
|
foreach (var t in boundFunctionCallExpression.Parameters)
|
|
{
|
|
var evaluate = EvaluateExpression(t);
|
|
ls.Add(evaluate);
|
|
}
|
|
|
|
var val = function.Run(_diagnostics, ls.ToArray(), _script, Scope, boundFunctionCallExpression.Span);
|
|
return val;
|
|
}
|
|
|
|
private void EvaluateReturnStatement(BoundReturnStatement b)
|
|
{
|
|
_returnValue = b.Expression == null ? null : EvaluateExpression(b.Expression);
|
|
_lastValue = _returnValue;
|
|
HasReturned = true;
|
|
}
|
|
|
|
private ScriptType EvaluateTableExpression(BoundTableExpression e)
|
|
{
|
|
var tableScope = EvaluationScope.CreateWithGetOnlyParent(Scope);
|
|
var innerEvaluator = new Evaluator(_diagnostics, tableScope, _script);
|
|
var currentPos = 1;
|
|
var isNumerated = true;
|
|
foreach (var boundStatement in e.Statements)
|
|
{
|
|
switch (boundStatement.Kind)
|
|
{
|
|
case BoundKind.BoundAssignmentStatement:
|
|
case BoundKind.BoundFunctionExpression:
|
|
case BoundKind.BoundFunctionAssignmentStatement:
|
|
innerEvaluator.EvaluateNode(boundStatement);
|
|
isNumerated = false;
|
|
break;
|
|
default:
|
|
{
|
|
innerEvaluator.EvaluateStatement(boundStatement);
|
|
if (innerEvaluator._lastValue != null)
|
|
tableScope.CreateLocal(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
|
|
innerEvaluator._lastValue);
|
|
innerEvaluator._lastValue = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
currentPos++;
|
|
}
|
|
|
|
if (isNumerated)
|
|
{
|
|
return new NumeratedScriptTable(tableScope);
|
|
}
|
|
return new GenericKeyedScriptTable(tableScope);
|
|
}
|
|
|
|
private ScriptType EvaluateIndexExpression(BoundIndexExpression e)
|
|
{
|
|
var variable = EvaluateExpression(e.Identifier);
|
|
if (!(variable is IIndexable indexable))
|
|
{
|
|
throw new Exception("Variable is not indexable.");
|
|
}
|
|
|
|
var scope = Scope;
|
|
if (variable is IScopeOwner scopeOwner)
|
|
{
|
|
scope = scopeOwner.EvaluationScope;
|
|
}
|
|
var indexer = EvaluateExpression(e.Index);
|
|
return indexable.Get(_diagnostics, e.Span, indexer, scope);
|
|
}
|
|
private ScriptType EvaluateFullStopIndexExpression(BoundFullStopIndexExpression e)
|
|
{
|
|
var variable = EvaluateExpression(e.Expression);
|
|
if (!(variable is IIndexable indexable))
|
|
{
|
|
throw new Exception("Variable is not indexable.");
|
|
}
|
|
|
|
var scope = Scope;
|
|
if (variable is IScopeOwner scopeOwner)
|
|
{
|
|
scope = scopeOwner.EvaluationScope;
|
|
}
|
|
|
|
return indexable.Get(_diagnostics, e.Span, e.Index.ToScriptType(), scope);
|
|
}
|
|
|
|
private void EvaluateTableAssignmentStatement(BoundTableAssigmentStatement e)
|
|
{
|
|
ScriptType table;
|
|
ScriptType index;
|
|
if (e.TableIndexExpression.Kind == BoundKind.BoundIndexExpression)
|
|
{
|
|
table = EvaluateExpression(((BoundIndexExpression)e.TableIndexExpression).Identifier);
|
|
index = EvaluateExpression(((BoundIndexExpression)e.TableIndexExpression).Index);
|
|
}
|
|
else
|
|
{
|
|
table = EvaluateExpression(((BoundFullStopIndexExpression)e.TableIndexExpression).Expression);
|
|
index = ((BoundFullStopIndexExpression) e.TableIndexExpression).Index.ToScriptType();
|
|
}
|
|
|
|
var value = EvaluateExpression(e.Value);
|
|
if (!(table is IIndexable indexable))
|
|
{
|
|
throw new Exception("Cant assign to that");
|
|
}
|
|
indexable.Set(_diagnostics, e.Span, index.ToString().ToScriptType(), value);
|
|
}
|
|
|
|
private void EvaluateNumericForStatement(BoundNumericForStatement e)
|
|
{
|
|
var innerEvaluator = new Evaluator(_diagnostics, Scope, _script);
|
|
var startVal = (ScriptNumberLong)innerEvaluator.EvaluateExpression(e.BoundStart);
|
|
innerEvaluator.Scope.CreateLocal(e.Variable, startVal);
|
|
var stopVal = (ScriptNumberLong)innerEvaluator.EvaluateExpression(e.BoundStop);
|
|
long step = 1;
|
|
if (e.BoundStep != null)
|
|
{
|
|
var stepVal = (ScriptNumberLong)innerEvaluator.EvaluateExpression(e.BoundStep);
|
|
step = stepVal.Value;
|
|
}
|
|
|
|
if (step > 0)
|
|
{
|
|
for (; startVal.Value <= stopVal.Value; startVal.Value = startVal.Value + step)
|
|
{
|
|
innerEvaluator.EvaluateBoundBlockStatement(e.Block);
|
|
if (innerEvaluator.HasBroken)
|
|
break;
|
|
}
|
|
}
|
|
else if (step < 0)
|
|
{
|
|
for (; startVal.Value >= stopVal.Value; startVal.Value = startVal.Value + step)
|
|
{
|
|
innerEvaluator.EvaluateBoundBlockStatement(e.Block);
|
|
if (innerEvaluator.HasBroken)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EvaluateGenericForStatement(BoundGenericForStatement e)
|
|
{
|
|
var innerEvaluator = new Evaluator(_diagnostics, Scope, _script);
|
|
var enumeratorObject = EvaluateExpression(e.BoundEnumerableExpression);
|
|
if (!(enumeratorObject is IIterable iterable))
|
|
{
|
|
throw new Exception($"Can't iterate over object with type '{enumeratorObject.Type}'");
|
|
}
|
|
|
|
using (var enumerator = iterable.GetScriptEnumerator())
|
|
{
|
|
while (enumerator.MoveNext())
|
|
{
|
|
var current = enumerator.Current;
|
|
if (current == null)
|
|
{
|
|
throw new Exception($"Can't assign result value of nothing to multiple values");
|
|
}
|
|
if (current.Type != Type.Table)
|
|
{
|
|
throw new Exception($"Can't assign result value with type '{current.Type}' to multiple values");
|
|
}
|
|
|
|
var table = (SimpleScriptTable)current;
|
|
if (e.Variables[0].Name != "_")
|
|
innerEvaluator.Scope.CreateLocal(e.Variables[0], table[0].ToScriptType());
|
|
if (e.Variables[1].Name != "_")
|
|
innerEvaluator.Scope.CreateLocal(e.Variables[1], table[1]);
|
|
innerEvaluator.EvaluateBoundBlockStatement((BoundBlockStatement) e.Block);
|
|
if (innerEvaluator.HasBroken || innerEvaluator.HasReturned)
|
|
{
|
|
if (innerEvaluator.HasReturned)
|
|
{
|
|
HasReturned = innerEvaluator.HasReturned;
|
|
_returnValue = innerEvaluator._returnValue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EvaluateWhileStatement(BoundWhileStatement e)
|
|
{
|
|
var innerEvaluator = new Evaluator(_diagnostics, Scope, _script);
|
|
|
|
var block = (BoundBlockStatement) e.Block;
|
|
while ((ScriptBoolean)innerEvaluator.EvaluateExpression(e.Condition))
|
|
{
|
|
innerEvaluator.EvaluateBoundBlockStatement(block);
|
|
if (innerEvaluator.HasBroken || innerEvaluator.HasReturned)
|
|
{
|
|
if (innerEvaluator.HasReturned)
|
|
{
|
|
HasReturned = innerEvaluator.HasReturned;
|
|
_returnValue = innerEvaluator._returnValue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |