// Parses a single "operation chain" private static CalcObject ParseChain(List <CLObjectPiece> pieces) { // First, we can't parse an empty list. if (pieces.Count == 0) { throw new CLSyntaxException("Empty list received.", 0); } LinkedList <CLExpressionBuilder> exps = new LinkedList <CLExpressionBuilder>(); CalcObject hold = null; bool valueLast = false; // Loop through all the pieces now. while (pieces.Count != 0) { CLObjectPiece piece = pieces[0]; // Get the next value, if there is one. CalcObject obj = null; bool err = false; // If it's a ()[]}, if (piece.Type == CLObjectPieceType.Spacer) { if (piece.Contents == "(") { if (!valueLast) { obj = ParseParentheses(pieces); } else { err = true; } } else if (piece.Contents == "[") { if (!valueLast) { obj = ParseList(pieces); } else { err = true; } } else /* ], ), }, , */ { // Send control back up a parser level break; } } else if (piece.Type == CLObjectPieceType.FunctionName) { if (!valueLast) { obj = ParseFunction(pieces); } else { err = true; } } else if (piece.Type == CLObjectPieceType.Number) { if (!valueLast) { obj = new CalcNumber(Decimal.Parse(piece.Contents)); pieces.RemoveAt(0); } else { err = true; } } else if (piece.Type == CLObjectPieceType.String) { if (!valueLast) { // Strip the quotes string check = piece.Contents.Substring(1, piece.Contents.Length - 2); // Parse special characters check = check.Replace(@"\\", "\uF000") .Replace(@"\n", "\n") .Replace(@"\t", "\t") .Replace(@"\", "") .Replace("\uF000", @"\"); obj = new CalcString(check); pieces.RemoveAt(0); } else { err = true; } } if (err) { throw new CLSyntaxException("Two consecutive values", piece.Position); } // If there's a value, put it in the most recent expression if (obj != null) { valueLast = true; // If there's no expression, just hold the value. if (exps.Count == 0) { hold = obj; } else { // Put it on the most recent expression CLExpressionBuilder exp = exps.Last.Value; exp.Right = obj; } continue; } // Otherwise, this piece must be an operator. CLExpressionBuilder expNew = new CLExpressionBuilder(); CLOperator op = null; valueLast = false; if (piece.Type == CLObjectPieceType.BinaryOperator) { op = CLOperators.BinaryOperators[piece.Contents]; } else if (piece.Type == CLObjectPieceType.PrefixOperator) { op = CLOperators.PrefixOperators[piece.Contents]; } else if (piece.Type == CLObjectPieceType.PostfixOperator) { op = CLOperators.PostfixOperators[piece.Contents]; } expNew.Operator = op; // If it's the first operator... if (exps.Count == 0) { // ... use the held value if one exists if (hold != null) { expNew.Left = hold; } exps.AddLast(expNew); } // Otherwise... else { // For prefix operators we don't need to check priorities to the // left. They can just stack on in. if (op is CLPrefixOperator) { exps.Last.Value.Right = expNew; exps.AddLast(expNew); } else { CLExpressionBuilder expOld = null; CLExpressionBuilder expNext = null; // This code removes expressions from the stack that are a // higher priority than the one being removed, or that are // postfix. while (exps.Count != 0) { expNext = exps.Last.Value; if ( // The next expression is a postfix expression. expNext.Operator is CLPostfixOperator || // The next expression on the stack is still higher // priority expNext.Operator.Priority > op.Priority || // The next expression on the stack is equal priority, // but evaluated left-to-right (expNext.Operator.Priority == op.Priority && !CLOperators.IsFromRight(op.Priority)) ) { expOld = exps.Last.Value; exps.RemoveLast(); expNext = null; } else { break; } } // The last removed expression becomes the left of this one. // (If there's no such expression, then the right of the next // expression on the stack becomes our left.) if (expOld != null) { expNew.Left = expOld; } else { expNew.Left = expNext.Right; } // Then, this expression becomes the right of the next one. if (expNext != null) { expNext.Right = expNew; } exps.AddLast(expNew); } } pieces.RemoveAt(0); } if (exps.Count == 0) { return(hold); } else { return(exps.First.Value.Build()); } }
public CalcOperation(CalcObject left, CLOperator oper, CalcObject right) { Left = left; Operator = oper; Right = right; }