jsonc-simple-parser
Version:
A simple JSON parser that supports comments and optional trailing commas.
134 lines (133 loc) • 4.93 kB
JavaScript
/* 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;