tarsec 0.0.11 → 0.0.12

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.
@@ -1,2 +1 @@
1
- import { GeneralParser, MergedResults, MergedCaptures, Parser } from "@/lib/types";
2
- export declare function seq<const T extends readonly GeneralParser<any, any>[], U>(parsers: T, transform: (results: MergedResults<T>[], captures: MergedCaptures<T>) => U, debugName?: string): Parser<U>;
1
+ export {};
@@ -4,120 +4,9 @@
4
4
  if (v !== undefined) module.exports = v;
5
5
  }
6
6
  else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports", "@/lib/trace", "@/lib/types", "@/lib/utils"], factory);
7
+ define(["require", "exports"], factory);
8
8
  }
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.seq = void 0;
13
- const trace_1 = require("@/lib/trace");
14
- const types_1 = require("@/lib/types");
15
- const utils_1 = require("@/lib/utils");
16
- /*
17
- To add backtracking support requires a fairly big change. Here's an example that needs backtracking.
18
-
19
- ```ts
20
- const parser = seq([
21
- str("hello "),
22
- or([str("world"), str("world!")]),
23
- optional("?")
24
- ], getResults);
25
- ```
26
-
27
- 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.
28
-
29
- 1. instead of just processing these parsers sequentially in a for loop, we need to model them as a tree
30
- 2. the OR parser needs to let us know that there are other branches to try.
31
-
32
- 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")])`.
33
-
34
- 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.
35
-
36
- ```ts
37
- parent: Node;
38
- parser: GeneralParser<any, any> | null;
39
- child: Node;
40
- ```
41
-
42
- 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.
43
-
44
- 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.
45
-
46
- So, going back to the hello world example, let's say we're stuck at the `optional`:
47
-
48
- ```ts
49
- const parser = seq([
50
- str("hello "),
51
- or([str("world"), str("world!")]),
52
- optional("?")
53
- ], getResults);
54
- ```
55
-
56
- 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.
57
-
58
- 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.
59
-
60
- 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.
61
-
62
- 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?
63
-
64
- This is where the final key on a tree node comes in. Nodes also have an optional `input` key.
65
-
66
- IF a parser succeeds, and
67
- IF there's a nextParser,
68
- We know we may come back to this node. So we save the current input as `.input` on the node.
69
-
70
- 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.
71
-
72
- 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.
73
- */
74
- function seq(parsers, transform, debugName = "") {
75
- return (0, trace_1.trace)(`seq(${debugName})`, (input) => {
76
- const results = [];
77
- let rest = input;
78
- const captures = {};
79
- const rootNode = (0, types_1.createTree)(parsers);
80
- let current = rootNode;
81
- while (current) {
82
- const parser = current.parser;
83
- if (!parser) {
84
- console.log({ current, parser, results, captures });
85
- throw new Error("parser is null");
86
- }
87
- const parsed = parser(rest);
88
- current.closed = true;
89
- /* console.log({ parsed }); */
90
- if (!parsed.success) {
91
- const [ancestor, count] = (0, utils_1.findAncestorWithNextParser)(current);
92
- if (ancestor) {
93
- current = ancestor;
94
- rest = ancestor.input;
95
- (0, utils_1.popMany)(results, count);
96
- continue;
97
- }
98
- else {
99
- // don't consume input if we're failing
100
- return Object.assign(Object.assign({}, parsed), { rest: input });
101
- }
102
- }
103
- results.push(parsed.result);
104
- if (parsed.nextParser) {
105
- /* console.log("setting next parser", parsed.nextParser); */
106
- current.parser = parsed.nextParser;
107
- current.input = rest;
108
- current.closed = false;
109
- }
110
- rest = parsed.rest;
111
- if ((0, types_1.isCaptureResult)(parsed)) {
112
- for (const key in parsed.captures) {
113
- captures[key] = parsed.captures[key];
114
- }
115
- }
116
- current = current.child;
117
- }
118
- const result = transform(results, captures);
119
- return (0, types_1.success)(result, rest);
120
- });
121
- }
122
- exports.seq = seq;
123
12
  });
