context: #region License, Terms and Conditions
  //
  // Jayrock - JSON and JSON-RPC for Microsoft .NET Framework and Mono
  // Written by Atif Aziz (www.raboof.com)
  // Copyright (c) 2005 Atif Aziz. All rights reserved.
  //
  // This library is free software; you can redistribute it and/or modify it under
  // the terms of the GNU Lesser General Public License as published by the Free
  // Software Foundation; either version 2.1 of the License, or (at your option)
  // any later version.
  //
  // This library is distributed in the hope that it will be useful, but WITHOUT
  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  // details.
  //
  // You should have received a copy of the GNU Lesser General Public License
  // along with this library; if not, write to the Free Software Foundation, Inc.,
  // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  //
  #endregion
  
  namespace Jayrock.Json
  {
      #region Imports
  
      using System;
      using System.Collections;
      using System.Diagnostics;
      using System.Globalization;
      using System.IO;
      using System.Text;
  
      #endregion
  
      /// <summary>
      /// Represents a reader that provides fast, non-cached, forward-only 
      /// access to JSON data over JSON text (RFC 4627). 
      /// </summary>
  
      public sealed class JsonTextReader : JsonReaderBase
      {
          private static readonly char[] _numNonDigitChars = new char[] { '.', 'e', 'E', '+', '-'};
  
          private BufferedCharReader _reader;
          private Stack _stack;
  
          private delegate JsonToken Continuation();
  
          private Continuation _methodParse;
          private Continuation _methodParseArrayFirst;
          private Continuation _methodParseArrayNext;
          private Continuation _methodParseObjectMember;
          private Continuation _methodParseObjectMemberValue;
          private Continuation _methodParseNextMember;
  
          public JsonTextReader(TextReader reader)
          {
              if (reader == null)
                  throw new ArgumentNullException("reader");
  
              _reader = new BufferedCharReader(reader);
              Push(ParseMethod);
          }
  
          public int LineNumber { get { return _reader.LineNumber; } }
          public int LinePosition { get { return _reader.LinePosition; } }
  
          /// <summary>
          /// Reads the next token and returns it.
          /// </summary>
  
          protected override JsonToken ReadTokenImpl()
          {
              if (_stack == null)
              {
                  return JsonToken.EOF();
              }
              else if (_stack.Count == 0)
              {
                  _stack = null;
                  _reader = null;
                  return JsonToken.EOF();
              }
              else
              {
                  return Pop()();
              }
          }
  
          /// <summary>
          /// Parses the next token from the input and returns it.
          /// </summary>
  
          private JsonToken Parse()
          {
              char ch = NextClean();
  
              //
              // String
              //
  
              if (ch == '"' || ch == '\'')
              {
                  return Yield(JsonToken.String(NextString(ch)));
              }
  
              //
              // Object
              //
  
              if (ch == '{')
              {
                  _reader.Back();
                  return ParseObject();
              }
  
              //
              // Array
              //
  
              if (ch == '[')
              {
                  _reader.Back();
                  return ParseArray();
              }
  
              //
              // Handle unquoted text. This could be the values true, false, or
              // null, or it can be a number. An implementation (such as this one)
              // is allowed to also accept non-standard forms.
              //
              // Accumulate characters until we reach the end of the text or a
              // formatting character.
              // 
  
              StringBuilder sb = new StringBuilder();
              char b = ch;
  
              while (ch >= ' ' && ",:]}/\\\"[{;=#".IndexOf(ch) < 0) 
              {
                  sb.Append(ch);
                  ch = _reader.Next();
              }
  
              _reader.Back();
  
              string s = sb.ToString().Trim();
  
              if (s.Length == 0)
                  throw SyntaxError("Missing value.");
              
              
              //
              // Boolean
              //
  
              if (s == JsonBoolean.TrueText || s == JsonBoolean.FalseText)
                  return Yield(JsonToken.Boolean(s == JsonBoolean.TrueText));
              
              //
              // Null
              //
  
              if (s == JsonNull.Text)
                  return Yield(JsonToken.Null());
              
              //
              // Number
              //
              // Try converting it. We support the 0- and 0x- conventions. 
              // If a number cannot be produced, then the value will just
              // be a string. Note that the 0-, 0x-, plus, and implied 
              // string conventions are non-standard, but a JSON text parser 
              // is free to accept non-JSON text forms as long as it accepts 
              // all correct JSON text forms.
              //
  
              if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+')
              {
                  if (b == '0' && s.Length > 1 && s.IndexOfAny(_numNonDigitChars) < 0)
                  {
                      if (s.Length > 2 && (s[1] == 'x' || s[1] == 'X'))
                      {
                          string parsed = TryParseHex(s);
                          if (!ReferenceEquals(parsed, s))
                              return Yield(JsonToken.Number(parsed));
                      }
                      else
                      {
                          string parsed = TryParseOctal(s);
                          if (!ReferenceEquals(parsed, s))
                              return Yield(JsonToken.Number(parsed));
                      }
                  }
                  else
                  {
                      double unused;
  
                      if ((b == '-' && s.Length >= 2 && s[1] == 'I') // rule out -Infinity that double parsing allows
                          || !double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out unused))
                          throw SyntaxError(string.Format("The text '{0}' has the incorrect syntax for a number.", s));
  
                      return Yield(JsonToken.Number(s));
                  }
              }
              
              //
              // Treat as String in all other cases, e.g. when unquoted.
              //
  
              return Yield(JsonToken.String(s));
          }
  
          private Continuation ParseMethod
          {
              get
              {
                  if (_methodParse == null) _methodParse = new Continuation(Parse);
                  return _methodParse;
              }
          }
  
          /// <summary>
          /// Parses expecting an array in the source.
          /// </summary>
  
          private JsonToken ParseArray()
          {
              if (NextClean() != '[')
                  throw SyntaxError("An array must start with '['.");
  
              return Yield(JsonToken.Array(), ParseArrayFirstMethod);
          }
  
          /// <summary>
          /// Parses the first element of an array or the end of the array if
          /// it is empty.
          /// </summary>
  
          private JsonToken ParseArrayFirst()
          {
              if (NextClean() == ']')
                  return Yield(JsonToken.EndArray());
  
              _reader.Back();
  
              Push(ParseArrayNextMethod);
              return Parse();
          }
  
          private Continuation ParseArrayFirstMethod
          {
              get
              {
                  if (_methodParseArrayFirst == null) _methodParseArrayFirst = new Continuation(ParseArrayFirst);
                  return _methodParseArrayFirst;
              }
          }
  
          /// <summary>
          /// Parses the next element in the array.
          /// </summary>
  
          private JsonToken ParseArrayNext()
          {
              switch (NextClean())
              {
                  case ';':
                  case ',':
                  {
                      if (NextClean() == ']')
                          return Yield(JsonToken.EndArray());
                      else
                          _reader.Back();
  
                      break;
                  }
  
                  case ']':
                  {
                      return Yield(JsonToken.EndArray());
                  }
  
                  default:
  
gt:                     throw SyntaxError("Expected a ',' or ']'.");