parsinator
Version:
A parser combinator library for javascript and typescript
495 lines (488 loc) • 15 kB
JavaScript
;
var Parsinator = (() => {
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/parsinator.ts
var parsinator_exports = {};
__export(parsinator_exports, {
ParseError: () => ParseError,
VERSION: () => VERSION,
between: () => between,
buildExpressionParser: () => buildExpressionParser,
choice: () => choice,
count: () => count,
debugTrace: () => debugTrace,
end: () => end,
fail: () => fail,
fromGenerator: () => fromGenerator,
many: () => many,
many1: () => many1,
map: () => map,
maybe: () => maybe,
peek: () => peek,
regex: () => regex,
regexMatch: () => regexMatch,
run: () => run,
runToEnd: () => runToEnd,
sepBy: () => sepBy,
sepBy1: () => sepBy1,
sequence: () => sequence,
str: () => str,
surround: () => surround,
until: () => until,
wrapFail: () => wrapFail
});
// src/lib/ParserTypes.ts
var ParseError = class extends Error {
constructor(msg, line, col, state) {
super(`Parse failure at ${line}:${col}: ${msg}
${formatState(state)}`);
this.line = line;
this.col = col;
this.msg = msg;
this.state = state;
this.offset = state.offset;
this.input = state.input;
}
};
function formatState(state) {
var startOffset = Math.max(0, state.offset - 40);
var endOffset = Math.min(state.input.length, state.offset + 40);
var substr = (startOffset === 0 ? "" : "...") + state.input.replace(/[\t\r\n\v\f]/g, "\xB7").slice(startOffset, endOffset) + (endOffset === state.input.length ? "" : "...");
var charsBefore = (startOffset === 0 ? 0 : 3) + state.input.slice(startOffset, state.offset).length;
var marker = " ".repeat(charsBefore) + "^";
return `-> \xAB${substr}\xBB
${marker} `;
}
// src/lib/ParserHelpers.ts
function resultFailure(msg, state) {
var lines = 0;
var lastLineStart = 0;
for (var i = 0; i < state.offset; ++i) {
if (state.input[i] === "\n") {
lines++;
lastLineStart = i + 1;
}
}
var line = 1 + lines;
var col = 1 + state.offset - lastLineStart;
return new ParseError(msg, line, col, state);
}
// src/lib/Parser.ts
function makeParser(generator, parserName) {
return Object.assign(() => generator(), {
[Symbol.iterator]: generator,
parserName
});
}
function regex(regex2) {
return makeParser(function* () {
const state = yield 0;
var remaining = state.input.slice(state.offset);
var duplicated = new RegExp(regex2);
var result = duplicated.exec(remaining);
if (result === null || result.index !== 0) {
throw resultFailure(
`regex /${regex2.source}/${regex2.flags} doesn't match`,
state
);
}
yield result[0].length;
return result[0];
}, `regex:${regex2.source}`);
}
function regexMatch(regex2) {
return makeParser(function* () {
const state = yield 0;
var remaining = state.input.slice(state.offset);
var duplicated = new RegExp(regex2);
var result = duplicated.exec(remaining);
if (result === null || result.index !== 0) {
throw resultFailure(
`regex /${regex2.source}/${regex2.flags} doesn't match`,
state
);
}
yield result[0].length;
return Array.from(result);
}, `regexMatch:${regex2.source}`);
}
function str(string) {
return makeParser(function* () {
const state = yield 0;
if (state.input.substr(state.offset, string.length) === string) {
yield string.length;
return string;
} else {
throw resultFailure(`"${string}" not found`, state);
}
}, `str:${string}`);
}
function fromGenerator(generator, parserName) {
return makeParser(function* () {
let state = yield 0;
var iterator = generator();
while (true) {
var result = iterator.next(state);
if (result.done) {
yield state;
return result.value;
} else {
if (typeof result.value === "number") {
state = __spreadProps(__spreadValues({}, state), { offset: state.offset + result.value });
} else {
state = result.value;
}
yield state;
}
}
}, parserName || generator.name);
}
function fail(message) {
return makeParser(function* () {
const state = yield 0;
throw resultFailure(message, state);
}, `fail:${message}`);
}
function wrapFail(parser, wrapper) {
return makeParser(function* () {
try {
return yield* parser;
} catch (e) {
if (e instanceof ParseError) {
const message = wrapper(e.msg);
throw resultFailure(message, { input: e.input, offset: e.offset });
}
throw e;
}
}, "wrapFail");
}
function debugTrace(log) {
return makeParser(function* () {
const state = yield 0;
log(formatState(state));
return void 0;
}, "debugTrace");
}
var end = makeParser(function* () {
const state = yield 0;
if (state.offset >= state.input.length) {
return null;
} else {
throw resultFailure("Not at end of string", state);
}
}, "end");
function runInner(parser, state) {
const iter = parser();
let step = iter.next(state);
while (!step.done) {
if (typeof step.value === "number") {
state = __spreadProps(__spreadValues({}, state), { offset: state.offset + step.value });
} else {
state = step.value;
}
step = iter.next(state);
}
return {
value: step.value,
state
};
}
function run(parser, input) {
const state = {
input,
offset: 0
};
return runInner(parser, state).value;
}
function runToEnd(parser, input) {
const state = {
input,
offset: 0
};
const result = runInner(parser, state);
const next = runInner(end, result.state);
return result.value;
}
// src/lib/ParserCombinators.ts
function maybe(parser) {
return fromGenerator(function* maybe2() {
const startState = yield 0;
try {
return yield* parser;
} catch (e) {
yield startState;
return null;
}
}, `maybe(${parser.parserName})`);
}
function many(parser) {
return fromGenerator(function* many2() {
var results = [];
while (true) {
const state = yield 0;
try {
var result = yield* parser;
} catch (e) {
yield state;
return results;
}
results.push(result);
}
}, `many(${parser.parserName})`);
}
function many1(parser) {
return fromGenerator(function* many12() {
var one = yield* parser;
var multiple = yield* many(parser);
return [one].concat(multiple);
}, `many1(${parser.parserName})`);
}
function choice(parsers) {
return fromGenerator(function* choice2() {
var errors = [];
const startState = yield 0;
for (var i = 0; i < parsers.length; ++i) {
try {
return yield* parsers[i];
} catch (e) {
if (e instanceof ParseError) {
errors.push(e);
} else {
throw e;
}
yield startState;
}
}
const state = yield 0;
yield startState;
errors.sort((a, b) => b.offset - a.offset);
throw resultFailure(
"Multiple choices; potential matches:\n- " + errors.map((error) => error.message.split("\n").join("\n ")).join("\n- "),
state
);
}, `choice(${parsers.map((parser) => parser.parserName).join(",")})`);
}
function sequence(parsers) {
return fromGenerator(function* sequence2() {
var results = [];
for (var i = 0; i < parsers.length; ++i) {
const state = yield 0;
results.push(yield* parsers[i]);
}
return results;
}, `sequence(${parsers.map((parser) => parser.name).join(",")})`);
}
function count(num, parser) {
return fromGenerator(function* count2() {
var results = [];
for (var i = 0; i < num; ++i) {
results.push(yield* parser);
}
return results;
}, `count(${num},${parser.parserName})`);
}
function sepBy1(sepParser, valParser) {
var maybeSeparator = maybe(sepParser);
return fromGenerator(function* sepBy12() {
var results = [];
while (true) {
results.push(yield* valParser);
var sepResult = yield* maybeSeparator;
if (sepResult === null) {
return results;
}
}
}, `sepBy1(${sepParser.parserName},${valParser.parserName})`);
}
function sepBy(sepParser, valParser) {
var maybeSeparator = maybe(sepParser);
var maybeParser = maybe(valParser);
return fromGenerator(function* sepBy2() {
var results = [];
var first = yield* maybeParser;
if (first === null) {
return results;
} else {
results.push(first);
}
while (true) {
var sepResult = yield* maybeSeparator;
if (sepResult === null) {
return results;
}
results.push(yield* valParser);
}
}, `sepBy(${sepParser.parserName},${valParser.parserName})`);
}
function peek(parser) {
return fromGenerator(function* peek2() {
const startState = yield 0;
let result;
try {
result = yield* parser;
} catch (e) {
yield startState;
throw e;
}
yield startState;
return result;
}, `peek(${parser.parserName})`);
}
function until(terminator) {
return fromGenerator(function* until2() {
let state = yield 0;
for (var i = state.offset; i <= state.input.length; ++i) {
const maybeEndState = yield __spreadProps(__spreadValues({}, state), { offset: i });
try {
yield* terminator;
yield maybeEndState;
return state.input.slice(state.offset, i);
} catch (e) {
}
}
throw resultFailure("Didn't find terminator", state);
}, `until(${terminator.parserName})`);
}
function between(start, end2) {
return fromGenerator(function* between2() {
yield* start;
var data = yield* until(end2);
yield* end2;
return data;
}, `between(${start.parserName},${end2.parserName})`);
}
function map(parser, fn) {
return fromGenerator(function* map2() {
var result = yield* parser;
return fn(result);
}, `map(${parser.parserName})`);
}
function surround(left, val, right) {
return fromGenerator(function* surround2() {
yield* left;
var v = yield* val;
yield* right;
return v;
}, `surround(${left.parserName},${val.parserName},${right.parserName})`);
}
function buildExpressionParser(operators, parseTermFactory) {
var parseTerm = null;
var preOps = [];
var postOps = [];
var binOps = [];
for (let i = 0; i < operators.length; ++i) {
let precedence = operators.length - i;
let operator = operators[i];
switch (operator.fixity) {
case "infix":
binOps.push({
precedence,
associativity: operator.associativity,
parser: operator.parser
});
break;
case "postfix":
postOps.push(operator.parser);
break;
case "prefix":
preOps.push(operator.parser);
break;
}
}
var parseExprTerm = fromGenerator(function* exprParserTerm() {
var preFuncs = [];
var postFuncs = [];
var f = null;
do {
f = yield* maybe(choice(preOps));
if (f !== null) {
preFuncs.push(f);
}
} while (f !== null);
if (parseTerm === null) {
parseTerm = parseTermFactory();
}
var result = yield* parseTerm;
do {
f = yield* maybe(choice(postOps));
if (f !== null) {
postFuncs.push(f);
}
} while (f !== null);
for (let f2 of preFuncs) {
result = f2(result);
}
for (let f2 of postFuncs) {
result = f2(result);
}
return result;
}, `expressionParser:term`);
function parseExpressionPrecedence(minPrec) {
return fromGenerator(function* exprParserPrecedence() {
var left = yield* parseExprTerm;
while (true) {
var action = null;
var associativity;
var precedence;
for (var i = 0; i < binOps.length && action === null; ++i) {
var op = binOps[i];
if (op.precedence >= minPrec) {
action = yield* maybe(op.parser);
associativity = op.associativity;
precedence = op.precedence;
}
}
if (action === null) {
return left;
}
var nextMinPrec;
if (associativity === "left") {
nextMinPrec = precedence + 1;
} else {
nextMinPrec = precedence;
}
var right = yield* parseExpressionPrecedence(nextMinPrec);
left = action(left, right);
}
}, `expressionParser:precedence`);
}
return parseExpressionPrecedence(0);
}
// src/parsinator.ts
var VERSION = false ? "debug" : "2.1.2";
return __toCommonJS(parsinator_exports);
})();
//# sourceMappingURL=parsinator.js.map