tarsec 0.0.12 → 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.
@@ -1,6 +1,25 @@
1
- import { CaptureParser, GeneralParser, MergedCaptures, MergedResults, Parser, PickParserType, Prettify } from "./types";
2
- export declare function many<T>(parser: Parser<T>): Parser<T[]>;
3
- export declare function many1<T>(parser: Parser<T>): Parser<T[]>;
1
+ import { CaptureParser, GeneralParser, InferManyReturnType, MergedCaptures, MergedResults, Parser, PickParserType, PlainObject } from "./types";
2
+ /**
3
+ * Takes a parser and runs it zero or more times, returning the results as an array.
4
+ * If the parser is a capture parser, it returns the captures as an array in this form:
5
+ *
6
+ * ```ts
7
+ * { captures: <array of captures> }
8
+ * ```
9
+ *
10
+ * Fails on empty strings
11
+ * @param parser - parser to run
12
+ * @returns - parser that runs the given parser zero to many times,
13
+ * and returns the result as an array
14
+ */
15
+ export declare function many<const T extends GeneralParser<any, any>>(parser: T): InferManyReturnType<T>;
16
+ /**
17
+ * Same as `many`, but fails if the parser doesn't match at least once.
18
+ *
19
+ * @param parser - parser to run
20
+ * @returns a parser that runs the given parser one to many times,
21
+ */
22
+ export declare function many1<const T extends GeneralParser<any, any>>(parser: T): InferManyReturnType<T>;
4
23
  /**
5
24
  * Takes a parser, runs it n times, and returns the results as an array.
6
25
  * If it cannot run the parser n times, it fails without consuming input.
@@ -9,7 +28,21 @@ export declare function many1<T>(parser: Parser<T>): Parser<T[]>;
9
28
  * @returns - parser that runs the given parser `num` times and returns an array of the results
10
29
  */
11
30
  export declare function count<T>(num: number, parser: Parser<T>): Parser<T[]>;
12
- export declare function manyWithJoin(parser: Parser<string>): Parser<string>;
31
+ /**
32
+ * Same as `many`, but joins the results into a single string.
33
+ *
34
+ * @param parser - parser to run. The parser must return a string as its result.
35
+ * @returns - parser that runs the given parser zero to many times,
36
+ * and returns the result as a single string
37
+ */
38
+ export declare function manyWithJoin<const T extends GeneralParser<string, any>>(parser: T): GeneralParser<string, any>;
39
+ /**
40
+ * Same as `many1`, but joins the results into a single string.
41
+ *
42
+ * @param parser - parser to run. The parser must return a string as its result.
43
+ * @returns - parser that runs the given parser one to many times,
44
+ * and returns the result as a single string
45
+ */
13
46
  export declare function many1WithJoin(parser: Parser<string>): Parser<string>;
14
47
  /**
15
48
  * `or` takes an array of parsers and runs them sequentially.
@@ -38,16 +71,53 @@ export declare function many1WithJoin(parser: Parser<string>): Parser<string>;
38
71
  * @returns - a parser that tries each parser in order. Returns the result of the first parser that succeeds.
39
72
  */
40
73
  export declare function or<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): PickParserType<T>;
74
+ /**
75
+ * Takes a parser and runs it. If the parser fails,
76
+ * optional returns a success with a null result.
77
+ *
78
+ * @param parser - parser to run
79
+ * @returns - a parser that runs the given parser.
80
+ * If it fails, returns a success with a null result.
81
+ */
41
82
  export declare function optional<T>(parser: Parser<T>): Parser<T | null>;
83
+ /**
84
+ * Takes a parser and runs it. If the parser fails,
85
+ * `not` returns a success with a `null` result.
86
+ * If the parser succeeds, `not` returns a failure.
87
+ *
88
+ * @param parser - parser to run
89
+ * @returns - a parser that runs the given parser.
90
+ * If it fails, returns a success with a `null` result.
91
+ * If it succeeds, returns a failure.
92
+ */
42
93
  export declare function not(parser: Parser<any>): Parser<null>;
