UNPKG

jsonc-simple-parser

Version:

A simple JSON parser that supports comments and optional trailing commas.

134 lines (133 loc) 4.93 kB
/* IMPORT */ import detokenize from './detokenize.js'; import parse from './parse.js'; import tokenize from './tokenize/index.js'; import Utils from './utils.js'; /* MAIN */ //FIXME: This is wonky, it should be much more robust, it should probably be rewritten from scratch const getLookupToken = (ast, position) => { let tokenPosition = null; let offsetCurrent = 0; const checkPositionToken = (token) => { if (token.start > position) return; if (token.end <= (position - 1)) return; if (tokenPosition && Utils.isTokenLiteral(tokenPosition)) return; if (tokenPosition && Utils.isTokenIgnored(token)) return; tokenPosition = token; }; const parseChild = (token, parent, depth, index) => { const { type, source } = token; const start = offsetCurrent; const end = (offsetCurrent += source.length); const ltoken = { type, source, token, parent, depth, index, start, end }; checkPositionToken(ltoken); return ltoken; }; const parseParent = (token, parent, depth, index) => { const { type } = token; const ltoken = { type, children: [], token, parent, depth, index }; ltoken.children = token.children.map((child, index) => parseToken(child, ltoken, depth + 1, index)).filter(Utils.isTokenLiteral); ltoken.children.forEach((token, index) => token.index = index); return ltoken; }; const parseToken = (token, parent, depth, index) => { if ('children' in token) return parseParent(token, parent, depth, index); return parseChild(token, parent, depth, index); }; parseToken(ast, null, -1, -1); return tokenPosition; }; const getLookupPath = (token) => { if (!token) return []; const path = []; while (token) { const parent = token.parent; if (!parent) break; if (Utils.isTokenLiteral(token)) { if (parent.type === 'Object') { if (Utils.isEven(token.index)) { path.unshift(JSON.parse(token.source)); } else { path.unshift(JSON.parse(parent.children[token.index - 1].source)); } } else if (parent.type === 'Array') { path.unshift(token.index); } } token = parent; } return path; }; const getLookupIsInsideProperty = (token) => { if (!token) return false; const parentType = token.parent?.type; if (parentType === 'Object') return Utils.isTokenLiteral(token) ? Utils.isEven(token.index) : token.parent?.parent?.type === 'Array'; if (parentType === 'Array') return Utils.isTokenLiteral(token) || token.parent?.parent?.type === 'Array'; return false; }; const getLookupIsInsideValue = (token) => { if (!token) return false; const isParentEmpty = !token.parent?.children.length; const parentType = token.parent?.type; if (parentType === 'Object') return isParentEmpty || Utils.isTokenDelimiter(token) || (Utils.isTokenLiteral(token) && Utils.isOdd(token.index)); if (parentType === 'Array') return (token.depth > 1); return false; }; const getLookupProperty = (token, isInsideProperty) => { if (!isInsideProperty || !token) return; const parentType = token.parent?.type; if (Utils.isTokenLiteral(token)) { if (parentType === 'Array') return token.index; return parse(detokenize(token)); } else { if (parentType === 'Array') return token.parent?.index; } }; const getLookupValue = (token, isInsideValue, usePartialScanning) => { if (!isInsideValue || !token) return; if (Utils.isTokenLiteral(token)) return parse(detokenize(token)); if (usePartialScanning) return; const { parent } = token; if (!parent || !parent.token) return; if (parent.type !== 'Array' && parent.type !== 'Object') return; return parse(detokenize(parent.token)); }; const lookup = (text, position, usePartialScanning = true) => { const limit = usePartialScanning ? position : Infinity; const ast = tokenize(text, limit); const token = getLookupToken(ast, position); const path = getLookupPath(token); const isInsideProperty = getLookupIsInsideProperty(token); const isInsideValue = getLookupIsInsideValue(token); const property = getLookupProperty(token, isInsideProperty); const value = getLookupValue(token, isInsideValue, usePartialScanning); const t = token ? { type: token.type, start: token.start, end: token.end, source: token.source } : undefined; const result = { path, property, value, token: t, isInsideProperty, isInsideValue }; return result; }; /* EXPORT */ export default lookup;