tarsec 0.0.11 → 0.0.13
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/dist/combinators/seq.d.ts +1 -2
- package/dist/combinators/seq.js +1 -112
- package/dist/combinators.d.ts +135 -8
- package/dist/combinators.js +337 -86
- package/dist/parsers/within.d.ts +2 -0
- package/dist/parsers/within.js +51 -0
- package/dist/parsers.d.ts +16 -1
- package/dist/parsers.js +30 -5
- package/dist/trace.d.ts +3 -0
- package/dist/trace.js +70 -8
- package/dist/types.d.ts +63 -4
- package/dist/types.js +8 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +5 -1
- package/package.json +2 -1
- package/dist/parsers/betweenWithin.d.ts +0 -2
- package/dist/parsers/betweenWithin.js +0 -68
package/dist/combinators.js
CHANGED
|
@@ -4,32 +4,68 @@
|
|
|
4
4
|
if (v !== undefined) module.exports = v;
|
|
5
5
|
}
|
|
6
6
|
else if (typeof define === "function" && define.amd) {
|
|
7
|
-
define(["require", "exports", "./
|
|
7
|
+
define(["require", "exports", "./parsers/within", "./trace", "./types", "./utils"], factory);
|
|
8
8
|
}
|
|
9
9
|
})(function (require, exports) {
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.transform = exports.
|
|
13
|
-
|
|
14
|
-
Object.defineProperty(exports, "seq", { enumerable: true, get: function () { return seq_1.seq; } });
|
|
12
|
+
exports.match = exports.seqC = exports.seqR = exports.seq = exports.search = exports.transform = exports.manyTillStr = exports.many1Till = exports.manyTill = exports.capture = exports.getCaptures = exports.getResults = exports.sepBy = exports.between = exports.not = exports.optional = exports.or = exports.many1WithJoin = exports.manyWithJoin = exports.count = exports.many1 = exports.many = void 0;
|
|
13
|
+
const within_1 = require("./parsers/within");
|
|
15
14
|
const trace_1 = require("./trace");
|
|
16
15
|
const types_1 = require("./types");
|
|
17
16
|
const utils_1 = require("./utils");
|
|
17
|
+
/**
|
|
18
|
+
* Takes a parser and runs it zero or more times, returning the results as an array.
|
|
19
|
+
* If the parser is a capture parser, it returns the captures as an array in this form:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* { captures: <array of captures> }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Fails on empty strings
|
|
26
|
+
* @param parser - parser to run
|
|
27
|
+
* @returns - parser that runs the given parser zero to many times,
|
|
28
|
+
* and returns the result as an array
|
|
29
|
+
*/
|
|
18
30
|
function many(parser) {
|
|
19
31
|
return (0, trace_1.trace)("many", (input) => {
|
|
20
32
|
let results = [];
|
|
33
|
+
let captures = [];
|
|
21
34
|
let rest = input;
|
|
22
35
|
while (true) {
|
|
23
36
|
let parsed = parser(rest);
|
|
24
37
|
if (!parsed.success) {
|
|
25
|
-
|
|
38
|
+
if (Object.keys(captures).length) {
|
|
39
|
+
return (0, types_1.captureSuccess)(results, rest, { captures });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return (0, types_1.success)(results, rest);
|
|
43
|
+
}
|
|
26
44
|
}
|
|
27
45
|
results.push(parsed.result);
|
|
46
|
+
if ((0, types_1.isCaptureResult)(parsed)) {
|
|
47
|
+
captures.push(parsed.captures);
|
|
48
|
+
}
|
|
28
49
|
rest = parsed.rest;
|
|
50
|
+
// don't loop infinitely on empty strings
|
|
51
|
+
if (rest === "") {
|
|
52
|
+
if (Object.keys(captures).length) {
|
|
53
|
+
return (0, types_1.captureSuccess)(results, rest, { captures });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return (0, types_1.success)(results, rest);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
29
59
|
}
|
|
30
60
|
});
|
|
31
61
|
}
|
|
32
62
|
exports.many = many;
|
|
63
|
+
/**
|
|
64
|
+
* Same as `many`, but fails if the parser doesn't match at least once.
|
|
65
|
+
*
|
|
66
|
+
* @param parser - parser to run
|
|
67
|
+
* @returns a parser that runs the given parser one to many times,
|
|
68
|
+
*/
|
|
33
69
|
function many1(parser) {
|
|
34
70
|
return (0, trace_1.trace)(`many1`, (input) => {
|
|
35
71
|
let result = many(parser)(input);
|
|
@@ -68,26 +104,74 @@
|
|
|
68
104
|
});
|
|
69
105
|
}
|
|
70
106
|
exports.count = count;
|
|
107
|
+
/**
|
|
108
|
+
* Same as `many`, but joins the results into a single string.
|
|
109
|
+
*
|
|
110
|
+
* @param parser - parser to run. The parser must return a string as its result.
|
|
111
|
+
* @returns - parser that runs the given parser zero to many times,
|
|
112
|
+
* and returns the result as a single string
|
|
113
|
+
*/
|
|
71
114
|
function manyWithJoin(parser) {
|
|
72
|
-
return
|
|
115
|
+
return (0, trace_1.trace)("manyWithJoin", (input) => {
|
|
116
|
+
const result = many(parser)(input);
|
|
117
|
+
if (result.success) {
|
|
118
|
+
return Object.assign(Object.assign({}, result), { result: result.result.join("") });
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
});
|
|
73
122
|
}
|
|
74
123
|
exports.manyWithJoin = manyWithJoin;
|
|
124
|
+
/**
|
|
125
|
+
* Same as `many1`, but joins the results into a single string.
|
|
126
|
+
*
|
|
127
|
+
* @param parser - parser to run. The parser must return a string as its result.
|
|
128
|
+
* @returns - parser that runs the given parser one to many times,
|
|
129
|
+
* and returns the result as a single string
|
|
130
|
+
*/
|
|
75
131
|
function many1WithJoin(parser) {
|
|
76
|
-
return
|
|
132
|
+
return (0, trace_1.trace)("many1WithJoin", (input) => {
|
|
133
|
+
const result = many1(parser)(input);
|
|
134
|
+
if (result.success) {
|
|
135
|
+
return Object.assign(Object.assign({}, result), { result: result.result.join("") });
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
});
|
|
77
139
|
}
|
|
78
140
|
exports.many1WithJoin = many1WithJoin;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
141
|
+
/**
|
|
142
|
+
* `or` takes an array of parsers and runs them sequentially.
|
|
143
|
+
* It returns the results of the first parser that succeeds.
|
|
144
|
+
* You can use `capture` in an `or`:
|
|
145
|
+
*
|
|
146
|
+
* ```ts
|
|
147
|
+
* const parser = or(capture(digit, "num"), capture(word, "name"));
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* `or` supports backtracking by returning a `nextParser`:
|
|
151
|
+
*
|
|
152
|
+
* ```ts
|
|
153
|
+
* const parser = or(str("hello"), str("hello!"));
|
|
154
|
+
*
|
|
155
|
+
* // this will match the first parser
|
|
156
|
+
* const result = parser("hello");
|
|
157
|
+
*
|
|
158
|
+
* // but or returns the untried parsers as a new parser
|
|
159
|
+
* result.nextParser("hello!"); // works
|
|
160
|
+
*
|
|
161
|
+
* // result.nextParser is the same as or(str("hello!"))
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* @param parsers - parsers to try
|
|
165
|
+
* @returns - a parser that tries each parser in order. Returns the result of the first parser that succeeds.
|
|
82
166
|
*/
|
|
83
|
-
function or(parsers
|
|
84
|
-
return (0, trace_1.trace)(`or(
|
|
167
|
+
function or(...parsers) {
|
|
168
|
+
return (0, trace_1.trace)(`or()`, (input) => {
|
|
85
169
|
for (let i = 0; i < parsers.length; i++) {
|
|
86
170
|
let result = parsers[i](input);
|
|
87
171
|
if (result.success) {
|
|
88
172
|
if (i === parsers.length - 1)
|
|
89
173
|
return result;
|
|
90
|
-
const nextParser = or(parsers.slice(i + 1)
|
|
174
|
+
const nextParser = or(...parsers.slice(i + 1));
|
|
91
175
|
/* console.log({ nextParser }, parsers.slice(i + 1)); */
|
|
92
176
|
return Object.assign(Object.assign({}, result), { nextParser });
|
|
93
177
|
}
|
|
@@ -96,6 +180,14 @@
|
|
|
96
180
|
});
|
|
97
181
|
}
|
|
98
182
|
exports.or = or;
|
|
183
|
+
/**
|
|
184
|
+
* Takes a parser and runs it. If the parser fails,
|
|
185
|
+
* optional returns a success with a null result.
|
|
186
|
+
*
|
|
187
|
+
* @param parser - parser to run
|
|
188
|
+
* @returns - a parser that runs the given parser.
|
|
189
|
+
* If it fails, returns a success with a null result.
|
|
190
|
+
*/
|
|
99
191
|
function optional(parser) {
|
|
100
192
|
return (0, trace_1.trace)("optional", (input) => {
|
|
101
193
|
let result = parser(input);
|
|
@@ -106,6 +198,16 @@
|
|
|
106
198
|
});
|
|
107
199
|
}
|
|
108
200
|
exports.optional = optional;
|
|
201
|
+
/**
|
|
202
|
+
* Takes a parser and runs it. If the parser fails,
|
|
203
|
+
* `not` returns a success with a `null` result.
|
|
204
|
+
* If the parser succeeds, `not` returns a failure.
|
|
205
|
+
*
|
|
206
|
+
* @param parser - parser to run
|
|
207
|
+
* @returns - a parser that runs the given parser.
|
|
208
|
+
* If it fails, returns a success with a `null` result.
|
|
209
|
+
* If it succeeds, returns a failure.
|
|
210
|
+
*/
|
|
109
211
|
function not(parser) {
|
|
110
212
|
return (0, trace_1.trace)("not", (input) => {
|
|
111
213
|
let result = parser(input);
|
|
@@ -113,13 +215,24 @@
|
|
|
113
215
|
return {
|
|
114
216
|
success: false,
|
|
115
217
|
rest: input,
|
|
116
|
-
message: "
|
|
218
|
+
message: "expected parser not to succeed",
|
|
117
219
|
};
|
|
118
220
|
}
|
|
119
221
|
return (0, types_1.success)(null, input);
|
|
120
222
|
});
|
|
121
223
|
}
|
|
122
224
|
exports.not = not;
|
|
225
|
+
/**
|
|
226
|
+
* Takes three parsers, `open`, `close`, and `parser`.
|
|
227
|
+
* `between` matches something that matches `parser`,
|
|
228
|
+
* surrounded by `open` and `close`. It returns the result of `parser`.
|
|
229
|
+
* If any of the parsers fail, `between` fails.
|
|
230
|
+
*
|
|
231
|
+
* @param open - parser for the opening delimiter
|
|
232
|
+
* @param close - parser for the closing delimiter
|
|
233
|
+
* @param parser - parser for the content
|
|
234
|
+
* @returns a parser that returns the result of `parser`.
|
|
235
|
+
*/
|
|
123
236
|
function between(open, close, parser) {
|
|
124
237
|
return (input) => {
|
|
125
238
|
const result1 = open(input);
|
|
@@ -179,82 +292,57 @@
|
|
|
179
292
|
});
|
|
180
293
|
}
|
|
181
294
|
exports.capture = capture;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
295
|
+
/**
|
|
296
|
+
* Returns a parser that consumes input till the given parser succeeds.
|
|
297
|
+
* @param parser - the stop parser
|
|
298
|
+
* @returns a parser that consumes the input string until the stop parser succeeds.
|
|
299
|
+
* Then it returns the consumed input as a string.
|
|
300
|
+
* The stop parser's match is not included in the result.
|
|
301
|
+
*/
|
|
302
|
+
function manyTill(parser) {
|
|
303
|
+
return (input) => {
|
|
304
|
+
let current = 0;
|
|
305
|
+
while (current < input.length) {
|
|
306
|
+
const parsed = parser(input.slice(current));
|
|
307
|
+
if (parsed.success) {
|
|
308
|
+
return (0, types_1.success)(input.slice(0, current), input.slice(current));
|
|
309
|
+
}
|
|
310
|
+
current++;
|
|
189
311
|
}
|
|
190
|
-
return
|
|
191
|
-
}
|
|
312
|
+
return (0, types_1.success)(input, "");
|
|
313
|
+
};
|
|
192
314
|
}
|
|
193
|
-
exports.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
315
|
+
exports.manyTill = manyTill;
|
|
316
|
+
function many1Till(parser) {
|
|
317
|
+
return (input) => {
|
|
318
|
+
let current = 0;
|
|
319
|
+
while (current < input.length) {
|
|
320
|
+
const parsed = parser(input.slice(current));
|
|
321
|
+
if (parsed.success) {
|
|
322
|
+
if (current === 0) {
|
|
323
|
+
return (0, types_1.failure)("expected to consume at least one character of input", input);
|
|
324
|
+
}
|
|
325
|
+
return (0, types_1.success)(input.slice(0, current), input.slice(current));
|
|
326
|
+
}
|
|
327
|
+
current++;
|
|
328
|
+
}
|
|
329
|
+
if (current === 0) {
|
|
330
|
+
return (0, types_1.failure)("expected to consume at least one character of input", input);
|
|
331
|
+
}
|
|
332
|
+
return (0, types_1.success)(input, "");
|
|
333
|
+
};
|
|
209
334
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
} */
|
|
220
|
-
/* export function captureCaptures<M, C extends string>(
|
|
221
|
-
parser: Parser<M>,
|
|
222
|
-
name: string
|
|
223
|
-
): Parser<M, C> {
|
|
224
|
-
return trace(`captures(${escape(name)})`, (input: string) => {
|
|
225
|
-
let result = parser(input);
|
|
226
|
-
if (result.success) {
|
|
227
|
-
const captures: Record<string, any> = {
|
|
228
|
-
[name]: result.captures,
|
|
229
|
-
};
|
|
230
|
-
return {
|
|
231
|
-
...result,
|
|
232
|
-
captures: mergeCaptures(result.captures || {}, captures),
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
return result;
|
|
236
|
-
});
|
|
335
|
+
exports.many1Till = many1Till;
|
|
336
|
+
function manyTillStr(str) {
|
|
337
|
+
return (0, trace_1.trace)(`manyTillStr(${str})`, (input) => {
|
|
338
|
+
const index = input.indexOf(str);
|
|
339
|
+
if (index === -1) {
|
|
340
|
+
return (0, types_1.success)(input, "");
|
|
341
|
+
}
|
|
342
|
+
return (0, types_1.success)(input.slice(0, index), input.slice(index));
|
|
343
|
+
});
|
|
237
344
|
}
|
|
238
|
-
|
|
239
|
-
*/
|
|
240
|
-
/* export function shapeCaptures<M, C extends string>(
|
|
241
|
-
parser: Parser<M>,
|
|
242
|
-
func: (captures: Record<string, any>) => Record<string, any>,
|
|
243
|
-
name: string
|
|
244
|
-
): Parser<M, C> {
|
|
245
|
-
return trace(`captures(${escape(name)})`, (input: string) => {
|
|
246
|
-
let result = parser(input);
|
|
247
|
-
if (result.success) {
|
|
248
|
-
const captures: Record<string, any> = result.captures || {};
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
...result,
|
|
252
|
-
captures: func(captures),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
return result;
|
|
256
|
-
});
|
|
257
|
-
} */
|
|
345
|
+
exports.manyTillStr = manyTillStr;
|
|
258
346
|
function transform(parser, transformerFunc) {
|
|
259
347
|
return (0, trace_1.trace)(`transform(${transformerFunc})`, (input) => {
|
|
260
348
|
let parsed = parser(input);
|
|
@@ -265,4 +353,167 @@
|
|
|
265
353
|
});
|
|
266
354
|
}
|
|
267
355
|
exports.transform = transform;
|
|
356
|
+
function search(parser) {
|
|
357
|
+
return (0, trace_1.trace)("search", (input) => {
|
|
358
|
+
let parsed = (0, within_1.within)(parser)(input);
|
|
359
|
+
if (parsed.success) {
|
|
360
|
+
const result = parsed.result
|
|
361
|
+
.filter((x) => x.type === "matched")
|
|
362
|
+
.map((x) => x.value);
|
|
363
|
+
const rest = parsed.result
|
|
364
|
+
.filter((x) => x.type === "unmatched")
|
|
365
|
+
.map((x) => x.value)
|
|
366
|
+
.join(" ");
|
|
367
|
+
return (0, types_1.success)(result, rest);
|
|
368
|
+
}
|
|
369
|
+
return (0, types_1.success)("", input);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
exports.search = search;
|
|
373
|
+
/*
|
|
374
|
+
To add backtracking support requires a fairly big change. Here's an example that needs backtracking.
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
const parser = seq([
|
|
378
|
+
str("hello "),
|
|
379
|
+
or(str("world"), str("world!")),
|
|
380
|
+
optional("?")
|
|
381
|
+
], getResults);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
If we try to parse `"hello world!"`, the first parser in the OR will succeed, but then we'll get stuck at the `optional`. Instead, we need to go back up the tree and try the second parser in the OR. A few things need to happen.
|
|
385
|
+
|
|
386
|
+
1. instead of just processing these parsers sequentially in a for loop, we need to model them as a tree
|
|
387
|
+
2. the OR parser needs to let us know that there are other branches to try.
|
|
388
|
+
|
|
389
|
+
For #2, there's an optional `nextParser` key on a parser success. The or parser can use this to say "a parser succeeded and here's the result, but there are other parsers that could be tried". `nextParser` is a parser that runs the remaining branches. So in this example, the OR would return a success with `nextParser = or(str("world"))`.
|
|
390
|
+
|
|
391
|
+
Next, we need to model this as a tree. Each node in the tree has a parent and child and the parser for that node.
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
parent: Node;
|
|
395
|
+
parser: GeneralParser<any, any> | null;
|
|
396
|
+
child: Node;
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Hopefully that is self-explanatory. We start at the root of the tree, try the parser there, then use `.child` to go to the next node and so on. We don't model multiple paths as multiple children. To keep the code simple, we do something else.
|
|
400
|
+
|
|
401
|
+
Each node also has a `closed` key. Once we've run the parser for a node, we mark it `closed`. Closed means there are no more branches here. UNLESS, the parser returns a `nextParser`. In that case, we *don't* mark it closed because there are still other options to try. In that case, we also *replace* the parser on that node with nextParser.
|
|
402
|
+
|
|
403
|
+
So, going back to the hello world example, let's say we're stuck at the `optional`:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
const parser = seq([
|
|
407
|
+
str("hello "),
|
|
408
|
+
or(str("world"), str("world!")),
|
|
409
|
+
optional("?")
|
|
410
|
+
], getResults);
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
We use `.parent` to go back up the tree. We're looking for a node that isn't closed. If we find one, we start again from there. In this case, we'd find an open node at the or with parser `or(str("world"))`. We can restart from there, but there's a bunch of state to reset.
|
|
414
|
+
|
|
415
|
+
1. From the new `or` parser, we need to go to the optional parser. We're doing it all again in the same order. This is one reason why it's easier to model this without multiple children. Otherwise, all the children would have to point to the next level, the next level would have to point to all the children in the previous level, and you'd have multiple parents, which is awful to deal with.
|
|
416
|
+
|
|
417
|
+
2. We have consumed input and added to the results. We need to undo that. At this point, the input is `!`, because we've consumed `hello world`. And the results array is `["hello ", "world"]`. We need to rewind both of those.
|
|
418
|
+
|
|
419
|
+
To do that, I count how many levels up we've gone to find another branch, and just pop that many elements off the results array. So results is now `["hello "]`. The input is trickier. How would I keep track of what the input was when we were at the OR the last time?
|
|
420
|
+
|
|
421
|
+
This is where the final key on a tree node comes in. Nodes also have an optional `input` key.
|
|
422
|
+
|
|
423
|
+
IF a parser succeeds, and
|
|
424
|
+
IF there's a nextParser,
|
|
425
|
+
We know we may come back to this node. So we save the current input as `.input` on the node.
|
|
426
|
+
|
|
427
|
+
This approach has some issues. Notably, it doesn't work if you need to backtrack at multiple points in the tree. The test `backtracking-deep.test.ts` shows this.
|
|
428
|
+
|
|
429
|
+
The code is also complex and it would be easy to have bugs in this logic. I wish there was a cleaner solution for rewinding state.
|
|
430
|
+
*/
|
|
431
|
+
/**
|
|
432
|
+
* seq takes an array of parsers and runs them sequentially.
|
|
433
|
+
* If any of the parsers fail, seq fails without consuming any input.
|
|
434
|
+
*
|
|
435
|
+
* The second argument to seq is a function.
|
|
436
|
+
* The first argument of that function is an array of results:
|
|
437
|
+
* one result from each of the parsers you gave to seq.
|
|
438
|
+
* The second is an object containing any captures.
|
|
439
|
+
* You can use this second argument, the transformer function,
|
|
440
|
+
* to transform these however you want and return a result
|
|
441
|
+
*
|
|
442
|
+
* Tarsec includes the utility functions `getResults` and `getCaptures`
|
|
443
|
+
* to just return the results array or captures object respectively for you.
|
|
444
|
+
*
|
|
445
|
+
* Finally, you don't need to use seq at all. You can just hand write the logic.
|
|
446
|
+
* But you'll need to do the error handling
|
|
447
|
+
* and pass the remaining input to the next parser yourself.
|
|
448
|
+
* seq also does some backtracking for you that you will need to do yourself.
|
|
449
|
+
*
|
|
450
|
+
* Also see `seqR` and `seqC` for convenience functions that return the results or captures respectively.
|
|
451
|
+
*
|
|
452
|
+
* @param parsers - parsers to run sequentially
|
|
453
|
+
* @param transform - function to transform the results and captures. The params are the results and captures
|
|
454
|
+
* @param debugName - optional name for trace debugging
|
|
455
|
+
* @returns
|
|
456
|
+
*/
|
|
457
|
+
function seq(parsers, transform, debugName = "") {
|
|
458
|
+
return (0, trace_1.trace)(`seq(${debugName})`, (input) => {
|
|
459
|
+
const results = [];
|
|
460
|
+
let rest = input;
|
|
461
|
+
const captures = {};
|
|
462
|
+
const rootNode = (0, types_1.createTree)(parsers);
|
|
463
|
+
let current = rootNode;
|
|
464
|
+
while (current) {
|
|
465
|
+
const parser = current.parser;
|
|
466
|
+
if (!parser) {
|
|
467
|
+
console.log({ current, parser, results, captures });
|
|
468
|
+
throw new Error("parser is null");
|
|
469
|
+
}
|
|
470
|
+
const parsed = parser(rest);
|
|
471
|
+
current.closed = true;
|
|
472
|
+
/* console.log({ parsed }); */
|
|
473
|
+
if (!parsed.success) {
|
|
474
|
+
const [ancestor, count] = (0, utils_1.findAncestorWithNextParser)(current);
|
|
475
|
+
if (ancestor) {
|
|
476
|
+
current = ancestor;
|
|
477
|
+
rest = ancestor.input;
|
|
478
|
+
(0, utils_1.popMany)(results, count);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
// don't consume input if we're failing
|
|
483
|
+
return Object.assign(Object.assign({}, parsed), { rest: input });
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
results.push(parsed.result);
|
|
487
|
+
if (parsed.nextParser) {
|
|
488
|
+
/* console.log("setting next parser", parsed.nextParser); */
|
|
489
|
+
current.parser = parsed.nextParser;
|
|
490
|
+
current.input = rest;
|
|
491
|
+
current.closed = false;
|
|
492
|
+
}
|
|
493
|
+
rest = parsed.rest;
|
|
494
|
+
if ((0, types_1.isCaptureResult)(parsed)) {
|
|
495
|
+
for (const key in parsed.captures) {
|
|
496
|
+
captures[key] = parsed.captures[key];
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
current = current.child;
|
|
500
|
+
}
|
|
501
|
+
const result = transform(results, captures);
|
|
502
|
+
return (0, types_1.success)(result, rest);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
exports.seq = seq;
|
|
506
|
+
function seqR(...parsers) {
|
|
507
|
+
return seq(parsers, getResults);
|
|
508
|
+
}
|
|
509
|
+
exports.seqR = seqR;
|
|
510
|
+
function seqC(...parsers) {
|
|
511
|
+
return seq(parsers, getCaptures);
|
|
512
|
+
}
|
|
513
|
+
exports.seqC = seqC;
|
|
514
|
+
function match(input, parser) {
|
|
515
|
+
const result = parser(input);
|
|
516
|
+
return result.success && result.rest === "";
|
|
517
|
+
}
|
|
518
|
+
exports.match = match;
|
|
268
519
|
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
(function (factory) {
|
|
2
|
+
if (typeof module === "object" && typeof module.exports === "object") {
|
|
3
|
+
var v = factory(require, exports);
|
|
4
|
+
if (v !== undefined) module.exports = v;
|
|
5
|
+
}
|
|
6
|
+
else if (typeof define === "function" && define.amd) {
|
|
7
|
+
define(["require", "exports", "../trace", "../types"], factory);
|
|
8
|
+
}
|
|
9
|
+
})(function (require, exports) {
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.within = void 0;
|
|
13
|
+
const trace_1 = require("../trace");
|
|
14
|
+
const types_1 = require("../types");
|
|
15
|
+
function within(parser) {
|
|
16
|
+
return (0, trace_1.trace)("within", (input) => {
|
|
17
|
+
let start = 0;
|
|
18
|
+
let current = 0;
|
|
19
|
+
const results = [];
|
|
20
|
+
while (current < input.length) {
|
|
21
|
+
const parsed = parser(input.slice(current));
|
|
22
|
+
if (parsed.success) {
|
|
23
|
+
const unmatchedValue = input.slice(start, current);
|
|
24
|
+
if (unmatchedValue.length > 0) {
|
|
25
|
+
results.push({
|
|
26
|
+
type: "unmatched",
|
|
27
|
+
value: unmatchedValue,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
results.push({
|
|
31
|
+
type: "matched",
|
|
32
|
+
value: parsed.result,
|
|
33
|
+
});
|
|
34
|
+
current += parsed.result.length;
|
|
35
|
+
start = current;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
current += 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (start < current) {
|
|
42
|
+
results.push({
|
|
43
|
+
type: "unmatched",
|
|
44
|
+
value: input.slice(start, current),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return (0, types_1.success)(results, "");
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
exports.within = within;
|
|
51
|
+
});
|
package/dist/parsers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Parser } from "./types";
|
|
2
|
-
export { betweenWithin } from "./parsers/
|
|
2
|
+
export { within as betweenWithin } from "./parsers/within";
|
|
3
3
|
/**
|
|
4
4
|
* Takes a character. Returns a parser that parses that character.
|
|
5
5
|
*
|
|
@@ -38,15 +38,30 @@ export declare function noneOf(chars: string): Parser<string>;
|
|
|
38
38
|
* @returns - ParserResult
|
|
39
39
|
*/
|
|
40
40
|
export declare const anyChar: any;
|
|
41
|
+
/** A parser that matches one of " \t\n\r". */
|
|
41
42
|
export declare const space: Parser<string>;
|
|
43
|
+
/** A parser that matches one or more spaces. */
|
|
42
44
|
export declare const spaces: Parser<string>;
|
|
45
|
+
/** A parser that matches one digit. */
|
|
43
46
|
export declare const digit: Parser<string>;
|
|
47
|
+
/** A parser that matches one letter, currently lowercase only. */
|
|
44
48
|
export declare const letter: Parser<string>;
|
|
49
|
+
/** A parser that matches one digit or letter, currently lowercase only. */
|
|
45
50
|
export declare const alphanum: Parser<string>;
|
|
51
|
+
/** A parser that matches one lowercase word. */
|
|
46
52
|
export declare const word: Parser<string>;
|
|
53
|
+
/** A parser that matches one or more digits. */
|
|
47
54
|
export declare const num: Parser<string>;
|
|
55
|
+
/** A parser that matches one single or double quote. */
|
|
48
56
|
export declare const quote: Parser<string>;
|
|
57
|
+
/** A parser that matches one tab character. */
|
|
49
58
|
export declare const tab: Parser<string>;
|
|
59
|
+
/** A parser that matches one newline ("\n" only) character. */
|
|
50
60
|
export declare const newline: Parser<string>;
|
|
61
|
+
/** A parser that succeeds on an empty string. Returns `null` as the result. */
|
|
51
62
|
export declare const eof: Parser<null>;
|
|
63
|
+
/** A parser that matches a quoted string, in single or double quotes.
|
|
64
|
+
* Returns the string as the result, including the quotes.
|
|
65
|
+
*/
|
|
52
66
|
export declare const quotedString: Parser<string>;
|
|
67
|
+
export declare function regexParser(str: string | RegExp): Parser<string>;
|