94
+ /**
95
+ * Takes three parsers, `open`, `close`, and `parser`.
96
+ * `between` matches something that matches `parser`,
97
+ * surrounded by `open` and `close`. It returns the result of `parser`.
98
+ * If any of the parsers fail, `between` fails.
99
+ *
100
+ * @param open - parser for the opening delimiter
101
+ * @param close - parser for the closing delimiter
102
+ * @param parser - parser for the content
103
+ * @returns a parser that returns the result of `parser`.
104
+ */
43
105
  export declare function between<O, C, P>(open: Parser<O>, close: Parser<C>, parser: Parser<P>): Parser<P>;
44
106
  export declare function sepBy<S, P>(separator: Parser<S>, parser: Parser<P>): Parser<P[]>;
45
107
  export declare function getResults<R, C>(results: R, captures: C): R;
46
108
  export declare function getCaptures<R, C>(results: R, captures: C): C;
47
109
  export declare function capture<T, const S extends string>(parser: Parser<T>, name: S): CaptureParser<T, Record<S, T>>;
48
- export declare function wrap<T, const S extends string>(parser: Parser<T>, name: S): Parser<Prettify<Record<S, T>>>;
110
+ /**
111
+ * Returns a parser that consumes input till the given parser succeeds.
112
+ * @param parser - the stop parser
113
+ * @returns a parser that consumes the input string until the stop parser succeeds.
114
+ * Then it returns the consumed input as a string.
115
+ * The stop parser's match is not included in the result.
116
+ */
49
117
  export declare function manyTill<T>(parser: Parser<T>): Parser<string>;
50
- export declare function transform<T, X>(parser: Parser<T>, transformerFunc: (x: T) => X): Parser<X>;
118
+ export declare function many1Till<T>(parser: Parser<T>): Parser<string>;
119
+ export declare function manyTillStr(str: string): Parser<string>;
120
+ export declare function transform<R, C extends PlainObject, X>(parser: GeneralParser<R, C>, transformerFunc: (x: R) => X): GeneralParser<X, C>;
51
121
  export declare function search(parser: Parser<string>): Parser<string[]>;
52
122
  /**
53
123
  * seq takes an array of parsers and runs them sequentially.
@@ -68,6 +138,8 @@ export declare function search(parser: Parser<string>): Parser<string[]>;
68
138
  * and pass the remaining input to the next parser yourself.
69
139
  * seq also does some backtracking for you that you will need to do yourself.
70
140
  *
141
+ * Also see `seqR` and `seqC` for convenience functions that return the results or captures respectively.
142
+ *
71
143
  * @param parsers - parsers to run sequentially
72
144
  * @param transform - function to transform the results and captures. The params are the results and captures
73
145
  * @param debugName - optional name for trace debugging
@@ -76,3 +148,4 @@ export declare function search(parser: Parser<string>): Parser<string[]>;
76
148
  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
149
  export declare function seqR<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<MergedResults<T>[]>;
78
150
  export declare function seqC<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<MergedCaptures<T>>;
151
+ export declare function match(input: string, parser: GeneralParser<any, any>): boolean;
@@ -9,26 +9,63 @@
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
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;
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
13
  const within_1 = require("./parsers/within");
14
14
  const trace_1 = require("./trace");
15
15
  const types_1 = require("./types");
16
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
+ */
17
30
  function many(parser) {
18
31
  return (0, trace_1.trace)("many", (input) => {
19
32
  let results = [];
33
+ let captures = [];
20
34
  let rest = input;
21
35
  while (true) {
22
36
  let parsed = parser(rest);
23
37
  if (!parsed.success) {
24
- return (0, types_1.success)(results, rest);
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
+ }
25
44
  }
26
45
  results.push(parsed.result);
46
+ if ((0, types_1.isCaptureResult)(parsed)) {
47
+ captures.push(parsed.captures);
48
+ }
27
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
+ }
28
59
  }
29
60
  });
30
61
  }
31
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
+ */
32
69
  function many1(parser) {
33
70
  return (0, trace_1.trace)(`many1`, (input) => {
34
71
  let result = many(parser)(input);
@@ -67,12 +104,38 @@
67
104
  });
68
105
  }
69
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
+ */
70
114
  function manyWithJoin(parser) {
71
- return transform(many(parser), (x) => x.join(""));
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
+ });
72
122
  }
