public JsonTextReader(TextReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            _parser = new TextParser(reader.ReadToEnd());
            _stack = new Stack();
            _stack.Push(ParseMethod);
        }
        private object Parse(TextParser parser)
        {
            Debug.Assert(parser != null);

            //
            // Fault-in an AST strategy if one wasn't supplied thus far.
            //

            if (_output == null)
                _output = new ParserOutput();

            //
            // Parse and return the next object found.
            //

            return NextObject(parser);
        }
        private object Parse(TextParser parser)
        {
            Debug.Assert(parser != null);

            //
            // Get the names from the first row. If no names are found then
            // bail out with a null.
            //

            ICollection nameCollection = (ICollection) ParseRowToArray(parser);

            if (nameCollection == null)
                return null;

            //
            // Turn the values in the names collection into an array of
            // string. Note, we need to handle this quite generically since
            // we cannot assume what objects were returned by the 
            // IParserOutput implementation.
            //

            int i = 0;
            string[] names = new string[nameCollection.Count];

            foreach (object nameValue in nameCollection)
                names[i++] = nameValue.ToString();

            //
            // Finally, build an array of objects, where each object comes
            // from a row, and then return the resulting array.
            //

            IParserOutput output = Output;
            output.StartArray();

            object item = ParseRowToObject(parser, names);

            while (item != null)
            {
                output.ArrayPut(item);
                item = ParseRowToObject(parser, names);
            }

            return output.EndArray();
        }
        /// <summary>
        /// Get the next value. The value can be wrapped in quotes. 
        /// The value can be empty.
        /// </summary>

        private string GetValue(TextParser parser) 
        {
            char ch;
            
            do 
            {
                ch = parser.Next();
            } 
            while (ch <= ' ' && ch != TextParser.EOF);

            switch (ch) 
            {
                case TextParser.EOF:
                    return null;
                case '"':
                case '\'':
                    return parser.NextString(ch);
                case ',':
                    parser.Back();
                    return "";
                default:
                    parser.Back();
                    return parser.NextTo(',');
            }
        }
        /// <summary>
        /// Parses the values from a row into an object given a set of names
        /// to use for the members.
        /// </summary>

        private object ParseRowToObject(TextParser parser, string[] names)
        {
            Debug.Assert(parser != null);

            if (names == null)
                return null;

            ICollection collection = (ICollection) ParseRowToArray(parser);
            
            if (collection == null)
                return null;
            
            object[] values = new object[collection.Count];
            collection.CopyTo(values, 0);

            IParserOutput output = Output;
            output.StartObject();

            for (int i = 0; i < Math.Min(names.Length, values.Length); i++)
                output.ObjectPut(names[i], values[i]);

            return output.EndObject();
        }
        private object ParseRowToArray(TextParser parser) 
        {
            Debug.Assert(parser != null);

            string value = GetValue(parser);

            if (value == null)
                return null;

            IParserOutput output = Output;
            output.StartArray();

            do
            {
                output.ArrayPut(value);

                while (true)
                {
                    char ch = parser.Next();

                    if (ch == ',') 
                    {
                        break;
                    }
                    else if (ch != ' ') 
                    {
                        if (ch == '\n' || ch == '\r' || ch == TextParser.EOF) 
                            return output.EndArray();

                        throw new ParseException("Bad character '" + ch.ToString() + "' (" + ((int) ch).ToString(CultureInfo.InvariantCulture) + ").");
                    }
                }

                value = GetValue(parser);
            }
            while (value != null);

            return output.EndArray();
        }
        /// <summary>
        /// Get the next char in the string, skipping whitespace
        /// and comments (slashslash and slashstar).
        /// </summary>
        /// <returns>A character, or 0 if there are no more characters.</returns>
        
        private char NextClean(TextParser parser)
        {
            Debug.Assert(parser != null);

            while (true) 
            {
                char ch = parser.Next();

                if (ch == '/') 
                {
                    switch (parser.Next()) 
                    {
                        case '/' :
                        {
                            do 
                            {
                                ch = parser.Next();
                            } 
                            while (ch != '\n' && ch != '\r' && ch != EOF);
                            break;
                        }
                        case '*' :
                        {
                            while (true) 
                            {
                                ch = parser.Next();

                                if (ch == EOF) 
                                    throw new ParseException("Unclosed comment.");

                                if (ch == '*') 
                                {
                                    if (parser.Next() == '/') 
                                        break;
                                    
                                    parser.Back();
                                }
                            }
                            break;
                        }
                        default :
                        {
                            parser.Back();
                            return '/';
                        }
                    }
                } 
                else if (ch == EOF || ch > ' ') 
                {
                    return ch;
                }
            }
        }
        private object ParseObject(TextParser parser)
        {
            Debug.Assert(parser != null);

            if (parser.Next() == '%')
                parser.Restart(Unescape(parser.Source), parser.Index);

            parser.Back();

            if (NextClean(parser) != '{') 
                throw new ParseException("An object must begin with '{'.");

            _output.StartObject();

            char ch = NextClean(parser);

            while (ch != '}')
            {
                string memberName;

                switch (ch) 
                {
                    case JsonParser.EOF :
                        throw new ParseException("An object must end with '}'.");
                    
                    case '}' :
                        continue;
                    
                    default :
                    {
                        parser.Back();
                        memberName = NextObject(parser).ToString();
                        break;
                    }
                }

                if (NextClean(parser) != ':') 
                    throw new ParseException("Expected a ':' after a key.");

                object memberValue = NextObject(parser);

                _output.ObjectPut(memberName, memberValue);

                switch (ch = NextClean(parser))
                {
                    case ',' :
                    {
                        if ((ch = NextClean(parser)) == '}') 
                            continue;

                        parser.Back();
                        break;
                    }
                    
                    case '}' :
                        continue;

                    default :
                        throw new ParseException("Expected a ',' or '}'.");
                }

                ch = NextClean(parser);
            }

            return _output.EndObject();
        }
        private object ParseArray(TextParser parser)
        {
            Debug.Assert(parser != null);

            if (NextClean(parser) != '[') 
                throw new ParseException("An array must start with '['.");

            _output.StartArray();
            
            if (NextClean(parser) != ']') 
            {
                parser.Back();

                bool end = false;

                do
                {
                    _output.ArrayPut(NextObject(parser));

                    switch (NextClean(parser)) 
                    {
                        case ',' :
                        {
                            if (NextClean(parser) == ']') 
                                end = true;
                            else
                                parser.Back();

                            break;
                        }

                        case ']' :
                        {
                            end = true; 
                            break;
                        }
    					
                        default :
                            throw new ParseException("Expected a ',' or ']'.");
                    }
                }
                while (!end);
            }

            return _output.EndArray();
        }
        /// <summary>
        /// Gets the next value as object. The value can be a Boolean, a Double,
        /// an Integer, an object array, a JObject, a String, or 
        /// JObject.NULL.
        /// </summary>
        
        private object NextObject(TextParser parser)
        {
            Debug.Assert(parser != null);

            char ch = NextClean(parser);

            //
            // String
            //

            if (ch == '"' || ch == '\'') 
                return _output.ToStringPrimitive(parser.NextString(ch));

            //
            // Object
            //

            if (ch == '{') 
            {
                parser.Back();
                return ParseObject(parser);
            }

            //
            // JSON Array
            //

            if (ch == '[')
            {
                parser.Back();
                return ParseArray(parser);
            }

            StringBuilder sb = new StringBuilder();
            char b = ch;
            
            while (ch >= ' ' && ch != ':' && ch != ',' && ch != ']' && ch != '}' && ch != '/')
            {
                sb.Append(ch);
                ch = parser.Next();
            }

            parser.Back();

            string s = sb.ToString().Trim();
            
            if (s == "true")
                return _output.TruePrimitive;
            
            if (s == "false")
                return _output.FalsePrimitive;

            if (s == "null")
                return _output.NullPrimitive;
            
            if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') 
            {
                object number = _output.ToNumberPrimitive(s);

                if (number == null)
                    throw new ParseException(string.Format("Cannot convert '{0}' to a number.", s));

                return number;
            }

            if (s.Length == 0)
                throw new ParseException("Missing value.");

            return s;
        }