Ejemplo n.º 1
0
        /// <summary>
        /// Creates an <see cref="InterpreterScope"/> with all variables required for the function call.
        /// </summary>
        public InterpreterScope CreateCaptureScope(InterpreterScope scope)
        {
            var captureScope = new InterpreterScope(scope);

            captureScope.Context = this;

            // only have to capture variables for anonymous functions
            var userFunctionDefinition = this as AnonymousUserFunctionDefinitionExpression;

            if (userFunctionDefinition != null)
            {
                foreach (var captured in userFunctionDefinition.CapturedVariables)
                {
                    captureScope.DefineVariable(captured.Variable, captured.Expression);
                }
            }

            // set the context to the function definition and return the new context
            return(captureScope);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Gets the string parameter from the <paramref name="scope"/> or <see cref="DefaultParameters"/> collections.
        /// </summary>
        /// <param name="scope">The scope.</param>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="parseError">[out] The error that occurred.</param>
        /// <returns>The parameter value, or <c>null</c> if an error occurred.</b></returns>
        protected StringConstantExpression GetStringParameter(InterpreterScope scope, string name, out ExpressionBase parseError)
        {
            var parameter = GetParameter(scope, name, out parseError);

            if (parameter == null)
            {
                return(null);
            }

            var typedParameter = parameter as StringConstantExpression;

            if (typedParameter == null)
            {
                parseError = new ParseErrorExpression(name + " is not a string", parameter);
                return(null);
            }

            parseError = null;
            return(typedParameter);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Gets a parameter from the <paramref name="scope"/> or <see cref="DefaultParameters"/> collections.
        /// </summary>
        /// <param name="scope">The scope.</param>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="parseError">[out] The error that occurred.</param>
        /// <returns>The parameter value, or <c>null</c> if an error occurred.</b></returns>
        protected ExpressionBase GetParameter(InterpreterScope scope, string name, out ExpressionBase parseError)
        {
            var parameter = scope.GetVariable(name);

            if (parameter == null)
            {
                parseError = new ParseErrorExpression("No value provided for " + name + " parameter");
                return(null);
            }

            parseError = null;

            // if it's a variable reference, return the referenced object.
            if (parameter.Type == ExpressionType.VariableReference)
            {
                return(((VariableReferenceExpression)parameter).Expression);
            }

            // WARNING: variable references may still exist within a varargs object
            return(parameter);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            ExpressionBase value = scope.GetVariable(Name);

            if (value == null)
            {
                var func = scope.GetFunction(Name);
                if (func != null)
                {
                    // special wrapper for returning a function as a variable
                    result = new FunctionReferenceExpression(Name);
                    result.CopyLocation(this);
                    return(true);
                }

                result = new UnknownVariableParseErrorExpression("Unknown variable: " + Name, this);
                return(false);
            }

            return(value.ReplaceVariables(scope, out result));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            ExpressionBase value = scope.GetVariable(Name);

            if (value == null)
            {
                var func = scope.GetFunction(Name);
                if (func != null)
                {
                    result = new ParseErrorExpression("Function used like a variable: " + Name, this);
                }
                else
                {
                    result = new ParseErrorExpression("Unknown variable: " + Name, this);
                }

                return(false);
            }

            return(value.ReplaceVariables(scope, out result));
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Merges all varaibles/functions from the provided <see cref="InterpreterScope"/> into this <see cref="InterpreterScope"/>.
        /// </summary>
        /// <param name="source">The <see cref="InterpreterScope"/> to merge variables/functions from.</param>
        internal void Merge(InterpreterScope source)
        {
            if (source._variable.Key != null)
            {
                DefineVariable(source._variable.Key, source._variable.Value);
            }
            else if (source._variables != null)
            {
                foreach (var kvp in source._variables)
                {
                    DefineVariable(kvp.Value.Key, kvp.Value.Value);
                }
            }

            if (source._functions != null)
            {
                foreach (var kvp in source._functions)
                {
                    AddFunction(kvp.Value);
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            ExpressionBase left;

            if (!Left.ReplaceVariables(scope, out left))
            {
                result = left;
                return(false);
            }

            ExpressionBase right;

            if (!Right.ReplaceVariables(scope, out right))
            {
                result = right;
                return(false);
            }

            var comparison = new ComparisonExpression(left, Operation, right);

            CopyLocation(comparison);
            result = comparison;
            return(true);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope"/>.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns><c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result"/> will likely be a <see cref="ParseErrorExpression"/>.</returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            if (!Evaluate(scope, out result))
            {
                return(false);
            }

            if (result == null)
            {
                var functionCall = scope.GetContext <FunctionCallExpression>();
                if (functionCall != null)
                {
                    result = new ParseErrorExpression(Name.Name + " did not return a value", functionCall.FunctionName);
                }
                else
                {
                    result = new ParseErrorExpression(Name.Name + " did not return a value");
                }

                return(false);
            }

            return(true);
        }
Ejemplo n.º 9
0
        private static ExpressionBase GetParameter(InterpreterScope parameterScope, InterpreterScope scope, AssignmentExpression assignment)
        {
            ExpressionBase value = assignment.Value;

            var variable = value as VariableExpression;

            if (variable != null)
            {
                value = scope.GetVariable(variable.Name);

                if (value == null)
                {
                    // could not find variable, fallback to VariableExpression.ReplaceVariables generating an error
                    value = assignment.Value;
                }
                else
                {
                    // when a parameter is assigned to a variable that is an array or dictionary,
                    // assume it has already been evaluated and pass it by reference. this is magnitudes
                    // more performant, and allows the function to modify the data in the container.
                    if (value.Type == ExpressionType.Dictionary || value.Type == ExpressionType.Array)
                    {
                        value = scope.GetVariableReference(variable.Name);
                        assignment.Value.CopyLocation(value);
                        return(value);
                    }
                }
            }

            if (value.IsConstant)
            {
                // already a basic type, do nothing
            }
            else if (value.Type == ExpressionType.FunctionDefinition)
            {
                var anonymousFunction = value as AnonymousUserFunctionDefinitionExpression;
                if (anonymousFunction != null)
                {
                    anonymousFunction.CaptureVariables(parameterScope);
                }
            }
            else
            {
                bool isLogicalUnit = value.IsLogicalUnit;

                // not a basic type, evaluate it
                var assignmentScope = new InterpreterScope(scope)
                {
                    Context = assignment
                };
                if (!value.ReplaceVariables(assignmentScope, out value))
                {
                    var error = (ParseErrorExpression)value;
                    return(new ParseErrorExpression("Invalid value for parameter: " + assignment.Variable.Name, assignment.Value)
                    {
                        InnerError = error
                    });
                }

                value.IsLogicalUnit = isLogicalUnit;
                assignment.Value.CopyLocation(value);
            }

            return(value);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Creates a new scope for calling a function and populates values for parameters passed to the function.
        /// </summary>
        /// <param name="function">The function defining the parameters to populate.</param>
        /// <param name="scope">The outer scope containing the function call.</param>
        /// <param name="error">[out] A <see cref="ParseErrorExpression"/> indicating why constructing the new scope failed.</param>
        /// <returns>The new scope, <c>null</c> if an error occurred - see <paramref name="error"/> for error details.</returns>
        public InterpreterScope GetParameters(FunctionDefinitionExpression function, InterpreterScope scope, out ExpressionBase error)
        {
            var parameterScope = function.CreateCaptureScope(scope);

            // optimization for no parameter function
            if (function.Parameters.Count == 0 && Parameters.Count == 0)
            {
                error = null;
                return(parameterScope);
            }

            // optimization for single parameter function
            if (function.Parameters.Count == 1 && Parameters.Count == 1)
            {
                if (GetSingleParameter(function, parameterScope, out error))
                {
                    return(parameterScope);
                }

                if (error != null)
                {
                    return(null);
                }
            }

            var providedParameters = new List <string>(function.Parameters.Count);

            foreach (var parameter in function.Parameters)
            {
                providedParameters.Add(parameter.Name);
            }

            ArrayExpression varargs = null;

            if (providedParameters.Remove("..."))
            {
                varargs = new ArrayExpression();
                parameterScope.DefineVariable(new VariableDefinitionExpression("varargs"), varargs);
            }

            var parameterCount = providedParameters.Count;

            int  index           = 0;
            bool namedParameters = false;

            foreach (var parameter in Parameters)
            {
                var assignedParameter = parameter as AssignmentExpression;
                if (assignedParameter != null)
                {
                    if (!providedParameters.Remove(assignedParameter.Variable.Name))
                    {
                        if (!function.Parameters.Any(p => p.Name == assignedParameter.Variable.Name))
                        {
                            error = new ParseErrorExpression(String.Format("'{0}' does not have a '{1}' parameter", function.Name.Name, assignedParameter.Variable.Name), parameter);
                            return(null);
                        }

                        error = new ParseErrorExpression(String.Format("'{0}' already has a value", assignedParameter.Variable.Name), assignedParameter.Variable);
                        return(null);
                    }

                    var value = GetParameter(parameterScope, scope, assignedParameter);
                    error = value as ParseErrorExpression;
                    if (error != null)
                    {
                        return(null);
                    }

                    parameterScope.DefineVariable(new VariableDefinitionExpression(assignedParameter.Variable), value);
                    namedParameters = true;
                }
                else
                {
                    if (namedParameters)
                    {
                        error = new ParseErrorExpression("Non-named parameter following named parameter", parameter);
                        return(null);
                    }

                    if (index >= parameterCount && varargs == null)
                    {
                        error = new ParseErrorExpression("Too many parameters passed to function", parameter);
                        return(null);
                    }

                    var variableName = (index < parameterCount) ? function.Parameters.ElementAt(index).Name : "...";

                    assignedParameter = new AssignmentExpression(new VariableExpression(variableName), parameter);
                    var value = GetParameter(parameterScope, scope, assignedParameter);
                    error = value as ParseErrorExpression;
                    if (error != null)
                    {
                        return(null);
                    }

                    if (index < parameterCount)
                    {
                        providedParameters.Remove(variableName);
                        parameterScope.DefineVariable(new VariableDefinitionExpression(variableName), value);
                    }
                    else
                    {
                        varargs.Entries.Add(value);
                    }
                }

                ++index;
            }

            foreach (var parameter in providedParameters)
            {
                ExpressionBase value;
                if (!function.DefaultParameters.TryGetValue(parameter, out value))
                {
                    error = new ParseErrorExpression(String.Format("Required parameter '{0}' not provided", parameter), FunctionName);
                    return(null);
                }

                var assignmentScope = new InterpreterScope(scope)
                {
                    Context = new AssignmentExpression(new VariableExpression(parameter), value)
                };
                if (!value.ReplaceVariables(assignmentScope, out value))
                {
                    error = new ParseErrorExpression(value, this);
                    return(null);
                }

                parameterScope.DefineVariable(new VariableDefinitionExpression(parameter), value);
            }

            error = null;
            return(parameterScope);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            if (Entries.Count == 0)
            {
                result = this;
                return(true);
            }

            var dictScope = new InterpreterScope(scope);

            var entries = new List <DictionaryEntry>();

            foreach (var entry in Entries)
            {
                ExpressionBase key, value;
                key = entry.Key;

                dictScope.Context = new AssignmentExpression(new VariableExpression("@key"), key);

                if (key.Type == ExpressionType.FunctionCall)
                {
                    var expression = (FunctionCallExpression)key;
                    if (!expression.ReplaceVariables(dictScope, out value))
                    {
                        result = value;
                        return(false);
                    }

                    key = value;
                }

                if (!key.ReplaceVariables(dictScope, out key))
                {
                    result = key;
                    return(false);
                }

                switch (key.Type)
                {
                case ExpressionType.StringConstant:
                    dictScope.Context = new AssignmentExpression(new VariableExpression("[" + ((StringConstantExpression)key).Value + "]"), entry.Value);
                    break;

                case ExpressionType.IntegerConstant:
                    dictScope.Context = new AssignmentExpression(new VariableExpression("[" + ((IntegerConstantExpression)key).Value.ToString() + "]"), entry.Value);
                    break;

                default:
                    result = new ParseErrorExpression("Dictionary key must evaluate to a constant", key);
                    return(false);
                }

                if (!entry.Value.ReplaceVariables(dictScope, out value))
                {
                    result = value;
                    return(false);
                }

                if (entries.Exists(e => e.Key == key))
                {
                    StringBuilder builder = new StringBuilder();
                    key.AppendString(builder);
                    builder.Append(" already exists in dictionary");
                    result = new ParseErrorExpression(builder.ToString(), entry.Key);
                    return(false);
                }

                entries.Add(new DictionaryEntry {
                    Key = key, Value = value
                });
            }

            result = new DictionaryExpression {
                Entries = entries
            };
            return(true);
        }
Ejemplo n.º 12
0
        private bool Evaluate(InterpreterScope scope, bool inAssignment, out ExpressionBase result)
        {
            var functionDefinition = scope.GetFunction(FunctionName.Name);

            if (functionDefinition == null)
            {
                if (scope.GetVariable(FunctionName.Name) != null)
                {
                    result = new UnknownVariableParseErrorExpression(FunctionName.Name + " is not a function", FunctionName);
                }
                else
                {
                    result = new UnknownVariableParseErrorExpression("Unknown function: " + FunctionName.Name, FunctionName);
                }

                return(false);
            }

            var functionParametersScope = GetParameters(functionDefinition, scope, out result);

            if (functionParametersScope == null || result is ParseErrorExpression)
            {
                return(false);
            }

            if (functionParametersScope.Depth >= 100)
            {
                result = new ParseErrorExpression("Maximum recursion depth exceeded", this);
                return(false);
            }

            functionParametersScope.Context = this;
            if (inAssignment)
            {
                // in assignment, just replace variables
                functionDefinition.ReplaceVariables(functionParametersScope, out result);

                if (result.Type == ExpressionType.FunctionCall)
                {
                    // if the result is a function call, check for any variable references. it can't be marked
                    // as fully expanded if any variable references are present.
                    var functionCall = (FunctionCallExpression)result;
                    if (!functionCall.Parameters.Any(p => p is VariableReferenceExpression))
                    {
                        functionCall._fullyExpanded = true;
                    }

                    // if there was no change, also mark the source as fully expanded.
                    if (result == this)
                    {
                        _fullyExpanded = true;
                    }

                    // when expanding the parameters, a new functionCall object will be created without a name
                    // location. if that has happened, replace the temporary name object with the real one.
                    if (functionCall.FunctionName.Location.Start.Line == 0 && functionCall.FunctionName.Name == FunctionName.Name)
                    {
                        functionCall.FunctionName = FunctionName;
                    }
                }
            }
            else
            {
                // not in assignment, evaluate the function
                functionDefinition.Evaluate(functionParametersScope, out result);
            }

            var error = result as ParseErrorExpression;

            if (error != null)
            {
                if (error.Location.Start.Line == 0)
                {
                    this.CopyLocation(error);
                }
                result = ParseErrorExpression.WrapError(error, FunctionName.Name + " call failed", FunctionName);
                return(false);
            }

            return(true);
        }
Ejemplo n.º 13
0
 public InterpreterScope(InterpreterScope parent)
     : this()
 {
     _parent = parent;
     Depth   = parent.Depth + 1;
 }
Ejemplo n.º 14
0
 public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
 {
     return(Expression.ReplaceVariables(scope, out result));
 }
Ejemplo n.º 15
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            ExpressionBase left;

            if (!Left.ReplaceVariables(scope, out left))
            {
                result = left;
                return(false);
            }

            ExpressionBase right;

            if (!Right.ReplaceVariables(scope, out right))
            {
                result = right;
                return(false);
            }

            var integerLeft  = left as IntegerConstantExpression;
            var integerRight = right as IntegerConstantExpression;

            switch (Operation)
            {
            case MathematicOperation.Add:
                var stringLeft  = left as StringConstantExpression;
                var stringRight = right as StringConstantExpression;
                if (stringLeft != null)
                {
                    if (stringRight != null)
                    {
                        result = new StringConstantExpression(stringLeft.Value + stringRight.Value);
                        return(true);
                    }

                    if (integerRight != null)
                    {
                        result = new StringConstantExpression(stringLeft.Value + integerRight.Value.ToString());
                        return(true);
                    }
                }
                else if (stringRight != null)
                {
                    if (integerLeft != null)
                    {
                        result = new StringConstantExpression(integerLeft.Value.ToString() + stringRight.Value);
                        return(true);
                    }
                }

                // prefer constants on right
                if (integerLeft != null && integerRight == null)
                {
                    var temp = left;
                    left         = right;
                    right        = temp;
                    integerRight = integerLeft;
                    integerLeft  = null;
                }

                if (integerRight != null)
                {
                    if (integerRight.Value == 0)     // anything plus 0 is itself
                    {
                        result = left;
                        return(true);
                    }

                    if (integerLeft != null)
                    {
                        result = new IntegerConstantExpression(integerLeft.Value + integerRight.Value);
                        return(true);
                    }
                }
                break;

            case MathematicOperation.Subtract:
                if (integerRight != null)
                {
                    if (integerRight.Value == 0)     // anything minus 0 is itself
                    {
                        result = left;
                        return(true);
                    }

                    if (integerLeft != null)
                    {
                        result = new IntegerConstantExpression(integerLeft.Value - integerRight.Value);
                        return(true);
                    }
                }

                break;

            case MathematicOperation.Multiply:
                // prefer constants on right
                if (integerLeft != null && integerRight == null)
                {
                    var temp = left;
                    left         = right;
                    right        = temp;
                    integerRight = integerLeft;
                    integerLeft  = null;
                }

                if (integerRight != null)
                {
                    if (integerRight.Value == 0)     // anything times 0 is 0
                    {
                        result = right;
                        return(true);
                    }

                    if (integerRight.Value == 1)     // anything times 1 is itself
                    {
                        result = left;
                        return(true);
                    }

                    if (integerLeft != null)
                    {
                        result = new IntegerConstantExpression(integerLeft.Value * integerRight.Value);
                        return(true);
                    }
                }
                break;

            case MathematicOperation.Divide:
                if (integerRight != null)
                {
                    if (integerRight.Value == 0)     // division by 0 is impossible
                    {
                        result = new ParseErrorExpression("division by zero", this);
                        return(false);
                    }

                    if (integerRight.Value == 1)     // anything divided by 1 is itself
                    {
                        result = left;
                        return(true);
                    }

                    if (integerLeft != null)
                    {
                        result = new IntegerConstantExpression(integerLeft.Value / integerRight.Value);
                        return(true);
                    }
                }
                break;

            case MathematicOperation.Modulus:
                if (integerRight != null)
                {
                    if (integerRight.Value == 0)     // division by 0 is impossible
                    {
                        result = new ParseErrorExpression("division by zero", this);
                        return(false);
                    }

                    if (integerRight.Value == 1)     // anything modulus 1 is 0
                    {
                        result = new IntegerConstantExpression(0);
                        return(true);
                    }

                    if (integerLeft != null)
                    {
                        result = new IntegerConstantExpression(integerLeft.Value % integerRight.Value);
                        return(true);
                    }
                }
                break;
            }

            var mathematic = new MathematicExpression(left, Operation, right);

            mathematic.Line   = Line;
            mathematic.Column = Column;
            result            = mathematic;
            return(true);
        }
Ejemplo n.º 16
0
 /// <summary>
 /// Replaces the variables in the expression with values from <paramref name="scope"/>.
 /// </summary>
 /// <param name="scope">The scope object containing variable values.</param>
 /// <param name="result">[out] The new expression containing the replaced variables.</param>
 /// <returns><c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result"/> will likely be a <see cref="ParseErrorExpression"/>.</returns>
 public virtual bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
 {
     result = this;
     return(true);
 }
Ejemplo n.º 17
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            if (_fullyExpanded)
            {
                result = this;
                return(true);
            }

            bool hasTrue   = false;
            bool hasFalse  = false;
            bool isChanged = false;

            var updatedConditions = new List <ExpressionBase>(_conditions.Count);

            for (int i = 0; i < _conditions.Count; ++i)
            {
                if (!_conditions[i].ReplaceVariables(scope, out result))
                {
                    return(false);
                }

                // can eliminate true/false now, but not things that evaluate to true/false.
                // (like always_true or always_false) as those may be used to generate explicit alt groups.
                var booleanExpression = result as BooleanConstantExpression;
                if (booleanExpression != null)
                {
                    if (booleanExpression.Value)
                    {
                        hasTrue = true;

                        if (Operation == ConditionalOperation.And)
                        {
                            isChanged = true;
                            continue;
                        }
                    }
                    else
                    {
                        hasFalse = true;

                        if (Operation == ConditionalOperation.Or)
                        {
                            isChanged = true;
                            continue;
                        }
                    }
                }

                isChanged |= !ReferenceEquals(result, _conditions[i]);
                updatedConditions.Add(result);
            }

            bool?logicalResult = null;

            switch (Operation)
            {
            case ConditionalOperation.Not:
                if (hasTrue)
                {
                    logicalResult = false;
                }
                else if (hasFalse)
                {
                    logicalResult = true;
                }
                else
                {
                    result = InvertExpression(updatedConditions[0]);
                    if (result.Type == ExpressionType.ParseError)
                    {
                        return(false);
                    }

                    CopyLocation(result);

                    // InvertExpression may distribute Nots to subnodes, recurse
                    return(result.ReplaceVariables(scope, out result));
                }
                break;

            case ConditionalOperation.Or:
                if (hasTrue)
                {
                    // anything or true is true
                    logicalResult = true;
                }
                else if (hasFalse && updatedConditions.Count == 0)
                {
                    // all conditions were false, entire condition is false
                    logicalResult = false;
                }
                break;

            case ConditionalOperation.And:
                if (hasFalse)
                {
                    // anything and false is false
                    logicalResult = false;
                }
                if (hasTrue && updatedConditions.Count == 0)
                {
                    // all conditions were true, entire condition is true
                    logicalResult = true;
                }
                break;
            }

            if (logicalResult == true)
            {
                result = new BooleanConstantExpression(true);
            }
            else if (logicalResult == false)
            {
                result = new BooleanConstantExpression(false);
            }
            else
            {
                // merge with nested logic when possible
                for (int i = updatedConditions.Count - 1; i >= 0; i--)
                {
                    var conditionalExpression = updatedConditions[i] as ConditionalExpression;
                    if (conditionalExpression != null && conditionalExpression.Operation == Operation)
                    {
                        updatedConditions.RemoveAt(i);
                        updatedConditions.InsertRange(i, conditionalExpression._conditions);
                        isChanged = true;
                    }
                }

                if (!isChanged)
                {
                    _fullyExpanded = true;
                    result         = this;
                    return(true);
                }
                else
                {
                    var newConditionalExpression = new ConditionalExpression(Operation, updatedConditions);
                    newConditionalExpression._fullyExpanded = true;
                    result = newConditionalExpression;
                }
            }

            CopyLocation(result);
            return(true);
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Determines whether the expression evaluates to true for the provided <paramref name="scope" />
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="error">[out] The error that prevented evaluation (or null if successful).</param>
        /// <returns>
        /// The result of evaluating the expression
        /// </returns>
        public override bool?IsTrue(InterpreterScope scope, out ParseErrorExpression error)
        {
            bool?isTrue;

            switch (Operation)
            {
            case ConditionalOperation.And:
                foreach (var condition in _conditions)
                {
                    isTrue = condition.IsTrue(scope, out error);
                    if (error != null)
                    {
                        return(isTrue);
                    }

                    if (isTrue == false)
                    {
                        return(false);
                    }

                    if (isTrue == null)
                    {
                        return(null);
                    }
                }

                error = null;
                return(true);

            case ConditionalOperation.Or:
                foreach (var condition in _conditions)
                {
                    isTrue = condition.IsTrue(scope, out error);
                    if (error != null)
                    {
                        return(isTrue);
                    }

                    if (isTrue == true)
                    {
                        return(true);
                    }

                    if (isTrue == null)
                    {
                        return(null);
                    }
                }

                error = null;
                return(false);

            case ConditionalOperation.Not:
                isTrue = _conditions[0].IsTrue(scope, out error);
                if (isTrue == null)
                {
                    return(null);
                }
                return(!isTrue);

            default:
                error = null;
                return(null);
            }
        }
Ejemplo n.º 19
0
        protected new ExpressionBase Parse(PositionalTokenizer tokenizer)
        {
            ExpressionBase.SkipWhitespace(tokenizer);
            if (tokenizer.NextChar != '(')
            {
                return(ExpressionBase.ParseError(tokenizer, "Expected '(' after function name", Name));
            }
            tokenizer.Advance();

            ExpressionBase.SkipWhitespace(tokenizer);
            if (tokenizer.NextChar != ')')
            {
                do
                {
                    var line   = tokenizer.Line;
                    var column = tokenizer.Column;

                    var parameter = tokenizer.ReadIdentifier();
                    if (parameter.IsEmpty)
                    {
                        return(ExpressionBase.ParseError(tokenizer, "Invalid parameter name", line, column));
                    }

                    var variableDefinition = new VariableDefinitionExpression(parameter.ToString(), line, column);
                    Parameters.Add(variableDefinition);

                    ExpressionBase.SkipWhitespace(tokenizer);

                    if (tokenizer.NextChar == '=')
                    {
                        tokenizer.Advance();
                        ExpressionBase.SkipWhitespace(tokenizer);

                        var value = ExpressionBase.Parse(tokenizer);
                        if (value.Type == ExpressionType.ParseError)
                        {
                            return(ExpressionBase.ParseError(tokenizer, "Invalid default value for " + parameter.ToString(), value));
                        }

                        var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope());
                        scope.Context = new TriggerBuilderContext(); // prevent errors passing memory references as default parameters

                        ExpressionBase evaluated;
                        if (!value.ReplaceVariables(scope, out evaluated))
                        {
                            return(ExpressionBase.ParseError(tokenizer, "Default value for " + parameter.ToString() + " is not constant", evaluated));
                        }

                        DefaultParameters[parameter.ToString()] = evaluated;
                    }
                    else if (DefaultParameters.Count > 0)
                    {
                        return(ExpressionBase.ParseError(tokenizer,
                                                         string.Format("Non-default parameter {0} appears after default parameters", parameter.ToString()), variableDefinition));
                    }

                    if (tokenizer.NextChar == ')')
                    {
                        break;
                    }

                    if (tokenizer.NextChar != ',')
                    {
                        return(ExpressionBase.ParseError(tokenizer, "Expected ',' or ')' after parameter name, found: " + tokenizer.NextChar));
                    }

                    tokenizer.Advance();
                    ExpressionBase.SkipWhitespace(tokenizer);
                } while (true);
            }

            tokenizer.Advance(); // closing parenthesis
            ExpressionBase.SkipWhitespace(tokenizer);

            ExpressionBase expression;

            if (tokenizer.Match("=>"))
            {
                return(ParseShorthandBody(tokenizer));
            }

            if (tokenizer.NextChar != '{')
            {
                return(ExpressionBase.ParseError(tokenizer, "Expected '{' after function declaration", Name));
            }

            tokenizer.Advance();
            ExpressionBase.SkipWhitespace(tokenizer);

            bool seenReturn = false;

            while (tokenizer.NextChar != '}')
            {
                expression = ExpressionBase.Parse(tokenizer);
                if (expression.Type == ExpressionType.ParseError)
                {
                    // the ExpressionTokenizer will capture the error, we should still return the incomplete FunctionDefinition
                    if (tokenizer is ExpressionTokenizer)
                    {
                        break;
                    }

                    // not an ExpressionTokenizer, just return the error
                    return(expression);
                }

                if (expression.Type == ExpressionType.Return)
                {
                    seenReturn = true;
                }
                else if (seenReturn)
                {
                    ExpressionBase.ParseError(tokenizer, "Expression after return statement", expression);
                }

                Expressions.Add(expression);

                ExpressionBase.SkipWhitespace(tokenizer);
            }

            Location = new TextRange(Location.Start, tokenizer.Location);
            tokenizer.Advance();
            return(MakeReadOnly(this));
        }
Ejemplo n.º 20
0
        internal DictionaryExpression.DictionaryEntry GetDictionaryEntry(InterpreterScope scope, out ExpressionBase result, bool create)
        {
            ExpressionBase index;

            if (Index.Type == ExpressionType.FunctionCall)
            {
                var expression = (FunctionCallExpression)Index;
                if (!expression.ReplaceVariables(scope, out index))
                {
                    result = index;
                    return(null);
                }
            }
            else if (!Index.ReplaceVariables(scope, out index))
            {
                result = index;
                return(null);
            }

            ExpressionBase value;
            var            indexed = Variable as IndexedVariableExpression;

            if (indexed != null)
            {
                var entry = indexed.GetDictionaryEntry(scope, out result, create);
                if (entry == null)
                {
                    return(null);
                }

                value = entry.Value;
            }
            else
            {
                var variable = Variable as VariableExpression;
                if (variable != null)
                {
                    value = scope.GetVariable(variable.Name);

                    if (value == null)
                    {
                        result = new ParseErrorExpression("Unknown variable: " + variable.Name, variable);
                        return(null);
                    }
                }
                else if (!Variable.ReplaceVariables(scope, out value))
                {
                    result = value;
                    return(null);
                }
            }

            var dict = value as DictionaryExpression;

            if (dict != null)
            {
                var entry = dict.Entries.FirstOrDefault(e => Object.Equals(e.Key, index));
                if (entry != null)
                {
                    result = dict;
                    return(entry);
                }

                if (create)
                {
                    entry = new DictionaryExpression.DictionaryEntry {
                        Key = index
                    };
                    dict.Entries.Add(entry);
                    result = dict;
                    return(entry);
                }

                var builder = new StringBuilder();
                builder.Append("No entry in dictionary for key: ");
                index.AppendString(builder);
                result = new ParseErrorExpression(builder.ToString(), Index);
            }
            else
            {
                var array = value as ArrayExpression;
                if (array != null)
                {
                    var intIndex = index as IntegerConstantExpression;
                    if (intIndex == null)
                    {
                        result = new ParseErrorExpression("Index does not evaluate to an integer constant", index);
                    }
                    else if (intIndex.Value < 0 || intIndex.Value >= array.Entries.Count)
                    {
                        result = new ParseErrorExpression(String.Format("Index {0} not in range 0-{1}", intIndex.Value, array.Entries.Count - 1), index);
                    }
                    else
                    {
                        result = array;
                        return(new ArrayDictionaryEntryWrapper {
                            Array = array, Key = index, Value = array.Entries[intIndex.Value]
                        });
                    }
                }
                else
                {
                    var builder = new StringBuilder();
                    builder.Append("Cannot index: ");
                    Variable.AppendString(builder);
                    builder.Append(" (");
                    builder.Append(value.Type);
                    builder.Append(')');
                    result = new ParseErrorExpression(builder.ToString(), Variable);
                }
            }

            return(null);
        }
Ejemplo n.º 21
0
 /// <summary>
 /// Determines whether the expression evaluates to true for the provided <paramref name="scope"/>
 /// </summary>
 /// <param name="scope">The scope object containing variable values.</param>
 /// <param name="error">[out] The error that prevented evaluation (or null if successful).</param>
 /// <returns>The result of evaluating the expression</returns>
 public virtual bool?IsTrue(InterpreterScope scope, out ParseErrorExpression error)
 {
     error = null;
     return(null);
 }
Ejemplo n.º 22
0
        /// <summary>
        /// Determines whether the expression evaluates to true for the provided <paramref name="scope" />
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="error">[out] The error that prevented evaluation (or null if successful).</param>
        /// <returns>
        /// The result of evaluating the expression
        /// </returns>
        public override bool IsTrue(InterpreterScope scope, out ParseErrorExpression error)
        {
            ExpressionBase left, right;

            if (!Left.ReplaceVariables(scope, out left))
            {
                error = left as ParseErrorExpression;
                return(false);
            }

            if (!Right.ReplaceVariables(scope, out right))
            {
                error = right as ParseErrorExpression;
                return(false);
            }

            error = null;

            var integerLeft = left as IntegerConstantExpression;

            if (integerLeft != null)
            {
                var integerRight = right as IntegerConstantExpression;
                if (integerRight == null)
                {
                    return(false);
                }

                switch (Operation)
                {
                case ComparisonOperation.Equal:
                    return(integerLeft.Value == integerRight.Value);

                case ComparisonOperation.NotEqual:
                    return(integerLeft.Value != integerRight.Value);

                case ComparisonOperation.GreaterThan:
                    return(integerLeft.Value > integerRight.Value);

                case ComparisonOperation.GreaterThanOrEqual:
                    return(integerLeft.Value >= integerRight.Value);

                case ComparisonOperation.LessThan:
                    return(integerLeft.Value < integerRight.Value);

                case ComparisonOperation.LessThanOrEqual:
                    return(integerLeft.Value <= integerRight.Value);

                default:
                    return(false);
                }
            }

            var stringLeft = left as StringConstantExpression;

            if (stringLeft != null)
            {
                var stringRight = right as StringConstantExpression;
                if (stringRight == null)
                {
                    return(false);
                }

                switch (Operation)
                {
                case ComparisonOperation.Equal:
                    return(stringLeft.Value == stringRight.Value);

                case ComparisonOperation.NotEqual:
                    return(stringLeft.Value != stringRight.Value);

                case ComparisonOperation.GreaterThan:
                    return(String.Compare(stringLeft.Value, stringRight.Value) > 0);

                case ComparisonOperation.GreaterThanOrEqual:
                    return(String.Compare(stringLeft.Value, stringRight.Value) >= 0);

                case ComparisonOperation.LessThan:
                    return(String.Compare(stringLeft.Value, stringRight.Value) < 0);

                case ComparisonOperation.LessThanOrEqual:
                    return(String.Compare(stringLeft.Value, stringRight.Value) <= 0);

                default:
                    return(false);
                }
            }

            return(false);
        }
Ejemplo n.º 23
0
 public override bool?IsTrue(InterpreterScope scope, out ParseErrorExpression error)
 {
     error = null;
     return(Value);
 }
Ejemplo n.º 24
0
 public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
 {
     result = new StringConstantExpression(Value);
     CopyLocation(result);
     return(true);
 }
Ejemplo n.º 25
0
        /// <summary>
        /// Replaces the variables in the expression with values from <paramref name="scope" />.
        /// </summary>
        /// <param name="scope">The scope object containing variable values.</param>
        /// <param name="result">[out] The new expression containing the replaced variables.</param>
        /// <returns>
        ///   <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />.
        /// </returns>
        public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result)
        {
            var newDict = new DictionaryExpression();
            var entries = newDict._entries;

            if (_state == DictionaryState.Unprocessed)
            {
                result = UpdateState();
                if (result != null)
                {
                    return(false);
                }
            }

            // constant dictionary
            if (_state == DictionaryState.ConstantSorted)
            {
                entries.AddRange(_entries);
                result = newDict;
                CopyLocation(result);
                return(true);
            }

            // non-constant dictionary - have to evaluate
            var dictScope = new InterpreterScope(scope);

            foreach (var entry in _entries)
            {
                ExpressionBase key, value;
                key = entry.Key;

                if (!key.IsConstant)
                {
                    dictScope.Context = new AssignmentExpression(new VariableExpression("@key"), key);
                    if (!key.ReplaceVariables(dictScope, out value))
                    {
                        result = value;
                        return(false);
                    }

                    if (!value.IsConstant)
                    {
                        result = new ParseErrorExpression("Dictionary key must evaluate to a constant", key);
                        return(false);
                    }

                    key = value;
                }

                if (entry.Value.IsConstant)
                {
                    value = entry.Value;
                }
                else
                {
                    var builder = new StringBuilder();
                    builder.Append('[');
                    key.AppendString(builder);
                    builder.Append(']');
                    dictScope.Context = new AssignmentExpression(new VariableExpression(builder.ToString()), entry.Value);

                    if (!entry.Value.ReplaceVariables(dictScope, out value))
                    {
                        result = value;
                        return(false);
                    }
                }

                var newEntry = new DictionaryEntry {
                    Key = key, Value = value
                };

                if (_state == DictionaryState.ConstantKeysSorted)
                {
                    entries.Add(newEntry);
                }
                else
                {
                    var index = entries.BinarySearch(newEntry, newEntry);
                    if (index >= 0)
                    {
                        StringBuilder builder = new StringBuilder();
                        key.AppendString(builder);
                        builder.Append(" already exists in dictionary");
                        result = new ParseErrorExpression(builder.ToString(), entry.Key);
                        return(false);
                    }

                    entries.Insert(~index, newEntry);
                }
            }

            newDict._state = DictionaryState.ConstantSorted;
            result         = newDict;
            CopyLocation(result);
            return(true);
        }