73
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
+ */
74
131
  function many1WithJoin(parser) {
75
- return transform(many1(parser), (x) => x.join(""));
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
+ });
76
139
  }
77
140
  exports.many1WithJoin = many1WithJoin;
78
141
  /**
@@ -117,6 +180,14 @@
117
180
  });
118
181
  }
119
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
+ */
120
191
  function optional(parser) {
121
192
  return (0, trace_1.trace)("optional", (input) => {
122
193
  let result = parser(input);
@@ -127,6 +198,16 @@
127
198
  });
128
199
  }
129
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
+ */
130
211
  function not(parser) {
131
212
  return (0, trace_1.trace)("not", (input) => {
132
213
  let result = parser(input);
@@ -134,13 +215,24 @@
134
215
  return {
135
216
  success: false,
136
217
  rest: input,
137
- message: "unexpected match",
218
+ message: "expected parser not to succeed",
138
219
  };
139
220
  }
140
221
  return (0, types_1.success)(null, input);
141
222
  });
142
223
  }
143
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
+ */
144
236
  function between(open, close, parser) {
145
237
  return (input) => {
146
238
  const result1 = open(input);
@@ -200,18 +292,13 @@
200
292
  });
201
293
  }
202
294
  exports.capture = capture;
203
- function wrap(parser, name) {
204
- return (0, trace_1.trace)(`capture(${(0, utils_1.escape)(name)})`, (input) => {
205
- let result = parser(input);
206
- if (result.success) {
207
- return Object.assign(Object.assign({}, result), { result: {
208
- [name]: result.result,
209
- } });
210
- }
211
- return result;
212
- });
213
- }
214
- exports.wrap = wrap;
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
+ */
215
302
  function manyTill(parser) {
216
303
  return (input) => {
217
304
  let current = 0;
@@ -226,6 +313,36 @@
226
313
  };
227
314
  }
228
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
+ };
334
+ }
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
+ });
344
+ }
345
+ exports.manyTillStr = manyTillStr;
229
346
  function transform(parser, transformerFunc) {
230
347
  return (0, trace_1.trace)(`transform(${transformerFunc})`, (input) => {
231
348
  let parsed = parser(input);
@@ -330,6 +447,8 @@
330
447
  * and pass the remaining input to the next parser yourself.
331
448
  * seq also does some backtracking for you that you will need to do yourself.
332
449
  *
450
+ * Also see `seqR` and `seqC` for convenience functions that return the results or captures respectively.
451
+ *
333
452
  * @param parsers - parsers to run sequentially
334
453
  * @param transform - function to transform the results and captures. The params are the results and captures
335
454
  * @param debugName - optional name for trace debugging
@@ -392,11 +511,9 @@
392
511
  return seq(parsers, getCaptures);
393
512
  }
394
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;
395
519
  });
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
- */
@@ -1,2 +1,2 @@
1
- import { BetweenWithinResult, Parser } from "../types";
2
- export declare function within(parser: Parser<string>): Parser<BetweenWithinResult[]>;
1
+ import { WithinResult, Parser } from "../types";
2
+ export declare function within(parser: Parser<string>): Parser<WithinResult[]>;
package/dist/parsers.d.ts CHANGED
@@ -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>;
package/dist/parsers.js CHANGED
@@ -9,7 +9,7 @@
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.quotedString = exports.eof = exports.newline = exports.tab = exports.quote = exports.num = exports.word = exports.alphanum = exports.letter = exports.digit = exports.spaces = exports.space = exports.anyChar = exports.noneOf = exports.oneOf = exports.str = exports.char = exports.betweenWithin = void 0;
12
+ exports.regexParser = exports.quotedString = exports.eof = exports.newline = exports.tab = exports.quote = exports.num = exports.word = exports.alphanum = exports.letter = exports.digit = exports.spaces = exports.space = exports.anyChar = exports.noneOf = exports.oneOf = exports.str = exports.char = exports.betweenWithin = void 0;
13
13
  const combinators_1 = require("./combinators");
14
14
  const trace_1 = require("./trace");
15
15
  const types_1 = require("./types");
@@ -105,16 +105,27 @@
105
105
  }
106
106
  return (0, types_1.success)(input[0], input.slice(1));
107
107
  });
108
+ /** A parser that matches one of " \t\n\r". */
108
109
  exports.space = oneOf(" \t\n\r");
