@player-ui/player
Version:
79 lines (64 loc) • 1.87 kB
text/typescript
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<any>;
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],
};
};