37 import java.util.List; 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 * @param strict are we in strict mode 58 */ 59 public JSONParser(final Source source, final ErrorManager errors, final boolean strict) { 60 super(source, errors, strict); 61 } 62 63 /** 64 * Implementation of the Quote(value) operation as defined in the ECMA script spec 65 * It wraps a String value in double quotes and escapes characters within in 66 * 67 * @param value string to quote 68 * 69 * @return quoted and escaped string 70 */ 71 public static String quote(final String value) { 72 73 final StringBuilder product = new StringBuilder(); 74 75 product.append("\""); 76 77 for (final char ch : value.toCharArray()) { 78 // TODO: should use a table? 79 switch (ch) { 80 case '\\': 118 * Public parsed method - start lexing a new token stream for 119 * a JSON script 120 * 121 * @return the JSON literal 122 */ 123 public Node parse() { 124 stream = new TokenStream(); 125 126 lexer = new Lexer(source, stream) { 127 128 @Override 129 protected boolean skipComments() { 130 return false; 131 } 132 133 @Override 134 protected boolean isStringDelimiter(final char ch) { 135 return ch == '\"'; 136 } 137 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 149 k = -1; 150 151 next(); 152 153 final Node resultNode = jsonLiteral(); 154 expect(EOF); 155 156 return resultNode; 157 } 158 159 @SuppressWarnings("fallthrough") 160 private LiteralNode<?> getStringLiteral() { 161 final LiteralNode<?> literal = getLiteral(); 162 final String str = (String)literal.getValue(); 163 164 for (int i = 0; i < str.length(); i++) { 165 final char ch = str.charAt(i); | 37 import java.util.List; 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); 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 '\\': 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 start = position; 153 // Assume value is a decimal. 154 TokenType type = 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 int digit = convertDigit(ch0, 10); 163 164 // If number begins with 0x. 165 if (digit == 0 && (ch1 == 'x' || ch1 == 'X') && convertDigit(ch2, 16) != -1) { 166 // Skip over 0xN. 167 skip(3); 168 // Skip over remaining digits. 169 while (convertDigit(ch0, 16) != -1) { 170 skip(1); 171 } 172 error(Lexer.message("json.no.hexadecimal.number"), STRING, position, limit); 173 type = TokenType.HEXADECIMAL; 174 } else { 175 // Check for possible octal constant. 176 boolean octal = digit == 0; 177 // Skip first digit if not leading '.'. 178 if (digit != -1) { 179 skip(1); 180 } 181 182 // Skip remaining digits. 183 while (convertDigit(ch0, 10) != -1) { 184 // Skip digit. 185 skip(1); 186 } 187 188 if (octal && position - start > 1) { 189 error(Lexer.message("json.no.octal.number"), STRING, position, limit); 190 type = TokenType.OCTAL; 191 } else if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') { 192 // Must be a double. 193 if (ch0 == '.') { 194 // Skip period. 195 skip(1); 196 boolean mantissa = false; 197 // Skip mantissa. 198 while (convertDigit(ch0, 10) != -1) { 199 skip(1); 200 mantissa = true; 201 } 202 203 if (! mantissa) { 204 // no digit after "." 205 error(Lexer.message("json.invalid.number"), STRING, position, limit); 206 } 207 } 208 209 // Detect exponent. 210 if (ch0 == 'E' || ch0 == 'e') { 211 // Skip E. 212 skip(1); 213 // Detect and skip exponent sign. 214 if (ch0 == '+' || ch0 == '-') { 215 skip(1); 216 } 217 boolean exponent = false; 218 // Skip exponent. 219 while (convertDigit(ch0, 10) != -1) { 220 exponent = true; 221 skip(1); 222 } 223 224 if (! exponent) { 225 // no digit after "E" 226 error(Lexer.message("json.invalid.number"), STRING, position, limit); 227 } 228 } 229 230 type = TokenType.FLOATING; 231 } 232 } 233 234 // Add number token. 235 add(type, start); 236 } 237 238 // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONEscapeCharacter 239 @Override 240 protected boolean isEscapeCharacter(final char ch) { 241 switch (ch) { 242 case '"': 243 case '/': 244 case '\\': 245 case 'b': 246 case 'f': 247 case 'n': 248 case 'r': 249 case 't': 250 // could be unicode escape 251 case 'u': 252 return true; 253 default: 254 return false; 255 } 256 } 257 }; 258 259 k = -1; 260 261 next(); 262 263 final Node resultNode = jsonLiteral(); 264 expect(EOF); 265 266 return resultNode; 267 } 268 269 @SuppressWarnings("fallthrough") 270 private LiteralNode<?> getStringLiteral() { 271 final LiteralNode<?> literal = getLiteral(); 272 final String str = (String)literal.getValue(); 273 274 for (int i = 0; i < str.length(); i++) { 275 final char ch = str.charAt(i); |