static IExpression ParseUntil(MathExpressionReader r, Stack<Operator> opStack, Stack<IExpression> output, Token readUntilToken,
            int startOpStackSize)
        {
            do
            {
                switch (r.CurrentTokenType)
                {
                    case Token.LeftParens:
                        {
                            opStack.Push(Ops[OperationType.LeftParens]);
                            r.ReadToken();
                            var arg = ParseUntil(r, opStack, output, Token.Coma | Token.RightParens,
                                opStack.Count);
                            if (r.CurrentTokenType == Token.Coma)
                                throw new InvalidDataException("Tuples not supported");
                            if (r.CurrentTokenType != Token.RightParens)
                                throw new InvalidDataException("Mismatched parens, missing a closing parens");
                            output.Push(arg);

                            while (opStack.TryPeek(out var stackOp) && stackOp.Type != OperationType.LeftParens)
                            {
                                opStack.Pop();
                                PopOpOperandsAndPushNode(stackOp);
                            }

                            if (opStack.TryPeek(out var leftParens) && leftParens.Type == OperationType.LeftParens)
                                opStack.Pop();
                            else
                                throw new InvalidDataException("Mismatched parens");
                            r.ReadToken();
                            break;
                        }
        /// <summary>
        /// Parses a mathematical expression.
        /// </summary>
        /// <param name="expressionStr">The expression in string to be parsed.</param>
        /// <param name="error">The error message if an exception occured.</param>
        /// <returns>An IExpression that contains the parsed expression.</returns>
        public static IExpression Parse(string expressionStr, out string error)
        {
            var output = new Stack<IExpression>();
            var opStack = new Stack<Operator>();

            var r = new MathExpressionReader(expressionStr);

            try
            {
                r.ReadToken();
                error = null;
                return ParseUntil(r, opStack, output, Token.None, 0);
            }
            catch (Exception e)
            {
                error = e.Message;
                return null;
            }
        }