private static JsonToken GetNumberToken(JsonBuffer buffer, int firstChar)
{
var c = firstChar;
// leading digit or '-' has already been read
var start = buffer.Position - 1;
NumberState state;
switch (c)
{
case '-': state = NumberState.SawLeadingMinus; break;
case '0': state = NumberState.SawLeadingZero; break;
default: state = NumberState.SawIntegerDigits; break;
}
var type = JsonTokenType.Int64; // assume integer until proved otherwise
while (true)
{
c = buffer.Read();
switch (state)
{
case NumberState.SawLeadingMinus:
switch (c)
{
case '0':
state = NumberState.SawLeadingZero;
break;
case 'I':
state = NumberState.SawMinusI;
break;
default:
if (char.IsDigit((char)c))
{
state = NumberState.SawIntegerDigits;
}
else
{
state = NumberState.Invalid;
}
break;
}
break;
case NumberState.SawLeadingZero:
switch (c)
{
case '.':
state = NumberState.SawDecimalPoint;
break;
case 'e':
case 'E':
state = NumberState.SawExponentLetter;
break;
case ',':
case '}':
case ']':
case ')':
case -1:
state = NumberState.Done;
break;
default:
if (char.IsWhiteSpace((char)c))
{
state = NumberState.Done;
}
else
{
state = NumberState.Invalid;
}
break;
}
break;
case NumberState.SawIntegerDigits:
switch (c)
{
case '.':
state = NumberState.SawDecimalPoint;
break;
case 'e':
case 'E':
state = NumberState.SawExponentLetter;
break;
case ',':
case '}':
case ']':
case ')':
case -1:
state = NumberState.Done;
break;
default:
if (char.IsDigit((char)c))
{
state = NumberState.SawIntegerDigits;
}
else if (char.IsWhiteSpace((char)c))
{
state = NumberState.Done;
}
else
{
state = NumberState.Invalid;
}
break;
}
break;
case NumberState.SawDecimalPoint:
type = JsonTokenType.Double;
if (char.IsDigit((char)c))
{
state = NumberState.SawFractionDigits;
}
else
{
state = NumberState.Invalid;
}
break;
case NumberState.SawFractionDigits:
switch (c)
{
case 'e':
case 'E':
state = NumberState.SawExponentLetter;
break;
case ',':
case '}':
case ']':
case ')':
case -1:
state = NumberState.Done;
break;
default:
if (char.IsDigit((char)c))
{
state = NumberState.SawFractionDigits;
}
else if (char.IsWhiteSpace((char)c))
{
state = NumberState.Done;
}
else
{
state = NumberState.Invalid;
}
break;
}
break;
case NumberState.SawExponentLetter:
type = JsonTokenType.Double;
switch (c)
{
case '+':
case '-':
state = NumberState.SawExponentSign;
break;
default:
if (char.IsDigit((char)c))
{
state = NumberState.SawExponentDigits;
}
else
{
state = NumberState.Invalid;
}
break;
}
break;
case NumberState.SawExponentSign:
if (char.IsDigit((char)c))
{
state = NumberState.SawExponentDigits;
}
else
{
state = NumberState.Invalid;
}
break;
case NumberState.SawExponentDigits:
switch (c)
{
case ',':
case '}':
case ']':
case ')':
case -1:
state = NumberState.Done;
break;
default:
if (char.IsDigit((char)c))
{
state = NumberState.SawExponentDigits;
}
else if (char.IsWhiteSpace((char)c))
{
state = NumberState.Done;
}
else
{
state = NumberState.Invalid;
}
break;
}
break;
case NumberState.SawMinusI:
var sawMinusInfinity = true;
var nfinity = new char[] { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
for (var i = 0; i < nfinity.Length; i++)
{
if (c != nfinity[i])
{
sawMinusInfinity = false;
break;
}
c = buffer.Read();
}
if (sawMinusInfinity)
{
type = JsonTokenType.Double;
switch (c)
{
case ',':
case '}':
case ']':
case ')':
case -1:
state = NumberState.Done;
break;
default:
if (char.IsWhiteSpace((char)c))
{
state = NumberState.Done;
}
else
{
state = NumberState.Invalid;
}
break;
}
}
else
{
state = NumberState.Invalid;
}
break;
}
switch (state)
{
case NumberState.Done:
buffer.UnRead(c);
var lexeme = buffer.Substring(start, buffer.Position - start);
if (type == JsonTokenType.Double)
{
var value = XmlConvert.ToDouble(lexeme);
return new DoubleJsonToken(lexeme, value);
}
else
{
var value = XmlConvert.ToInt64(lexeme);
if (value < int.MinValue || value > int.MaxValue)
{
return new Int64JsonToken(lexeme, value);
}
else
{
return new Int32JsonToken(lexeme, (int)value);
}
}
case NumberState.Invalid:
throw new Exception(FormatMessage("Invalid JSON number", buffer, start));
}
}
}