/// <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); }
/// <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); }
/// <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); }
/// <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> /// 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); } } }
/// <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); }
/// <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); }
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); }
/// <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); }
/// <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); }
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); }
public InterpreterScope(InterpreterScope parent) : this() { _parent = parent; Depth = parent.Depth + 1; }
public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result) { return(Expression.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 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); }
/// <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); }
/// <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); }
/// <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); } }
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)); }
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); }
/// <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); }
/// <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); }
public override bool?IsTrue(InterpreterScope scope, out ParseErrorExpression error) { error = null; return(Value); }
public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result) { result = new StringConstantExpression(Value); CopyLocation(result); return(true); }
/// <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); }