You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					252 lines
				
				8.2 KiB
			
		
		
			
		
	
	
					252 lines
				
				8.2 KiB
			| 
											11 months ago
										 | '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); | ||
|  |   } | ||
|  | }); |