110
+ /** A parser that matches one or more spaces. */
109
111
  exports.spaces = (0, combinators_1.many1WithJoin)(exports.space);
112
+ /** A parser that matches one digit. */
110
113
  exports.digit = oneOf("0123456789");
114
+ /** A parser that matches one letter, currently lowercase only. */
111
115
  exports.letter = oneOf("abcdefghijklmnopqrstuvwxyz");
116
+ /** A parser that matches one digit or letter, currently lowercase only. */
112
117
  exports.alphanum = oneOf("abcdefghijklmnopqrstuvwxyz0123456789");
118
+ /** A parser that matches one lowercase word. */
113
119
  exports.word = (0, combinators_1.many1WithJoin)(exports.letter);
120
+ /** A parser that matches one or more digits. */
114
121
  exports.num = (0, combinators_1.many1WithJoin)(exports.digit);
122
+ /** A parser that matches one single or double quote. */
115
123
  exports.quote = oneOf(`'"`);
124
+ /** A parser that matches one tab character. */
116
125
  exports.tab = char("\t");
126
+ /** A parser that matches one newline ("\n" only) character. */
117
127
  exports.newline = char("\n");
128
+ /** A parser that succeeds on an empty string. Returns `null` as the result. */
118
129
  const eof = (input) => {
119
130
  if (input === "") {
120
131
  return (0, types_1.success)(null, input);
@@ -122,5 +133,19 @@
122
133
  return (0, types_1.failure)("expected end of input", input);
123
134
  };
124
135
  exports.eof = eof;
136
+ /** A parser that matches a quoted string, in single or double quotes.
137
+ * Returns the string as the result, including the quotes.
138
+ */
125
139
  exports.quotedString = (0, combinators_1.seq)([exports.quote, (0, combinators_1.manyWithJoin)(noneOf(`"'`)), exports.quote], (results) => results.join(""));
140
+ function regexParser(str) {
141
+ const re = typeof str === "string" ? new RegExp(`^(${str})`) : str;
142
+ return (0, trace_1.trace)(`regex(${str})`, (input) => {
143
+ const match = input.match(re);
144
+ if (match) {
145
+ return (0, types_1.success)(match[0], input.slice(match[0].length));
146
+ }
147
+ return (0, types_1.failure)(`expected ${str}, got ${input.slice(0, 10)}`, input);
148
+ });
149
+ }
150
+ exports.regexParser = regexParser;
126
151
  });
package/dist/trace.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  import { ParserResult } from "./types";
2
2
  export declare function resultToString<T>(name: string, result: ParserResult<T>): string;
3
3
  export declare function trace(name: string, parser: any): any;
4
+ export declare function parserTime(callback: Function): number | null;
5
+ export declare function printTime(name: string, callback: Function): void;
6
+ export declare function parserDebug(name: string, callback: Function): void;
package/dist/trace.js CHANGED
@@ -9,7 +9,7 @@
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.trace = exports.resultToString = void 0;
12
+ exports.parserDebug = exports.printTime = exports.parserTime = exports.trace = exports.resultToString = void 0;
13
13
  const utils_1 = require("./utils");
14
14
  const STEP = 2;
15
15
  function resultToString(name, result) {
@@ -20,23 +20,85 @@
20
20
  }
21
21
  exports.resultToString = resultToString;
22
22
  let level = 0;
