public ParsedExpression Parse(string expression) { IList<IExpressionElement> tokens = Tokenize(expression); if (tokens.Count == 0) { throw new ArgumentException("Empty expression."); } List<IExpressionElement> output = new List<IExpressionElement>(); Stack<IExpressionElement> stack = new Stack<IExpressionElement>(); Stack<int> argumentsStack = new Stack<int>(); Stack<bool> insideFunctionStack = new Stack<bool>(); bool afterFunction = false; bool afterFnParenthesis = false; bool afterNonFnParenthesis = false; bool afterClosingParenthesis = false; bool afterSeparator = false; bool insideFunction = false; int arguments = 0; bool mustBeUnary = true; int tokenIndex = -1; foreach (IExpressionElement token in tokens) { tokenIndex++; IExpressionElement nextToken = tokenIndex + 1 < tokens.Count ? tokens[tokenIndex + 1] : null; // If the token is a number, then add it to the output queue. if (!afterFunction && !afterClosingParenthesis && (token is ILiteral || token is IConstant || token is IVariable)) { output.Add(token); mustBeUnary = false; afterClosingParenthesis = afterNonFnParenthesis = afterFnParenthesis = afterSeparator = false; } // If the token is a function token, then push it onto the stack. else if (!afterFunction && !afterClosingParenthesis && (token is IFunction || (token is MultipleElements && (token as MultipleElements).Elements[0] is IFunction))) { stack.Push(token); afterFunction = true; afterClosingParenthesis = afterNonFnParenthesis = afterFnParenthesis = afterSeparator = false; } // If the token is a function argument separator (e.g., a comma): // • Until the topmost element of the stack is a left parenthesis, pop the element onto the output queue. // If no left parentheses are encountered, either the separator was misplaced or parentheses were mismatched. else if (!afterFunction && insideFunction && token == Symbol.FunctionArgumentSeparator) { if (afterFnParenthesis) { throw new ArgumentException("Missing argument."); } while (stack.Peek() != Symbol.LeftParenthesis) { output.Add(stack.Pop()); } if (stack.Peek() != Symbol.LeftParenthesis) { throw new ArgumentException("Either the separator is misplaced or parentheses are mismatched."); } arguments++; afterClosingParenthesis = afterNonFnParenthesis = afterFnParenthesis = false; afterSeparator = true; } // If the token is an operator, o1, then: // • while there is an operator, o2, at the top of the stack, and either // o1 is associative or left-associative and its precedence is less than (lower precedence) or equal to that of o2, or // o1 is right-associative and its precedence is less than (lower precedence) that of o2, // pop o2 off the stack, onto the output queue; // • push o1 onto the stack. else if (!afterFunction && (token is IOperator || (token is MultipleElements && (token as MultipleElements).Elements[0] is IOperator))) { IOperator op = null; if ((mustBeUnary && token is IUnaryOperator) || (!mustBeUnary && token is IBinaryOperator)) { op = token as IOperator; } else if (token is MultipleElements) { foreach (IExpressionElement elem in (token as MultipleElements).Elements) { if ((mustBeUnary && elem is IUnaryOperator) || (!mustBeUnary && elem is IBinaryOperator)) { op = elem as IOperator; break; } } } if (op == null) { throw new ArgumentException(string.Format("{0} is not {1} operator.", token.Name, mustBeUnary ? "an unary" : "a binary")); } IOperator sop = stack.Count > 0 ? stack.Peek() as IOperator : null; while (sop != null && ( ( op is IBinaryOperator && (op as IBinaryOperator).Associativity != OperatorAssociativity.RightAssociative && op.Precedence <= sop.Precedence ) || ( ( (op is IBinaryOperator && (op as IBinaryOperator).Associativity == OperatorAssociativity.RightAssociative) || op is IUnaryOperator ) && op.Precedence < sop.Precedence ))) { output.Add(stack.Pop()); sop = stack.Count > 0 ? stack.Peek() as IOperator : null; } stack.Push(op); mustBeUnary = true; afterClosingParenthesis = afterFnParenthesis = afterNonFnParenthesis = afterSeparator = false; } // If the token is a left parenthesis, then push it onto the stack. else if (!afterClosingParenthesis && token == Symbol.LeftParenthesis) { stack.Push(token); argumentsStack.Push(arguments); insideFunctionStack.Push(insideFunction); // initial argument count arguments = nextToken == Symbol.RightParenthesis ? 0 : 1; if (afterFunction) { afterNonFnParenthesis = afterFunction = false; insideFunction = afterFnParenthesis = true; } else { afterNonFnParenthesis = true; insideFunction = afterFnParenthesis = false; } mustBeUnary = true; afterClosingParenthesis = afterSeparator = false; } // If the token is a right parenthesis: // • Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue. // • Pop the left parenthesis from the stack, but not onto the output queue. // • If the token at the top of the stack is a function token, pop it and onto the output queue. // • If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. else if (!afterFunction && !afterSeparator && token == Symbol.RightParenthesis) { if (afterNonFnParenthesis) { throw new ArgumentException("Unexpected token: '()'."); } if (stack.Count == 0) { throw new ArgumentException("There are mismatched parentheses."); } while (stack.Peek() != Symbol.LeftParenthesis) { output.Add(stack.Pop()); if (stack.Count == 0) { throw new ArgumentException("There are mismatched parentheses."); } } IExpressionElement parenthesis = stack.Pop(); // parenthesis if (stack.Count > 0) { IExpressionElement elem = stack.Peek(); if (elem is IFunction || (elem is MultipleElements && (elem as MultipleElements).Elements[0] is IFunction)) { elem = stack.Pop(); IExpressionElement func = null; if (elem is IFunction) { if ((elem as IFunction).ParametersCount != arguments) { UndefinedFunctionFoundEventArgs args = new UndefinedFunctionFoundEventArgs(elem.Name, arguments); if (UndefinedFunctionFound != null) { UndefinedFunctionFound(this, args); } if (!args.Handled) { throw new ArgumentException(string.Format("There is no function ‘{0}’ with {1} parameters defined.", elem.Name, arguments)); } elem = new UnknownFunction(elem.Name); (elem as UnknownFunction).ParametersCount = arguments; } func = elem; } else { foreach (IFunction f in (elem as MultipleElements).Elements) { if (f.ParametersCount == arguments) { func = f; break; } } if (func == null) { UndefinedFunctionFoundEventArgs args = new UndefinedFunctionFoundEventArgs((elem as MultipleElements).Elements[0].Name, arguments); if (UndefinedFunctionFound != null) { UndefinedFunctionFound(this, args); } if (!args.Handled) { throw new ArgumentException(string.Format("There is no function ‘{0}’ with {1} parameters defined.", args.Name, arguments)); } func = new UnknownFunction(args.Name); (func as UnknownFunction).ParametersCount = arguments; } } output.Add(func); } } arguments = argumentsStack.Count > 0 ? argumentsStack.Pop() : 0; insideFunction = insideFunctionStack.Pop(); afterFnParenthesis = afterNonFnParenthesis = mustBeUnary = afterSeparator = false; afterClosingParenthesis = true; } else { IExpressionElement elem = token; if (token is MultipleElements) { elem = (token as MultipleElements).Elements[0]; } throw new ArgumentException(string.Format("Unexpected token: ‘{0}’", elem.Name)); } } // When there are no more tokens to read: // • While there are still operator tokens in the stack: // • If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses. // • Pop the operator onto the output queue. while (stack.Count > 0) { if (stack.Peek() == Symbol.LeftParenthesis || stack.Peek() == Symbol.RightParenthesis) { var r = stack.Peek(); throw new ArgumentException("There are mismatched parentheses."); } output.Add(stack.Pop()); } return new ParsedExpression(output, ZeroOnError); }
public ParsedExpression Parse(string expression) { IList <IExpressionElement> tokens = Tokenize(expression); if (tokens.Count == 0) { throw new ArgumentException("Empty expression."); } List <IExpressionElement> output = new List <IExpressionElement>(); Stack <IExpressionElement> stack = new Stack <IExpressionElement>(); Stack <int> argumentsStack = new Stack <int>(); Stack <bool> insideFunctionStack = new Stack <bool>(); bool afterFunction = false; bool afterFnParenthesis = false; bool afterNonFnParenthesis = false; bool afterClosingParenthesis = false; bool afterSeparator = false; bool insideFunction = false; int arguments = 0; bool mustBeUnary = true; int tokenIndex = -1; foreach (IExpressionElement token in tokens) { tokenIndex++; IExpressionElement nextToken = tokenIndex + 1 < tokens.Count ? tokens[tokenIndex + 1] : null; // If the token is a number, then add it to the output queue. if (!afterFunction && !afterClosingParenthesis && (token is ILiteral || token is IConstant || token is IVariable)) { output.Add(token); mustBeUnary = false; afterClosingParenthesis = afterNonFnParenthesis = afterFnParenthesis = afterSeparator = false; } // If the token is a function token, then push it onto the stack. else if (!afterFunction && !afterClosingParenthesis && (token is IFunction || (token is MultipleElements && (token as MultipleElements).Elements[0] is IFunction))) { stack.Push(token); afterFunction = true; afterClosingParenthesis = afterNonFnParenthesis = afterFnParenthesis = afterSeparator = false; } // If the token is a function argument separator (e.g., a comma): // • Until the topmost element of the stack is a left parenthesis, pop the element onto the output queue. // If no left parentheses are encountered, either the separator was misplaced or parentheses were mismatched. else if (!afterFunction && insideFunction && token == Symbol.FunctionArgumentSeparator) { if (afterFnParenthesis) { throw new ArgumentException("Missing argument."); } while (stack.Peek() != Symbol.LeftParenthesis) { output.Add(stack.Pop()); } if (stack.Peek() != Symbol.LeftParenthesis) { throw new ArgumentException("Either the separator is misplaced or parentheses are mismatched."); } arguments++; afterClosingParenthesis = afterNonFnParenthesis = afterFnParenthesis = false; afterSeparator = true; } // If the token is an operator, o1, then: // • while there is an operator, o2, at the top of the stack, and either // o1 is associative or left-associative and its precedence is less than (lower precedence) or equal to that of o2, or // o1 is right-associative and its precedence is less than (lower precedence) that of o2, // pop o2 off the stack, onto the output queue; // • push o1 onto the stack. else if (!afterFunction && (token is IOperator || (token is MultipleElements && (token as MultipleElements).Elements[0] is IOperator))) { IOperator op = null; if ((mustBeUnary && token is IUnaryOperator) || (!mustBeUnary && token is IBinaryOperator)) { op = token as IOperator; } else if (token is MultipleElements) { foreach (IExpressionElement elem in (token as MultipleElements).Elements) { if ((mustBeUnary && elem is IUnaryOperator) || (!mustBeUnary && elem is IBinaryOperator)) { op = elem as IOperator; break; } } } if (op == null) { throw new ArgumentException(string.Format("{0} is not {1} operator.", token.Name, mustBeUnary ? "an unary" : "a binary")); } IOperator sop = stack.Count > 0 ? stack.Peek() as IOperator : null; while (sop != null && ( ( op is IBinaryOperator && (op as IBinaryOperator).Associativity != OperatorAssociativity.RightAssociative && op.Precedence <= sop.Precedence ) || ( ( (op is IBinaryOperator && (op as IBinaryOperator).Associativity == OperatorAssociativity.RightAssociative) || op is IUnaryOperator ) && op.Precedence < sop.Precedence ))) { output.Add(stack.Pop()); sop = stack.Count > 0 ? stack.Peek() as IOperator : null; } stack.Push(op); mustBeUnary = true; afterClosingParenthesis = afterFnParenthesis = afterNonFnParenthesis = afterSeparator = false; } // If the token is a left parenthesis, then push it onto the stack. else if (!afterClosingParenthesis && token == Symbol.LeftParenthesis) { stack.Push(token); argumentsStack.Push(arguments); insideFunctionStack.Push(insideFunction); // initial argument count arguments = nextToken == Symbol.RightParenthesis ? 0 : 1; if (afterFunction) { afterNonFnParenthesis = afterFunction = false; insideFunction = afterFnParenthesis = true; } else { afterNonFnParenthesis = true; insideFunction = afterFnParenthesis = false; } mustBeUnary = true; afterClosingParenthesis = afterSeparator = false; } // If the token is a right parenthesis: // • Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue. // • Pop the left parenthesis from the stack, but not onto the output queue. // • If the token at the top of the stack is a function token, pop it and onto the output queue. // • If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. else if (!afterFunction && !afterSeparator && token == Symbol.RightParenthesis) { if (afterNonFnParenthesis) { throw new ArgumentException("Unexpected token: '()'."); } if (stack.Count == 0) { throw new ArgumentException("There are mismatched parentheses."); } while (stack.Peek() != Symbol.LeftParenthesis) { output.Add(stack.Pop()); if (stack.Count == 0) { throw new ArgumentException("There are mismatched parentheses."); } } IExpressionElement parenthesis = stack.Pop(); // parenthesis if (stack.Count > 0) { IExpressionElement elem = stack.Peek(); if (elem is IFunction || (elem is MultipleElements && (elem as MultipleElements).Elements[0] is IFunction)) { elem = stack.Pop(); IExpressionElement func = null; if (elem is IFunction) { if ((elem as IFunction).ParametersCount != arguments) { UndefinedFunctionFoundEventArgs args = new UndefinedFunctionFoundEventArgs(elem.Name, arguments); if (UndefinedFunctionFound != null) { UndefinedFunctionFound(this, args); } if (!args.Handled) { throw new ArgumentException(string.Format("There is no function ‘{0}’ with {1} parameters defined.", elem.Name, arguments)); } elem = new UnknownFunction(elem.Name); (elem as UnknownFunction).ParametersCount = arguments; } func = elem; } else { foreach (IFunction f in (elem as MultipleElements).Elements) { if (f.ParametersCount == arguments) { func = f; break; } } if (func == null) { UndefinedFunctionFoundEventArgs args = new UndefinedFunctionFoundEventArgs((elem as MultipleElements).Elements[0].Name, arguments); if (UndefinedFunctionFound != null) { UndefinedFunctionFound(this, args); } if (!args.Handled) { throw new ArgumentException(string.Format("There is no function ‘{0}’ with {1} parameters defined.", args.Name, arguments)); } func = new UnknownFunction(args.Name); (func as UnknownFunction).ParametersCount = arguments; } } output.Add(func); } } arguments = argumentsStack.Count > 0 ? argumentsStack.Pop() : 0; insideFunction = insideFunctionStack.Pop(); afterFnParenthesis = afterNonFnParenthesis = mustBeUnary = afterSeparator = false; afterClosingParenthesis = true; } else { IExpressionElement elem = token; if (token is MultipleElements) { elem = (token as MultipleElements).Elements[0]; } throw new ArgumentException(string.Format("Unexpected token: ‘{0}’", elem.Name)); } } // When there are no more tokens to read: // • While there are still operator tokens in the stack: // • If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses. // • Pop the operator onto the output queue. while (stack.Count > 0) { if (stack.Peek() == Symbol.LeftParenthesis || stack.Peek() == Symbol.RightParenthesis) { var r = stack.Peek(); throw new ArgumentException("There are mismatched parentheses."); } output.Add(stack.Pop()); } return(new ParsedExpression(output, ZeroOnError)); }