'use strict'; var at; // The index of the current character var ch; // The current character var escapee = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' }; var text; // Call error when something is wrong. function error(m) { throw { name: 'SyntaxError', message: m, at: at, text: text }; } function next(c) { // If a c parameter is provided, verify that it matches the current character. if (c && c !== ch) { error("Expected '" + c + "' instead of '" + ch + "'"); } // Get the next character. When there are no more characters, return the empty string. ch = text.charAt(at); at += 1; return ch; } function number() { // Parse a number value. var num; var str = ''; if (ch === '-') { str = '-'; next('-'); } while (ch >= '0' && ch <= '9') { str += ch; next(); } if (ch === '.') { str += '.'; while (next() && ch >= '0' && ch <= '9') { str += ch; } } if (ch === 'e' || ch === 'E') { str += ch; next(); if (ch === '-' || ch === '+') { str += ch; next(); } while (ch >= '0' && ch <= '9') { str += ch; next(); } } num = Number(str); if (!isFinite(num)) { error('Bad number'); } return num; } function string() { // Parse a string value. var hex; var i; var str = ''; var uffff; // When parsing for string values, we must look for " and \ characters. if (ch === '"') { while (next()) { if (ch === '"') { next(); return str; } else if (ch === '\\') { next(); if (ch === 'u') { uffff = 0; for (i = 0; i < 4; i += 1) { hex = parseInt(next(), 16); if (!isFinite(hex)) { break; } uffff = (uffff * 16) + hex; } str += String.fromCharCode(uffff); } else if (typeof escapee[ch] === 'string') { str += escapee[ch]; } else { break; } } else { str += ch; } } } error('Bad string'); } // Skip whitespace. function white() { while (ch && ch <= ' ') { next(); } } // true, false, or null. function word() { switch (ch) { case 't': next('t'); next('r'); next('u'); next('e'); return true; case 'f': next('f'); next('a'); next('l'); next('s'); next('e'); return false; case 'n': next('n'); next('u'); next('l'); next('l'); return null; default: error("Unexpected '" + ch + "'"); } } // Parse an array value. function array() { var arr = []; if (ch === '[') { next('['); white(); if (ch === ']') { next(']'); return arr; // empty array } while (ch) { arr.push(value()); // eslint-disable-line no-use-before-define white(); if (ch === ']') { next(']'); return arr; } next(','); white(); } } error('Bad array'); } // Parse an object value. function object() { var key; var obj = {}; if (ch === '{') { next('{'); white(); if (ch === '}') { next('}'); return obj; // empty object } while (ch) { key = string(); white(); next(':'); if (Object.prototype.hasOwnProperty.call(obj, key)) { error('Duplicate key "' + key + '"'); } obj[key] = value(); // eslint-disable-line no-use-before-define white(); if (ch === '}') { next('}'); return obj; } next(','); white(); } } error('Bad object'); } // Parse a JSON value. It could be an object, an array, a string, a number, or a word. function value() { white(); switch (ch) { case '{': return object(); case '[': return array(); case '"': return string(); case '-': return number(); default: return ch >= '0' && ch <= '9' ? number() : word(); } } // Return the json_parse function. It will have access to all of the above functions and variables. module.exports = function (source, reviver) { var result; text = source; at = 0; ch = ' '; result = value(); white(); if (ch) { error('Syntax error'); } // If there is a reviver function, we recursively walk the new structure, // passing each name/value pair to the reviver function for possible // transformation, starting with a temporary root object that holds the result // in an empty key. If there is not a reviver function, we simply return the // result. return typeof reviver === 'function' ? (function walk(holder, key) { var k; var v; var val = holder[key]; if (val && typeof val === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(val, k)) { v = walk(val, k); if (typeof v === 'undefined') { delete val[k]; } else { val[k] = v; } } } } return reviver.call(holder, key, val); }({ '': result }, '')) : result; };