src/jdk/nashorn/internal/parser/JSONParser.java

Tue, 07 May 2013 14:36:57 +0200

author
lagergren
date
Tue, 07 May 2013 14:36:57 +0200
changeset 252
544e17632e96
parent 211
3a209cbd1d8f
child 324
0feca8a93cb3
permissions
-rw-r--r--

8013913: Removed Source field from all nodes except FunctionNode in order to save footprint
Reviewed-by: jlaskey, attila

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.parser;
jlaskey@3 27
jlaskey@3 28 import static jdk.nashorn.internal.parser.TokenType.COLON;
jlaskey@3 29 import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT;
jlaskey@3 30 import static jdk.nashorn.internal.parser.TokenType.EOF;
jlaskey@3 31 import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
jlaskey@3 32 import static jdk.nashorn.internal.parser.TokenType.RBRACE;
jlaskey@3 33 import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
jlaskey@3 34 import static jdk.nashorn.internal.parser.TokenType.STRING;
jlaskey@3 35
jlaskey@3 36 import java.util.ArrayList;
jlaskey@3 37 import java.util.List;
jlaskey@3 38 import jdk.nashorn.internal.ir.LiteralNode;
jlaskey@3 39 import jdk.nashorn.internal.ir.Node;
jlaskey@3 40 import jdk.nashorn.internal.ir.ObjectNode;
jlaskey@3 41 import jdk.nashorn.internal.ir.PropertyNode;
jlaskey@3 42 import jdk.nashorn.internal.ir.UnaryNode;
jlaskey@3 43 import jdk.nashorn.internal.runtime.ErrorManager;
jlaskey@3 44 import jdk.nashorn.internal.runtime.Source;
jlaskey@3 45
jlaskey@3 46 /**
jlaskey@3 47 * Parses JSON text and returns the corresponding IR node. This is derived from the objectLiteral production of the main parser.
jlaskey@3 48 *
jlaskey@3 49 * See: 15.12.1.2 The JSON Syntactic Grammar
jlaskey@3 50 */
jlaskey@3 51 public class JSONParser extends AbstractParser {
jlaskey@3 52
jlaskey@3 53 /**
jlaskey@3 54 * Constructor
jlaskey@3 55 * @param source the source
jlaskey@3 56 * @param errors the error manager
jlaskey@3 57 * @param strict are we in strict mode
jlaskey@3 58 */
jlaskey@3 59 public JSONParser(final Source source, final ErrorManager errors, final boolean strict) {
jlaskey@3 60 super(source, errors, strict);
jlaskey@3 61 }
jlaskey@3 62
jlaskey@3 63 /**
jlaskey@3 64 * Implementation of the Quote(value) operation as defined in the ECMA script spec
jlaskey@3 65 * It wraps a String value in double quotes and escapes characters within in
jlaskey@3 66 *
jlaskey@3 67 * @param value string to quote
jlaskey@3 68 *
jlaskey@3 69 * @return quoted and escaped string
jlaskey@3 70 */
jlaskey@3 71 public static String quote(final String value) {
jlaskey@3 72
jlaskey@3 73 final StringBuilder product = new StringBuilder();
jlaskey@3 74
jlaskey@3 75 product.append("\"");
jlaskey@3 76
jlaskey@3 77 for (final char ch : value.toCharArray()) {
jlaskey@3 78 // TODO: should use a table?
jlaskey@3 79 switch (ch) {
jlaskey@3 80 case '\\':
jlaskey@3 81 product.append("\\\\");
jlaskey@3 82 break;
jlaskey@3 83 case '"':
jlaskey@3 84 product.append("\\\"");
jlaskey@3 85 break;
jlaskey@3 86 case '\b':
jlaskey@3 87 product.append("\\b");
jlaskey@3 88 break;
jlaskey@3 89 case '\f':
jlaskey@3 90 product.append("\\f");
jlaskey@3 91 break;
jlaskey@3 92 case '\n':
jlaskey@3 93 product.append("\\n");
jlaskey@3 94 break;
jlaskey@3 95 case '\r':
jlaskey@3 96 product.append("\\r");
jlaskey@3 97 break;
jlaskey@3 98 case '\t':
jlaskey@3 99 product.append("\\t");
jlaskey@3 100 break;
jlaskey@3 101 default:
jlaskey@3 102 if (ch < ' ') {
jlaskey@3 103 product.append(Lexer.unicodeEscape(ch));
jlaskey@3 104 break;
jlaskey@3 105 }
jlaskey@3 106
jlaskey@3 107 product.append(ch);
jlaskey@3 108 break;
jlaskey@3 109 }
jlaskey@3 110 }
jlaskey@3 111
jlaskey@3 112 product.append("\"");
jlaskey@3 113
jlaskey@3 114 return product.toString();
jlaskey@3 115 }
jlaskey@3 116
jlaskey@3 117 /**
jlaskey@3 118 * Public parsed method - start lexing a new token stream for
jlaskey@3 119 * a JSON script
jlaskey@3 120 *
jlaskey@3 121 * @return the JSON literal
jlaskey@3 122 */
jlaskey@3 123 public Node parse() {
jlaskey@3 124 stream = new TokenStream();
jlaskey@3 125
jlaskey@3 126 lexer = new Lexer(source, stream) {
jlaskey@3 127
jlaskey@3 128 @Override
jlaskey@3 129 protected boolean skipComments() {
jlaskey@3 130 return false;
jlaskey@3 131 }
jlaskey@3 132
jlaskey@3 133 @Override
jlaskey@3 134 protected boolean isStringDelimiter(final char ch) {
jlaskey@3 135 return ch == '\"';
jlaskey@3 136 }
jlaskey@3 137
jlaskey@3 138 @Override
jlaskey@3 139 protected boolean isWhitespace(final char ch) {
jlaskey@3 140 return Lexer.isJsonWhitespace(ch);
jlaskey@3 141 }
jlaskey@3 142
jlaskey@3 143 @Override
jlaskey@3 144 protected boolean isEOL(final char ch) {
jlaskey@3 145 return Lexer.isJsonEOL(ch);
jlaskey@3 146 }
jlaskey@3 147 };
jlaskey@3 148
jlaskey@3 149 k = -1;
jlaskey@3 150
jlaskey@3 151 next();
jlaskey@3 152
jlaskey@3 153 final Node resultNode = jsonLiteral();
jlaskey@3 154 expect(EOF);
jlaskey@3 155
jlaskey@3 156 return resultNode;
jlaskey@3 157 }
jlaskey@3 158
jlaskey@3 159 @SuppressWarnings("fallthrough")
jlaskey@3 160 private LiteralNode<?> getStringLiteral() {
jlaskey@3 161 final LiteralNode<?> literal = getLiteral();
jlaskey@3 162 final String str = (String)literal.getValue();
jlaskey@3 163
jlaskey@3 164 for (int i = 0; i < str.length(); i++) {
jlaskey@3 165 final char ch = str.charAt(i);
jlaskey@3 166 switch (ch) {
jlaskey@3 167 default:
jlaskey@3 168 if (ch > 0x001f) {
jlaskey@3 169 break;
jlaskey@3 170 }
jlaskey@3 171 case '"':
jlaskey@3 172 case '\\':
lagergren@211 173 throw error(AbstractParser.message("unexpected.token", str));
jlaskey@3 174 }
jlaskey@3 175 }
jlaskey@3 176
jlaskey@3 177 return literal;
jlaskey@3 178 }
jlaskey@3 179
jlaskey@3 180 /**
jlaskey@3 181 * Parse a JSON literal from the token stream
jlaskey@3 182 * @return the JSON literal as a Node
jlaskey@3 183 */
jlaskey@3 184 private Node jsonLiteral() {
jlaskey@3 185 final long literalToken = token;
jlaskey@3 186
jlaskey@3 187 switch (type) {
jlaskey@3 188 case STRING:
jlaskey@3 189 return getStringLiteral();
jlaskey@3 190 case ESCSTRING:
jlaskey@3 191 case DECIMAL:
jlaskey@3 192 case FLOATING:
jlaskey@3 193 return getLiteral();
jlaskey@3 194 case FALSE:
jlaskey@3 195 next();
lagergren@252 196 return LiteralNode.newInstance(literalToken, finish, false);
jlaskey@3 197 case TRUE:
jlaskey@3 198 next();
lagergren@252 199 return LiteralNode.newInstance(literalToken, finish, true);
jlaskey@3 200 case NULL:
jlaskey@3 201 next();
lagergren@252 202 return LiteralNode.newInstance(literalToken, finish);
jlaskey@3 203 case LBRACKET:
jlaskey@3 204 return arrayLiteral();
jlaskey@3 205 case LBRACE:
jlaskey@3 206 return objectLiteral();
jlaskey@3 207 /*
jlaskey@3 208 * A.8.1 JSON Lexical Grammar
jlaskey@3 209 *
jlaskey@3 210 * JSONNumber :: See 15.12.1.1
jlaskey@3 211 * -opt DecimalIntegerLiteral JSONFractionopt ExponentPartopt
jlaskey@3 212 */
jlaskey@3 213 case SUB:
jlaskey@3 214 next();
jlaskey@3 215
jlaskey@3 216 final long realToken = token;
jlaskey@3 217 final Object value = getValue();
jlaskey@3 218
jlaskey@3 219 if (value instanceof Number) {
jlaskey@3 220 next();
lagergren@252 221 return new UnaryNode(literalToken, LiteralNode.newInstance(realToken, finish, (Number)value));
jlaskey@3 222 }
jlaskey@3 223
lagergren@211 224 throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
jlaskey@3 225 default:
jlaskey@3 226 break;
jlaskey@3 227 }
jlaskey@3 228
lagergren@211 229 throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
jlaskey@3 230 }
jlaskey@3 231
jlaskey@3 232 /**
jlaskey@3 233 * Parse an array literal from the token stream
jlaskey@3 234 * @return the array literal as a Node
jlaskey@3 235 */
jlaskey@3 236 private Node arrayLiteral() {
jlaskey@3 237 // Unlike JavaScript array literals, elison is not permitted in JSON.
jlaskey@3 238
jlaskey@3 239 // Capture LBRACKET token.
jlaskey@3 240 final long arrayToken = token;
jlaskey@3 241 // LBRACKET tested in caller.
jlaskey@3 242 next();
jlaskey@3 243
jlaskey@3 244 Node result = null;
jlaskey@3 245 // Prepare to accummulating elements.
jlaskey@3 246 final List<Node> elements = new ArrayList<>();
jlaskey@3 247
jlaskey@3 248 loop:
jlaskey@3 249 while (true) {
jlaskey@3 250 switch (type) {
jlaskey@3 251 case RBRACKET:
jlaskey@3 252 next();
lagergren@252 253 result = LiteralNode.newInstance(arrayToken, finish, elements);
jlaskey@3 254 break loop;
jlaskey@3 255
jlaskey@3 256 case COMMARIGHT:
jlaskey@3 257 next();
jlaskey@3 258 break;
jlaskey@3 259
jlaskey@3 260 default:
jlaskey@3 261 // Add expression element.
jlaskey@3 262 elements.add(jsonLiteral());
jlaskey@3 263 // Comma between array elements is mandatory in JSON.
jlaskey@3 264 if (type != COMMARIGHT && type != RBRACKET) {
lagergren@211 265 throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
jlaskey@3 266 }
jlaskey@3 267 break;
jlaskey@3 268 }
jlaskey@3 269 }
jlaskey@3 270
jlaskey@3 271 return result;
jlaskey@3 272 }
jlaskey@3 273
jlaskey@3 274 /**
jlaskey@3 275 * Parse an object literal from the token stream
jlaskey@3 276 * @return the object literal as a Node
jlaskey@3 277 */
jlaskey@3 278 private Node objectLiteral() {
jlaskey@3 279 // Capture LBRACE token.
jlaskey@3 280 final long objectToken = token;
jlaskey@3 281 // LBRACE tested in caller.
jlaskey@3 282 next();
jlaskey@3 283
jlaskey@3 284 // Prepare to accumulate elements.
jlaskey@3 285 final List<Node> elements = new ArrayList<>();
jlaskey@3 286
jlaskey@3 287 // Create a block for the object literal.
jlaskey@3 288 loop:
jlaskey@3 289 while (true) {
jlaskey@3 290 switch (type) {
jlaskey@3 291 case RBRACE:
jlaskey@3 292 next();
jlaskey@3 293 break loop;
jlaskey@3 294
jlaskey@3 295 case COMMARIGHT:
jlaskey@3 296 next();
jlaskey@3 297 break;
jlaskey@3 298
jlaskey@3 299 default:
jlaskey@3 300 // Get and add the next property.
jlaskey@3 301 final Node property = propertyAssignment();
jlaskey@3 302 elements.add(property);
jlaskey@3 303
jlaskey@3 304 // Comma between property assigments is mandatory in JSON.
jlaskey@3 305 if (type != RBRACE && type != COMMARIGHT) {
lagergren@211 306 throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
jlaskey@3 307 }
jlaskey@3 308 break;
jlaskey@3 309 }
jlaskey@3 310 }
jlaskey@3 311
jlaskey@3 312 // Construct new object literal.
lagergren@252 313 return new ObjectNode(objectToken, finish, elements);
jlaskey@3 314 }
jlaskey@3 315
jlaskey@3 316 /**
jlaskey@3 317 * Parse a property assignment from the token stream
jlaskey@3 318 * @return the property assignment as a Node
jlaskey@3 319 */
jlaskey@3 320 private Node propertyAssignment() {
jlaskey@3 321 // Capture firstToken.
jlaskey@3 322 final long propertyToken = token;
jlaskey@3 323 LiteralNode<?> name = null;
jlaskey@3 324
jlaskey@3 325 if (type == STRING) {
jlaskey@3 326 name = getStringLiteral();
jlaskey@3 327 } else if (type == ESCSTRING) {
jlaskey@3 328 name = getLiteral();
jlaskey@3 329 }
jlaskey@3 330
jlaskey@3 331 if (name != null) {
jlaskey@3 332 expect(COLON);
jlaskey@3 333 final Node value = jsonLiteral();
lagergren@252 334 return new PropertyNode(propertyToken, value.getFinish(), name, value, null, null);
jlaskey@3 335 }
jlaskey@3 336
jlaskey@3 337 // Raise an error.
lagergren@211 338 throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
jlaskey@3 339 }
jlaskey@3 340
jlaskey@3 341 }

mercurial