import flatten from "arr-flatten"; import type { Parser } from "parsimmon"; import P from "parsimmon"; import type { Parser as BindingParser } from "../../ast"; import { toValue, toConcatenatedNode, toQuery, toPath, toExpression, } from "../../ast"; const doubleQuote = P.string('"'); const singleQuote = P.string("'"); const backTick = P.string("`"); const identifier = P.regex(/[\w\-@]+/) .desc("identifier") .map(toValue); // eslint-disable-next-line prefer-const let path: Parser; const futurePath = P.lazy(() => path); const nestedPath = futurePath .trim(P.optWhitespace) .wrap(P.string("{{"), P.string("}}")) .map(toPath); const nestedExpression = P.regex(/[^`]*/) .wrap(backTick, backTick) .map(toExpression); const segment = P.alt(identifier, nestedPath, nestedExpression) .atLeast(1) .map(flatten) .map(toConcatenatedNode as any); const optionallyQuotedSegment = P.alt( P.regex(/[^"]*/).wrap(doubleQuote, doubleQuote).map(toValue), P.regex(/[^']*/).wrap(singleQuote, singleQuote).map(toValue), segment, ); const query = P.seq( optionallyQuotedSegment, P.string("=").times(1, 3).trim(P.optWhitespace), optionallyQuotedSegment, ).map(([key, , value]) => toQuery(key as any, value as any)); const brackets = P.alt(query, optionallyQuotedSegment) .trim(P.optWhitespace) .wrap(P.string("["), P.string("]")) .many(); const segmentAndBrackets = P.seqMap(segment, brackets, (s, bs) => [s, ...bs]); path = P.sepBy(segmentAndBrackets, P.string(".")).map(flatten); /** Parse a binding using parsimmon */ export const parse: BindingParser = (binding) => { const result = path.parse(binding); if (result.status) { return { status: true, path: { name: "PathNode", path: result.value, }, }; } return { status: false, error: result.expected[0], }; };