From 237f2fefd9112ba1d410c2d54ec8b637b81e9738 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Wed, 13 Feb 2019 16:29:58 +0100 Subject: [PATCH] Refactor evaluation to not be handled in a single large class --- .../ScriptFunction/ScriptRuntimeFunction.cs | 26 +- Upsilon/Binder/Binder.cs | 94 +-- .../BoundExpressions/BoundBadExpression.cs | 8 +- .../BoundExpressions/BoundBinaryExpression.cs | 167 +++- .../BoundExpressions/BoundExpression.cs | 5 +- .../BoundFunctionCallExpression.cs | 25 +- .../BoundFunctionExpression.cs | 12 +- .../BoundExpressions/BoundIndexExpression.cs | 56 +- .../BoundLiteralExpression.cs | 8 +- .../BoundExpressions/BoundTableExpression.cs | 53 +- .../BoundExpressions/BoundUnaryExpression.cs | 49 +- .../BoundVariableExpression.cs | 9 +- .../BoundStatements/BoundBlockStatement.cs | 14 +- .../BoundStatements/BoundBreakStatement.cs | 6 + .../BoundExpressionStatement.cs | 7 + .../BoundFunctionAssignmentStatement.cs | 30 + .../BoundGenericForStatement.cs | 53 +- .../BoundStatements/BoundIfStatement.cs | 25 + .../BoundMultiAssignmentStatement.cs | 29 + .../BoundNumericForStatement.cs | 53 ++ .../BoundStatements/BoundReturnStatement.cs | 10 +- Upsilon/Binder/BoundStatements/BoundScript.cs | 5 + .../Binder/BoundStatements/BoundStatement.cs | 3 + .../BoundTableAssigmentStatement.cs | 29 + .../BoundVariableAssignment.cs | 15 + .../BoundStatements/BoundWhileStatement.cs | 28 +- Upsilon/Binder/BoundVariableSymbol.cs | 16 +- .../InternalFunctionVariableSymbol.cs | 6 +- .../ScriptFunctionVariableSymbol.cs | 8 +- .../BoundTypes/UserDataBoundTypeDefinition.cs | 4 +- Upsilon/Evaluator/EvaluationState.cs | 13 + Upsilon/Evaluator/Evaluator.cs | 719 +----------------- Upsilon/Evaluator/Script.cs | 1 - Upsilon/StandardLibraries/BasicFunctions.cs | 4 +- UpsilonTests/GeneralTests/IfTests.cs | 10 +- 35 files changed, 791 insertions(+), 809 deletions(-) create mode 100644 Upsilon/Evaluator/EvaluationState.cs diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs index 9d97cbb..e1c087d 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs @@ -107,9 +107,9 @@ namespace Upsilon.BaseTypes.ScriptFunction var callingVariable = variables[index]; var optionVariable = option.Parameters[index]; if (callingVariable.Type == BaseTypes.Type.Unknown || callingVariable.Type == BaseTypes.Type.Nil || - optionVariable.Type == BaseTypes.Type.Unknown) + optionVariable.ValueType == BaseTypes.Type.Unknown) continue; - if (callingVariable.Type != optionVariable.Type) + if (callingVariable.Type != optionVariable.ValueType) { isCompatible = false; break; @@ -135,10 +135,10 @@ namespace Upsilon.BaseTypes.ScriptFunction { var callingVariable = variables[index]; var optionVariable = option.Parameters[index]; - if (callingVariable.Type == BaseTypes.Type.Unknown || callingVariable.Type == BaseTypes.Type.Nil || - optionVariable.Type == BaseTypes.Type.Unknown) + if (callingVariable.ValueType == BaseTypes.Type.Unknown || callingVariable.ValueType == BaseTypes.Type.Nil || + optionVariable.ValueType == BaseTypes.Type.Unknown) continue; - if (callingVariable.Type != optionVariable.Type) + if (callingVariable.ValueType != optionVariable.ValueType) { isCompatible = false; break; @@ -163,24 +163,30 @@ namespace Upsilon.BaseTypes.ScriptFunction EvaluationScope = scope; } - public EvaluationScope EvaluationScope { get; } + public EvaluationScope EvaluationScope { get; set; } public BoundBlockStatement Block { get; } public ImmutableArray Parameters { get; } - public ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span) + public ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope outerScope, TextSpan span) { - var innerEvaluator = new Evaluator.Evaluator(diagnostics, EvaluationScope, script); + var innerScope = new EvaluationScope(EvaluationScope); + var state = new EvaluationState() + { + Script = script, + }; if (Parameters != null) { for (var i = 0; i < Parameters.Length; i++) { var parameterVariable = Parameters[i]; var parameterValue = variables[i]; - innerEvaluator.Scope.CreateLocal(parameterVariable.VariableSymbol, parameterValue); + innerScope.CreateLocal(parameterVariable.VariableSymbol, parameterValue); } } - return innerEvaluator.EvaluateNode(Block); + + Block.Evaluate(innerScope, diagnostics, ref state); + return state.ReturnValue; } } } diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 72feae0..999a8ff 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -139,10 +139,10 @@ namespace Upsilon.Binder var inExp = BindExpression(e.Expression); if (inExp.Kind == BoundKind.BoundBadExpression) return new BoundBadExpression(e.Span); - var op = BoundUnaryOperator.Bind(e.Operator.Kind, inExp.Type); + var op = BoundUnaryOperator.Bind(e.Operator.Kind, inExp.ValueType); if (op == null) { - _diagnostics.LogUnknownUnaryOperator(e.Span, e.Operator.Kind, inExp.Type); + _diagnostics.LogUnknownUnaryOperator(e.Span, e.Operator.Kind, inExp.ValueType); return inExp; } @@ -155,10 +155,10 @@ namespace Upsilon.Binder var right = BindExpression(e.Right); if (left.Kind == BoundKind.BoundBadExpression || right.Kind == BoundKind.BoundBadExpression) return new BoundBadExpression(e.Span); - var op = BoundBinaryOperator.Bind(e.Operator.Kind, left.Type, right.Type); + var op = BoundBinaryOperator.Bind(e.Operator.Kind, left.ValueType, right.ValueType); if (op == null) { - _diagnostics.LogUnknownBinaryOperator(e.Span, e.Operator.Kind, left.Type, right.Type); + _diagnostics.LogUnknownBinaryOperator(e.Span, e.Operator.Kind, left.ValueType, right.ValueType); return left; } return new BoundBinaryExpression(op, left, right, op.OutType, e.Span); @@ -201,7 +201,7 @@ namespace Upsilon.Binder private BoundExpression BindFunctionCallExpression(FunctionCallExpressionSyntax e) { var expression = BindExpression(e.Identifier); - if (expression.Type != Type.Function && expression.Type != Type.Unknown) + if (expression.ValueType != Type.Function && expression.ValueType != Type.Unknown) { _diagnostics.LogError($"Unknown function called.", e.Span); return new BoundBadExpression(e.Span); @@ -225,7 +225,7 @@ namespace Upsilon.Binder { if (function is ScriptFunctionVariableSymbol scriptFunction) { - if ((scriptFunction.GetFirstValid(parameters.Select(x => x.Type).ToArray()) is ScriptFunctionVariableOption functionOption)) + if ((scriptFunction.GetFirstValid(parameters.Select(x => x.ValueType).ToArray()) is ScriptFunctionVariableOption functionOption)) { if (!functionOption.IsBound) { @@ -234,7 +234,7 @@ namespace Upsilon.Binder { var functionVariable = functionOption.Parameters[index]; var callingVariable = parameters[index]; - functionVariable.TypeContainer = callingVariable.Type; + functionVariable.TypeContainer = callingVariable.ValueType; Scope.DefineLocalVariable(functionVariable); } @@ -305,7 +305,7 @@ namespace Upsilon.Binder if (!isValid) { _diagnostics.LogError( - $"No valid function with name '{function.Name}' and parameter types {string.Join(", ", parameters.Select(x => $"'{x.Type}'"))} found", + $"No valid function with name '{function.Name}' and parameter types {string.Join(", ", parameters.Select(x => $"'{x.ValueType}'"))} found", expression.Span); } } @@ -320,7 +320,7 @@ namespace Upsilon.Binder if (option == null) { _diagnostics.LogError( - $"No valid function with name '{ubMethod.Name}' and parameter types {string.Join(", ", parameters.Select(x => $"'{x.Type}'"))} found", + $"No valid function with name '{ubMethod.Name}' and parameter types {string.Join(", ", parameters.Select(x => $"'{x.ValueType}'"))} found", expression.Span); } else @@ -421,7 +421,7 @@ namespace Upsilon.Binder { return new VariableSymbol("", compositeTypeContainer.Types[1], true); } - return new VariableSymbol("", expression.Type, true); + return new VariableSymbol("", expression.ValueType, true); } else if (indexerVariable.TypeContainer == Type.Unknown) { @@ -430,18 +430,18 @@ namespace Upsilon.Binder } else if (expression.Kind == BoundKind.BoundFunctionCallExpression) { - if (expression.Type == Type.UserData) + if (expression.ValueType == Type.UserData) { - var ud = expression.Type.UserData; + var ud = expression.ValueType.UserData; if (!string.IsNullOrEmpty(ud)) { var boundDef = BoundTypeHandler.GetTypeDefinition(ud); return new UserDataVariableSymbol("", boundDef, true); } } - return new VariableSymbol("", expression.Type, true); + return new VariableSymbol("", expression.ValueType, true); } - else if (expression.Type == Type.Unknown) + else if (expression.ValueType == Type.Unknown) { return new VariableSymbol("", Type.Unknown, true); } @@ -473,27 +473,27 @@ namespace Upsilon.Binder var isCreation = false; if (!Scope.TryGetVariable(name, !isLocal, out var variable)) { - if (assignment.Type == Type.Table) + if (assignment.ValueType == Type.Table) { if (assignment.Kind == BoundKind.BoundTableExpression) { variable = new TableVariableSymbol(name, isLocal, - ((BoundTableExpression) assignment).Expressions, assignment.Type); + ((BoundTableExpression) assignment).Expressions, assignment.ValueType); } else if (assignment.Kind == BoundKind.VariableExpression) { variable = new TableVariableSymbol(name, isLocal, ((TableVariableSymbol) ((BoundVariableExpression) assignment).Variable.VariableSymbol) - .Variables, assignment.Type); + .Variables, assignment.ValueType); } else { - variable = new TableVariableSymbol(name, isLocal, assignment.Type); + variable = new TableVariableSymbol(name, isLocal, assignment.ValueType); } } - else if (assignment.Type == Type.UserData) + else if (assignment.ValueType == Type.UserData) { - var ud = assignment.Type.UserData; + var ud = assignment.ValueType.UserData; if (string.IsNullOrEmpty(ud)) { variable = new VariableSymbol(name, Type.Unknown, isLocal); @@ -508,13 +508,13 @@ namespace Upsilon.Binder } else { - variable = new VariableSymbol(name, assignment.Type, isLocal); + variable = new VariableSymbol(name, assignment.ValueType, isLocal); } } } else { - variable = new VariableSymbol(name, assignment.Type, isLocal); + variable = new VariableSymbol(name, assignment.ValueType, isLocal); } variable.CommentValue = commentData; @@ -529,26 +529,26 @@ namespace Upsilon.Binder else { // don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables - if (assignment.Type != variable.TypeContainer) + if (assignment.ValueType != variable.TypeContainer) { - if (variable.TypeContainer == Type.Nil || assignment.Type == Type.Nil) + if (variable.TypeContainer == Type.Nil || assignment.ValueType == Type.Nil) { - variable.TypeContainer = assignment.Type; + variable.TypeContainer = assignment.ValueType; } else if (variable.TypeContainer == Type.Unknown) { - variable.TypeContainer = assignment.Type; + variable.TypeContainer = assignment.ValueType; } - else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v) + else if (assignment.ValueType == Type.Unknown && assignment is BoundVariableExpression v) { v.Variable.VariableSymbol.TypeContainer = variable.TypeContainer; } - else if (assignment.Type == Type.Unknown) + else if (assignment.ValueType == Type.Unknown) { } else { - _diagnostics.LogCannotConvert(assignment.Type, variable.TypeContainer, assignment.Span); + _diagnostics.LogCannotConvert(assignment.ValueType, variable.TypeContainer, assignment.Span); return (null, false); } } @@ -615,7 +615,7 @@ namespace Upsilon.Binder { Scope = new BoundScope(Scope); var condition = BindExpressionStatement(e.Condition); - if (condition.Expression.Type != Type.Boolean && condition.Expression.Type != Type.Unknown) + if (condition.Expression.ValueType != Type.Boolean && condition.Expression.ValueType != Type.Unknown) { _diagnostics.LogError("Condition for if statement should be a boolean.", condition.Span); } @@ -678,7 +678,7 @@ namespace Upsilon.Binder innerScope.DefineLocalVariable(variableSymbol); } - if (parameters.All(x => x.Type != Type.Unknown)) + if (parameters.All(x => x.ValueType != Type.Unknown)) { Scope = innerScope; var block = BindBlockStatement(e.Block); @@ -770,17 +770,17 @@ namespace Upsilon.Binder return new BoundReturnStatement(null, e.Span); var expression = BindExpression(e.Expression); - if (expression.Type != Type.Unknown && expression.Type != Type.Unknown && + if (expression.ValueType != Type.Unknown && expression.ValueType != Type.Unknown && Scope.ReturnType != Type.Unknown && Scope.ReturnType != Type.Nil) { - if (expression.Type != Scope.ReturnType) + if (expression.ValueType != Scope.ReturnType) { - _diagnostics.LogError($"Can't return type '{expression.Type}' from this scope, earlier in the" + + _diagnostics.LogError($"Can't return type '{expression.ValueType}' from this scope, earlier in the" + $" scope a return type of '{Scope.ReturnType}' is defined.", e.Span); } } - if (expression.Type != Type.Unknown && expression.Type != Type.Nil) - Scope.ReturnType = expression.Type; + if (expression.ValueType != Type.Unknown && expression.ValueType != Type.Nil) + Scope.ReturnType = expression.ValueType; return new BoundReturnStatement(expression, e.Span); } @@ -800,7 +800,7 @@ namespace Upsilon.Binder var boundExpression = (BoundExpressionStatement)bound; current++; innerVariables.Add(current.ToString(), - new VariableSymbol(current.ToString(), boundExpression.Expression.Type, false)); + new VariableSymbol(current.ToString(), boundExpression.Expression.ValueType, false)); } statements.Add(bound); } @@ -816,7 +816,7 @@ namespace Upsilon.Binder var expression = BindExpression(e.Expression); var index = BindExpression(e.Index); - switch (expression.Type.Type) + switch (expression.ValueType.Type) { case Type.Table: if (isAssignment) @@ -856,7 +856,7 @@ namespace Upsilon.Binder _diagnostics.LogError($"No variable '{realIndex.Value}' found in table '{realTable.VariableSymbol.Name}'.", e.Span); } - else if (expression.Type is CompositeTypeContainer compositeTypeContainer) + else if (expression.ValueType is CompositeTypeContainer compositeTypeContainer) { return new BoundIndexExpression(expression, index, compositeTypeContainer.Types[1], e.Span); } @@ -864,10 +864,10 @@ namespace Upsilon.Binder case Type.UserData: case Type.Unknown: return new BoundIndexExpression(expression, index, Type.Unknown, e.Span); - case Type.String when index.Type == Type.Number: + case Type.String when index.ValueType == Type.Number: return new BoundIndexExpression(expression, index, Type.String, e.Span); default: - _diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span); + _diagnostics.LogInvalidIndexExpression(expression.ValueType, index.ValueType, e.Span); return new BoundBadExpression(e.Span); } } @@ -876,7 +876,7 @@ namespace Upsilon.Binder { var expression = BindExpression(e.Expression); var index = e.Index.Name; - switch (expression.Type.Type) + switch (expression.ValueType.Type) { case Type.Table: if (isAssignment) @@ -936,7 +936,7 @@ namespace Upsilon.Binder case Type.String: return new BoundFullStopIndexExpression(expression, index, Type.String, e.Span); default: - _diagnostics.LogInvalidIndexExpression(expression.Type, Type.String, e.Span); + _diagnostics.LogInvalidIndexExpression(expression.ValueType, Type.String, e.Span); return new BoundBadExpression(e.Span); } } @@ -964,7 +964,7 @@ namespace Upsilon.Binder if (variableSymbol is TableVariableSymbol tableVariableSymbol) { tableVariableSymbol.Variables.Add(index.Value.ToString(), - new VariableSymbol(index.Value.ToString(), value.Type, false)); + new VariableSymbol(index.Value.ToString(), value.ValueType, false)); } } indexableExpression = indexable; @@ -979,10 +979,10 @@ namespace Upsilon.Binder if (indexable.Expression.Kind == BoundKind.VariableExpression) { var variable = (BoundVariableExpression)indexable.Expression; - if (variable.Type == Type.Table) + if (variable.ValueType == Type.Table) { ((TableVariableSymbol)variable.Variable.VariableSymbol).Variables.Add(indexable.Index, - new VariableSymbol(indexable.Index, value.Type, false)); + new VariableSymbol(indexable.Index, value.ValueType, false)); } } indexableExpression = indexable; @@ -1022,7 +1022,7 @@ namespace Upsilon.Binder var valueVar = e.Variables[1]; VariableSymbol valueVariable; - if (boundEnumerableExpression.Type is CompositeTypeContainer composite && composite.Types.Length == 2) + if (boundEnumerableExpression.ValueType is CompositeTypeContainer composite && composite.Types.Length == 2) { var keyType = composite.Types[0]; keyVariable.TypeContainer = keyType; diff --git a/Upsilon/Binder/BoundExpressions/BoundBadExpression.cs b/Upsilon/Binder/BoundExpressions/BoundBadExpression.cs index be0b7a9..78ff5dc 100644 --- a/Upsilon/Binder/BoundExpressions/BoundBadExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundBadExpression.cs @@ -1,5 +1,7 @@ +using System; using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -17,6 +19,10 @@ namespace Upsilon.Binder yield break; } - public override TypeContainer Type => BaseTypes.Type.Nil; + public override TypeContainer ValueType => BaseTypes.Type.Nil; + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + throw new Exception("Can't evaluate bad syntax."); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundExpressions/BoundBinaryExpression.cs b/Upsilon/Binder/BoundExpressions/BoundBinaryExpression.cs index 188055b..c34cb1a 100644 --- a/Upsilon/Binder/BoundExpressions/BoundBinaryExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundBinaryExpression.cs @@ -1,19 +1,24 @@ -using System.Collections; +using System; using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.Number; +using Upsilon.BaseTypes.UserData; +using Upsilon.Evaluator; +using Upsilon.Exceptions; using Upsilon.Text; +using Type = Upsilon.BaseTypes.Type; namespace Upsilon.Binder { public class BoundBinaryExpression : BoundExpression { public BoundBinaryExpression(BoundBinaryOperator op, BoundExpression leftExpression, - BoundExpression rightExpression, Type type, TextSpan span) : base(span) + BoundExpression rightExpression, Type valueType, TextSpan span) : base(span) { Operator = op; LeftExpression = leftExpression; RightExpression = rightExpression; - Type = type; + ValueType = valueType; } public override BoundKind Kind => BoundKind.BoundBinaryExpression; @@ -23,7 +28,161 @@ namespace Upsilon.Binder yield return RightExpression; } - public override TypeContainer Type { get; } + public override TypeContainer ValueType { get; } + + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + if (Operator.Kind == BoundBinaryOperator.OperatorKind.Or) + { + var l = LeftExpression.Evaluate(scope, diagnostics, ref state); + if (l.Type == Type.Boolean && ((ScriptBoolean)l).Value) + return new ScriptBoolean(true); + var r = RightExpression.Evaluate(scope, diagnostics, ref state); + if (r.Type == Type.Boolean && ((ScriptBoolean)r).Value) + return new ScriptBoolean(true); + return new ScriptBoolean(false); + } + if (Operator.Kind == BoundBinaryOperator.OperatorKind.And) + { + var l = LeftExpression.Evaluate(scope, diagnostics, ref state); + if (l.Type != Type.Boolean || !((ScriptBoolean)l).Value) + return new ScriptBoolean(false); + var r = RightExpression.Evaluate(scope, diagnostics, ref state); + if (r.Type != Type.Boolean || !((ScriptBoolean)r).Value) + return new ScriptBoolean(false); + return new ScriptBoolean(true); + } + + var left = LeftExpression.Evaluate(scope, diagnostics, ref state); + var right = RightExpression.Evaluate(scope, diagnostics, ref state); + switch (Operator.Kind) + { + case BoundBinaryOperator.OperatorKind.Addition: + if (left.Type == Type.Number) + { + if (right.Type == Type.Number) + { + return ((ScriptNumber)left) + ((ScriptNumber)right); + } + if (right.Type == Type.String) + { + return new ScriptString(left + right.ToString()); + } + } + 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); + } + else if (left.Type == Type.UserData) + { + var ud = (GenericUserData) left; + var (type, failed) = ud.BinaryOperator(left, OperatorType.Modulo, right); + if (failed) goto default; + return type; + } + 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); + } + + throw new ScriptRuntimeException(state.Script.FileName, + $"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span, + diagnostics.ScriptString.GetSpan(Span)); + case BoundBinaryOperator.OperatorKind.LessEquals: + if (left.Type == Type.Number && right.Type == Type.Number) + { + return ((ScriptNumber) left) <= ((ScriptNumber) right); + } + + throw new ScriptRuntimeException(state.Script.FileName, + $"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span, + diagnostics.ScriptString.GetSpan(Span)); + case BoundBinaryOperator.OperatorKind.Greater: + if (left.Type == Type.Number && right.Type == Type.Number) + { + return ((ScriptNumber) left) > ((ScriptNumber) right); + } + + throw new ScriptRuntimeException(state.Script.FileName, + $"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span, + diagnostics.ScriptString.GetSpan(Span)); + case BoundBinaryOperator.OperatorKind.GreaterEquals: + if (left.Type == Type.Number && right.Type == Type.Number) + { + return ((ScriptNumber) left) >= ((ScriptNumber) right); + } + + throw new ScriptRuntimeException(state.Script.FileName, + $"Can't find operator '{Operator.Kind}' for types '{left.Type}' and '{right.Type}'", Span, + diagnostics.ScriptString.GetSpan(Span)); + default: + throw new Exception("Invalid Binary Operator: " + Operator.Kind); + } + } public BoundBinaryOperator Operator { get; } public BoundExpression LeftExpression { get; } diff --git a/Upsilon/Binder/BoundExpressions/BoundExpression.cs b/Upsilon/Binder/BoundExpressions/BoundExpression.cs index 645589e..bb15a15 100644 --- a/Upsilon/Binder/BoundExpressions/BoundExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundExpression.cs @@ -1,4 +1,5 @@ using Upsilon.BaseTypes; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -9,6 +10,8 @@ namespace Upsilon.Binder { } - public abstract TypeContainer Type { get; } + public abstract TypeContainer ValueType { get; } + + internal abstract ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state); } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs b/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs index 290b071..3a2e252 100644 --- a/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.ScriptFunction; +using Upsilon.Evaluator; +using Upsilon.Exceptions; using Upsilon.Text; namespace Upsilon.Binder @@ -15,7 +18,7 @@ namespace Upsilon.Binder { Identifier = identifier; Parameters = parameters; - Type = type; + ValueType = type; } public override BoundKind Kind => BoundKind.BoundFunctionCallExpression; @@ -29,6 +32,24 @@ namespace Upsilon.Binder } } - public override TypeContainer Type { get; } + + public override TypeContainer ValueType { get; } + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var variable = Identifier.Evaluate(scope, diagnostics, ref state); + if (!(variable is ScriptFunction function)) + { + throw new EvaluationException(state.Script.FileName, $"Variable is not a function.", Identifier.Span); + } + var ls = new List(); + foreach (var t in Parameters) + { + var evaluate = t.Evaluate(scope, diagnostics, ref state); + ls.Add(evaluate); + } + + var val = function.Run(diagnostics, ls.ToArray(), state.Script, scope, Span); + return val; + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs b/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs index 6bf1e75..9f59955 100644 --- a/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.ScriptFunction; +using Upsilon.Evaluator; using Upsilon.Text; using Type = Upsilon.BaseTypes.Type; @@ -32,7 +34,15 @@ namespace Upsilon.Binder yield return Block; } - public override TypeContainer Type => BaseTypes.Type.Function; + public override TypeContainer ValueType => BaseTypes.Type.Function; + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var option = new ScriptRuntimeFunction.ScriptRuntimeFunctionOption(Parameters, Block, scope); + var func = new ScriptRuntimeFunction(new List(){option}); + return func; + + } + public BoundScope Scope { get; set; } public Type ReturnType { get; } } diff --git a/Upsilon/Binder/BoundExpressions/BoundIndexExpression.cs b/Upsilon/Binder/BoundExpressions/BoundIndexExpression.cs index 1130a1b..a7e53b1 100644 --- a/Upsilon/Binder/BoundExpressions/BoundIndexExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundIndexExpression.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.ScriptTypeInterfaces; +using Upsilon.Evaluator; +using Upsilon.Exceptions; using Upsilon.Parser; using Upsilon.Text; @@ -11,7 +14,7 @@ namespace Upsilon.Binder { Identifier = identifier; Index = index; - Type = type; + ValueType = type; } public BoundExpression Identifier { get; } @@ -25,7 +28,30 @@ namespace Upsilon.Binder yield return Index; } - public override TypeContainer Type { get; } + public override TypeContainer ValueType { get; } + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var variable = Identifier.Evaluate(scope, diagnostics, ref state); + if (variable.Type == Type.Nil) + { + throw new EvaluationException(state.Script.FileName, $"Nil variable can't be indexed", Span); + } + if (!(variable is IIndexable indexable)) + { + throw new EvaluationException(state.Script.FileName, + $"Variable of type '{variable.Type}' is not indexable.", Span); + } + + var innerScope = scope; + if (variable is IScopeOwner scopeOwner) + { + innerScope = scopeOwner.EvaluationScope; + } + + var indexer = Index.Evaluate(scope, diagnostics, ref state); + return indexable.Get(diagnostics, Span, indexer, innerScope); + + } } public class BoundFullStopIndexExpression : BoundExpression @@ -37,7 +63,7 @@ namespace Upsilon.Binder { Expression = expression; Index = index; - Type = type; + ValueType = type; } public override BoundKind Kind => BoundKind.BoundFullstopIndexExpression; @@ -47,6 +73,28 @@ namespace Upsilon.Binder yield return Expression; } - public override TypeContainer Type { get; } + public override TypeContainer ValueType { get; } + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var variable = Expression.Evaluate(scope, diagnostics, ref state); + if (variable.Type == Type.Nil) + { + throw new EvaluationException(state.Script.FileName, $"Nil variable can't be indexed", Span); + } + if (!(variable is IIndexable indexable)) + { + throw new EvaluationException(state.Script.FileName, + $"Variable of type '{variable.Type}' is not indexable.", Span); + } + + var innerScope = scope; + if (variable is IScopeOwner scopeOwner) + { + innerScope = scopeOwner.EvaluationScope; + } + + return indexable.Get(diagnostics, Span, Index.ToScriptType(), innerScope); + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundExpressions/BoundLiteralExpression.cs b/Upsilon/Binder/BoundExpressions/BoundLiteralExpression.cs index 6904e22..4bcb54d 100644 --- a/Upsilon/Binder/BoundExpressions/BoundLiteralExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundLiteralExpression.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -18,7 +19,12 @@ namespace Upsilon.Binder yield break; } - public override TypeContainer Type => Value.Type; + public override TypeContainer ValueType => Value.Type; + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + return Value; + } + public ScriptType Value { get; } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs b/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs index 1d10ad5..8eba300 100644 --- a/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.ScriptTable; using Upsilon.Binder.VariableSymbols; +using Upsilon.Evaluator; using Upsilon.Text; using Type = Upsilon.BaseTypes.Type; @@ -24,7 +26,7 @@ namespace Upsilon.Binder return Statements; } - public override TypeContainer Type + public override TypeContainer ValueType { get { @@ -38,10 +40,10 @@ namespace Upsilon.Binder } if (valueType == null) { - valueType = exp.Expression.Type; + valueType = exp.Expression.ValueType; continue; } - if (valueType == exp.Expression.Type) continue; + if (valueType == exp.Expression.ValueType) continue; valueType = BaseTypes.Type.Unknown; break; } @@ -55,6 +57,51 @@ namespace Upsilon.Binder } } + internal override ScriptType Evaluate(EvaluationScope outerScope, Diagnostics diagnostics, ref EvaluationState outerState) + { + var tableScope = EvaluationScope.CreateWithGetOnlyParent(outerScope); + var innerState = new EvaluationState() + { + Script = outerState.Script + }; + + + var currentPos = 1; + var isNumerated = true; + foreach (var boundStatement in Statements) + { + switch (boundStatement.Kind) + { + case BoundKind.BoundAssignmentStatement: + case BoundKind.BoundFunctionExpression: + case BoundKind.BoundFunctionAssignmentStatement: + boundStatement.Evaluate(tableScope, diagnostics, ref innerState); + isNumerated = false; + break; + default: + { + boundStatement.Evaluate(tableScope, diagnostics, ref innerState); + if (innerState.LastValue != null) + { + tableScope.CreateLocal(new VariableSymbol(currentPos.ToString(), innerState.LastValue.Type, false), + innerState.LastValue); + } + innerState.LastValue = null; + break; + } + } + + currentPos++; + } + + if (isNumerated) + { + return new NumeratedScriptTable(tableScope); + } + return new GenericKeyedScriptTable(tableScope); + + } + public Dictionary Expressions { get; } public ImmutableArray Statements { get; } diff --git a/Upsilon/Binder/BoundExpressions/BoundUnaryExpression.cs b/Upsilon/Binder/BoundExpressions/BoundUnaryExpression.cs index 69370d5..f779383 100644 --- a/Upsilon/Binder/BoundExpressions/BoundUnaryExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundUnaryExpression.cs @@ -1,5 +1,10 @@ using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.Number; +using Upsilon.BaseTypes.ScriptTypeInterfaces; +using Upsilon.BaseTypes.UserData; +using Upsilon.Evaluator; +using Upsilon.Exceptions; using Upsilon.Text; namespace Upsilon.Binder @@ -13,13 +18,53 @@ namespace Upsilon.Binder yield return InExpression; } - public override TypeContainer Type { get; } + public override TypeContainer ValueType { get; } + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var operand = InExpression.Evaluate(scope, diagnostics, ref state); + switch (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 EvaluationException(state.Script.FileName, "Invalid Unary Operator: " + Operator.Kind, Span); + } + + } public BoundUnaryExpression(BoundUnaryOperator op, BoundExpression inExpression, Type type, TextSpan span) : base(span) { Operator = op; InExpression = inExpression; - Type = type; + ValueType = type; } public BoundExpression InExpression { get; } diff --git a/Upsilon/Binder/BoundExpressions/BoundVariableExpression.cs b/Upsilon/Binder/BoundExpressions/BoundVariableExpression.cs index 172cd48..35ed619 100644 --- a/Upsilon/Binder/BoundExpressions/BoundVariableExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundVariableExpression.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.Evaluator; +using Upsilon.Exceptions; using Upsilon.Text; -using Type = Upsilon.BaseTypes.Type; namespace Upsilon.Binder { @@ -21,6 +22,10 @@ namespace Upsilon.Binder yield break; } - public override TypeContainer Type => Variable.Type; + public override TypeContainer ValueType => Variable.ValueType; + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + return Variable.Evaluate(scope, diagnostics, ref state); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundBlockStatement.cs b/Upsilon/Binder/BoundStatements/BoundBlockStatement.cs index 4b15179..2c59126 100644 --- a/Upsilon/Binder/BoundStatements/BoundBlockStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundBlockStatement.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -20,5 +20,17 @@ namespace Upsilon.Binder } public ImmutableArray Statements { get; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + foreach (var statement in Statements) + { + statement.Evaluate(scope, diagnostics, ref state); + if (state.Returned) + return; + if (state.HasBroken) + return; + } + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs b/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs index acc08c7..970cdfd 100644 --- a/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -15,5 +16,10 @@ namespace Upsilon.Binder { yield break; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + state.HasBroken = true; + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundExpressionStatement.cs b/Upsilon/Binder/BoundStatements/BoundExpressionStatement.cs index 0ce8699..5145bcb 100644 --- a/Upsilon/Binder/BoundStatements/BoundExpressionStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundExpressionStatement.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -17,5 +18,11 @@ namespace Upsilon.Binder { Expression = expression; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var value = Expression.Evaluate(scope, diagnostics, ref state); + state.LastValue = value; + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs b/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs index 186df83..e1fcd51 100644 --- a/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using Upsilon.BaseTypes.ScriptFunction; using Upsilon.Binder.VariableSymbols; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -21,5 +23,33 @@ namespace Upsilon.Binder { yield return Func; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var func = (ScriptRuntimeFunction)Func.Evaluate(scope, diagnostics, ref state); + if (Variable.Local) + { + if (scope.Variables.TryGetValue(Variable.Name, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction) + { + scriptRuntimeFunction.AddOption(func.Options[0]); + } + else + { + scope.CreateLocal(Variable, func); + } + } + else + { + if (scope.TryGet(Variable, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction) + { + scriptRuntimeFunction.AddOption(func.Options[0]); + } + else + { + scope.AssignToNearest(Variable, func); + } + } + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs b/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs index 8aa0d07..ac954a0 100644 --- a/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs @@ -1,7 +1,11 @@ +using System; using System.Collections.Generic; using System.Collections.Immutable; -using Upsilon.Binder.VariableSymbols; +using Upsilon.BaseTypes; +using Upsilon.BaseTypes.ScriptTypeInterfaces; +using Upsilon.Evaluator; using Upsilon.Text; +using Type = System.Type; namespace Upsilon.Binder { @@ -30,5 +34,52 @@ namespace Upsilon.Binder yield return BoundEnumerableExpression; yield return Block; } + + internal override void Evaluate(EvaluationScope outerScope, Diagnostics diagnostics, ref EvaluationState outerState) + { + var innerScope = new EvaluationScope(outerScope); + var innerState = new EvaluationState() + { + Script = outerState.Script, + }; + + var enumeratorObject = BoundEnumerableExpression.Evaluate(outerScope, diagnostics, ref innerState); + 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 != BaseTypes.Type.Table) + { + throw new Exception($"Can't assign result value with type '{current.Type}' to multiple values"); + } + + var table = (SimpleScriptTable)current; + if (Variables[0].VariableSymbol.Name != "_") + innerScope.CreateLocal(Variables[0].VariableSymbol, table[0].ToScriptType()); + if (Variables[1].VariableSymbol.Name != "_") + innerScope.CreateLocal(Variables[1].VariableSymbol, table[1]); + Block.Evaluate(innerScope, diagnostics, ref innerState); + if (innerState.HasBroken || innerState.Returned) + { + if (innerState.Returned) + { + outerState.Returned = true; + outerState.ReturnValue = innerState.ReturnValue; + } + break; + } + } + } + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundIfStatement.cs b/Upsilon/Binder/BoundStatements/BoundIfStatement.cs index 5e54c07..f5b9897 100644 --- a/Upsilon/Binder/BoundStatements/BoundIfStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundIfStatement.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using Upsilon.BaseTypes; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -37,6 +39,24 @@ namespace Upsilon.Binder public BoundBlockStatement Block { get; } public BoundIfStatement NextElseIf { get; } public BoundElseStatement ElseStatement { get; } + + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var condition = Condition.Expression.Evaluate(scope, diagnostics, ref state); + if ((ScriptBoolean) condition) + { + Block.Evaluate(scope, diagnostics, ref state); + } + else if (NextElseIf != null) + { + NextElseIf.Evaluate(scope, diagnostics, ref state); + } + else + { + ElseStatement?.Evaluate(scope, diagnostics, ref state); + } + } } public class BoundElseStatement : BoundStatement @@ -54,5 +74,10 @@ namespace Upsilon.Binder { yield return Block; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + Block.Evaluate(scope, diagnostics, ref state); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs b/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs index 540479c..f3fc1e8 100644 --- a/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Upsilon.BaseTypes.Number; +using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Binder.VariableSymbols; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -23,5 +26,31 @@ namespace Upsilon.Binder { yield return Assignment; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var val = Assignment.Evaluate(scope, diagnostics, ref state); + if (val is IIndexable table) + { + for (var i = 0; i < Variables.Length; i++) + { + var variableSymbol = Variables[i]; + if (variableSymbol == null) + continue; + if (variableSymbol.Name == "_") + continue; + var value = table.Get(diagnostics, 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.", Span); + } + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs b/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs index 3363bdc..3716547 100644 --- a/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using Upsilon.BaseTypes.Number; using Upsilon.Binder.VariableSymbols; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -31,5 +33,56 @@ namespace Upsilon.Binder yield return BoundStep; yield return Block; } + + internal override void Evaluate(EvaluationScope outerScope, Diagnostics diagnostics, ref EvaluationState outerState) + { + var innerScope = new EvaluationScope(outerScope); + var innerState = new EvaluationState() + { + Script = outerState.Script, + }; + + var startVal = (ScriptNumberLong) BoundStart.Evaluate(innerScope, diagnostics, ref innerState); + innerScope.CreateLocal(Variable, startVal); + var stopVal = (ScriptNumberLong)BoundStop.Evaluate(innerScope, diagnostics, ref innerState); + long step = 1; + if (BoundStep != null) + { + var stepVal = (ScriptNumberLong) BoundStep.Evaluate(innerScope, diagnostics, ref innerState); + step = stepVal.Value; + } + + if (step > 0) + { + for (; startVal.Value <= stopVal.Value; startVal.Value = startVal.Value + step) + { + Block.Evaluate(innerScope, diagnostics, ref innerState); + if (innerState.HasBroken) + break; + if (innerState.Returned) + { + outerState.Returned = true; + outerState.ReturnValue = innerState.ReturnValue; + return; + } + } + } + else if (step < 0) + { + for (; startVal.Value >= stopVal.Value; startVal.Value = startVal.Value + step) + { + Block.Evaluate(innerScope, diagnostics, ref innerState); + if (innerState.HasBroken) + break; + if (innerState.Returned) + { + outerState.Returned = true; + outerState.ReturnValue = innerState.ReturnValue; + return; + } + } + } + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundReturnStatement.cs b/Upsilon/Binder/BoundStatements/BoundReturnStatement.cs index fb9f0de..46ff7af 100644 --- a/Upsilon/Binder/BoundStatements/BoundReturnStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundReturnStatement.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder { - public class BoundReturnStatement : BoundStatement + public class BoundReturnStatement : BoundStatement { public BoundReturnStatement(BoundExpression expression, TextSpan span) : base(span) { @@ -17,5 +18,12 @@ namespace Upsilon.Binder { yield return Expression; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + state.ReturnValue = Expression?.Evaluate(scope, diagnostics, ref state); + state.Returned = true; + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundScript.cs b/Upsilon/Binder/BoundStatements/BoundScript.cs index e807895..2646257 100644 --- a/Upsilon/Binder/BoundStatements/BoundScript.cs +++ b/Upsilon/Binder/BoundStatements/BoundScript.cs @@ -26,5 +26,10 @@ namespace Upsilon.Binder public BoundBlockStatement Statement { get; } public BoundScope Scope { get; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + Statement.Evaluate(scope, diagnostics, ref state); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundStatement.cs b/Upsilon/Binder/BoundStatements/BoundStatement.cs index d5d71ad..c9c59fd 100644 --- a/Upsilon/Binder/BoundStatements/BoundStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundStatement.cs @@ -1,3 +1,4 @@ +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -9,5 +10,7 @@ namespace Upsilon.Binder } internal bool HasBreakpoint { get; set; } + + internal abstract void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state); } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundTableAssigmentStatement.cs b/Upsilon/Binder/BoundStatements/BoundTableAssigmentStatement.cs index e0a16d7..a2a9f0a 100644 --- a/Upsilon/Binder/BoundStatements/BoundTableAssigmentStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundTableAssigmentStatement.cs @@ -1,4 +1,8 @@ +using System; using System.Collections.Generic; +using Upsilon.BaseTypes; +using Upsilon.BaseTypes.ScriptTypeInterfaces; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -22,5 +26,30 @@ namespace Upsilon.Binder yield return TableIndexExpression; yield return Value; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + ScriptType table; + ScriptType index; + if (TableIndexExpression.Kind == BoundKind.BoundIndexExpression) + { + var indexExpression = (BoundIndexExpression)TableIndexExpression; + table = indexExpression.Identifier.Evaluate(scope, diagnostics, ref state); + index = indexExpression.Index.Evaluate(scope, diagnostics, ref state); + } + else + { + var fullStopIndexExpression = (BoundFullStopIndexExpression) TableIndexExpression; + table = fullStopIndexExpression.Expression.Evaluate(scope, diagnostics, ref state); + index = fullStopIndexExpression.Index.ToScriptType(); + } + + var value = Value.Evaluate(scope, diagnostics, ref state); + if (!(table is IIndexable indexable)) + { + throw new Exception("Cant assign to that"); + } + indexable.Set(diagnostics, Span, index.ToString().ToScriptType(), value); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundVariableAssignment.cs b/Upsilon/Binder/BoundStatements/BoundVariableAssignment.cs index bf20c88..104d7fa 100644 --- a/Upsilon/Binder/BoundStatements/BoundVariableAssignment.cs +++ b/Upsilon/Binder/BoundStatements/BoundVariableAssignment.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -23,5 +24,19 @@ namespace Upsilon.Binder { yield return BoundExpression; } + + internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + var val = BoundExpression.Evaluate(scope, diagnostics, ref state); + if (IsLocalDefinition) + { + scope.CreateLocal(Variable.VariableSymbol, val); + } + else + { + scope.AssignToNearest(Variable.VariableSymbol, val); + } + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundWhileStatement.cs b/Upsilon/Binder/BoundStatements/BoundWhileStatement.cs index e5ef10f..6b261ab 100644 --- a/Upsilon/Binder/BoundStatements/BoundWhileStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundWhileStatement.cs @@ -1,5 +1,6 @@ -using System; using System.Collections.Generic; +using Upsilon.BaseTypes; +using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.Binder @@ -23,5 +24,30 @@ namespace Upsilon.Binder yield return Condition; yield return Block; } + + internal override void Evaluate(EvaluationScope outerScope, Diagnostics diagnostics, ref EvaluationState outerState) + { + var innerScope = new EvaluationScope(outerScope); + var innerState = new EvaluationState() + { + Script = outerState.Script + }; + + + while ((ScriptBoolean) Condition.Evaluate(innerScope, diagnostics, ref innerState)) + { + Block.Evaluate(innerScope, diagnostics, ref innerState); + if (innerState.HasBroken || innerState.Returned) + { + if (innerState.Returned) + { + outerState.Returned = true; + outerState.ReturnValue = innerState.ReturnValue; + } + break; + } + } + + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundVariableSymbol.cs b/Upsilon/Binder/BoundVariableSymbol.cs index e3533b8..28f5eb5 100644 --- a/Upsilon/Binder/BoundVariableSymbol.cs +++ b/Upsilon/Binder/BoundVariableSymbol.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using Upsilon.BaseTypes; using Upsilon.Binder.VariableSymbols; +using Upsilon.Evaluator; +using Upsilon.Exceptions; using Upsilon.Text; namespace Upsilon.Binder @@ -27,6 +29,18 @@ namespace Upsilon.Binder yield break; } - public override TypeContainer Type => VariableSymbol.TypeContainer; + public override TypeContainer ValueType => VariableSymbol.TypeContainer; + + + internal override ScriptType Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state) + { + if (scope.TryGet(VariableSymbol, out var val)) + { + return val; + } + + throw new EvaluationException(state.Script.FileName, $"Cannot find variable: '{VariableSymbol.Name}'", + Span); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs index 943440c..68540f9 100644 --- a/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs @@ -36,14 +36,14 @@ namespace Upsilon.Binder.VariableSymbols { var functionParameter = option.FunctionParameters[i]; var callingParameter = callingParameters[i]; - if (callingParameter.Type == Type.Unknown || callingParameter.Type == Type.Nil) + if (callingParameter.ValueType == Type.Unknown || callingParameter.ValueType == Type.Nil) { continue; } if (functionParameter.ValidTypes == Type.Unknown) continue; - if (!functionParameter.ValidTypes.Type.HasFlag(callingParameter.Type)) + if (!functionParameter.ValidTypes.Type.HasFlag(callingParameter.ValueType)) { isValid = false; break; @@ -85,7 +85,7 @@ namespace Upsilon.Binder.VariableSymbols { var functionParameter = option.FunctionParameters[i]; var callingParameter = parameters[i]; - if (!functionParameter.ValidTypes.Type.HasFlag(callingParameter.Type)) + if (!functionParameter.ValidTypes.Type.HasFlag(callingParameter.ValueType)) { isValid = false; break; diff --git a/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs index f019cd6..b689c4d 100644 --- a/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs @@ -27,11 +27,11 @@ namespace Upsilon.Binder.VariableSymbols var functionParameter = option.Parameters[i]; var callingParameter = callingParameters[i]; if (functionParameter.TypeContainer == Type.Unknown || - callingParameter.Type == Type.Unknown || - callingParameter.Type == Type.Nil) + callingParameter.ValueType == Type.Unknown || + callingParameter.ValueType == Type.Nil) continue; - if (!functionParameter.TypeContainer.Type.HasFlag(callingParameter.Type)) + if (!functionParameter.TypeContainer.Type.HasFlag(callingParameter.ValueType)) { isValid = false; break; @@ -46,7 +46,7 @@ namespace Upsilon.Binder.VariableSymbols if (string.IsNullOrEmpty(userData)) continue; if (!string.Equals(userData, - callingParameter.Type.UserData)) + callingParameter.ValueType.UserData)) { isValid = false; break; diff --git a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs index 253da1d..39a47fb 100644 --- a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs +++ b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs @@ -242,11 +242,11 @@ namespace Upsilon.BoundTypes { var functionParameter = Parameters[i]; var callingParameter = callingParameters[i]; - if (callingParameter.Type == Type.Unknown || callingParameter.Type == Type.Nil || + if (callingParameter.ValueType == Type.Unknown || callingParameter.ValueType == Type.Nil || functionParameter.Type == Type.Unknown) continue; - if (!functionParameter.Type.Type.HasFlag(callingParameter.Type)) + if (!functionParameter.Type.Type.HasFlag(callingParameter.ValueType)) { return false; } diff --git a/Upsilon/Evaluator/EvaluationState.cs b/Upsilon/Evaluator/EvaluationState.cs new file mode 100644 index 0000000..90af54b --- /dev/null +++ b/Upsilon/Evaluator/EvaluationState.cs @@ -0,0 +1,13 @@ +using Upsilon.BaseTypes; + +namespace Upsilon.Evaluator +{ + internal struct EvaluationState + { + public Script Script; + public bool Returned; + public bool HasBroken; + public ScriptType ReturnValue; + public ScriptType LastValue; + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 3aae2be..c624e20 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -1,15 +1,8 @@ using System; -using System.Collections.Generic; using System.Linq; -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; @@ -18,22 +11,11 @@ using Type = Upsilon.BaseTypes.Type; namespace Upsilon.Evaluator { - internal class Evaluator : IDisposable + internal class Evaluator { 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 variables, Script script) - { - _diagnostics = diagnostics; - _script = script; - Scope = new EvaluationScope(variables); - } internal Evaluator(Diagnostics diagnostics, EvaluationScope parentScope, Script script) { @@ -52,13 +34,6 @@ namespace Upsilon.Evaluator 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)) @@ -73,10 +48,15 @@ namespace Upsilon.Evaluator } } } - EvaluateNode(e.Statement); - if (_returnValue == null) - return _lastValue; - return _returnValue; + + var evaluationState = new EvaluationState() + { + Script = _script + }; + e.Evaluate(Scope, _diagnostics, ref evaluationState); + if (evaluationState.ReturnValue != null) + return evaluationState.ReturnValue; + return evaluationState.LastValue; } public ScriptType Evaluate(BoundScript e, string functionName, object[] parameters) @@ -102,684 +82,5 @@ namespace Upsilon.Evaluator var result = option.Run(_diagnostics, parameters?.Select(x => x.ToScriptType()).ToArray(), _script, Scope, new TextSpan()); 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 EvaluationException(_script.FileName, "Invalid Unary Operator: " + e.Operator.Kind, e.Span); - } - } - - 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); - } - if (right.Type == Type.String) - { - return new ScriptString(left + right.ToString()); - } - } - 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); - } - else if (left.Type == Type.UserData) - { - var ud = (GenericUserData) left; - var (type, failed) = ud.BinaryOperator(left, OperatorType.Modulo, right); - if (failed) goto default; - return type; - } - 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(_script.FileName, 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 EvaluationException(_script.FileName, $"Cannot find variable: '{e.Variable.VariableSymbol.Name}'", - e.Span); - } - - private readonly Stack _todoStatements = new Stack(); - - 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 = (ScriptRuntimeFunction)EvaluateBoundFunctionStatement(e.Func); - if (e.Variable.Local) - { - if (Scope.Variables.TryGetValue(e.Variable.Name, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction) - { - scriptRuntimeFunction.AddOption(func.Options[0]); - } - else - { - Scope.CreateLocal(e.Variable, func); - } - } - else - { - if (Scope.TryGet(e.Variable, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction) - { - scriptRuntimeFunction.AddOption(func.Options[0]); - } - else - { - Scope.AssignToNearest(e.Variable, func); - } - } - } - - private ScriptType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression) - { - var option = new ScriptRuntimeFunction.ScriptRuntimeFunctionOption(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope); - var func = new ScriptRuntimeFunction(new List(){option}); - return func; - } - - private ScriptType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression) - { - var option = new ScriptRuntimeFunction.ScriptRuntimeFunctionOption(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope); - var func = new ScriptRuntimeFunction(new List(){option}); - return func; - } - - private ScriptType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression) - { - var variable = EvaluateExpression(boundFunctionCallExpression.Identifier); - if (!(variable is ScriptFunction function)) - { - throw new EvaluationException(_script.FileName, $"Variable is not a function.", - boundFunctionCallExpression.Identifier.Span); - } - var ls = new List(); - 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.Type == Type.Nil) - { - throw new EvaluationException(_script.FileName, $"Nil variable can't be indexed", e.Span); - } - if (!(variable is IIndexable indexable)) - { - throw new EvaluationException(_script.FileName, - $"Variable of type '{variable.Type}' is not indexable.", e.Span); - } - - 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.Type == Type.Nil) - { - throw new EvaluationException(_script.FileName, $"Nil variable can't be indexed", e.Span); - } - if (!(variable is IIndexable indexable)) - { - throw new EvaluationException(_script.FileName, - $"Variable of type '{variable.Type}' is not indexable.", e.Span); - } - - 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].VariableSymbol.Name != "_") - innerEvaluator.Scope.CreateLocal(e.Variables[0].VariableSymbol, table[0].ToScriptType()); - if (e.Variables[1].VariableSymbol.Name != "_") - innerEvaluator.Scope.CreateLocal(e.Variables[1].VariableSymbol, 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; - } - } - } } } \ No newline at end of file diff --git a/Upsilon/Evaluator/Script.cs b/Upsilon/Evaluator/Script.cs index b9fe72f..2818ba1 100644 --- a/Upsilon/Evaluator/Script.cs +++ b/Upsilon/Evaluator/Script.cs @@ -171,7 +171,6 @@ namespace Upsilon.Evaluator public void Dispose() { Binder?.Dispose(); - Evaluator?.Dispose(); Scope?.Dispose(); _parsed = null; _bound = null; diff --git a/Upsilon/StandardLibraries/BasicFunctions.cs b/Upsilon/StandardLibraries/BasicFunctions.cs index d9d2d24..532feba 100644 --- a/Upsilon/StandardLibraries/BasicFunctions.cs +++ b/Upsilon/StandardLibraries/BasicFunctions.cs @@ -58,7 +58,7 @@ namespace Upsilon.StandardLibraries if (variableSymbols.Length != 1) return BaseTypes.Type.Unknown; var parameter = variableSymbols[0]; - return parameter.Type; + return parameter.ValueType; } [ScriptFunction("print", "Prints a message to the action given in the script options", passScriptReference: true, @@ -122,7 +122,7 @@ namespace Upsilon.StandardLibraries if (expressions.Length != 2) return BaseTypes.Type.Unknown; var typeNameExpression = expressions[1]; - if (!(typeNameExpression is BoundLiteralExpression literal) || literal.Type != BaseTypes.Type.String) + if (!(typeNameExpression is BoundLiteralExpression literal) || literal.ValueType != BaseTypes.Type.String) return BaseTypes.Type.Unknown; var boundType = BoundTypeHandler.GetTypeDefinition(((ScriptString) literal.Value)); diff --git a/UpsilonTests/GeneralTests/IfTests.cs b/UpsilonTests/GeneralTests/IfTests.cs index d215419..33dd1c6 100644 --- a/UpsilonTests/GeneralTests/IfTests.cs +++ b/UpsilonTests/GeneralTests/IfTests.cs @@ -31,9 +31,9 @@ return false"; var input = $@" if {condition} then - val = {in1} + return {in1} else - val = {in2} + return {in2} end"; var actual = Executor.EvaluateScript(input, Options); Assert.Equal(expected, actual); @@ -47,11 +47,11 @@ end"; var input = $@" if {condition1} then - val = {in1} + return {in1} elseif {condition2} then - val = {in2} + return {in2} else - val = {in3} + return {in3} end"; var actual = Executor.EvaluateScript(input, Options); Assert.Equal(expected, actual);