src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java

Print this page


   1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.parser;
  27 
  28 import static jdk.nashorn.internal.parser.TokenType.COLON;
  29 import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT;
  30 import static jdk.nashorn.internal.parser.TokenType.EOF;
  31 import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
  32 import static jdk.nashorn.internal.parser.TokenType.RBRACE;
  33 import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
  34 import static jdk.nashorn.internal.parser.TokenType.STRING;
  35 import java.util.ArrayList;

  36 import java.util.List;
  37 import jdk.nashorn.internal.ir.Expression;
  38 import jdk.nashorn.internal.ir.LiteralNode;
  39 import jdk.nashorn.internal.ir.Node;
  40 import jdk.nashorn.internal.ir.ObjectNode;
  41 import jdk.nashorn.internal.ir.PropertyNode;
  42 import jdk.nashorn.internal.ir.UnaryNode;
  43 import jdk.nashorn.internal.runtime.ErrorManager;



  44 import jdk.nashorn.internal.runtime.Source;
  45 


  46 /**
  47  * Parses JSON text and returns the corresponding IR node. This is derived from the objectLiteral production of the main parser.
  48  *
  49  * See: 15.12.1.2 The JSON Syntactic Grammar
  50  */
  51 public class JSONParser extends AbstractParser {














  52 
  53     /**
  54      * Constructor
  55      * @param source  the source
  56      * @param errors  the error manager
  57      */
  58     public JSONParser(final Source source, final ErrorManager errors) {
  59         super(source, errors, false, 0);

  60     }
  61 
  62     /**
  63      * Implementation of the Quote(value) operation as defined in the ECMA script spec
  64      * It wraps a String value in double quotes and escapes characters within in
  65      *
  66      * @param value string to quote
  67      *
  68      * @return quoted and escaped string
  69      */
  70     public static String quote(final String value) {
  71 
  72         final StringBuilder product = new StringBuilder();
  73 
  74         product.append("\"");
  75 
  76         for (final char ch : value.toCharArray()) {
  77             // TODO: should use a table?
  78             switch (ch) {
  79             case '\\':


  97             case '\t':
  98                 product.append("\\t");
  99                 break;
 100             default:
 101                 if (ch < ' ') {
 102                     product.append(Lexer.unicodeEscape(ch));
 103                     break;
 104                 }
 105 
 106                 product.append(ch);
 107                 break;
 108             }
 109         }
 110 
 111         product.append("\"");
 112 
 113         return product.toString();
 114     }
 115 
 116     /**
 117      * Public parsed method - start lexing a new token stream for
 118      * a JSON script
 119      *
 120      * @return the JSON literal
 121      */
 122     public Node parse() {
 123         stream = new TokenStream();





































 124 
 125         lexer = new Lexer(source, stream) {


 126 
 127             @Override
 128             protected boolean skipComments() {
 129                 return false;
 130             }
 131 
 132             @Override
 133             protected boolean isStringDelimiter(final char ch) {
 134                 return ch == '\"';
 135             }
 136 
 137             // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONWhiteSpace
 138             @Override
 139             protected boolean isWhitespace(final char ch) {
 140                 return Lexer.isJsonWhitespace(ch);
 141             }
 142 
 143             @Override
 144             protected boolean isEOL(final char ch) {
 145                 return Lexer.isJsonEOL(ch);
























 146             }
 147 
 148             // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONNumber
 149             @Override
 150             protected void scanNumber() {
 151                 // Record beginning of number.
 152                 final int startPosition = position;
 153                 // Assume value is a decimal.
 154                 TokenType valueType = TokenType.DECIMAL;
 155 
 156                 // floating point can't start with a "." with no leading digit before
 157                 if (ch0 == '.') {
 158                     error(Lexer.message("json.invalid.number"), STRING, position, limit);
 159                 }
 160 
 161                 // First digit of number.
 162                 final int digit = convertDigit(ch0, 10);
 163 
 164                 // skip first digit
 165                 skip(1);

 166 
 167                 if (digit != 0) {
 168                     // Skip over remaining digits.
 169                     while (convertDigit(ch0, 10) != -1) {
 170                         skip(1);
 171                     }






 172                 }
 173 
 174                 if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') {
 175                     // Must be a double.
 176                     if (ch0 == '.') {
 177                         // Skip period.
 178                         skip(1);
 179 
 180                         boolean mantissa = false;
 181                         // Skip mantissa.
 182                         while (convertDigit(ch0, 10) != -1) {
 183                             mantissa = true;
 184                             skip(1);
 185                         }
 186 
 187                         if (! mantissa) {
 188                             // no digit after "."
 189                             error(Lexer.message("json.invalid.number"), STRING, position, limit);
 190                         }
 191                     }
 192 
 193                     // Detect exponent.
 194                     if (ch0 == 'E' || ch0 == 'e') {
 195                         // Skip E.
 196                         skip(1);
 197                         // Detect and skip exponent sign.
 198                         if (ch0 == '+' || ch0 == '-') {
 199                             skip(1);
 200                         }
 201                         boolean exponent = false;
 202                         // Skip exponent.
 203                         while (convertDigit(ch0, 10) != -1) {
 204                             exponent = true;
 205                             skip(1);










 206                         }



 207 
 208                         if (! exponent) {
 209                             // no digit after "E"
 210                             error(Lexer.message("json.invalid.number"), STRING, position, limit);

 211                         }

 212                     }
 213 
 214                     valueType = TokenType.FLOATING;
 215                 }
 216 
 217                 // Add number token.
 218                 add(valueType, startPosition);
 219             }
 220 
 221             // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONEscapeCharacter
 222             @Override
 223             protected boolean isEscapeCharacter(final char ch) {
 224                 switch (ch) {
 225                     case '"':
 226                     case '/':
 227                     case '\\':



 228                     case 'b':

 229                     case 'f':

 230                     case 'n':

 231                     case 'r':

 232                     case 't':
 233                     // could be unicode escape
 234                     case 'u':
 235                         return true;
 236                     default:
 237                         return false;
 238                 }
 239             }
 240         };
 241 
 242         k = -1;
 243 
 244         next();
 245 
 246         final Node resultNode = jsonLiteral();
 247         expect(EOF);
 248 
 249         return resultNode;









 250     }
 251 
 252     @SuppressWarnings("fallthrough")
 253     private LiteralNode<?> getStringLiteral() {
 254         final LiteralNode<?> literal = getLiteral();
 255         final String         str     = (String)literal.getValue();
 256 
 257         for (int i = 0; i < str.length(); i++) {
 258             final char ch = str.charAt(i);
 259             switch (ch) {
 260             default:
 261                 if (ch > 0x001f) {
 262                     break;
 263                 }
 264             case '"':
 265             case '\\':
 266                 throw error(AbstractParser.message("unexpected.token", str));
 267             }
 268         }
 269 
 270         return literal;
 271     }
 272 
 273     /**
 274      * Parse a JSON literal from the token stream
 275      * @return the JSON literal as a Node
 276      */
 277     private Expression jsonLiteral() {
 278         final long literalToken = token;
 279 
 280         switch (type) {
 281         case STRING:
 282             return getStringLiteral();
 283         case ESCSTRING:
 284         case DECIMAL:
 285         case FLOATING:
 286             return getLiteral();
 287         case FALSE:
 288             next();
 289             return LiteralNode.newInstance(literalToken, finish, false);
 290         case TRUE:
 291             next();
 292             return LiteralNode.newInstance(literalToken, finish, true);
 293         case NULL:
 294             next();
 295             return LiteralNode.newInstance(literalToken, finish);
 296         case LBRACKET:
 297             return arrayLiteral();
 298         case LBRACE:
 299             return objectLiteral();
 300         /*
 301          * A.8.1 JSON Lexical Grammar
 302          *
 303          * JSONNumber :: See 15.12.1.1
 304          *    -opt DecimalIntegerLiteral JSONFractionopt ExponentPartopt
 305          */
 306         case SUB:
 307             next();
 308 
 309             final long realToken = token;
 310             final Object value = getValue();
 311 
 312             if (value instanceof Number) {
 313                 next();
 314                 return new UnaryNode(literalToken, LiteralNode.newInstance(realToken, finish, (Number)value));
 315             }
 316 
 317             throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
 318         default:
 319             break;
 320         }
 321 
 322         throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));

 323     }
 324 
 325     /**
 326      * Parse an array literal from the token stream
 327      * @return the array literal as a Node
 328      */
 329     private LiteralNode<Expression[]> arrayLiteral() {
 330         // Unlike JavaScript array literals, elison is not permitted in JSON.
 331 
 332         // Capture LBRACKET token.
 333         final long arrayToken = token;
 334         // LBRACKET tested in caller.
 335         next();
 336 
 337         LiteralNode<Expression[]> result = null;
 338         // Prepare to accummulating elements.
 339         final List<Expression> elements = new ArrayList<>();
 340 
 341 loop:
 342         while (true) {
 343             switch (type) {
 344             case RBRACKET:
 345                 next();
 346                 result = LiteralNode.newInstance(arrayToken, finish, elements);
 347                 break loop;
 348 
 349             case COMMARIGHT:
 350                 next();
 351                 // check for trailing comma - not allowed in JSON
 352                 if (type == RBRACKET) {
 353                     throw error(AbstractParser.message("trailing.comma.in.json", type.getNameOrType()));
 354                 }
 355                 break;
 356 
 357             default:
 358                 // Add expression element.
 359                 elements.add(jsonLiteral());
 360                 // Comma between array elements is mandatory in JSON.
 361                 if (type != COMMARIGHT && type != RBRACKET) {
 362                    throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));

 363                 }
 364                 break;

 365             }

 366         }
 367 
 368         return result;






 369     }
 370 
 371     /**
 372      * Parse an object literal from the token stream
 373      * @return the object literal as a Node
 374      */
 375     private ObjectNode objectLiteral() {
 376         // Capture LBRACE token.
 377         final long objectToken = token;
 378         // LBRACE tested in caller.
 379         next();
 380 
 381         // Prepare to accumulate elements.
 382         final List<PropertyNode> elements = new ArrayList<>();
 383 
 384         // Create a block for the object literal.
 385 loop:
 386         while (true) {
 387             switch (type) {
 388             case RBRACE:
 389                 next();
 390                 break loop;
 391 
 392             case COMMARIGHT:
 393                 next();
 394                 // check for trailing comma - not allowed in JSON
 395                 if (type == RBRACE) {
 396                     throw error(AbstractParser.message("trailing.comma.in.json", type.getNameOrType()));
 397                 }
 398                 break;
 399 
 400             default:
 401                 // Get and add the next property.
 402                 final PropertyNode property = propertyAssignment();
 403                 elements.add(property);


 404 
 405                 // Comma between property assigments is mandatory in JSON.
 406                 if (type != RBRACE && type != COMMARIGHT) {
 407                     throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));

 408                 }









 409                 break;



 410             }
 411         }
 412 
 413         // Construct new object literal.
 414         return new ObjectNode(objectToken, finish, elements);
 415     }
 416 
 417     /**
 418      * Parse a property assignment from the token stream
 419      * @return the property assignment as a Node
 420      */
 421     private PropertyNode propertyAssignment() {
 422         // Capture firstToken.
 423         final long propertyToken = token;
 424         LiteralNode<?> name = null;

 425 
 426         if (type == STRING) {
 427             name = getStringLiteral();
 428         } else if (type == ESCSTRING) {
 429             name = getLiteral();
 430         }
 431 
 432         if (name != null) {
 433             expect(COLON);
 434             final Expression value = jsonLiteral();
 435             return new PropertyNode(propertyToken, value.getFinish(), name, value, null, null);
 436         }
 437 
 438         // Raise an error.
 439         throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
 440     }
 441 




 442 }
   1 /*
   2  * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.parser;
  27 







  28 import java.util.ArrayList;
  29 import java.util.LinkedHashMap;
  30 import java.util.List;
  31 import java.util.Map;
  32 import jdk.nashorn.internal.runtime.ECMAErrors;




  33 import jdk.nashorn.internal.runtime.ErrorManager;
  34 import jdk.nashorn.internal.runtime.JSErrorType;
  35 import jdk.nashorn.internal.runtime.JSType;
  36 import jdk.nashorn.internal.runtime.ParserException;
  37 import jdk.nashorn.internal.runtime.Source;
  38 
  39 import static jdk.nashorn.internal.parser.TokenType.STRING;
  40 
  41 /**
  42  * Parses JSON text and returns the corresponding IR node. This is derived from the objectLiteral production of the main parser.
  43  *
  44  * See: 15.12.1.2 The JSON Syntactic Grammar
  45  */
  46 public class JSONParser {
  47 
  48     final private String source;
  49     final int length;
  50     int pos = 0;
  51 
  52     private static final int EOF = -1;
  53 
  54     private static final String TRUE  = "true";
  55     private static final String FALSE = "false";
  56     private static final String NULL  = "null";
  57 
  58     private static final int STATE_EMPTY          = 0;
  59     private static final int STATE_ELEMENT_PARSED = 1;
  60     private static final int STATE_COMMA_PARSED   = 2;
  61 
  62     /**
  63      * Constructor
  64      * @param source  the source

  65      */
  66     public JSONParser(final String source) {
  67         this.source = source;
  68         this.length = source.length();
  69     }
  70 
  71     /**
  72      * Implementation of the Quote(value) operation as defined in the ECMA script spec
  73      * It wraps a String value in double quotes and escapes characters within in
  74      *
  75      * @param value string to quote
  76      *
  77      * @return quoted and escaped string
  78      */
  79     public static String quote(final String value) {
  80 
  81         final StringBuilder product = new StringBuilder();
  82 
  83         product.append("\"");
  84 
  85         for (final char ch : value.toCharArray()) {
  86             // TODO: should use a table?
  87             switch (ch) {
  88                 case '\\':


 106                 case '\t':
 107                     product.append("\\t");
 108                     break;
 109                 default:
 110                     if (ch < ' ') {
 111                         product.append(Lexer.unicodeEscape(ch));
 112                         break;
 113                     }
 114 
 115                     product.append(ch);
 116                     break;
 117             }
 118         }
 119 
 120         product.append("\"");
 121 
 122         return product.toString();
 123     }
 124 
 125     /**
 126      * Public parse method. Parse a string into a JSON object.

 127      *
 128      * @return the parsed JSON Object
 129      */
 130     public Object parse() {
 131         final Object value = parseLiteral();
 132         skipWhiteSpace();
 133         if (pos < length) {
 134             throw expectedError(pos, "eof", toString(peek()));
 135         }
 136         return value;
 137     }
 138 
 139     private Object parseLiteral() {
 140         skipWhiteSpace();
 141 
 142         final int c = peek();
 143         if (c == EOF) {
 144             throw expectedError(pos, "json literal", "eof");
 145         }
 146         switch (c) {
 147             case '{':
 148                 return parseObject();
 149             case '[':
 150                 return parseArray();
 151             case '"':
 152                 return parseString();
 153             case 'f':
 154                 return parseKeyword(FALSE, Boolean.FALSE);
 155             case 't':
 156                 return parseKeyword(TRUE, Boolean.TRUE);
 157             case 'n':
 158                 return parseKeyword(NULL, null);
 159             default:
 160                 if (isDigit(c) || c == '-') {
 161                     return parseNumber();
 162                 } else if (c == '.') {
 163                     throw numberError(pos);
 164                 } else {
 165                     throw expectedError(pos, "json literal", toString(c));
 166                 }
 167         }
 168     }
 169 
 170     private Object parseObject() {
 171         final Map<String, Object> map = new LinkedHashMap<>();
 172         int state = STATE_EMPTY;
 173 
 174         assert peek() == '{';
 175         pos++;


 176 
 177         while (pos < length) {
 178             skipWhiteSpace();
 179             final int c = peek();

 180 
 181             switch (c) {
 182                 case '"':
 183                     if (state == STATE_ELEMENT_PARSED) {
 184                         throw expectedError(pos - 1, ", or }", toString(c));
 185                     }
 186                     final String id = parseString();
 187                     skipWhiteSpace();
 188                     final int n = next();
 189                     if (n != ':') {
 190                         throw expectedError(pos - 1, ":", toString(n));
 191                     }
 192                     final Object value = parseLiteral();
 193                     map.put(id, value);
 194                     state = STATE_ELEMENT_PARSED;
 195                     break;
 196                 case ',':
 197                     if (state != STATE_ELEMENT_PARSED) {
 198                         throw error(AbstractParser.message("trailing.comma.in.json"), pos);
 199                     }
 200                     state = STATE_COMMA_PARSED;
 201                     pos++;
 202                     break;
 203                 case '}':
 204                     if (state == STATE_COMMA_PARSED) {
 205                         throw error(AbstractParser.message("trailing.comma.in.json"), pos);
 206                     }
 207                     pos++;
 208                     return map;
 209                 default:
 210                     throw expectedError(pos, ", or }", toString(c));
 211             }
 212         }
 213         throw expectedError(pos, ", or }", "eof");
 214     }
 215 







 216 
 217     private Object parseArray() {
 218         final List<Object> list = new ArrayList<>();
 219         int state = STATE_EMPTY;

 220 
 221         assert peek() == '[';
 222         pos++;
 223 
 224         while (pos < length) {
 225             skipWhiteSpace();
 226             final int c = peek();
 227 
 228             switch (c) {
 229                 case ',':
 230                     if (state != STATE_ELEMENT_PARSED) {
 231                         throw error(AbstractParser.message("trailing.comma.in.json"), pos);
 232                     }
 233                     state = STATE_COMMA_PARSED;
 234                     pos++;
 235                     break;
 236                 case ']':
 237                     if (state == STATE_COMMA_PARSED) {
 238                         throw error(AbstractParser.message("trailing.comma.in.json"), pos);
 239                     }
 240                     pos++;
 241                     return list;
 242                 default:
 243                     if (state == STATE_ELEMENT_PARSED) {
 244                         throw expectedError(pos, ", or ]", toString(c));







 245                     }
 246                     list.add(parseLiteral());
 247                     state = STATE_ELEMENT_PARSED;
 248                     break;

 249             }
 250         }
 251 
 252         throw expectedError(pos, ", or ]", "eof");






 253     }
 254 
 255     private String parseString() {
 256         // String buffer is only instantiated if string contains escape sequences.
 257         int start = ++pos;
 258         StringBuilder sb = null;
 259 
 260         while (pos < length) {
 261             final int c = next();
 262             if (c <= 0x1f) {
 263                 // Characters < 0x1f are not allowed in JSON strings.
 264                 throw syntaxError(pos, "String contains control character");
 265 
 266             } else if (c == '\\') {
 267                 if (sb == null) {
 268                     sb = new StringBuilder(Math.max(32, (pos - start) * 2));
 269                 }
 270                 sb.append(source, start, pos - 1);
 271                 sb.append(parseEscapeSequence());
 272                 start = pos;
 273 
 274             } else if (c == '"') {
 275                 if (sb != null) {
 276                     sb.append(source, start, pos - 1);
 277                     return sb.toString();
 278                 }
 279                 return source.substring(start, pos - 1);
 280             }


 281         }
 282 
 283         throw error(Lexer.message("missing.close.quote"), pos, length);

 284     }
 285 
 286     private char parseEscapeSequence() {
 287         final int c = next();
 288         switch (c) {

 289             case '"':
 290                 return '"';
 291             case '\\':
 292                 return '\\';
 293             case '/':
 294                 return '/';
 295             case 'b':
 296                 return '\b';
 297             case 'f':
 298                 return '\f';
 299             case 'n':
 300                 return '\n';
 301             case 'r':
 302                 return '\r';
 303             case 't':
 304                 return '\t';
 305             case 'u':
 306                 return parseUnicodeEscape();
 307             default:
 308                 throw error(Lexer.message("invalid.escape.char"), pos - 1, length);
 309         }
 310     }

 311 
 312     private char parseUnicodeEscape() {
 313         return (char) (parseHexDigit() << 12 | parseHexDigit() << 8 | parseHexDigit() << 4 | parseHexDigit());
 314     }



 315 
 316     private int parseHexDigit() {
 317         final int c = next();
 318         if (c >= '0' && c <= '9') {
 319             return c - '0';
 320         } else if (c >= 'A' && c <= 'F') {
 321             return c + 10 - 'A';
 322         } else if (c >= 'a' && c <= 'f') {
 323             return c + 10 - 'a';
 324         }
 325         throw error(Lexer.message("invalid.hex"), pos - 1, length);
 326     }
 327 
 328     private boolean isDigit(final int c) {
 329         return c >= '0' && c <= '9';
 330     }

 331 
 332     private void skipDigits() {
 333         while (pos < length) {
 334             final int c = peek();
 335             if (!isDigit(c)) {

 336                 break;
 337             }
 338             pos++;



 339         }


 340     }
 341 
 342     private Number parseNumber() {
 343         final int start = pos;
 344         int c = next();
































 345 
 346         if (c == '-') {
 347             c = next();




 348         }
 349         if (!isDigit(c)) {
 350             throw numberError(start);


 351         }
 352         // no more digits allowed after 0
 353         if (c != '0') {
 354             skipDigits();
 355         }
 356 
 357         // fraction
 358         if (peek() == '.') {
 359             pos++;
 360             if (!isDigit(next())) {
 361                 throw numberError(pos - 1);
 362             }
 363             skipDigits();






















 364         }

 365 
 366         // exponent
 367         c = peek();
 368         if (c == 'e' || c == 'E') {
 369             pos++;
 370             c = next();
 371             if (c == '-' || c == '+') {
 372                 c = next();
 373             }
 374             if (!isDigit(c)) {
 375                 throw numberError(pos - 1);
 376             }
 377             skipDigits();
 378         }
 379 
 380         final double d = Double.parseDouble(source.substring(start, pos));
 381         if (JSType.isRepresentableAsInt(d)) {
 382             return (int) d;
 383         } else if (JSType.isRepresentableAsLong(d)) {
 384             return (long) d;
 385         }
 386         return d;
 387     }
 388 
 389     private Object parseKeyword(final String keyword, final Object value) {
 390         if (!source.regionMatches(pos, keyword, 0, keyword.length())) {
 391             throw expectedError(pos, "json literal", "ident");
 392         }
 393         pos += keyword.length();
 394         return value;




















 395     }

 396 
 397     private int peek() {
 398         if (pos >= length) {
 399             return -1;
 400         }
 401         return source.charAt(pos);
 402     }
 403 
 404     private int next() {
 405         final int next = peek();
 406         pos++;
 407         return next;
 408     }
 409 
 410     private void skipWhiteSpace() {
 411         while (pos < length) {
 412             switch (peek()) {
 413             case '\t':
 414             case '\r':
 415             case '\n':
 416             case ' ':
 417                 pos++;
 418                 break;
 419             default:
 420                 return;
 421             }
 422         }
 423     }
 424 
 425     private static String toString(final int c) {
 426         return c == EOF ? "eof" : String.valueOf((char) c);
 427     }
 428 
 429     ParserException error(final String message, final int start, final int length) throws ParserException {
 430         final long token     = Token.toDesc(STRING, start, length);
 431         final int  pos       = Token.descPosition(token);
 432         final Source src     = Source.sourceFor("<json>", source);
 433         final int  lineNum   = src.getLine(pos);
 434         final int  columnNum = src.getColumn(pos);
 435         final String formatted = ErrorManager.format(message, src, lineNum, columnNum, token);
 436         return new ParserException(JSErrorType.SYNTAX_ERROR, formatted, src, lineNum, columnNum, token);
 437     }
 438 
 439     private ParserException error(final String message, final int start) {
 440         return error(message, start, length);


 441     }
 442 
 443     private ParserException numberError(final int start) {
 444         return error(Lexer.message("json.invalid.number"), start);


 445     }
 446 
 447     private ParserException expectedError(final int start, final String expected, final String found) {
 448         return error(AbstractParser.message("expected", expected, found), start);
 449     }
 450 
 451     private ParserException syntaxError(final int start, final String reason) {
 452         final String message = ECMAErrors.getMessage("syntax.error.invalid.json", reason);
 453         return error(message, start);
 454     }
 455 }