parser-combinators 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.mjs +4 -0
- package/dist/parsers/any.js +3 -14
- package/dist/parsers/anyString.d.ts +3 -6
- package/dist/parsers/anyString.js +4 -2
- package/dist/parsers/between.js +12 -8
- package/dist/parsers/many.d.ts +2 -2
- package/dist/parsers/many.js +89 -23
- package/dist/parsers/map.js +7 -2
- package/dist/parsers/optimizations.d.ts +11 -0
- package/dist/parsers/optimizations.js +23 -0
- package/dist/parsers/str.d.ts +2 -10
- package/dist/parsers/str.js +2 -2
- package/dist/parsers/utilities.d.ts +4 -7
- package/dist/parsers/utilities.js +11 -13
- package/dist-esm/index.d.ts +3 -0
- package/dist-esm/index.js +3 -0
- package/dist-esm/parser.d.ts +9 -0
- package/dist-esm/parser.js +18 -0
- package/dist-esm/parsers/any.d.ts +16 -0
- package/dist-esm/parsers/any.js +33 -0
- package/dist-esm/parsers/anyString.d.ts +12 -0
- package/dist-esm/parsers/anyString.js +84 -0
- package/dist-esm/parsers/between.d.ts +5 -0
- package/dist-esm/parsers/between.js +21 -0
- package/dist-esm/parsers/exhaust.d.ts +5 -0
- package/dist-esm/parsers/exhaust.js +22 -0
- package/dist-esm/parsers/index.d.ts +12 -0
- package/dist-esm/parsers/index.js +12 -0
- package/dist-esm/parsers/many.d.ts +17 -0
- package/dist-esm/parsers/many.js +121 -0
- package/dist-esm/parsers/map.d.ts +5 -0
- package/dist-esm/parsers/map.js +23 -0
- package/dist-esm/parsers/opt.d.ts +5 -0
- package/dist-esm/parsers/opt.js +13 -0
- package/dist-esm/parsers/optimizations.d.ts +11 -0
- package/dist-esm/parsers/optimizations.js +16 -0
- package/dist-esm/parsers/recovery.d.ts +14 -0
- package/dist-esm/parsers/recovery.js +49 -0
- package/dist-esm/parsers/regex.d.ts +5 -0
- package/dist-esm/parsers/regex.js +17 -0
- package/dist-esm/parsers/seq.d.ts +16 -0
- package/dist-esm/parsers/seq.js +14 -0
- package/dist-esm/parsers/str.d.ts +9 -0
- package/dist-esm/parsers/str.js +32 -0
- package/dist-esm/parsers/utilities.d.ts +30 -0
- package/dist-esm/parsers/utilities.js +85 -0
- package/dist-esm/parsers/values.d.ts +31 -0
- package/dist-esm/parsers/values.js +44 -0
- package/dist-esm/types.d.ts +40 -0
- package/dist-esm/types.js +38 -0
- package/package.json +61 -56
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { failure, success } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Optimization for `any(str(), str(), ...)` which replaces the parser tree with one parser which tries all strings together
|
|
4
|
+
*/
|
|
5
|
+
export function anyString(matches) {
|
|
6
|
+
const lastMatchInQuotes = `'${matches[matches.length - 1][0]}'`;
|
|
7
|
+
let tree;
|
|
8
|
+
return Object.assign((ctx) => {
|
|
9
|
+
tree ??= createSearchTree(matches);
|
|
10
|
+
const result = searchThroughTree(tree, ctx.text, ctx.index);
|
|
11
|
+
if (result) {
|
|
12
|
+
return success({ ...ctx, index: result.end }, matches[result.matchIndex][2](matches[result.matchIndex][0]));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
return failure(ctx, lastMatchInQuotes, ['any', lastMatchInQuotes]);
|
|
16
|
+
}
|
|
17
|
+
}, { parserType: 'anyString', matches });
|
|
18
|
+
}
|
|
19
|
+
function createSearchTree(matches) {
|
|
20
|
+
const tree = new Map();
|
|
21
|
+
matches.forEach((match, index) => addMatchToTree(tree, match, index));
|
|
22
|
+
return tree;
|
|
23
|
+
}
|
|
24
|
+
function addMatchToTree(node, match, idIndex, charIndex = 0) {
|
|
25
|
+
if (charIndex >= match[0].length) {
|
|
26
|
+
if (node.matchIndex != undefined) {
|
|
27
|
+
if (node.matchIndex > idIndex) {
|
|
28
|
+
node.matchIndex = idIndex;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
node.matchIndex = idIndex;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const char = match[0][charIndex];
|
|
37
|
+
const lowercase = char.toLowerCase();
|
|
38
|
+
const uppercase = char.toUpperCase();
|
|
39
|
+
const newNode = new Map();
|
|
40
|
+
if (match[1] && (lowercase !== uppercase)) {
|
|
41
|
+
const lower = node.get(lowercase) ?? newNode;
|
|
42
|
+
const upper = node.get(uppercase) ?? newNode;
|
|
43
|
+
node.set(lowercase, lower);
|
|
44
|
+
node.set(uppercase, upper);
|
|
45
|
+
addMatchToTree(lower, match, idIndex, charIndex + 1);
|
|
46
|
+
if (lower !== upper) {
|
|
47
|
+
addMatchToTree(upper, match, idIndex, charIndex + 1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const charNode = node.get(char) ?? newNode;
|
|
52
|
+
node.set(char, charNode);
|
|
53
|
+
const lower = node.get(lowercase);
|
|
54
|
+
if (lower === node.get(uppercase)) {
|
|
55
|
+
const copy = new Map(lower.entries());
|
|
56
|
+
copy.matchIndex = lower.matchIndex;
|
|
57
|
+
node.set(uppercase, copy);
|
|
58
|
+
}
|
|
59
|
+
addMatchToTree(charNode, match, idIndex, charIndex + 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function searchThroughTree(node, text, start) {
|
|
64
|
+
let lastSuccessIndex = undefined;
|
|
65
|
+
let lastSuccessMatchIndex = undefined;
|
|
66
|
+
let currentNode = node;
|
|
67
|
+
for (let index = start; index < text.length; index++) {
|
|
68
|
+
const char = text[index];
|
|
69
|
+
if (currentNode.matchIndex != undefined && (lastSuccessMatchIndex == undefined || currentNode.matchIndex <= lastSuccessMatchIndex)) {
|
|
70
|
+
lastSuccessIndex = index;
|
|
71
|
+
lastSuccessMatchIndex = currentNode.matchIndex;
|
|
72
|
+
}
|
|
73
|
+
currentNode = currentNode.get(char);
|
|
74
|
+
if (!currentNode)
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if (currentNode && currentNode.matchIndex != undefined && (lastSuccessMatchIndex == undefined || currentNode.matchIndex <= lastSuccessMatchIndex)) {
|
|
78
|
+
lastSuccessIndex = text.length;
|
|
79
|
+
lastSuccessMatchIndex = currentNode.matchIndex;
|
|
80
|
+
}
|
|
81
|
+
return lastSuccessIndex == undefined
|
|
82
|
+
? undefined
|
|
83
|
+
: { end: lastSuccessIndex, matchIndex: lastSuccessMatchIndex };
|
|
84
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isFailure, success } from '../types';
|
|
2
|
+
/** Parses a sequence of three parsers.
|
|
3
|
+
* @returns A parser returning only the middle parser's result.
|
|
4
|
+
*/
|
|
5
|
+
export function between(left, parser, right) {
|
|
6
|
+
return (ctx) => {
|
|
7
|
+
const resLeft = left(ctx);
|
|
8
|
+
if (isFailure(resLeft)) {
|
|
9
|
+
return resLeft;
|
|
10
|
+
}
|
|
11
|
+
const resParse = parser(resLeft.ctx);
|
|
12
|
+
if (isFailure(resParse)) {
|
|
13
|
+
return resParse;
|
|
14
|
+
}
|
|
15
|
+
const resRight = right(resParse.ctx);
|
|
16
|
+
if (isFailure(resRight)) {
|
|
17
|
+
return resRight;
|
|
18
|
+
}
|
|
19
|
+
return success(resRight.ctx, resParse.value);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Parser } from '../types';
|
|
2
|
+
/** Parses using the passed in parser, until the input is exhausted or until the `until` condition is satisfied.
|
|
3
|
+
* @returns A parser returning an array of parsed results.
|
|
4
|
+
*/
|
|
5
|
+
export declare function exhaust<T, V>(parser: Parser<T>, until?: Parser<V> | null): Parser<T[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { failure, isFailure, success } from '../types';
|
|
2
|
+
/** Parses using the passed in parser, until the input is exhausted or until the `until` condition is satisfied.
|
|
3
|
+
* @returns A parser returning an array of parsed results.
|
|
4
|
+
*/
|
|
5
|
+
export function exhaust(parser, until = null) {
|
|
6
|
+
return (ctx) => {
|
|
7
|
+
const results = [];
|
|
8
|
+
while (true) {
|
|
9
|
+
const res = parser(ctx);
|
|
10
|
+
if (isFailure(res)) {
|
|
11
|
+
if (until === null || isFailure(until(ctx))) {
|
|
12
|
+
return failure(res.ctx, res.expected, ['exhaust', ...res.history]);
|
|
13
|
+
}
|
|
14
|
+
return success(ctx, results);
|
|
15
|
+
}
|
|
16
|
+
ctx = res.ctx;
|
|
17
|
+
results.push(res.value);
|
|
18
|
+
if (res.ctx.index === res.ctx.text.length)
|
|
19
|
+
return success(res.ctx, results);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './any';
|
|
2
|
+
export * from './between';
|
|
3
|
+
export * from './exhaust';
|
|
4
|
+
export * from './many';
|
|
5
|
+
export * from './map';
|
|
6
|
+
export * from './opt';
|
|
7
|
+
export * from './regex';
|
|
8
|
+
export * from './seq';
|
|
9
|
+
export { str, stri } from './str';
|
|
10
|
+
export * from './recovery';
|
|
11
|
+
export * from './utilities';
|
|
12
|
+
export * from './values';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './any';
|
|
2
|
+
export * from './between';
|
|
3
|
+
export * from './exhaust';
|
|
4
|
+
export * from './many';
|
|
5
|
+
export * from './map';
|
|
6
|
+
export * from './opt';
|
|
7
|
+
export * from './regex';
|
|
8
|
+
export * from './seq';
|
|
9
|
+
export { str, stri } from './str';
|
|
10
|
+
export * from './recovery';
|
|
11
|
+
export * from './utilities';
|
|
12
|
+
export * from './values';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Parser } from "../types";
|
|
2
|
+
/** Parses zero or more occurences of the given parser.
|
|
3
|
+
* @returns A parser returning an array of many parses.
|
|
4
|
+
*/
|
|
5
|
+
export declare function many<T>(parser: Parser<T>): Parser<T[]>;
|
|
6
|
+
/** Parses zero or more occurences of the given parser, separated with the separator parser.
|
|
7
|
+
* @returns A parser returning an array of many parses, omitting the separator.
|
|
8
|
+
*/
|
|
9
|
+
export declare function zeroOrMany<T, V>(item: Parser<T>, separator?: Parser<V> | undefined): Parser<T[]>;
|
|
10
|
+
/** Parses one or more occurences of the given parser, separated with the separator parser.
|
|
11
|
+
* @returns A parser returning an array of many parses, omitting the separator.
|
|
12
|
+
*/
|
|
13
|
+
export declare function oneOrMany<T, V>(item: Parser<T>, separator?: Parser<V> | undefined): Parser<T[]>;
|
|
14
|
+
/** Parses one or more occurences of the given parser, separated with the separator parser.
|
|
15
|
+
* @returns A parser returning the result of many parses, reduced using the `reducer` function passed in.
|
|
16
|
+
*/
|
|
17
|
+
export declare function oneOrManyRed<T, V, U = T>(item: Parser<T>, separator: Parser<V>, reducer: (left: U | T, right: T, sep: V) => U): Parser<U | T>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { failure, isFailure, success } from "../types";
|
|
2
|
+
/** Parses zero or more occurences of the given parser.
|
|
3
|
+
* @returns A parser returning an array of many parses.
|
|
4
|
+
*/
|
|
5
|
+
export function many(parser) {
|
|
6
|
+
return (ctx) => {
|
|
7
|
+
const results = [];
|
|
8
|
+
while (true) {
|
|
9
|
+
const res = parser(ctx);
|
|
10
|
+
if (isFailure(res)) {
|
|
11
|
+
return success(ctx, results);
|
|
12
|
+
}
|
|
13
|
+
ctx = res.ctx;
|
|
14
|
+
results.push(res.value);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Parses zero or more occurences of the given parser, separated with the separator parser.
|
|
19
|
+
* @returns A parser returning an array of many parses, omitting the separator.
|
|
20
|
+
*/
|
|
21
|
+
export function zeroOrMany(item, separator = undefined) {
|
|
22
|
+
if (separator) {
|
|
23
|
+
return (ctx) => {
|
|
24
|
+
const results = [];
|
|
25
|
+
const res = item(ctx);
|
|
26
|
+
if (isFailure(res)) {
|
|
27
|
+
return success(ctx, results);
|
|
28
|
+
}
|
|
29
|
+
ctx = res.ctx;
|
|
30
|
+
results.push(res.value);
|
|
31
|
+
while (true) {
|
|
32
|
+
const resSep = separator(ctx);
|
|
33
|
+
if (isFailure(resSep)) {
|
|
34
|
+
return success(ctx, results);
|
|
35
|
+
}
|
|
36
|
+
const res = item(resSep.ctx);
|
|
37
|
+
if (isFailure(res)) {
|
|
38
|
+
return success(ctx, results);
|
|
39
|
+
}
|
|
40
|
+
ctx = res.ctx;
|
|
41
|
+
results.push(res.value);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
return many(item);
|
|
47
|
+
}
|
|
48
|
+
/** Parses one or more occurences of the given parser, separated with the separator parser.
|
|
49
|
+
* @returns A parser returning an array of many parses, omitting the separator.
|
|
50
|
+
*/
|
|
51
|
+
export function oneOrMany(item, separator = undefined) {
|
|
52
|
+
if (separator) {
|
|
53
|
+
return (ctx) => {
|
|
54
|
+
const results = [];
|
|
55
|
+
const res = item(ctx);
|
|
56
|
+
if (isFailure(res))
|
|
57
|
+
return res;
|
|
58
|
+
ctx = res.ctx;
|
|
59
|
+
results.push(res.value);
|
|
60
|
+
while (true) {
|
|
61
|
+
const resSep = separator(ctx);
|
|
62
|
+
if (isFailure(resSep)) {
|
|
63
|
+
return success(ctx, results);
|
|
64
|
+
}
|
|
65
|
+
const res = item(resSep.ctx);
|
|
66
|
+
if (isFailure(res)) {
|
|
67
|
+
return success(ctx, results);
|
|
68
|
+
}
|
|
69
|
+
ctx = res.ctx;
|
|
70
|
+
results.push(res.value);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return (ctx) => {
|
|
76
|
+
const results = [];
|
|
77
|
+
const res = item(ctx);
|
|
78
|
+
if (isFailure(res))
|
|
79
|
+
return res;
|
|
80
|
+
ctx = res.ctx;
|
|
81
|
+
results.push(res.value);
|
|
82
|
+
while (true) {
|
|
83
|
+
const res = item(ctx);
|
|
84
|
+
if (isFailure(res)) {
|
|
85
|
+
return success(ctx, results);
|
|
86
|
+
}
|
|
87
|
+
ctx = res.ctx;
|
|
88
|
+
results.push(res.value);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Parses one or more occurences of the given parser, separated with the separator parser.
|
|
94
|
+
* @returns A parser returning the result of many parses, reduced using the `reducer` function passed in.
|
|
95
|
+
*/
|
|
96
|
+
export function oneOrManyRed(item, separator, reducer) {
|
|
97
|
+
return (ctx) => {
|
|
98
|
+
const res = item(ctx);
|
|
99
|
+
if (isFailure(res))
|
|
100
|
+
return res;
|
|
101
|
+
ctx = res.ctx;
|
|
102
|
+
let result = res.value;
|
|
103
|
+
while (true) {
|
|
104
|
+
const resSep = separator(ctx);
|
|
105
|
+
if (isFailure(resSep)) {
|
|
106
|
+
return success(ctx, result);
|
|
107
|
+
}
|
|
108
|
+
const res = item(resSep.ctx);
|
|
109
|
+
if (isFailure(res)) {
|
|
110
|
+
return success(ctx, result);
|
|
111
|
+
}
|
|
112
|
+
ctx = res.ctx;
|
|
113
|
+
try {
|
|
114
|
+
result = reducer(result, res.value, resSep.value);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return failure(res.ctx, 'Error while reducing', ['oneOrManyRed']);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Parser } from '../types';
|
|
2
|
+
/** Parses the input using the given parser, then maps the output.
|
|
3
|
+
* @returns A parser that parses the same input, but has output mapped using the mapping function.
|
|
4
|
+
*/
|
|
5
|
+
export declare function map<A, B>(parser: Parser<A>, mapper: (val: A) => B): Parser<B>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { failure, isFailure, success } from '../types';
|
|
2
|
+
import { isStringParser, shouldPerformFusions } from './optimizations';
|
|
3
|
+
/** Parses the input using the given parser, then maps the output.
|
|
4
|
+
* @returns A parser that parses the same input, but has output mapped using the mapping function.
|
|
5
|
+
*/
|
|
6
|
+
export function map(parser, mapper) {
|
|
7
|
+
let marker = {};
|
|
8
|
+
if (shouldPerformFusions() && isStringParser(parser)) {
|
|
9
|
+
marker = { parserType: 'anyString', matches: parser.matches.map(m => ([m[0], m[1], (v) => mapper(m[2](v))])) };
|
|
10
|
+
}
|
|
11
|
+
return Object.assign((ctx) => {
|
|
12
|
+
const res = parser(ctx);
|
|
13
|
+
if (isFailure(res))
|
|
14
|
+
return failure(res.ctx, res.expected, ['map', ...res.history]);
|
|
15
|
+
try {
|
|
16
|
+
const newValue = mapper(res.value);
|
|
17
|
+
return success(res.ctx, newValue);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return failure(res.ctx, 'Error while mapping', ['map']);
|
|
21
|
+
}
|
|
22
|
+
}, marker);
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isFailure, success } from '../types';
|
|
2
|
+
/** Makes the parser optional.
|
|
3
|
+
* @returns A parser that succeeds with `null` result if the parsing didn't succeed.
|
|
4
|
+
*/
|
|
5
|
+
export function opt(parser) {
|
|
6
|
+
return (ctx) => {
|
|
7
|
+
const parseResult = parser(ctx);
|
|
8
|
+
if (isFailure(parseResult)) {
|
|
9
|
+
return success(ctx, null);
|
|
10
|
+
}
|
|
11
|
+
return parseResult;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Parser } from "../types";
|
|
2
|
+
import { AnyStringParser } from "./anyString";
|
|
3
|
+
export declare const shouldPerformFusions: () => boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Disables all parser combinator fusions.
|
|
6
|
+
*
|
|
7
|
+
* Should probably be used only for testing performance.
|
|
8
|
+
*/
|
|
9
|
+
export declare const toggleFusions: (value: boolean) => void;
|
|
10
|
+
export declare function isStringParser<T>(parser: Parser<T>): parser is AnyStringParser<T>;
|
|
11
|
+
export declare function allStringParsers<T>(parsers: Parser<T>[]): parsers is AnyStringParser<T>[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const shouldPerformFusions = () => performFusions;
|
|
2
|
+
let performFusions = true;
|
|
3
|
+
/**
|
|
4
|
+
* Disables all parser combinator fusions.
|
|
5
|
+
*
|
|
6
|
+
* Should probably be used only for testing performance.
|
|
7
|
+
*/
|
|
8
|
+
export const toggleFusions = (value) => {
|
|
9
|
+
performFusions = value;
|
|
10
|
+
};
|
|
11
|
+
export function isStringParser(parser) {
|
|
12
|
+
return 'parserType' in parser && typeof parser.parserType === 'string' && parser.parserType === 'anyString';
|
|
13
|
+
}
|
|
14
|
+
export function allStringParsers(parsers) {
|
|
15
|
+
return parsers.every(isStringParser);
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Parser } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Tries to automatically recover a parsing failure by removing characters from the front.
|
|
4
|
+
*
|
|
5
|
+
* If the inner parser fails, the recovery parser will restart parsing skipping a character, up to {@link chars} skips
|
|
6
|
+
*/
|
|
7
|
+
export declare function recoverBySkippingChars<T>(parser: Parser<T>, chars: number): Parser<T>;
|
|
8
|
+
/**
|
|
9
|
+
* Tries to automatically recover a parsing failure by adding characters to the front.
|
|
10
|
+
*
|
|
11
|
+
* If the inner parser fails, the recovery parser will restart parsing adding a character, up to {@link chars}
|
|
12
|
+
* @todo Inserting characters in the middle of a context string is inefficient - maybe there's a better way?
|
|
13
|
+
*/
|
|
14
|
+
export declare function recoverByAddingChars<T>(parser: Parser<T>, chars: string): Parser<T>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isFailure } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Tries to automatically recover a parsing failure by removing characters from the front.
|
|
4
|
+
*
|
|
5
|
+
* If the inner parser fails, the recovery parser will restart parsing skipping a character, up to {@link chars} skips
|
|
6
|
+
*/
|
|
7
|
+
export function recoverBySkippingChars(parser, chars) {
|
|
8
|
+
return (ctx) => {
|
|
9
|
+
let firstFailure = null;
|
|
10
|
+
for (let i = 0; i <= chars; i++) {
|
|
11
|
+
const result = parser({
|
|
12
|
+
index: ctx.index + i,
|
|
13
|
+
path: ctx.path,
|
|
14
|
+
text: ctx.text,
|
|
15
|
+
});
|
|
16
|
+
if (isFailure(result)) {
|
|
17
|
+
firstFailure ??= result;
|
|
18
|
+
}
|
|
19
|
+
else
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return firstFailure;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Tries to automatically recover a parsing failure by adding characters to the front.
|
|
27
|
+
*
|
|
28
|
+
* If the inner parser fails, the recovery parser will restart parsing adding a character, up to {@link chars}
|
|
29
|
+
* @todo Inserting characters in the middle of a context string is inefficient - maybe there's a better way?
|
|
30
|
+
*/
|
|
31
|
+
export function recoverByAddingChars(parser, chars) {
|
|
32
|
+
return (ctx) => {
|
|
33
|
+
let firstFailure = null;
|
|
34
|
+
for (let i = 0; i <= chars.length; i++) {
|
|
35
|
+
const addedChars = chars.slice(0, i);
|
|
36
|
+
const result = parser({
|
|
37
|
+
index: ctx.index,
|
|
38
|
+
path: ctx.path,
|
|
39
|
+
text: `${ctx.text.slice(0, ctx.index)}${addedChars}${ctx.text.slice(ctx.index)}`,
|
|
40
|
+
});
|
|
41
|
+
if (isFailure(result)) {
|
|
42
|
+
firstFailure ??= result;
|
|
43
|
+
}
|
|
44
|
+
else
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
return firstFailure;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { failure, success } from '../types';
|
|
2
|
+
/** Parses a regex and returns the matched result of the parse.
|
|
3
|
+
* @returns A parser parsing a given regex and returning a match.
|
|
4
|
+
*/
|
|
5
|
+
export function regex(match, expected) {
|
|
6
|
+
const regexp = new RegExp(match, typeof match === 'string' ? 'y' : match.flags + 'y');
|
|
7
|
+
return (ctx) => {
|
|
8
|
+
regexp.lastIndex = ctx.index;
|
|
9
|
+
const regexMatch = regexp.exec(ctx.text);
|
|
10
|
+
if (regexMatch !== null && regexMatch.index === ctx.index) {
|
|
11
|
+
return success({ ...ctx, index: ctx.index + regexMatch[0].length }, regexMatch[0]);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return failure(ctx, expected, [expected]);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Parser } from '../types';
|
|
2
|
+
/** Parses a sequence of parsers going from left to right, returning the results of the parsers.
|
|
3
|
+
* @returns A parser parsing the sequence of parsers.
|
|
4
|
+
*/
|
|
5
|
+
export declare function seq<T, U, V, W, X, Z, I, J, K, L, M>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>, Parser<Z>, Parser<I>, Parser<J>, Parser<K>, Parser<L>, Parser<M>]): Parser<[T, U, V, W, X, Z, I, J, K, L, M]>;
|
|
6
|
+
export declare function seq<T, U, V, W, X, Z, I, J, K, L>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>, Parser<Z>, Parser<I>, Parser<J>, Parser<K>, Parser<L>]): Parser<[T, U, V, W, X, Z, I, J, K, L]>;
|
|
7
|
+
export declare function seq<T, U, V, W, X, Z, I, J, K>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>, Parser<Z>, Parser<I>, Parser<J>, Parser<K>]): Parser<[T, U, V, W, X, Z, I, J, K]>;
|
|
8
|
+
export declare function seq<T, U, V, W, X, Z, I, J>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>, Parser<Z>, Parser<I>, Parser<J>]): Parser<[T, U, V, W, X, Z, I, J]>;
|
|
9
|
+
export declare function seq<T, U, V, W, X, Z, I>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>, Parser<Z>, Parser<I>]): Parser<[T, U, V, W, X, Z, I]>;
|
|
10
|
+
export declare function seq<T, U, V, W, X, Z>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>, Parser<Z>]): Parser<[T, U, V, W, X, Z]>;
|
|
11
|
+
export declare function seq<T, U, V, W, X>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>, Parser<X>]): Parser<[T, U, V, W, X]>;
|
|
12
|
+
export declare function seq<T, U, V, W>(...parsers: [Parser<T>, Parser<U>, Parser<V>, Parser<W>]): Parser<[T, U, V, W]>;
|
|
13
|
+
export declare function seq<T, U, V>(...parsers: [Parser<T>, Parser<U>, Parser<V>]): Parser<[T, U, V]>;
|
|
14
|
+
export declare function seq<T, U>(...parsers: [Parser<T>, Parser<U>]): Parser<[T, U]>;
|
|
15
|
+
export declare function seq<T>(...parsers: [Parser<T>]): Parser<[T]>;
|
|
16
|
+
export declare function seq<T>(...parsers: Parser<T>[]): Parser<T[]>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { failure, isFailure, success } from '../types';
|
|
2
|
+
export function seq(...parsers) {
|
|
3
|
+
return (ctx) => {
|
|
4
|
+
const values = [];
|
|
5
|
+
for (const parser of parsers) {
|
|
6
|
+
const res = parser(ctx);
|
|
7
|
+
ctx = res.ctx;
|
|
8
|
+
if (isFailure(res))
|
|
9
|
+
return failure(res.ctx, res.expected, ['seq', ...res.history]);
|
|
10
|
+
values.push(res.value);
|
|
11
|
+
}
|
|
12
|
+
return success(ctx, values);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Parser } from '../types';
|
|
2
|
+
/** Parses a string and returns it as a result of the parse.
|
|
3
|
+
* @returns A parser parsing a given string, case-sensitive.
|
|
4
|
+
*/
|
|
5
|
+
export declare function str<T extends string>(match: T): Parser<T>;
|
|
6
|
+
/** Parses a string case-insensitively and returns it as a result of the parse.
|
|
7
|
+
* @returns A parser parsing a given string, case-insensitive.
|
|
8
|
+
*/
|
|
9
|
+
export declare function stri<T extends string>(match: T): Parser<Lowercase<T>>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { failure, success } from '../types';
|
|
2
|
+
/** Parses a string and returns it as a result of the parse.
|
|
3
|
+
* @returns A parser parsing a given string, case-sensitive.
|
|
4
|
+
*/
|
|
5
|
+
export function str(match) {
|
|
6
|
+
const inQuotes = `'${match}'`;
|
|
7
|
+
return Object.assign((ctx) => {
|
|
8
|
+
if (ctx.text.startsWith(match, ctx.index)) {
|
|
9
|
+
return success({ ...ctx, index: ctx.index + match.length }, match);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return failure(ctx, inQuotes, [inQuotes]);
|
|
13
|
+
}
|
|
14
|
+
}, { parserType: 'anyString', matches: [[match, false, (v) => v]] });
|
|
15
|
+
}
|
|
16
|
+
const collator = new Intl.Collator('en', { sensitivity: 'accent' });
|
|
17
|
+
/** Parses a string case-insensitively and returns it as a result of the parse.
|
|
18
|
+
* @returns A parser parsing a given string, case-insensitive.
|
|
19
|
+
*/
|
|
20
|
+
export function stri(match) {
|
|
21
|
+
const lowercase = match.toLowerCase();
|
|
22
|
+
const inQuotes = `'${lowercase}'`;
|
|
23
|
+
return Object.assign((ctx) => {
|
|
24
|
+
const textSlice = ctx.text.slice(ctx.index, ctx.index + match.length);
|
|
25
|
+
if (collator.compare(match, textSlice) == 0) {
|
|
26
|
+
return success({ ...ctx, index: ctx.index + match.length }, lowercase);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return failure(ctx, inQuotes, [inQuotes]);
|
|
30
|
+
}
|
|
31
|
+
}, { parserType: 'anyString', matches: [[lowercase, true, (v) => v]] });
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Parser, Token } from '../types';
|
|
2
|
+
/** Allows to make a condition on the result of the parsing function.
|
|
3
|
+
* @returns A parser returning the same but also performing a given check on the result.
|
|
4
|
+
*/
|
|
5
|
+
export declare function ref<T>(parser: Parser<T>, check: ((p: T) => boolean), expected?: string): Parser<T>;
|
|
6
|
+
/** Changes the expected value for when the parser fails.
|
|
7
|
+
* @returns A parser returning the same but with a different expected value.
|
|
8
|
+
*/
|
|
9
|
+
export declare function expect<T>(parser: Parser<T>, expected: string): Parser<T>;
|
|
10
|
+
/** Changes the expected value for when the parser fails. Erases the previous history.
|
|
11
|
+
* @returns A parser returning the same but with a different expected value.
|
|
12
|
+
*/
|
|
13
|
+
export declare function expectErase<T>(parser: Parser<T>, expected: string): Parser<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Marks a parser as a branch that has to be executed to the end by the {@link any} parser
|
|
16
|
+
*/
|
|
17
|
+
export declare function surely<T>(parser: Parser<T>): Parser<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Wraps the value returned by the parser with the token information (the start and end index in the text)
|
|
20
|
+
* @returns A parser returning the value wrapped in token information.
|
|
21
|
+
*/
|
|
22
|
+
export declare function token<T>(parser: Parser<T>): Parser<Token<T>>;
|
|
23
|
+
/**
|
|
24
|
+
* Checks whether the parser parses successfully, but doesn't move the cursor forward
|
|
25
|
+
*/
|
|
26
|
+
export declare function lookaround<T>(parser: Parser<T>): Parser<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Turns a function creating a parser into a parser. Useful for recursive grammars.
|
|
29
|
+
*/
|
|
30
|
+
export declare function lazy<T>(parserGetter: () => Parser<T>): Parser<T>;
|