/// <summary> /// Scans for the longest valid EcmaScript identifier /// </summary> /// <returns>identifier</returns> /// <remarks> /// http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf /// /// IdentifierName = /// IdentifierStart | IdentifierName IdentifierPart /// IdentifierStart = /// Letter | '$' | '_' /// IdentifierPart = /// IdentifierStart | Digit /// </remarks> private static string ScanIdentifier(ITextStream scanner) { bool identPart = false; scanner.BeginChunk(); while (true) { char ch = scanner.Peek(); // digits are only allowed after first char // rest can be in head or tail if ((identPart && CharUtility.IsDigit(ch)) || CharUtility.IsLetter(ch) || (ch == '_') || (ch == '$')) { identPart = true; scanner.Pop(); ch = scanner.Peek(); continue; } // get ident string return(scanner.EndChunk()); } }
private static Token <ModelTokenType> ScanNumber(ITextStream scanner) { // store for error cases long numPos = scanner.Index + 1; int numLine = scanner.Line; int numCol = scanner.Column; scanner.BeginChunk(); char ch = scanner.Peek(); bool isNeg = false; if (ch == JsonGrammar.OperatorUnaryPlus) { // consume positive signing (as is extraneous) scanner.Pop(); ch = scanner.Peek(); // reset buffering scanner.BeginChunk(); } else if (ch == JsonGrammar.OperatorUnaryMinus) { // optional minus part scanner.Pop(); ch = scanner.Peek(); isNeg = true; } if (!CharUtility.IsDigit(ch) && ch != JsonGrammar.OperatorDecimalPoint) { // possibly "-Infinity" scanner.EndChunk(); return(null); } // integer part while (!scanner.IsCompleted && CharUtility.IsDigit(ch)) { // consume digit scanner.Pop(); ch = scanner.Peek(); } bool hasDecimal = false; if (!scanner.IsCompleted && (ch == JsonGrammar.OperatorDecimalPoint)) { // consume decimal scanner.Pop(); ch = scanner.Peek(); // fraction part while (!scanner.IsCompleted && CharUtility.IsDigit(ch)) { // consume digit scanner.Pop(); ch = scanner.Peek(); hasDecimal = true; } if (!hasDecimal) { // fractional digits required when '.' present throw new DeserializationException(JsonTokenizer.ErrorIllegalNumber, numPos, numLine, numCol); } } // note the number of significant digits int precision = scanner.ChunkSize; if (hasDecimal) { precision--; } if (isNeg) { precision--; } if (precision < 1) { // missing digits all together throw new DeserializationException(JsonTokenizer.ErrorIllegalNumber, numPos, numLine, numCol); } bool hasExponent = false; // optional exponent part if (!scanner.IsCompleted && (ch == 'e' || ch == 'E')) { // consume 'e' scanner.Pop(); ch = scanner.Peek(); // optional minus/plus part if (!scanner.IsCompleted && ch == JsonGrammar.OperatorUnaryMinus || ch == JsonGrammar.OperatorUnaryPlus) { // consume sign scanner.Pop(); ch = scanner.Peek(); } // exp part while (!scanner.IsCompleted && CharUtility.IsDigit(ch)) { // consume digit scanner.Pop(); ch = scanner.Peek(); hasExponent = true; } if (!hasExponent) { // exponent digits required when 'e' present throw new DeserializationException(JsonTokenizer.ErrorIllegalNumber, numPos, numLine, numCol); } } // specifically check for 0x-style hex numbers if (!scanner.IsCompleted && CharUtility.IsLetter(ch)) { throw new DeserializationException(JsonTokenizer.ErrorIllegalNumber, numPos, numLine, numCol); } // by this point, we have the full number string and know its characteristics string buffer = scanner.EndChunk(); if (!hasDecimal && !hasExponent && precision < 19) { // Integer value decimal number = 0; try{ number = Decimal.Parse( buffer, NumberStyles.Integer, NumberFormatInfo.InvariantInfo); }catch (Exception) { throw new DeserializationException(JsonTokenizer.ErrorIllegalNumber, numPos, numLine, numCol); } if (number >= Int32.MinValue && number <= Int32.MaxValue) { // int most common return(ModelGrammar.TokenPrimitive((int)number)); } if (number >= Int64.MinValue && number <= Int64.MaxValue) { // long more flexible return(ModelGrammar.TokenPrimitive((long)number)); } // decimal most flexible return(ModelGrammar.TokenPrimitive(number)); } else { // Floating Point value double number; try{ number = Double.Parse( buffer, NumberStyles.Float, NumberFormatInfo.InvariantInfo); }catch (Exception) { throw new DeserializationException(JsonTokenizer.ErrorIllegalNumber, numPos, numLine, numCol); } // native EcmaScript number (IEEE-754) return(ModelGrammar.TokenPrimitive(number)); } }