@@ -1,5 +1,4 @@
1
- export { seq } from "./combinators/seq";
2
- import { CaptureParser, MergedResults, Parser, Prettify } from "./types";
1
+ import { CaptureParser, GeneralParser, MergedCaptures, MergedResults, Parser, PickParserType, Prettify } from "./types";
3
2
  export declare function many<T>(parser: Parser<T>): Parser<T[]>;
4
3
  export declare function many1<T>(parser: Parser<T>): Parser<T[]>;
5
4
  /**
@@ -12,7 +11,33 @@ export declare function many1<T>(parser: Parser<T>): Parser<T[]>;
12
11
  export declare function count<T>(num: number, parser: Parser<T>): Parser<T[]>;
13
12
  export declare function manyWithJoin(parser: Parser<string>): Parser<string>;
14
13
  export declare function many1WithJoin(parser: Parser<string>): Parser<string>;
15
- export declare function or<const T extends readonly Parser<any>[]>(parsers: T, name?: string): Parser<MergedResults<T>>;
14
+ /**
15
+ * `or` takes an array of parsers and runs them sequentially.
16
+ * It returns the results of the first parser that succeeds.
17
+ * You can use `capture` in an `or`:
18
+ *
19
+ * ```ts
20
+ * const parser = or(capture(digit, "num"), capture(word, "name"));
21
+ * ```
22
+ *
23
+ * `or` supports backtracking by returning a `nextParser`:
24
+ *
25
+ * ```ts
26
+ * const parser = or(str("hello"), str("hello!"));
27
+ *
28
+ * // this will match the first parser
29
+ * const result = parser("hello");
30
+ *
31
+ * // but or returns the untried parsers as a new parser
32
+ * result.nextParser("hello!"); // works
33
+ *
34
+ * // result.nextParser is the same as or(str("hello!"))
35
+ * ```
36
+ *
37
+ * @param parsers - parsers to try
38
+ * @returns - a parser that tries each parser in order. Returns the result of the first parser that succeeds.
39
+ */
40
+ export declare function or<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): PickParserType<T>;
16
41
  export declare function optional<T>(parser: Parser<T>): Parser<T | null>;
17
42
  export declare function not(parser: Parser<any>): Parser<null>;
18
43
  export declare function between<O, C, P>(open: Parser<O>, close: Parser<C>, parser: Parser<P>): Parser<P>;
@@ -21,4 +46,33 @@ export declare function getResults<R, C>(results: R, captures: C): R;
21
46
  export declare function getCaptures<R, C>(results: R, captures: C): C;
22
47
  export declare function capture<T, const S extends string>(parser: Parser<T>, name: S): CaptureParser<T, Record<S, T>>;
23
48
  export declare function wrap<T, const S extends string>(parser: Parser<T>, name: S): Parser<Prettify<Record<S, T>>>;
49
+ export declare function manyTill<T>(parser: Parser<T>): Parser<string>;
24
50
  export declare function transform<T, X>(parser: Parser<T>, transformerFunc: (x: T) => X): Parser<X>;
51
+ export declare function search(parser: Parser<string>): Parser<string[]>;
52
+ /**
53
+ * seq takes an array of parsers and runs them sequentially.
54
+ * If any of the parsers fail, seq fails without consuming any input.
55
+ *
56
+ * The second argument to seq is a function.
57
+ * The first argument of that function is an array of results:
58
+ * one result from each of the parsers you gave to seq.
59
+ * The second is an object containing any captures.
60
+ * You can use this second argument, the transformer function,
61
+ * to transform these however you want and return a result
62
+ *
63
+ * Tarsec includes the utility functions `getResults` and `getCaptures`
64
+ * to just return the results array or captures object respectively for you.
65
+ *
66
+ * Finally, you don't need to use seq at all. You can just hand write the logic.
67
+ * But you'll need to do the error handling
68
+ * and pass the remaining input to the next parser yourself.
69
+ * seq also does some backtracking for you that you will need to do yourself.
70
+ *
71
+ * @param parsers - parsers to run sequentially
72
+ * @param transform - function to transform the results and captures. The params are the results and captures
73
+ * @param debugName - optional name for trace debugging
74
+ * @returns
75
+ */
76
+ export declare function seq<const T extends readonly GeneralParser<any, any>[], U>(parsers: T, transform: (results: MergedResults<T>[], captures: MergedCaptures<T>) => U, debugName?: string): Parser<U>;
77
+ export declare function seqR<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<MergedResults<T>[]>;
78
+ export declare function seqC<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<MergedCaptures<T>>;
@@ -4,14 +4,13 @@
4
4
  if (v !== undefined) module.exports = v;