23
+ let counts = {};
24
+ let times = {};
25
+ let debugFlag = !!process.env.DEBUG;
26
+ let stepCount = 0;
27
+ let stepLimit = -1;
23
28
  function trace(name, parser) {
24
29
  return (input) => {
25
- if (process.env.DEBUG) {
30
+ if (debugFlag) {
26
31
  console.log(" ".repeat(level) + `🔍 ${name} -- input: ${(0, utils_1.escape)(input)}`);
27
- }
28
- level += STEP;
29
- const result = parser(input);
30
- level -= STEP;
31
- if (process.env.DEBUG) {
32
+ let result;
33
+ const time = parserTime(() => {
34
+ level += STEP;
35
+ result = parser(input);
36
+ level -= STEP;
37
+ });
38
+ counts[name] = counts[name] ? counts[name] + 1 : 1;
39
+ stepCount += 1;
40
+ if (time) {
41
+ times[name] = times[name] ? times[name] + time : time;
42
+ }
32
43
  console.log(" ".repeat(level) + resultToString(name, result));
33
44
  if (result.success && result.captures) {
34
45
  console.log(" ".repeat(level) +
35
46
  `⭐ ${name} -- captures: ${JSON.stringify(result.captures)}`);
36
47
  }
48
+ return result;
49
+ }
50
+ else {
51
+ return parser(input);
37
52
  }
38
- return result;
39
53
  };
40
54
  }
41
55
  exports.trace = trace;
56
+ function parserTime(callback) {
57
+ if (performance && performance.now) {
58
+ const start = performance.now();
59
+ callback();
60
+ const end = performance.now();
61
+ return end - start;
62
+ }
63
+ else {
64
+ console.error("performance.now not available");
65
+ callback();
66
+ return null;
67
+ }
68
+ }
69
+ exports.parserTime = parserTime;
70
+ function printTime(name, callback) {
71
+ const time = parserTime(callback);
72
+ if (time) {
73
+ console.log(`⏱ ${name} -- time: ${(0, utils_1.round)(time)}ms`);
74
+ }
75
+ }
76
+ exports.printTime = printTime;
77
+ function parserDebug(name, callback) {
78
+ debugFlag = true;
79
+ stepCount = 0;
80
+ counts = {};
81
+ times = {};
82
+ printTime(name, callback);
83
+ debugFlag = false;
84
+ console.log("\n");
85
+ console.log(`📊 ${name} -- counts:`);
86
+ const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
87
+ for (const [name, count] of sorted) {
88
+ console.log(` ${name}: ${count}`);
89
+ }
90
+ console.log("\n");
91
+ console.log(`📊 ${name} -- times:`);
92
+ const sortedTimes = Object.entries(times).sort((a, b) => b[1] - a[1]);
93
+ for (const [name, time] of sortedTimes) {
94
+ console.log(` ${name}: ${(0, utils_1.round)(time)}ms`);
95
+ }
96
+ console.log("\n");
97
+ console.log(`📊 ${name} -- step count: ${stepCount}`);
98
+ console.log("\n\n");
99
+ stepCount = 0;
100
+ counts = {};
101
+ times = {};
102
+ }
103
+ exports.parserDebug = parserDebug;
42
104
  });
package/dist/types.d.ts CHANGED
@@ -1,13 +1,21 @@
1
+ /** A generic object type. */
1
2
  export type PlainObject = Record<string, unknown>;
3
+ /** Represents a parse success with no captures. */
2
4
  export type ParserSuccess<T> = {
3
5
  success: true;
4
6
  result: T;
5
7
  rest: string;
6
- nextParser?: GeneralParser<any, any>;
8
+ nextParser?: Parser<any>;
7
9
  };
