447 lines
12 KiB
JavaScript
447 lines
12 KiB
JavaScript
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
var util = require('./util.cjs');
|
|
|
|
function emit(value) {
|
|
return (data, i) => ({
|
|
matched: true,
|
|
position: i,
|
|
value: value
|
|
});
|
|
}
|
|
function make(
|
|
f) {
|
|
return (data, i) => ({
|
|
matched: true,
|
|
position: i,
|
|
value: f(data, i)
|
|
});
|
|
}
|
|
function action(
|
|
f) {
|
|
return (data, i) => {
|
|
f(data, i);
|
|
return {
|
|
matched: true,
|
|
position: i,
|
|
value: null
|
|
};
|
|
};
|
|
}
|
|
function fail(
|
|
data, i) {
|
|
return { matched: false };
|
|
}
|
|
function error(message) {
|
|
return (data, i) => {
|
|
throw new Error((message instanceof Function) ? message(data, i) : message);
|
|
};
|
|
}
|
|
function token(
|
|
onToken,
|
|
onEnd) {
|
|
return (data, i) => {
|
|
let position = i;
|
|
let value = undefined;
|
|
if (i < data.tokens.length) {
|
|
value = onToken(data.tokens[i], data, i);
|
|
if (value !== undefined) {
|
|
position++;
|
|
}
|
|
}
|
|
else {
|
|
onEnd?.(data, i);
|
|
}
|
|
return (value === undefined)
|
|
? { matched: false }
|
|
: {
|
|
matched: true,
|
|
position: position,
|
|
value: value
|
|
};
|
|
};
|
|
}
|
|
function any(data, i) {
|
|
return (i < data.tokens.length)
|
|
? {
|
|
matched: true,
|
|
position: i + 1,
|
|
value: data.tokens[i]
|
|
}
|
|
: { matched: false };
|
|
}
|
|
function satisfy(
|
|
test) {
|
|
return (data, i) => (i < data.tokens.length && test(data.tokens[i], data, i))
|
|
? {
|
|
matched: true,
|
|
position: i + 1,
|
|
value: data.tokens[i]
|
|
}
|
|
: { matched: false };
|
|
}
|
|
function mapInner(r, f) {
|
|
return (r.matched) ? ({
|
|
matched: true,
|
|
position: r.position,
|
|
value: f(r.value, r.position)
|
|
}) : r;
|
|
}
|
|
function mapOuter(r, f) {
|
|
return (r.matched) ? f(r) : r;
|
|
}
|
|
function map(p, mapper) {
|
|
return (data, i) => mapInner(p(data, i), (v, j) => mapper(v, data, i, j));
|
|
}
|
|
function map1(p,
|
|
mapper) {
|
|
return (data, i) => mapOuter(p(data, i), (m) => mapper(m, data, i));
|
|
}
|
|
function peek(p, f) {
|
|
return (data, i) => {
|
|
const r = p(data, i);
|
|
f(r, data, i);
|
|
return r;
|
|
};
|
|
}
|
|
function option(p, def) {
|
|
return (data, i) => {
|
|
const r = p(data, i);
|
|
return (r.matched)
|
|
? r
|
|
: {
|
|
matched: true,
|
|
position: i,
|
|
value: def
|
|
};
|
|
};
|
|
}
|
|
function not(p) {
|
|
return (data, i) => {
|
|
const r = p(data, i);
|
|
return (r.matched)
|
|
? { matched: false }
|
|
: {
|
|
matched: true,
|
|
position: i,
|
|
value: true
|
|
};
|
|
};
|
|
}
|
|
function choice(...ps) {
|
|
return (data, i) => {
|
|
for (const p of ps) {
|
|
const result = p(data, i);
|
|
if (result.matched) {
|
|
return result;
|
|
}
|
|
}
|
|
return { matched: false };
|
|
};
|
|
}
|
|
function otherwise(pa, pb) {
|
|
return (data, i) => {
|
|
const r1 = pa(data, i);
|
|
return (r1.matched)
|
|
? r1
|
|
: pb(data, i);
|
|
};
|
|
}
|
|
function longest(...ps) {
|
|
return (data, i) => {
|
|
let match = undefined;
|
|
for (const p of ps) {
|
|
const result = p(data, i);
|
|
if (result.matched && (!match || match.position < result.position)) {
|
|
match = result;
|
|
}
|
|
}
|
|
return match || { matched: false };
|
|
};
|
|
}
|
|
function takeWhile(p,
|
|
test) {
|
|
return (data, i) => {
|
|
const values = [];
|
|
let success = true;
|
|
do {
|
|
const r = p(data, i);
|
|
if (r.matched && test(r.value, values.length + 1, data, i, r.position)) {
|
|
values.push(r.value);
|
|
i = r.position;
|
|
}
|
|
else {
|
|
success = false;
|
|
}
|
|
} while (success);
|
|
return {
|
|
matched: true,
|
|
position: i,
|
|
value: values
|
|
};
|
|
};
|
|
}
|
|
function takeUntil(p,
|
|
test) {
|
|
return takeWhile(p, (value, n, data, i, j) => !test(value, n, data, i, j));
|
|
}
|
|
function takeWhileP(pValue, pTest) {
|
|
return takeWhile(pValue, (value, n, data, i) => pTest(data, i).matched);
|
|
}
|
|
function takeUntilP(pValue, pTest) {
|
|
return takeWhile(pValue, (value, n, data, i) => !pTest(data, i).matched);
|
|
}
|
|
function many(p) {
|
|
return takeWhile(p, () => true);
|
|
}
|
|
function many1(p) {
|
|
return ab(p, many(p), (head, tail) => [head, ...tail]);
|
|
}
|
|
function ab(pa, pb, join) {
|
|
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join(ma.value, vb, data, i, j)));
|
|
}
|
|
function left(pa, pb) {
|
|
return ab(pa, pb, (va) => va);
|
|
}
|
|
function right(pa, pb) {
|
|
return ab(pa, pb, (va, vb) => vb);
|
|
}
|
|
function abc(pa, pb, pc, join) {
|
|
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join(ma.value, mb.value, vc, data, i, j))));
|
|
}
|
|
function middle(pa, pb, pc) {
|
|
return abc(pa, pb, pc, (ra, rb) => rb);
|
|
}
|
|
function all(...ps) {
|
|
return (data, i) => {
|
|
const result = [];
|
|
let position = i;
|
|
for (const p of ps) {
|
|
const r1 = p(data, position);
|
|
if (r1.matched) {
|
|
result.push(r1.value);
|
|
position = r1.position;
|
|
}
|
|
else {
|
|
return { matched: false };
|
|
}
|
|
}
|
|
return {
|
|
matched: true,
|
|
position: position,
|
|
value: result
|
|
};
|
|
};
|
|
}
|
|
function skip(...ps) {
|
|
return map(all(...ps), () => null);
|
|
}
|
|
function flatten(...ps) {
|
|
return flatten1(all(...ps));
|
|
}
|
|
function flatten1(p) {
|
|
return map(p, (vs) => vs.flatMap((v) => v));
|
|
}
|
|
function sepBy1(pValue, pSep) {
|
|
return ab(pValue, many(right(pSep, pValue)), (head, tail) => [head, ...tail]);
|
|
}
|
|
function sepBy(pValue, pSep) {
|
|
return otherwise(sepBy1(pValue, pSep), emit([]));
|
|
}
|
|
function chainReduce(acc,
|
|
f) {
|
|
return (data, i) => {
|
|
let loop = true;
|
|
let acc1 = acc;
|
|
let pos = i;
|
|
do {
|
|
const r = f(acc1, data, pos)(data, pos);
|
|
if (r.matched) {
|
|
acc1 = r.value;
|
|
pos = r.position;
|
|
}
|
|
else {
|
|
loop = false;
|
|
}
|
|
} while (loop);
|
|
return {
|
|
matched: true,
|
|
position: pos,
|
|
value: acc1
|
|
};
|
|
};
|
|
}
|
|
function reduceLeft(acc, p,
|
|
reducer) {
|
|
return chainReduce(acc, (acc) => map(p, (v, data, i, j) => reducer(acc, v, data, i, j)));
|
|
}
|
|
function reduceRight(p, acc,
|
|
reducer) {
|
|
return map(many(p), (vs, data, i, j) => vs.reduceRight((acc, v) => reducer(v, acc, data, i, j), acc));
|
|
}
|
|
function leftAssoc1(pLeft, pOper) {
|
|
return chain(pLeft, (v0) => reduceLeft(v0, pOper, (acc, f) => f(acc)));
|
|
}
|
|
function rightAssoc1(pOper, pRight) {
|
|
return ab(reduceRight(pOper, (y) => y, (f, acc) => (y) => f(acc(y))), pRight, (f, v) => f(v));
|
|
}
|
|
function leftAssoc2(pLeft, pOper, pRight) {
|
|
return chain(pLeft, (v0) => reduceLeft(v0, ab(pOper, pRight, (f, y) => [f, y]), (acc, [f, y]) => f(acc, y)));
|
|
}
|
|
function rightAssoc2(pLeft, pOper, pRight) {
|
|
return ab(reduceRight(ab(pLeft, pOper, (x, f) => [x, f]), (y) => y, ([x, f], acc) => (y) => f(x, acc(y))), pRight, (f, v) => f(v));
|
|
}
|
|
function condition(cond, pTrue, pFalse) {
|
|
return (data, i) => (cond(data, i))
|
|
? pTrue(data, i)
|
|
: pFalse(data, i);
|
|
}
|
|
function decide(p) {
|
|
return (data, i) => mapOuter(p(data, i), (m1) => m1.value(data, m1.position));
|
|
}
|
|
function chain(p,
|
|
f) {
|
|
return (data, i) => mapOuter(p(data, i), (m1) => f(m1.value, data, i, m1.position)(data, m1.position));
|
|
}
|
|
function ahead(p) {
|
|
return (data, i) => mapOuter(p(data, i), (m1) => ({
|
|
matched: true,
|
|
position: i,
|
|
value: m1.value
|
|
}));
|
|
}
|
|
function recursive(f) {
|
|
return function (data, i) {
|
|
return f()(data, i);
|
|
};
|
|
}
|
|
function start(data, i) {
|
|
return (i !== 0)
|
|
? { matched: false }
|
|
: {
|
|
matched: true,
|
|
position: i,
|
|
value: true
|
|
};
|
|
}
|
|
function end(data, i) {
|
|
return (i < data.tokens.length)
|
|
? { matched: false }
|
|
: {
|
|
matched: true,
|
|
position: i,
|
|
value: true
|
|
};
|
|
}
|
|
function remainingTokensNumber(data, i) {
|
|
return data.tokens.length - i;
|
|
}
|
|
function parserPosition(data, i, formatToken, contextTokens = 3) {
|
|
const len = data.tokens.length;
|
|
const lowIndex = util.clamp(0, i - contextTokens, len - contextTokens);
|
|
const highIndex = util.clamp(contextTokens, i + 1 + contextTokens, len);
|
|
const tokensSlice = data.tokens.slice(lowIndex, highIndex);
|
|
const lines = [];
|
|
const indexWidth = String(highIndex - 1).length + 1;
|
|
if (i < 0) {
|
|
lines.push(`${String(i).padStart(indexWidth)} >>`);
|
|
}
|
|
if (0 < lowIndex) {
|
|
lines.push('...'.padStart(indexWidth + 6));
|
|
}
|
|
for (let j = 0; j < tokensSlice.length; j++) {
|
|
const index = lowIndex + j;
|
|
lines.push(`${String(index).padStart(indexWidth)} ${(index === i ? '>' : ' ')} ${util.escapeWhitespace(formatToken(tokensSlice[j]))}`);
|
|
}
|
|
if (highIndex < len) {
|
|
lines.push('...'.padStart(indexWidth + 6));
|
|
}
|
|
if (len <= i) {
|
|
lines.push(`${String(i).padStart(indexWidth)} >>`);
|
|
}
|
|
return lines.join('\n');
|
|
}
|
|
function parse(parser, tokens, options, formatToken = JSON.stringify) {
|
|
const data = { tokens: tokens, options: options };
|
|
const result = parser(data, 0);
|
|
if (!result.matched) {
|
|
throw new Error('No match');
|
|
}
|
|
if (result.position < data.tokens.length) {
|
|
throw new Error(`Partial match. Parsing stopped at:\n${parserPosition(data, result.position, formatToken)}`);
|
|
}
|
|
return result.value;
|
|
}
|
|
function tryParse(parser, tokens, options) {
|
|
const result = parser({ tokens: tokens, options: options }, 0);
|
|
return (result.matched)
|
|
? result.value
|
|
: undefined;
|
|
}
|
|
function match(matcher, tokens, options) {
|
|
const result = matcher({ tokens: tokens, options: options }, 0);
|
|
return result.value;
|
|
}
|
|
|
|
exports.ab = ab;
|
|
exports.abc = abc;
|
|
exports.action = action;
|
|
exports.ahead = ahead;
|
|
exports.all = all;
|
|
exports.and = all;
|
|
exports.any = any;
|
|
exports.chain = chain;
|
|
exports.chainReduce = chainReduce;
|
|
exports.choice = choice;
|
|
exports.condition = condition;
|
|
exports.decide = decide;
|
|
exports.discard = skip;
|
|
exports.eitherOr = otherwise;
|
|
exports.emit = emit;
|
|
exports.end = end;
|
|
exports.eof = end;
|
|
exports.error = error;
|
|
exports.fail = fail;
|
|
exports.flatten = flatten;
|
|
exports.flatten1 = flatten1;
|
|
exports.left = left;
|
|
exports.leftAssoc1 = leftAssoc1;
|
|
exports.leftAssoc2 = leftAssoc2;
|
|
exports.longest = longest;
|
|
exports.lookAhead = ahead;
|
|
exports.make = make;
|
|
exports.many = many;
|
|
exports.many1 = many1;
|
|
exports.map = map;
|
|
exports.map1 = map1;
|
|
exports.match = match;
|
|
exports.middle = middle;
|
|
exports.not = not;
|
|
exports.of = emit;
|
|
exports.option = option;
|
|
exports.or = choice;
|
|
exports.otherwise = otherwise;
|
|
exports.parse = parse;
|
|
exports.parserPosition = parserPosition;
|
|
exports.peek = peek;
|
|
exports.recursive = recursive;
|
|
exports.reduceLeft = reduceLeft;
|
|
exports.reduceRight = reduceRight;
|
|
exports.remainingTokensNumber = remainingTokensNumber;
|
|
exports.right = right;
|
|
exports.rightAssoc1 = rightAssoc1;
|
|
exports.rightAssoc2 = rightAssoc2;
|
|
exports.satisfy = satisfy;
|
|
exports.sepBy = sepBy;
|
|
exports.sepBy1 = sepBy1;
|
|
exports.skip = skip;
|
|
exports.some = many1;
|
|
exports.start = start;
|
|
exports.takeUntil = takeUntil;
|
|
exports.takeUntilP = takeUntilP;
|
|
exports.takeWhile = takeWhile;
|
|
exports.takeWhileP = takeWhileP;
|
|
exports.token = token;
|
|
exports.tryParse = tryParse;
|