'use strict'; var $ = require('../internals/export'); var DESCRIPTORS = require('../internals/descriptors'); var global = require('../internals/global'); var getBuiltIn = require('../internals/get-built-in'); var uncurryThis = require('../internals/function-uncurry-this'); var call = require('../internals/function-call'); var isCallable = require('../internals/is-callable'); var isObject = require('../internals/is-object'); var isArray = require('../internals/is-array'); var hasOwn = require('../internals/has-own-property'); var toString = require('../internals/to-string'); var lengthOfArrayLike = require('../internals/length-of-array-like'); var createProperty = require('../internals/create-property'); var fails = require('../internals/fails'); var parseJSONString = require('../internals/parse-json-string'); var NATIVE_SYMBOL = require('../internals/symbol-constructor-detection'); var JSON = global.JSON; var Number = global.Number; var SyntaxError = global.SyntaxError; var nativeParse = JSON && JSON.parse; var enumerableOwnProperties = getBuiltIn('Object', 'keys'); // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var at = uncurryThis(''.charAt); var slice = uncurryThis(''.slice); var exec = uncurryThis(/./.exec); var push = uncurryThis([].push); var IS_DIGIT = /^\d$/; var IS_NON_ZERO_DIGIT = /^[1-9]$/; var IS_NUMBER_START = /^(?:-|\d)$/; var IS_WHITESPACE = /^[\t\n\r ]$/; var PRIMITIVE = 0; var OBJECT = 1; var $parse = function (source, reviver) { source = toString(source); var context = new Context(source, 0, ''); var root = context.parse(); var value = root.value; var endIndex = context.skip(IS_WHITESPACE, root.end); if (endIndex < source.length) { throw new SyntaxError('Unexpected extra character: "' + at(source, endIndex) + '" after the parsed data at: ' + endIndex); } return isCallable(reviver) ? internalize({ '': value }, '', reviver, root) : value; }; var internalize = function (holder, name, reviver, node) { var val = holder[name]; var unmodified = node && val === node.value; var context = unmodified && typeof node.source == 'string' ? { source: node.source } : {}; var elementRecordsLen, keys, len, i, P; if (isObject(val)) { var nodeIsArray = isArray(val); var nodes = unmodified ? node.nodes : nodeIsArray ? [] : {}; if (nodeIsArray) { elementRecordsLen = nodes.length; len = lengthOfArrayLike(val); for (i = 0; i < len; i++) { internalizeProperty(val, i, internalize(val, '' + i, reviver, i < elementRecordsLen ? nodes[i] : undefined)); } } else { keys = enumerableOwnProperties(val); len = lengthOfArrayLike(keys); for (i = 0; i < len; i++) { P = keys[i]; internalizeProperty(val, P, internalize(val, P, reviver, hasOwn(nodes, P) ? nodes[P] : undefined)); } } } return call(reviver, holder, name, val, context); }; var internalizeProperty = function (object, key, value) { if (DESCRIPTORS) { var descriptor = getOwnPropertyDescriptor(object, key); if (descriptor && !descriptor.configurable) return; } if (value === undefined) delete object[key]; else createProperty(object, key, value); }; var Node = function (value, end, source, nodes) { this.value = value; this.end = end; this.source = source; this.nodes = nodes; }; var Context = function (source, index) { this.source = source; this.index = index; }; // https://www.json.org/json-en.html Context.prototype = { fork: function (nextIndex) { return new Context(this.source, nextIndex); }, parse: function () { var source = this.source; var i = this.skip(IS_WHITESPACE, this.index); var fork = this.fork(i); var chr = at(source, i); if (exec(IS_NUMBER_START, chr)) return fork.number(); switch (chr) { case '{': return fork.object(); case '[': return fork.array(); case '"': return fork.string(); case 't': return fork.keyword(true); case 'f': return fork.keyword(false); case 'n': return fork.keyword(null); } throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i); }, node: function (type, value, start, end, nodes) { return new Node(value, end, type ? null : slice(this.source, start, end), nodes); }, object: function () { var source = this.source; var i = this.index + 1; var expectKeypair = false; var object = {}; var nodes = {}; while (i < source.length) { i = this.until(['"', '}'], i); if (at(source, i) === '}' && !expectKeypair) { i++; break; } // Parsing the key var result = this.fork(i).string(); var key = result.value; i = result.end; i = this.until([':'], i) + 1; // Parsing value i = this.skip(IS_WHITESPACE, i); result = this.fork(i).parse(); createProperty(nodes, key, result); createProperty(object, key, result.value); i = this.until([',', '}'], result.end); var chr = at(source, i); if (chr === ',') { expectKeypair = true; i++; } else if (chr === '}') { i++; break; } } return this.node(OBJECT, object, this.index, i, nodes); }, array: function () { var source = this.source; var i = this.index + 1; var expectElement = false; var array = []; var nodes = []; while (i < source.length) { i = this.skip(IS_WHITESPACE, i); if (at(source, i) === ']' && !expectElement) { i++; break; } var result = this.fork(i).parse(); push(nodes, result); push(array, result.value); i = this.until([',', ']'], result.end); if (at(source, i) === ',') { expectElement = true; i++; } else if (at(source, i) === ']') { i++; break; } } return this.node(OBJECT, array, this.index, i, nodes); }, string: function () { var index = this.index; var parsed = parseJSONString(this.source, this.index + 1); return this.node(PRIMITIVE, parsed.value, index, parsed.end); }, number: function () { var source = this.source; var startIndex = this.index; var i = startIndex; if (at(source, i) === '-') i++; if (at(source, i) === '0') i++; else if (exec(IS_NON_ZERO_DIGIT, at(source, i))) i = this.skip(IS_DIGIT, ++i); else throw new SyntaxError('Failed to parse number at: ' + i); if (at(source, i) === '.') i = this.skip(IS_DIGIT, ++i); if (at(source, i) === 'e' || at(source, i) === 'E') { i++; if (at(source, i) === '+' || at(source, i) === '-') i++; var exponentStartIndex = i; i = this.skip(IS_DIGIT, i); if (exponentStartIndex === i) throw new SyntaxError("Failed to parse number's exponent value at: " + i); } return this.node(PRIMITIVE, Number(slice(source, startIndex, i)), startIndex, i); }, keyword: function (value) { var keyword = '' + value; var index = this.index; var endIndex = index + keyword.length; if (slice(this.source, index, endIndex) !== keyword) throw new SyntaxError('Failed to parse value at: ' + index); return this.node(PRIMITIVE, value, index, endIndex); }, skip: function (regex, i) { var source = this.source; for (; i < source.length; i++) if (!exec(regex, at(source, i))) break; return i; }, until: function (array, i) { i = this.skip(IS_WHITESPACE, i); var chr = at(this.source, i); for (var j = 0; j < array.length; j++) if (array[j] === chr) return i; throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i); } }; var NO_SOURCE_SUPPORT = fails(function () { var unsafeInt = '9007199254740993'; var source; nativeParse(unsafeInt, function (key, value, context) { source = context.source; }); return source !== unsafeInt; }); var PROPER_BASE_PARSE = NATIVE_SYMBOL && !fails(function () { // Safari 9 bug return 1 / nativeParse('-0 \t') !== -Infinity; }); // `JSON.parse` method // https://tc39.es/ecma262/#sec-json.parse // https://github.com/tc39/proposal-json-parse-with-source $({ target: 'JSON', stat: true, forced: NO_SOURCE_SUPPORT }, { parse: function parse(text, reviver) { return PROPER_BASE_PARSE && !isCallable(reviver) ? nativeParse(text) : $parse(text, reviver); } });