5
5
  }
6
6
  else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports", "./combinators/seq", "./trace", "./types", "./utils"], factory);
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.wrap = 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 = exports.seq = void 0;
13
- var seq_1 = require("./combinators/seq");
14
- Object.defineProperty(exports, "seq", { enumerable: true, get: function () { return seq_1.seq; } });
12
+ exports.seqC = exports.seqR = exports.seq = exports.search = exports.transform = exports.manyTill = exports.wrap = 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");
@@ -76,18 +75,40 @@
76
75
  return transform(many1(parser), (x) => x.join(""));
77
76
  }
78
77
  exports.many1WithJoin = many1WithJoin;
79
- /* seq<, U>(
80
- parsers: T,
81
- transform: (results: MergedResults<T>[], captures: MergedCaptures<T>) => U,
78
+ /**
79
+ * `or` takes an array of parsers and runs them sequentially.
80
+ * It returns the results of the first parser that succeeds.
81
+ * You can use `capture` in an `or`:
82
+ *
83
+ * ```ts
84
+ * const parser = or(capture(digit, "num"), capture(word, "name"));
85
+ * ```
86
+ *
87
+ * `or` supports backtracking by returning a `nextParser`:
88
+ *
89
+ * ```ts
90
+ * const parser = or(str("hello"), str("hello!"));
91
+ *
92
+ * // this will match the first parser
93
+ * const result = parser("hello");
94
+ *
95
+ * // but or returns the untried parsers as a new parser
96
+ * result.nextParser("hello!"); // works
97
+ *
98
+ * // result.nextParser is the same as or(str("hello!"))
99
+ * ```
100
+ *
101
+ * @param parsers - parsers to try
102
+ * @returns - a parser that tries each parser in order. Returns the result of the first parser that succeeds.
82
103
  */