8
- export type CaptureParserSuccess<T, C extends PlainObject> = ParserSuccess<T> & {
10
+ /** Represents a parse success with captures. Notice nextParser is also a CaptureParser. */
11
+ export type CaptureParserSuccess<T, C extends PlainObject> = {
12
+ success: true;
13
+ result: T;
14
+ rest: string;
9
15
  captures: C;
16
+ nextParser?: CaptureParser<any, any>;
10
17
  };
18
+ /** Represents a parse failure. */
11
19
  export type ParserFailure = {
12
20
  success: false;
13
21
  rest: string;
@@ -15,22 +23,46 @@ export type ParserFailure = {
15
23
  };
16
24
  export type ParserResult<T> = ParserSuccess<T> | ParserFailure;
17
25
  export type CaptureParserResult<T, C extends PlainObject> = CaptureParserSuccess<T, C> | ParserFailure;
26
+ /** A parser is any function that takes a string and returns a ParserResult. */
18
27
  export type Parser<T> = (input: string) => ParserResult<T>;
28
+ /** A capture parser is any function that takes a string and returns a CaptureParserResult.
29
+ * A CaptureParserResult is the same as a ParserResult, except it also includes captures,
30
+ * i.e. matches selected using `capture`. */
19
31
  export type CaptureParser<T, C extends PlainObject> = (input: string) => CaptureParserResult<T, C>;
20
32
  export type GeneralParser<T, C extends PlainObject> = Parser<T> | CaptureParser<T, C>;
21
33
  export declare function isCaptureResult<T, C extends PlainObject>(result: ParserResult<T>): result is CaptureParserSuccess<T, C>;
34
+ /** Convenience function to return a ParserSuccess */
22
35
  export declare function success<T>(result: T, rest: string): ParserSuccess<T>;
36
+ /** Convenience function to return a CaptureParserSuccess */
23
37
  export declare function captureSuccess<T, C extends PlainObject>(result: T, rest: string, captures: C): CaptureParserSuccess<T, C>;
38
+ /** Convenience function to return a ParserFailure */
24
39
  export declare function failure(message: string, rest: string): ParserFailure;
40
+ /** Prettify an intersected type, to make it easier to read. */
25
41
  export type Prettify<T> = {
26
42
  [K in keyof T]: T[K];
27
43
  } & {};
44
+ /** see <https://stackoverflow.com/a/50375286/3625> */
28
45
  export type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
46
+ /** Convenience type to get the result type out of a parser. */
29
47
  type ExtractResults<T> = T extends Parser<infer U> ? U : never;
48
+ /** Convenience type to get the capture type out of a capture parser. */
30
49
  type ExtractCaptures<T> = T extends CaptureParser<any, infer U> ? U : never;
50
+ /** Convenience type where given an array of parsers and capture parsers,
51
+ * it returns the types of the capture parsers, like
52
+ * CaptureParser<string, { name: string }> | CaptureParser<number, { age: number }>
53
+ */
31
54
  type ExtractCaptureParsers<T extends readonly GeneralParser<any, any>[]> = Extract<T[number], CaptureParser<any, any>>;
55
+ /** Convenience type where given an array of parsers and capture parsers,
56
+ * it returns a single capture type that merges all the capture types. */
32
57
  export type MergedCaptures<T extends readonly GeneralParser<any, any>[]> = Prettify<UnionToIntersection<UnionOfCaptures<T>>>;
58
+ /** Convenience type where given an array of parsers and capture parsers,
59
+ * it first gets the capture parsers, then extracts the capture types,
60
+ * and returns a union of all the capture types. Example:
61
+ * { name: string } | { age: number }
62
+ */
33
63
  export type UnionOfCaptures<T extends readonly GeneralParser<any, any>[]> = Prettify<ExtractCaptures<ExtractCaptureParsers<T>>>;
64
+ /** Convenience type where given an array of parsers and capture parsers,
65
+ * it returns true if there are any capture parsers, otherwise false. */
34
66
  export type HasCaptureParsers<T extends readonly GeneralParser<any, any>[]> = ExtractCaptureParsers<T> extends never ? false : true;
35
67
  /**
36
68
  * For a given array of GeneralParsers, if any of them is a CaptureParser,
@@ -40,7 +72,18 @@ export type HasCaptureParsers<T extends readonly GeneralParser<any, any>[]> = Ex
40
72
  * which is not able to infer its return type correctly.
41
73
  */
42
74
  export type PickParserType<T extends readonly GeneralParser<any, any>[]> = HasCaptureParsers<T> extends true ? CaptureParser<MergedResults<T>, UnionOfCaptures<T>> : Parser<MergedResults<T>>;
75
+ /** This is used to generate a return type for the `many` and `many1` combinators.
76
+ * Given a parser we want to apply `many` to. Suppose its type is `Parser<string>`.
77
+ * This will return `Parser<string[]>` as the return type.
78
+ *
79
+ * Suppose the parser is a `CaptureParser<string, { name: string }>`.
80
+ * This will return `CaptureParser<string[], { captures: { name: string }[] }>` as the return type.
81
+ */
82
+ export type InferManyReturnType<T extends GeneralParser<any, any>> = T extends CaptureParser<infer R, infer C> ? CaptureParser<R[], {
83
+ captures: C[];
84
+ }> : Parser<T[]>;
43
85
  export type MergedResults<T extends readonly GeneralParser<any, any>[]> = ExtractResults<T[number]>;
86
+ /** Used to create a parser tree for backtracking. */
44
87
  export type Node = ParserNode | EmptyNode;
45
88
  export type ParserNode = {
46
89
  parent: Node;
@@ -50,8 +93,14 @@ export type ParserNode = {
50
93
  closed: boolean;
51
94
  };
52
95
  export type EmptyNode = null;
96
+ /** Convenience function to create a ParserNode. */
53
97
  export declare function createNode(parent: Node | null, parser: GeneralParser<any, any>): ParserNode;
98
+ /** Convenience function where, given an array of parsers, it creates a tree we can use for backtracking.
99
+ * This tree is what `seq` use. It's used to keep track of the parsers we've tried so far,
100
+ * so we can backtrack if we need to.
101
+ */
54
102
  export declare function createTree(parsers: readonly GeneralParser<any, any>[]): Node;
103
+ /** Used by `within`. */
55
104
  export type Matched = {
56
105
  type: "matched";
57
106
  value: string;
@@ -60,5 +109,5 @@ export type Unmatched = {
60
109
  type: "unmatched";
61
110
  value: string;
62
111
  };
63
- export type BetweenWithinResult = Matched | Unmatched;
112
+ export type WithinResult = Matched | Unmatched;
64
113
  export {};
package/dist/types.js CHANGED
@@ -14,18 +14,22 @@
14
14
  return "captures" in result;
15
15
  }
16
16
  exports.isCaptureResult = isCaptureResult;
17
+ /** Convenience function to return a ParserSuccess */
17
18
  function success(result, rest) {
18
19
  return { success: true, result, rest };
19
20
  }
20
21
  exports.success = success;
22
+ /** Convenience function to return a CaptureParserSuccess */
21
23
  function captureSuccess(result, rest, captures) {
22
24
  return { success: true, result, rest, captures };
23
25
  }
24
26
  exports.captureSuccess = captureSuccess;
27
+ /** Convenience function to return a ParserFailure */
25
28
  function failure(message, rest) {
26
29
  return { success: false, message, rest };
27
30
  }
28
31
  exports.failure = failure;
32
+ /** Convenience function to create a ParserNode. */
29
33
  function createNode(parent, parser) {
30
34
  return {
31
35
  parent,
@@ -35,6 +39,10 @@
35
39
  };
36
40
  }
37
41
  exports.createNode = createNode;
42
+ /** Convenience function where, given an array of parsers, it creates a tree we can use for backtracking.
43
+ * This tree is what `seq` use. It's used to keep track of the parsers we've tried so far,
44
+ * so we can backtrack if we need to.
45
+ */
38
46
  function createTree(parsers) {
39
47
  if (parsers.length === 0) {
40
48
  return null;
package/dist/utils.d.ts CHANGED
@@ -4,3 +4,4 @@ export declare function merge(a: any | any[], b: any | any[]): any[];
4
4
  export declare function mergeCaptures(a: Record<string, any>, b: Record<string, any>): Record<string, any>;
5
5
  export declare function findAncestorWithNextParser(node: Node, count?: number): [Node, number];
6
6
  export declare function popMany(arr: any[], count: number): void;
7
+ export declare function round(num: number, places?: number): number;
package/dist/utils.js CHANGED
@@ -9,7 +9,7 @@
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.popMany = exports.findAncestorWithNextParser = exports.mergeCaptures = exports.merge = exports.escape = void 0;
12
+ exports.round = exports.popMany = exports.findAncestorWithNextParser = exports.mergeCaptures = exports.merge = exports.escape = void 0;
13
13
  function escape(str) {
14
14
  return JSON.stringify(str);
15
15
  }
@@ -63,4 +63,8 @@
63
63
  }
64
64
  }
65
65
  exports.popMany = popMany;
66
+ function round(num, places = 2) {
67
+ return Math.round(num * 10 ** places) / 10 ** places;
68
+ }
69
+ exports.round = round;
66
70
  });
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "tarsec",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "A parser combinator library for TypeScript, inspired by Parsec.",
5
5
  "homepage": "https://github.com/egonSchiele/tarsec",
6
6
  "scripts": {
7
7
  "test": "vitest",
8
+ "test:tsc": "tsc -p tests/tsconfig.json",
8
9
  "coverage": "vitest --coverage",
9
10
  "build": "rm -rf dist && tsc",
10
11
  "start": "cd dist && node index.js",