Spaces:
Running
Running
| const util = require('./util') | |
| let source | |
| let parseState | |
| let stack | |
| let pos | |
| let line | |
| let column | |
| let token | |
| let key | |
| let root | |
| module.exports = function parse (text, reviver) { | |
| source = String(text) | |
| parseState = 'start' | |
| stack = [] | |
| pos = 0 | |
| line = 1 | |
| column = 0 | |
| token = undefined | |
| key = undefined | |
| root = undefined | |
| do { | |
| token = lex() | |
| // This code is unreachable. | |
| // if (!parseStates[parseState]) { | |
| // throw invalidParseState() | |
| // } | |
| parseStates[parseState]() | |
| } while (token.type !== 'eof') | |
| if (typeof reviver === 'function') { | |
| return internalize({'': root}, '', reviver) | |
| } | |
| return root | |
| } | |
| function internalize (holder, name, reviver) { | |
| const value = holder[name] | |
| if (value != null && typeof value === 'object') { | |
| if (Array.isArray(value)) { | |
| for (let i = 0; i < value.length; i++) { | |
| const key = String(i) | |
| const replacement = internalize(value, key, reviver) | |
| if (replacement === undefined) { | |
| delete value[key] | |
| } else { | |
| Object.defineProperty(value, key, { | |
| value: replacement, | |
| writable: true, | |
| enumerable: true, | |
| configurable: true, | |
| }) | |
| } | |
| } | |
| } else { | |
| for (const key in value) { | |
| const replacement = internalize(value, key, reviver) | |
| if (replacement === undefined) { | |
| delete value[key] | |
| } else { | |
| Object.defineProperty(value, key, { | |
| value: replacement, | |
| writable: true, | |
| enumerable: true, | |
| configurable: true, | |
| }) | |
| } | |
| } | |
| } | |
| } | |
| return reviver.call(holder, name, value) | |
| } | |
| let lexState | |
| let buffer | |
| let doubleQuote | |
| let sign | |
| let c | |
| function lex () { | |
| lexState = 'default' | |
| buffer = '' | |
| doubleQuote = false | |
| sign = 1 | |
| for (;;) { | |
| c = peek() | |
| // This code is unreachable. | |
| // if (!lexStates[lexState]) { | |
| // throw invalidLexState(lexState) | |
| // } | |
| const token = lexStates[lexState]() | |
| if (token) { | |
| return token | |
| } | |
| } | |
| } | |
| function peek () { | |
| if (source[pos]) { | |
| return String.fromCodePoint(source.codePointAt(pos)) | |
| } | |
| } | |
| function read () { | |
| const c = peek() | |
| if (c === '\n') { | |
| line++ | |
| column = 0 | |
| } else if (c) { | |
| column += c.length | |
| } else { | |
| column++ | |
| } | |
| if (c) { | |
| pos += c.length | |
| } | |
| return c | |
| } | |
| const lexStates = { | |
| default () { | |
| switch (c) { | |
| case '\t': | |
| case '\v': | |
| case '\f': | |
| case ' ': | |
| case '\u00A0': | |
| case '\uFEFF': | |
| case '\n': | |
| case '\r': | |
| case '\u2028': | |
| case '\u2029': | |
| read() | |
| return | |
| case '/': | |
| read() | |
| lexState = 'comment' | |
| return | |
| case undefined: | |
| read() | |
| return newToken('eof') | |
| } | |
| if (util.isSpaceSeparator(c)) { | |
| read() | |
| return | |
| } | |
| // This code is unreachable. | |
| // if (!lexStates[parseState]) { | |
| // throw invalidLexState(parseState) | |
| // } | |
| return lexStates[parseState]() | |
| }, | |
| comment () { | |
| switch (c) { | |
| case '*': | |
| read() | |
| lexState = 'multiLineComment' | |
| return | |
| case '/': | |
| read() | |
| lexState = 'singleLineComment' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| multiLineComment () { | |
| switch (c) { | |
| case '*': | |
| read() | |
| lexState = 'multiLineCommentAsterisk' | |
| return | |
| case undefined: | |
| throw invalidChar(read()) | |
| } | |
| read() | |
| }, | |
| multiLineCommentAsterisk () { | |
| switch (c) { | |
| case '*': | |
| read() | |
| return | |
| case '/': | |
| read() | |
| lexState = 'default' | |
| return | |
| case undefined: | |
| throw invalidChar(read()) | |
| } | |
| read() | |
| lexState = 'multiLineComment' | |
| }, | |
| singleLineComment () { | |
| switch (c) { | |
| case '\n': | |
| case '\r': | |
| case '\u2028': | |
| case '\u2029': | |
| read() | |
| lexState = 'default' | |
| return | |
| case undefined: | |
| read() | |
| return newToken('eof') | |
| } | |
| read() | |
| }, | |
| value () { | |
| switch (c) { | |
| case '{': | |
| case '[': | |
| return newToken('punctuator', read()) | |
| case 'n': | |
| read() | |
| literal('ull') | |
| return newToken('null', null) | |
| case 't': | |
| read() | |
| literal('rue') | |
| return newToken('boolean', true) | |
| case 'f': | |
| read() | |
| literal('alse') | |
| return newToken('boolean', false) | |
| case '-': | |
| case '+': | |
| if (read() === '-') { | |
| sign = -1 | |
| } | |
| lexState = 'sign' | |
| return | |
| case '.': | |
| buffer = read() | |
| lexState = 'decimalPointLeading' | |
| return | |
| case '0': | |
| buffer = read() | |
| lexState = 'zero' | |
| return | |
| case '1': | |
| case '2': | |
| case '3': | |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| buffer = read() | |
| lexState = 'decimalInteger' | |
| return | |
| case 'I': | |
| read() | |
| literal('nfinity') | |
| return newToken('numeric', Infinity) | |
| case 'N': | |
| read() | |
| literal('aN') | |
| return newToken('numeric', NaN) | |
| case '"': | |
| case "'": | |
| doubleQuote = (read() === '"') | |
| buffer = '' | |
| lexState = 'string' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| identifierNameStartEscape () { | |
| if (c !== 'u') { | |
| throw invalidChar(read()) | |
| } | |
| read() | |
| const u = unicodeEscape() | |
| switch (u) { | |
| case '$': | |
| case '_': | |
| break | |
| default: | |
| if (!util.isIdStartChar(u)) { | |
| throw invalidIdentifier() | |
| } | |
| break | |
| } | |
| buffer += u | |
| lexState = 'identifierName' | |
| }, | |
| identifierName () { | |
| switch (c) { | |
| case '$': | |
| case '_': | |
| case '\u200C': | |
| case '\u200D': | |
| buffer += read() | |
| return | |
| case '\\': | |
| read() | |
| lexState = 'identifierNameEscape' | |
| return | |
| } | |
| if (util.isIdContinueChar(c)) { | |
| buffer += read() | |
| return | |
| } | |
| return newToken('identifier', buffer) | |
| }, | |
| identifierNameEscape () { | |
| if (c !== 'u') { | |
| throw invalidChar(read()) | |
| } | |
| read() | |
| const u = unicodeEscape() | |
| switch (u) { | |
| case '$': | |
| case '_': | |
| case '\u200C': | |
| case '\u200D': | |
| break | |
| default: | |
| if (!util.isIdContinueChar(u)) { | |
| throw invalidIdentifier() | |
| } | |
| break | |
| } | |
| buffer += u | |
| lexState = 'identifierName' | |
| }, | |
| sign () { | |
| switch (c) { | |
| case '.': | |
| buffer = read() | |
| lexState = 'decimalPointLeading' | |
| return | |
| case '0': | |
| buffer = read() | |
| lexState = 'zero' | |
| return | |
| case '1': | |
| case '2': | |
| case '3': | |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| buffer = read() | |
| lexState = 'decimalInteger' | |
| return | |
| case 'I': | |
| read() | |
| literal('nfinity') | |
| return newToken('numeric', sign * Infinity) | |
| case 'N': | |
| read() | |
| literal('aN') | |
| return newToken('numeric', NaN) | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| zero () { | |
| switch (c) { | |
| case '.': | |
| buffer += read() | |
| lexState = 'decimalPoint' | |
| return | |
| case 'e': | |
| case 'E': | |
| buffer += read() | |
| lexState = 'decimalExponent' | |
| return | |
| case 'x': | |
| case 'X': | |
| buffer += read() | |
| lexState = 'hexadecimal' | |
| return | |
| } | |
| return newToken('numeric', sign * 0) | |
| }, | |
| decimalInteger () { | |
| switch (c) { | |
| case '.': | |
| buffer += read() | |
| lexState = 'decimalPoint' | |
| return | |
| case 'e': | |
| case 'E': | |
| buffer += read() | |
| lexState = 'decimalExponent' | |
| return | |
| } | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| return | |
| } | |
| return newToken('numeric', sign * Number(buffer)) | |
| }, | |
| decimalPointLeading () { | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| lexState = 'decimalFraction' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| decimalPoint () { | |
| switch (c) { | |
| case 'e': | |
| case 'E': | |
| buffer += read() | |
| lexState = 'decimalExponent' | |
| return | |
| } | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| lexState = 'decimalFraction' | |
| return | |
| } | |
| return newToken('numeric', sign * Number(buffer)) | |
| }, | |
| decimalFraction () { | |
| switch (c) { | |
| case 'e': | |
| case 'E': | |
| buffer += read() | |
| lexState = 'decimalExponent' | |
| return | |
| } | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| return | |
| } | |
| return newToken('numeric', sign * Number(buffer)) | |
| }, | |
| decimalExponent () { | |
| switch (c) { | |
| case '+': | |
| case '-': | |
| buffer += read() | |
| lexState = 'decimalExponentSign' | |
| return | |
| } | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| lexState = 'decimalExponentInteger' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| decimalExponentSign () { | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| lexState = 'decimalExponentInteger' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| decimalExponentInteger () { | |
| if (util.isDigit(c)) { | |
| buffer += read() | |
| return | |
| } | |
| return newToken('numeric', sign * Number(buffer)) | |
| }, | |
| hexadecimal () { | |
| if (util.isHexDigit(c)) { | |
| buffer += read() | |
| lexState = 'hexadecimalInteger' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| hexadecimalInteger () { | |
| if (util.isHexDigit(c)) { | |
| buffer += read() | |
| return | |
| } | |
| return newToken('numeric', sign * Number(buffer)) | |
| }, | |
| string () { | |
| switch (c) { | |
| case '\\': | |
| read() | |
| buffer += escape() | |
| return | |
| case '"': | |
| if (doubleQuote) { | |
| read() | |
| return newToken('string', buffer) | |
| } | |
| buffer += read() | |
| return | |
| case "'": | |
| if (!doubleQuote) { | |
| read() | |
| return newToken('string', buffer) | |
| } | |
| buffer += read() | |
| return | |
| case '\n': | |
| case '\r': | |
| throw invalidChar(read()) | |
| case '\u2028': | |
| case '\u2029': | |
| separatorChar(c) | |
| break | |
| case undefined: | |
| throw invalidChar(read()) | |
| } | |
| buffer += read() | |
| }, | |
| start () { | |
| switch (c) { | |
| case '{': | |
| case '[': | |
| return newToken('punctuator', read()) | |
| // This code is unreachable since the default lexState handles eof. | |
| // case undefined: | |
| // return newToken('eof') | |
| } | |
| lexState = 'value' | |
| }, | |
| beforePropertyName () { | |
| switch (c) { | |
| case '$': | |
| case '_': | |
| buffer = read() | |
| lexState = 'identifierName' | |
| return | |
| case '\\': | |
| read() | |
| lexState = 'identifierNameStartEscape' | |
| return | |
| case '}': | |
| return newToken('punctuator', read()) | |
| case '"': | |
| case "'": | |
| doubleQuote = (read() === '"') | |
| lexState = 'string' | |
| return | |
| } | |
| if (util.isIdStartChar(c)) { | |
| buffer += read() | |
| lexState = 'identifierName' | |
| return | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| afterPropertyName () { | |
| if (c === ':') { | |
| return newToken('punctuator', read()) | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| beforePropertyValue () { | |
| lexState = 'value' | |
| }, | |
| afterPropertyValue () { | |
| switch (c) { | |
| case ',': | |
| case '}': | |
| return newToken('punctuator', read()) | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| beforeArrayValue () { | |
| if (c === ']') { | |
| return newToken('punctuator', read()) | |
| } | |
| lexState = 'value' | |
| }, | |
| afterArrayValue () { | |
| switch (c) { | |
| case ',': | |
| case ']': | |
| return newToken('punctuator', read()) | |
| } | |
| throw invalidChar(read()) | |
| }, | |
| end () { | |
| // This code is unreachable since it's handled by the default lexState. | |
| // if (c === undefined) { | |
| // read() | |
| // return newToken('eof') | |
| // } | |
| throw invalidChar(read()) | |
| }, | |
| } | |
| function newToken (type, value) { | |
| return { | |
| type, | |
| value, | |
| line, | |
| column, | |
| } | |
| } | |
| function literal (s) { | |
| for (const c of s) { | |
| const p = peek() | |
| if (p !== c) { | |
| throw invalidChar(read()) | |
| } | |
| read() | |
| } | |
| } | |
| function escape () { | |
| const c = peek() | |
| switch (c) { | |
| case 'b': | |
| read() | |
| return '\b' | |
| case 'f': | |
| read() | |
| return '\f' | |
| case 'n': | |
| read() | |
| return '\n' | |
| case 'r': | |
| read() | |
| return '\r' | |
| case 't': | |
| read() | |
| return '\t' | |
| case 'v': | |
| read() | |
| return '\v' | |
| case '0': | |
| read() | |
| if (util.isDigit(peek())) { | |
| throw invalidChar(read()) | |
| } | |
| return '\0' | |
| case 'x': | |
| read() | |
| return hexEscape() | |
| case 'u': | |
| read() | |
| return unicodeEscape() | |
| case '\n': | |
| case '\u2028': | |
| case '\u2029': | |
| read() | |
| return '' | |
| case '\r': | |
| read() | |
| if (peek() === '\n') { | |
| read() | |
| } | |
| return '' | |
| case '1': | |
| case '2': | |
| case '3': | |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| throw invalidChar(read()) | |
| case undefined: | |
| throw invalidChar(read()) | |
| } | |
| return read() | |
| } | |
| function hexEscape () { | |
| let buffer = '' | |
| let c = peek() | |
| if (!util.isHexDigit(c)) { | |
| throw invalidChar(read()) | |
| } | |
| buffer += read() | |
| c = peek() | |
| if (!util.isHexDigit(c)) { | |
| throw invalidChar(read()) | |
| } | |
| buffer += read() | |
| return String.fromCodePoint(parseInt(buffer, 16)) | |
| } | |
| function unicodeEscape () { | |
| let buffer = '' | |
| let count = 4 | |
| while (count-- > 0) { | |
| const c = peek() | |
| if (!util.isHexDigit(c)) { | |
| throw invalidChar(read()) | |
| } | |
| buffer += read() | |
| } | |
| return String.fromCodePoint(parseInt(buffer, 16)) | |
| } | |
| const parseStates = { | |
| start () { | |
| if (token.type === 'eof') { | |
| throw invalidEOF() | |
| } | |
| push() | |
| }, | |
| beforePropertyName () { | |
| switch (token.type) { | |
| case 'identifier': | |
| case 'string': | |
| key = token.value | |
| parseState = 'afterPropertyName' | |
| return | |
| case 'punctuator': | |
| // This code is unreachable since it's handled by the lexState. | |
| // if (token.value !== '}') { | |
| // throw invalidToken() | |
| // } | |
| pop() | |
| return | |
| case 'eof': | |
| throw invalidEOF() | |
| } | |
| // This code is unreachable since it's handled by the lexState. | |
| // throw invalidToken() | |
| }, | |
| afterPropertyName () { | |
| // This code is unreachable since it's handled by the lexState. | |
| // if (token.type !== 'punctuator' || token.value !== ':') { | |
| // throw invalidToken() | |
| // } | |
| if (token.type === 'eof') { | |
| throw invalidEOF() | |
| } | |
| parseState = 'beforePropertyValue' | |
| }, | |
| beforePropertyValue () { | |
| if (token.type === 'eof') { | |
| throw invalidEOF() | |
| } | |
| push() | |
| }, | |
| beforeArrayValue () { | |
| if (token.type === 'eof') { | |
| throw invalidEOF() | |
| } | |
| if (token.type === 'punctuator' && token.value === ']') { | |
| pop() | |
| return | |
| } | |
| push() | |
| }, | |
| afterPropertyValue () { | |
| // This code is unreachable since it's handled by the lexState. | |
| // if (token.type !== 'punctuator') { | |
| // throw invalidToken() | |
| // } | |
| if (token.type === 'eof') { | |
| throw invalidEOF() | |
| } | |
| switch (token.value) { | |
| case ',': | |
| parseState = 'beforePropertyName' | |
| return | |
| case '}': | |
| pop() | |
| } | |
| // This code is unreachable since it's handled by the lexState. | |
| // throw invalidToken() | |
| }, | |
| afterArrayValue () { | |
| // This code is unreachable since it's handled by the lexState. | |
| // if (token.type !== 'punctuator') { | |
| // throw invalidToken() | |
| // } | |
| if (token.type === 'eof') { | |
| throw invalidEOF() | |
| } | |
| switch (token.value) { | |
| case ',': | |
| parseState = 'beforeArrayValue' | |
| return | |
| case ']': | |
| pop() | |
| } | |
| // This code is unreachable since it's handled by the lexState. | |
| // throw invalidToken() | |
| }, | |
| end () { | |
| // This code is unreachable since it's handled by the lexState. | |
| // if (token.type !== 'eof') { | |
| // throw invalidToken() | |
| // } | |
| }, | |
| } | |
| function push () { | |
| let value | |
| switch (token.type) { | |
| case 'punctuator': | |
| switch (token.value) { | |
| case '{': | |
| value = {} | |
| break | |
| case '[': | |
| value = [] | |
| break | |
| } | |
| break | |
| case 'null': | |
| case 'boolean': | |
| case 'numeric': | |
| case 'string': | |
| value = token.value | |
| break | |
| // This code is unreachable. | |
| // default: | |
| // throw invalidToken() | |
| } | |
| if (root === undefined) { | |
| root = value | |
| } else { | |
| const parent = stack[stack.length - 1] | |
| if (Array.isArray(parent)) { | |
| parent.push(value) | |
| } else { | |
| Object.defineProperty(parent, key, { | |
| value, | |
| writable: true, | |
| enumerable: true, | |
| configurable: true, | |
| }) | |
| } | |
| } | |
| if (value !== null && typeof value === 'object') { | |
| stack.push(value) | |
| if (Array.isArray(value)) { | |
| parseState = 'beforeArrayValue' | |
| } else { | |
| parseState = 'beforePropertyName' | |
| } | |
| } else { | |
| const current = stack[stack.length - 1] | |
| if (current == null) { | |
| parseState = 'end' | |
| } else if (Array.isArray(current)) { | |
| parseState = 'afterArrayValue' | |
| } else { | |
| parseState = 'afterPropertyValue' | |
| } | |
| } | |
| } | |
| function pop () { | |
| stack.pop() | |
| const current = stack[stack.length - 1] | |
| if (current == null) { | |
| parseState = 'end' | |
| } else if (Array.isArray(current)) { | |
| parseState = 'afterArrayValue' | |
| } else { | |
| parseState = 'afterPropertyValue' | |
| } | |
| } | |
| // This code is unreachable. | |
| // function invalidParseState () { | |
| // return new Error(`JSON5: invalid parse state '${parseState}'`) | |
| // } | |
| // This code is unreachable. | |
| // function invalidLexState (state) { | |
| // return new Error(`JSON5: invalid lex state '${state}'`) | |
| // } | |
| function invalidChar (c) { | |
| if (c === undefined) { | |
| return syntaxError(`JSON5: invalid end of input at ${line}:${column}`) | |
| } | |
| return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`) | |
| } | |
| function invalidEOF () { | |
| return syntaxError(`JSON5: invalid end of input at ${line}:${column}`) | |
| } | |
| // This code is unreachable. | |
| // function invalidToken () { | |
| // if (token.type === 'eof') { | |
| // return syntaxError(`JSON5: invalid end of input at ${line}:${column}`) | |
| // } | |
| // const c = String.fromCodePoint(token.value.codePointAt(0)) | |
| // return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`) | |
| // } | |
| function invalidIdentifier () { | |
| column -= 5 | |
| return syntaxError(`JSON5: invalid identifier character at ${line}:${column}`) | |
| } | |
| function separatorChar (c) { | |
| console.warn(`JSON5: '${formatChar(c)}' in strings is not valid ECMAScript; consider escaping`) | |
| } | |
| function formatChar (c) { | |
| const replacements = { | |
| "'": "\\'", | |
| '"': '\\"', | |
| '\\': '\\\\', | |
| '\b': '\\b', | |
| '\f': '\\f', | |
| '\n': '\\n', | |
| '\r': '\\r', | |
| '\t': '\\t', | |
| '\v': '\\v', | |
| '\0': '\\0', | |
| '\u2028': '\\u2028', | |
| '\u2029': '\\u2029', | |
| } | |
| if (replacements[c]) { | |
| return replacements[c] | |
| } | |
| if (c < ' ') { | |
| const hexString = c.charCodeAt(0).toString(16) | |
| return '\\x' + ('00' + hexString).substring(hexString.length) | |
| } | |
| return c | |
| } | |
| function syntaxError (message) { | |
| const err = new SyntaxError(message) | |
| err.lineNumber = line | |
| err.columnNumber = column | |
| return err | |
| } | |