forked from FINAKON/HelpProject
1. Initial Commit - a boiler plate code and POC to realize the concept of context sensitive help 2. Frontend code written in ReactJS 3. Backend code written in Java, Spring Boot Framework 4. Frontend Start: pre-requisites : node, npm npm run dev ==> to start the frontend vite server 5. Backend Start: pre-requisites : java, mvn mvn spring-boot:run ==> to start the backend server 6. Visit http://localhost:5173/ for basic demo of help, press F1 in textboxes 7. Visit http://localhost:5173/editor and enter "admin123" to add/modify texts. Happy Coding !!! Thank you, Bhargava.
1115 lines
22 KiB
JavaScript
1115 lines
22 KiB
JavaScript
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
|
|
}
|