/// <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) { // FunctionDefinition.ReplaceVariables is called when evaluating a function for an assignment. // For user functions (see UserFunctionDefinition.ReplaceVariables) - it will just evaluate the // function call and return the result. Several internal functions have very special Evaluate // handling that should not be executed when defining variables. Those functions rely on this // behavior to just evaluate the parameters without calling Evaluate. There are some built-in // functions that should call Evaluate when ReplaceVariables is called. They will override // ReplaceVariables to do that. var parameters = new ExpressionBase[Parameters.Count]; int i = 0; foreach (var parameterName in Parameters) { // do a direct lookup here. calling GetParameter will discard the VariableReference // and we want to preserve those for now. var parameter = scope.GetVariable(parameterName.Name); if (parameter == null) { result = new ParseErrorExpression("No value provided for " + parameterName.Name + " parameter", parameterName); return(false); } parameters[i++] = parameter; } result = new FunctionCallExpression(Name.Name, parameters); CopyLocation(result); return(true); }
public void CaptureVariables(InterpreterScope scope) { // Initialize a new scope object with a FunctionCall context so we can determine which // variables have to be captured. The FunctionCall context will only see the globals. var captureScope = new InterpreterScope(scope); captureScope.Context = new FunctionCallExpression("NonAnonymousFunction", new ExpressionBase[0]); var capturedVariables = new List <VariableReferenceExpression>(); var possibleDependencies = new HashSet <string>(); ((INestedExpressions)this).GetDependencies(possibleDependencies); foreach (var dependency in possibleDependencies) { if (captureScope.GetVariable(dependency) == null) { // the variable is not visible to the function scope. check to see if it's visible // in the calling scope. if it is, create a copy for the function call. var variable = scope.GetVariableReference(dependency); if (variable != null) { capturedVariables.Add(variable); } } } if (capturedVariables.Count > 0) { CapturedVariables = capturedVariables.ToArray(); } }
protected FunctionDefinitionExpression GetFunctionParameter(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); } var functionDefinition = parameter as FunctionDefinitionExpression; if (functionDefinition == null) { var functionReference = parameter as FunctionReferenceExpression; if (functionReference == null) { parseError = new ParseErrorExpression(name + " must be a function reference"); return(null); } functionDefinition = scope.GetFunction(functionReference.Name); if (functionDefinition == null) { parseError = new ParseErrorExpression("Undefined function: " + functionReference.Name); return(null); } } parseError = null; return(functionDefinition); }
/// <summary> /// Gets the variable reference 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 variable reference, or <c>null</c> if an error occurred.</b></returns> protected VariableReferenceExpression GetReferenceParameter(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); } var typedParameter = parameter as VariableReferenceExpression; if (typedParameter == null) { var originalParameter = LocateParameter(scope, name); if (originalParameter != null) { parameter = originalParameter; } parseError = new ParseErrorExpression(name + " is not a reference", parameter); return(null); } parseError = null; return(typedParameter); }
private void GetContainerIndex(InterpreterScope scope, out ExpressionBase container, out ExpressionBase index) { if (Index.Type == ExpressionType.FunctionCall) { var expression = (FunctionCallExpression)Index; if (!expression.ReplaceVariables(scope, out index)) { container = index; return; } } else if (!Index.ReplaceVariables(scope, out index)) { container = index; return; } var indexed = Variable as IndexedVariableExpression; if (indexed != null) { indexed.ReplaceVariables(scope, out container); return; } container = scope.GetVariable(Variable.Name); if (container == null) { container = new UnknownVariableParseErrorExpression("Unknown variable: " + Variable.Name, Variable); return; } var variableReference = container as VariableReferenceExpression; if (variableReference != null) { container = variableReference.Expression; } var array = container as ArrayExpression; if (array != null) { var intIndex = index as IntegerConstantExpression; if (intIndex == null) { container = new ParseErrorExpression("Index does not evaluate to an integer constant", index); } else if (intIndex.Value < 0 || intIndex.Value >= array.Entries.Count) { container = new ParseErrorExpression(String.Format("Index {0} not in range 0-{1}", intIndex.Value, array.Entries.Count - 1), index); } } }
/// <summary> /// Gets the 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; return(parameter); }
/// <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)); }
/// <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)); }
/// <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); }
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); }
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); }
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); }