83
- function or(parsers, name = "") {
84
- return (0, trace_1.trace)(`or(${name})`, (input) => {
104
+ function or(...parsers) {
105
+ return (0, trace_1.trace)(`or()`, (input) => {
85
106
  for (let i = 0; i < parsers.length; i++) {
86
107
  let result = parsers[i](input);
87
108
  if (result.success) {
88
109
  if (i === parsers.length - 1)
89
110
  return result;
90
- const nextParser = or(parsers.slice(i + 1), name);
111
+ const nextParser = or(...parsers.slice(i + 1));
91
112
  /* console.log({ nextParser }, parsers.slice(i + 1)); */
92
113
  return Object.assign(Object.assign({}, result), { nextParser });
93
114
  }
@@ -191,70 +212,20 @@
191
212
  });
192
213
  }
193
214
  exports.wrap = wrap;
194
- /*
195
- export function setCapturesAsMatch<M, C extends PlainObject>(
196
- parser: Parser<M, C>
197
- ): Parser<C> {
198
- return trace(`setCapturesAsMatch`, (input: string) => {
199
- let result = parser(input);
200
- if (result.success) {
201
- return {
202
- ...result,
203
- match: result.captures as any,
204
- captures: {},
205
- };
206
- }
207
- return result;
208
- });
209
- }
210
-
211
- export function captureCaptures<
212
- M,
213
- C extends PlainObject,
214
- const S extends string
215
- >(parser: Parser<M, C>, name: S): Parser<C, Record<S, C>> {
216
- return trace(`captureCaptures(${escape(name)})`, (input: string) => {
217
- return capture(setCapturesAsMatch(parser), name)(input);
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
- });
215
+ function manyTill(parser) {
216
+ return (input) => {
217
+ let current = 0;
218
+ while (current < input.length) {
219
+ const parsed = parser(input.slice(current));
220
+ if (parsed.success) {
221
+ return (0, types_1.success)(input.slice(0, current), input.slice(current));
222
+ }
223
+ current++;
224
+ }
225
+ return (0, types_1.success)(input, "");
226
+ };
237
227
  }
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
- } */
228
+ exports.manyTill = manyTill;
258
229
  function transform(parser, transformerFunc) {
259
230
  return (0, trace_1.trace)(`transform(${transformerFunc})`, (input) => {
260
231
  let parsed = parser(input);
@@ -265,4 +236,167 @@
265
236
  });
266
237
  }
267
238
  exports.transform = transform;
239
+ function search(parser) {
240
+ return (0, trace_1.trace)("search", (input) => {
241
+ let parsed = (0, within_1.within)(parser)(input);
242
+ if (parsed.success) {
243
+ const result = parsed.result
244
+ .filter((x) => x.type === "matched")
245
+ .map((x) => x.value);
246
+ const rest = parsed.result
247
+ .filter((x) => x.type === "unmatched")
248
+ .map((x) => x.value)
249
+ .join(" ");
250
+ return (0, types_1.success)(result, rest);
251
+ }
252
+ return (0, types_1.success)("", input);
253
+ });
254
+ }
255
+ exports.search = search;
256
+ /*
257
+ To add backtracking support requires a fairly big change. Here's an example that needs backtracking.
258
+
259
+ ```ts
260
+ const parser = seq([
261
+ str("hello "),
262
+ or(str("world"), str("world!")),
263
+ optional("?")
264
+ ], getResults);
265
+ ```
266
+
267
+ 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.
268
+
269
+ 1. instead of just processing these parsers sequentially in a for loop, we need to model them as a tree
270
+ 2. the OR parser needs to let us know that there are other branches to try.
271
+
272
+ 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"))`.
273
+
274
+ 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.
275
+
276
+ ```ts
277
+ parent: Node;
278
+ parser: GeneralParser<any, any> | null;
279
+ child: Node;
280
+ ```
281
+
282
+ 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.
283
+
284
+ 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.
285
+
286
+ So, going back to the hello world example, let's say we're stuck at the `optional`:
287
+
288
+ ```ts
289
+ const parser = seq([
290
+ str("hello "),
291
+ or(str("world"), str("world!")),
292
+ optional("?")
293
+ ], getResults);
294
+ ```
295
+
296
+ 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.
297
+
298
+ 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.
299
+
300
+ 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.
301
+
302
+ 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?
303
+
304
+ This is where the final key on a tree node comes in. Nodes also have an optional `input` key.
305
+
306
+ IF a parser succeeds, and
307
+ IF there's a nextParser,
308
+ We know we may come back to this node. So we save the current input as `.input` on the node.
309
+
310
+ 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.
311
+
312
+ 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.
313
+ */
314
+ /**
315
+ * seq takes an array of parsers and runs them sequentially.
316
+ * If any of the parsers fail, seq fails without consuming any input.
317
+ *
318
+ * The second argument to seq is a function.
319
+ * The first argument of that function is an array of results:
320
+ * one result from each of the parsers you gave to seq.
321
+ * The second is an object containing any captures.
322
+ * You can use this second argument, the transformer function,
323
+ * to transform these however you want and return a result
324
+ *
325
+ * Tarsec includes the utility functions `getResults` and `getCaptures`
326
+ * to just return the results array or captures object respectively for you.
327
+ *
328
+ * Finally, you don't need to use seq at all. You can just hand write the logic.
329
+ * But you'll need to do the error handling
330
+ * and pass the remaining input to the next parser yourself.
331
+ * seq also does some backtracking for you that you will need to do yourself.
332
+ *
333
+ * @param parsers - parsers to run sequentially
334
+ * @param transform - function to transform the results and captures. The params are the results and captures
335
+ * @param debugName - optional name for trace debugging
336
+ * @returns
337
+ */
338
+ function seq(parsers, transform, debugName = "") {
339
+ return (0, trace_1.trace)(`seq(${debugName})`, (input) => {
340
+ const results = [];
341
+ let rest = input;
342
+ const captures = {};
343
+ const rootNode = (0, types_1.createTree)(parsers);
344
+ let current = rootNode;
345
+ while (current) {
346
+ const parser = current.parser;
347
+ if (!parser) {
348
+ console.log({ current, parser, results, captures });
349
+ throw new Error("parser is null");
350
+ }
351
+ const parsed = parser(rest);
352
+ current.closed = true;
353
+ /* console.log({ parsed }); */
354
+ if (!parsed.success) {
355
+ const [ancestor, count] = (0, utils_1.findAncestorWithNextParser)(current);
356
+ if (ancestor) {
357
+ current = ancestor;
358
+ rest = ancestor.input;
359
+ (0, utils_1.popMany)(results, count);
360
+ continue;
361
+ }
362
+ else {
363
+ // don't consume input if we're failing
364
+ return Object.assign(Object.assign({}, parsed), { rest: input });
365
+ }
366
+ }
367
+ results.push(parsed.result);
368
+ if (parsed.nextParser) {
369
+ /* console.log("setting next parser", parsed.nextParser); */
370
+ current.parser = parsed.nextParser;
371
+ current.input = rest;
372
+ current.closed = false;
373
+ }
374
+ rest = parsed.rest;
375
+ if ((0, types_1.isCaptureResult)(parsed)) {
376
+ for (const key in parsed.captures) {
377
+ captures[key] = parsed.captures[key];
378
+ }
379
+ }
380
+ current = current.child;
381
+ }
382
+ const result = transform(results, captures);
383
+ return (0, types_1.success)(result, rest);
384
+ });
385
+ }
386
+ exports.seq = seq;
387
+ function seqR(...parsers) {
388
+ return seq(parsers, getResults);
389
+ }
390
+ exports.seqR = seqR;
391
+ function seqC(...parsers) {
392
+ return seq(parsers, getCaptures);
393
+ }
394
+ exports.seqC = seqC;
268
395
  });
396
+ /*
397
+ export function seqX<const T extends readonly GeneralParser<any, any>[], U>(
398
+ parsers: T,
399
+ transform: (results: MergedResults<T>[], captures: MergedCaptures<T>) => U,
400
+ debugName: string = ""
401
+ ): Parser<U> {
402
+ */
@@ -0,0 +1,2 @@
1
+ import { BetweenWithinResult, Parser } from "../types";
2
+ export declare function within(parser: Parser<string>): Parser<BetweenWithinResult[]>;
@@ -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/betweenWithin";
2
+ export { within as betweenWithin } from "./parsers/within";
3
3
  /**
4
4
  * Takes a character. Returns a parser that parses that character.
5
5
  *
package/dist/parsers.js CHANGED
@@ -4,7 +4,7 @@
4
4
  if (v !== undefined) module.exports = v;
5
5
  }
6
6
  else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports", "./combinators", "./trace", "./types", "./utils", "./parsers/betweenWithin"], factory);
7
+ define(["require", "exports", "./combinators", "./trace", "./types", "./utils", "./parsers/within"], factory);
8
8
  }
9
9
  })(function (require, exports) {
10
10
  "use strict";
@@ -14,8 +14,8 @@
14
14
  const trace_1 = require("./trace");
15
15
  const types_1 = require("./types");
16
16
  const utils_1 = require("./utils");
17
- var betweenWithin_1 = require("./parsers/betweenWithin");
18
- Object.defineProperty(exports, "betweenWithin", { enumerable: true, get: function () { return betweenWithin_1.betweenWithin; } });
17
+ var within_1 = require("./parsers/within");
18
+ Object.defineProperty(exports, "betweenWithin", { enumerable: true, get: function () { return within_1.within; } });
19
19
  /**
20
20
  * Takes a character. Returns a parser that parses that character.
21
21
  *
@@ -122,5 +122,5 @@
122
122
  return (0, types_1.failure)("expected end of input", input);
123
123
  };
124
124
  exports.eof = eof;
125
- exports.quotedString = (0, combinators_1.seq)([exports.quote, exports.word, exports.quote], (results) => results.join(""));
125
+ exports.quotedString = (0, combinators_1.seq)([exports.quote, (0, combinators_1.manyWithJoin)(noneOf(`"'`)), exports.quote], (results) => results.join(""));
126
126
  });
package/dist/types.d.ts CHANGED
@@ -29,7 +29,17 @@ export type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) ex
29
29
  type ExtractResults<T> = T extends Parser<infer U> ? U : never;
30
30
  type ExtractCaptures<T> = T extends CaptureParser<any, infer U> ? U : never;
31
31
  type ExtractCaptureParsers<T extends readonly GeneralParser<any, any>[]> = Extract<T[number], CaptureParser<any, any>>;
32
- export type MergedCaptures<T extends readonly GeneralParser<any, any>[]> = Prettify<UnionToIntersection<ExtractCaptures<ExtractCaptureParsers<T>>>>;
32
+ export type MergedCaptures<T extends readonly GeneralParser<any, any>[]> = Prettify<UnionToIntersection<UnionOfCaptures<T>>>;
33
+ export type UnionOfCaptures<T extends readonly GeneralParser<any, any>[]> = Prettify<ExtractCaptures<ExtractCaptureParsers<T>>>;
34
+ export type HasCaptureParsers<T extends readonly GeneralParser<any, any>[]> = ExtractCaptureParsers<T> extends never ? false : true;
35
+ /**
36
+ * For a given array of GeneralParsers, if any of them is a CaptureParser,
37
+ * PickParserType says the array is an array of CaptureParsers,
38
+ * otherwise it's an array of Parsers. It also correctly merges
39
+ * the result and capture types. This is useful for a combinator like `or`
40
+ * which is not able to infer its return type correctly.
41
+ */
42
+ export type PickParserType<T extends readonly GeneralParser<any, any>[]> = HasCaptureParsers<T> extends true ? CaptureParser<MergedResults<T>, UnionOfCaptures<T>> : Parser<MergedResults<T>>;
33
43
  export type MergedResults<T extends readonly GeneralParser<any, any>[]> = ExtractResults<T[number]>;
34
44
  export type Node = ParserNode | EmptyNode;
35
45
  export type ParserNode = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tarsec",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "A parser combinator library for TypeScript, inspired by Parsec.",
5
5
  "homepage": "https://github.com/egonSchiele/tarsec",
6
6
  "scripts": {
@@ -1,2 +0,0 @@
1
- import { BetweenWithinResult, Parser } from "../types";
2
- export declare function betweenWithin(parser: Parser<string>): Parser<BetweenWithinResult[]>;
@@ -1,68 +0,0 @@
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.betweenWithin = void 0;
13
- const trace_1 = require("../trace");
14
- const types_1 = require("../types");
15
- function betweenWithin(parser) {
16
- return (0, trace_1.trace)("betweenWithin", (input) => {
17
- let start = 0;
18
- let current = 0;
19
- let currentlyMatching = false;
20
- const results = [];
21
- while (current < input.length) {
22
- const parsed = parser(input.slice(current));
23
- if (parsed.success) {
24
- if (currentlyMatching) {
25
- const value = input.slice(start, current + parsed.result.length);
26
- if (value.length > 0) {
27
- results.push({
28
- type: "matched",
29
- value,
30
- });
31
- }
32
- current += parsed.result.length;
33
- start = current;
34
- currentlyMatching = false;
35
- }
36
- else {
37
- const value = input.slice(start, current);
38
- if (value.length > 0) {
39
- results.push({
40
- type: "unmatched",
41
- value,
42
- });
43
- }
44
- currentlyMatching = true;
45
- start = current;
46
- current += parsed.result.length;
47
- }
48
- }
49
- else {
50
- current += 1;
51
- }
52
- }
53
- if (start < current) {
54
- if (currentlyMatching) {
55
- return (0, types_1.failure)("unexpected end of input", "");
56
- }
57
- else {
58
- results.push({
59
- type: "unmatched",
60
- value: input.slice(start, current),
61
- });
62
- }
63
- }
64
- return (0, types_1.success)(results, "");
65
- });
66
- }
67
- exports.betweenWithin = betweenWithin;
68
- });