// Parses a parenthesized expression.
        private static CalcObject ParseParentheses(List <CLObjectPiece> pieces)
        {
            // First, get the "(" that spawned this method.
            CLObjectPiece lpar = pieces[0];

            pieces.RemoveAt(0);

            CalcObject ret = ParseChain(pieces);

            // If we now have an empty expression, we never closed the "(".
            if (pieces.Count == 0)
            {
                throw new CLSyntaxException("Unmatched (", lpar.Position);
            }
            else
            {
                // Otherwise, we should have a ")" next.
                CLObjectPiece rpar = pieces[0];
                pieces.RemoveAt(0);
                if (rpar.Contents != ")")
                {
                    // Maybe it's a "]", "}", or "," though.
                    throw new CLSyntaxException("Unmatched ( and " + rpar.Contents, lpar.Position);
                }
            }

            // But if we've made it here, we're fine.
            return(ret);
        }
        // Parses a list.
        private static CalcListExpression ParseList(List <CLObjectPiece> pieces)
        {
            // First, get the "[" that spawned this method.
            CLObjectPiece lbracket = pieces[0];

            pieces.RemoveAt(0);

            List <CalcObject> ret = new List <CalcObject>();

            // Also, we'll allow empty lists.
            CLObjectPiece next = pieces[0];

            if (next.Contents == "]")
            {
                pieces.RemoveAt(0);
                return(new CalcListExpression(ret.ToArray()));
            }

            // But anyway, let's start populating non-empty lists.
            while (pieces.Count != 0)
            {
                // Get the value
                CalcObject obj = ParseChain(pieces);

                // But no empty values are allowed
                if (obj == null)
                {
                    throw new CLSyntaxException("Value expected in list", pieces[0].Position);
                }

                // Add it to the list
                ret.Add(obj);

                // Now get the next bracket
                next = pieces[0];
                pieces.RemoveAt(0);

                // If it's a ")" or "}", error.
                if (next.Contents == ")" || next.Contents == "}")
                {
                    throw new CLSyntaxException("Unclosed [ and " + next.Contents, lbracket.Position);
                }

                // If it's a "]", we're done.
                if (next.Contents == "]")
                {
                    return(new CalcListExpression(ret.ToArray()));
                }

                // Otherwise, keep looping.
            }

            // Oh, if we're out of pieces...
            throw new CLSyntaxException("Unclosed [", lbracket.Position);
        }
        /// <summary>
        /// The method that turns a <c>List</c> of pieces into a full tree of
        ///   <c>CalcObject</c>s.
        /// </summary>
        /// <param name="pieces">The list of pieces.</param>
        public static CalcObject Parse(List <CLObjectPiece> pieces)
        {
            CalcObject obj = ParseChain(pieces);

            if (pieces.Count > 0)
            {
                CLObjectPiece piece = pieces[0];
                throw new CLSyntaxException("Unmatched " + piece.Contents, piece.Position);
            }

            return(obj);
        }
        // And this method parses Functions, including Code Functions.
        private static CalcFunction ParseFunction(List <CLObjectPiece> pieces)
        {
            // As with the other two methods, we'll get and store the opener.
            CLObjectPiece lbrace = pieces[0];

            pieces.RemoveAt(0);

            string opener = lbrace.Contents;

            List <CalcObject> pars    = new List <CalcObject>();
            Match             mtcName = rgxName.Match(opener);
            string            name    = mtcName.Groups[1].Value;

            // And again, no-param functions are allowed.
            CLObjectPiece next = pieces[0];

            if (next.Contents == "}")
            {
                pieces.RemoveAt(0);
                return(new CalcFunction(name, pars.ToArray()));
            }

            // If the next piece is a comma, let's just get rid of it.
            if (next.Contents == ",")
            {
                pieces.RemoveAt(0);
            }

            // And parse through the params.
            while (pieces.Count != 0)
            {
                // Get the value
                CalcObject obj = ParseChain(pieces);

                // But no empty values are allowed
                if (obj == null)
                {
                    throw new CLSyntaxException("Value expected in function", pieces[0].Position);
                }

                // Add it to the params
                pars.Add(obj);

                // Now get the next bracket
                next = pieces[0];
                pieces.RemoveAt(0);

                // If it's a ")" or "]", error.
                if (next.Contents == ")" || next.Contents == "]")
                {
                    throw new CLSyntaxException("Unclosed { and " + next.Contents, lbrace.Position);
                }

                // If it's a "}", we're done.
                if (next.Contents == "}")
                {
                    return(new CalcFunction(name, pars.ToArray()));
                }

                // Otherwise, keep looping.
            }

            // Oh, if we're out of pieces...
            throw new CLSyntaxException("Unclosed {", lbrace.Position);
        }
        // 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());
            }
        }