tarsec 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/combinators.d.ts +173 -7
- package/dist/combinators.js +253 -31
- package/dist/parsers/within.d.ts +2 -2
- package/dist/parsers.d.ts +31 -1
- package/dist/parsers.js +60 -6
- package/dist/trace.d.ts +89 -0
- package/dist/trace.js +156 -8
- package/dist/types.d.ts +52 -3
- package/dist/types.js +8 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +5 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -41,7 +41,11 @@ parser("hello there"); // failure
|
|
|
41
41
|
## Features
|
|
42
42
|
- tarsec is entirely TypeScript. There's nothing to compile.
|
|
43
43
|
- [Debug mode](/tutorials/debugging.md) that prints what's happening step-by-step
|
|
44
|
+
- Tools to debug your parser's [performance](/tutorials/performance.md)
|
|
44
45
|
- Derived types: tarsec will generate TypeScript types for your parser
|
|
45
46
|
- Partial [backtracking](/tutorials/backtracking.md) support
|
|
46
47
|
|
|
47
|
-
Read more about [use cases for tarsec](/tutorials/use-case.md).
|
|
48
|
+
Read more about [use cases for tarsec](/tutorials/use-case.md).
|
|
49
|
+
|
|
50
|
+
## Contributing
|
|
51
|
+
PRs for documentation, tests, and bug fixes are welcome.
|
package/dist/combinators.d.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
|
-
import { CaptureParser, GeneralParser, MergedCaptures, MergedResults, Parser, PickParserType,
|
|
2
|
-
|
|
3
|
-
|
|
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
|
+
* @param parser - parser to run
|
|
11
|
+
* @returns - parser that runs the given parser zero to many times,
|
|
12
|
+
* and returns the result as an array
|
|
13
|
+
*/
|
|
14
|
+
export declare function many<const T extends GeneralParser<any, any>>(parser: T): InferManyReturnType<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Same as `many`, but fails if the parser doesn't match at least once.
|
|
17
|
+
*
|
|
18
|
+
* @param parser - parser to run
|
|
19
|
+
* @returns a parser that runs the given parser one to many times,
|
|
20
|
+
*/
|
|
21
|
+
export declare function many1<const T extends GeneralParser<any, any>>(parser: T): InferManyReturnType<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Takes a parser, runs it, and returns the number of times it succeeded.
|
|
24
|
+
* @param parser - parser to run
|
|
25
|
+
* @returns - the number of times the parser succeeded.
|
|
26
|
+
*/
|
|
27
|
+
export declare function count<T>(parser: Parser<T>): Parser<T[]>;
|
|
4
28
|
/**
|
|
5
29
|
* Takes a parser, runs it n times, and returns the results as an array.
|
|
6
30
|
* If it cannot run the parser n times, it fails without consuming input.
|
|
@@ -8,8 +32,22 @@ export declare function many1<T>(parser: Parser<T>): Parser<T[]>;
|
|
|
8
32
|
* @param parser - parser to run
|
|
9
33
|
* @returns - parser that runs the given parser `num` times and returns an array of the results
|
|
10
34
|
*/
|
|
11
|
-
export declare function
|
|
12
|
-
|
|
35
|
+
export declare function exactly<T>(num: number, parser: Parser<T>): Parser<T[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Same as `many`, but joins the results into a single string.
|
|
38
|
+
*
|
|
39
|
+
* @param parser - parser to run. The parser must return a string as its result.
|
|
40
|
+
* @returns - parser that runs the given parser zero to many times,
|
|
41
|
+
* and returns the result as a single string
|
|
42
|
+
*/
|
|
43
|
+
export declare function manyWithJoin<const T extends GeneralParser<string, any>>(parser: T): GeneralParser<string, any>;
|
|
44
|
+
/**
|
|
45
|
+
* Same as `many1`, but joins the results into a single string.
|
|
46
|
+
*
|
|
47
|
+
* @param parser - parser to run. The parser must return a string as its result.
|
|
48
|
+
* @returns - parser that runs the given parser one to many times,
|
|
49
|
+
* and returns the result as a single string
|
|
50
|
+
*/
|
|
13
51
|
export declare function many1WithJoin(parser: Parser<string>): Parser<string>;
|
|
14
52
|
/**
|
|
15
53
|
* `or` takes an array of parsers and runs them sequentially.
|
|
@@ -38,16 +76,127 @@ export declare function many1WithJoin(parser: Parser<string>): Parser<string>;
|
|
|
38
76
|
* @returns - a parser that tries each parser in order. Returns the result of the first parser that succeeds.
|
|
39
77
|
*/
|
|
40
78
|
export declare function or<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): PickParserType<T>;
|
|
79
|
+
/**
|
|
80
|
+
* Takes a parser and runs it. If the parser fails,
|
|
81
|
+
* optional returns a success with a null result.
|
|
82
|
+
*
|
|
83
|
+
* @param parser - parser to run
|
|
84
|
+
* @returns - a parser that runs the given parser.
|
|
85
|
+
* If it fails, returns a success with a null result.
|
|
86
|
+
*/
|
|
41
87
|
export declare function optional<T>(parser: Parser<T>): Parser<T | null>;
|
|
88
|
+
/**
|
|
89
|
+
* Takes a parser and runs it. If the parser fails,
|
|
90
|
+
* `not` returns a success with a `null` result.
|
|
91
|
+
* If the parser succeeds, `not` returns a failure.
|
|
92
|
+
*
|
|
93
|
+
* @param parser - parser to run
|
|
94
|
+
* @returns - a parser that runs the given parser.
|
|
95
|
+
* If it fails, returns a success with a `null` result.
|
|
96
|
+
* If it succeeds, returns a failure.
|
|
97
|
+
*/
|
|
42
98
|
export declare function not(parser: Parser<any>): Parser<null>;
|
|
99
|
+
/**
|
|
100
|
+
* Takes three parsers, `open`, `close`, and `parser`.
|
|
101
|
+
* `between` matches something that matches `parser`,
|
|
102
|
+
* surrounded by `open` and `close`. It returns the result of `parser`.
|
|
103
|
+
* If any of the parsers fail, `between` fails.
|
|
104
|
+
*
|
|
105
|
+
* @param open - parser for the opening delimiter
|
|
106
|
+
* @param close - parser for the closing delimiter
|
|
107
|
+
* @param parser - parser for the content
|
|
108
|
+
* @returns a parser that returns the result of `parser`.
|
|
109
|
+
*/
|
|
43
110
|
export declare function between<O, C, P>(open: Parser<O>, close: Parser<C>, parser: Parser<P>): Parser<P>;
|
|
111
|
+
/**
|
|
112
|
+
* Parses many instances of the parser separated by separator.
|
|
113
|
+
* @param separator
|
|
114
|
+
* @param parser
|
|
115
|
+
* @returns a parser that runs the given parser zero to many times, separated by the separator parser.
|
|
116
|
+
*/
|
|
44
117
|
export declare function sepBy<S, P>(separator: Parser<S>, parser: Parser<P>): Parser<P[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Convenience function to use as the second argument to `seq` to get all the results from `seq`
|
|
120
|
+
* @param results
|
|
121
|
+
* @param captures
|
|
122
|
+
* @returns `results`
|
|
123
|
+
*/
|
|
45
124
|
export declare function getResults<R, C>(results: R, captures: C): R;
|
|
125
|
+
/**
|
|
126
|
+
* Convenience function to use as the second argument to seq to get all the captures.
|
|
127
|
+
* @param results
|
|
128
|
+
* @param captures
|
|
129
|
+
* @returns `captures`
|
|
130
|
+
*/
|
|
46
131
|
export declare function getCaptures<R, C>(results: R, captures: C): C;
|
|
132
|
+
/**
|
|
133
|
+
* `capture` is the only way to create a capture. Given a parser and a name,
|
|
134
|
+
* `capture` runs the parser and saves its result in a captures object
|
|
135
|
+
* with the given name as the key. It returns the result from the parser,
|
|
136
|
+
* and attaches the captures object along with it.
|
|
137
|
+
*
|
|
138
|
+
* @param parser - parser to run
|
|
139
|
+
* @param name - name of the capture
|
|
140
|
+
* @returns - the results of the parser, with the captures object attached.
|
|
141
|
+
*/
|
|
47
142
|
export declare function capture<T, const S extends string>(parser: Parser<T>, name: S): CaptureParser<T, Record<S, T>>;
|
|
48
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Returns a parser that consumes input till the given parser succeeds.
|
|
145
|
+
* @param parser - the stop parser
|
|
146
|
+
* @returns a parser that consumes the input string until the stop parser succeeds.
|
|
147
|
+
* Then it returns the consumed input as a string.
|
|
148
|
+
* The stop parser's match is not included in the result.
|
|
149
|
+
*/
|
|
49
150
|
export declare function manyTill<T>(parser: Parser<T>): Parser<string>;
|
|
50
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Just like `manyTill`, but fails unless at least one character of input is consumed.
|
|
153
|
+
* @param parser - the stop parser
|
|
154
|
+
* @returns a parser that consumes the input string until the stop parser succeeds.
|
|
155
|
+
*/
|
|
156
|
+
export declare function many1Till<T>(parser: Parser<T>): Parser<string>;
|
|
157
|
+
/**
|
|
158
|
+
* `manyTillStr` is an optimized version of `manyTill`.
|
|
159
|
+
* The `manyTill` combinator is slow because it runs the given parser
|
|
160
|
+
* on every character of the string until it succeeds. However, if you
|
|
161
|
+
* just want to consume input until you get to a substring,
|
|
162
|
+
* use `manyTillStr`. It uses `indexOf`, which is significantly faster
|
|
163
|
+
* than running a parser over every character.
|
|
164
|
+
*
|
|
165
|
+
* @param str - the string to stop at
|
|
166
|
+
* @param options - object of optional parameters. { caseInsensitive: boolean }
|
|
167
|
+
* @returns a parser that consumes the input string until the given string is found.
|
|
168
|
+
*/
|
|
169
|
+
export declare function manyTillStr(str: string, { caseInsensitive }?: {
|
|
170
|
+
caseInsensitive?: boolean;
|
|
171
|
+
}): Parser<string>;
|
|
172
|
+
/**
|
|
173
|
+
* Like `manyTillStr`, but case insensitive.
|
|
174
|
+
* @param str - the string to stop at
|
|
175
|
+
* @returns a parser that consumes the input string until the given string is found.
|
|
176
|
+
*/
|
|
177
|
+
export declare function iManyTillStr(str: string): Parser<string>;
|
|
178
|
+
/**
|
|
179
|
+
* `map` is a parser combinator that takes a parser and a mapper function.
|
|
180
|
+
* If the parser succeeds, it maps its result using the mapper function.
|
|
181
|
+
* You can think of map as a general `map`, like for functors, applied to a parser.
|
|
182
|
+
* Since `map` itself is a parser, you can use it in `seq` or other combinators.
|
|
183
|
+
*
|
|
184
|
+
* @param parser - parser to run
|
|
185
|
+
* @param mapperFunc - function to map the result of the parser
|
|
186
|
+
* @returns
|
|
187
|
+
*/
|
|
188
|
+
export declare function map<R, C extends PlainObject, X>(parser: GeneralParser<R, C>, mapperFunc: (x: R) => X): GeneralParser<X, C>;
|
|
189
|
+
/**
|
|
190
|
+
* Given a parser that returns a string, `search` looks for all substrings in a string that match that parser.
|
|
191
|
+
* For example, given a parser that matches quoted strings, `search` will return an array of all the quoted strings
|
|
192
|
+
* it finds in the input, as an array.
|
|
193
|
+
*
|
|
194
|
+
* The rest of the input that isn't part of the result is simply joined together and returned as a string.
|
|
195
|
+
* If you need a more structured result + rest, you can use `within` instead.
|
|
196
|
+
*
|
|
197
|
+
* @param parser - a parser that returns a string
|
|
198
|
+
* @returns - a parser that returns an array of strings
|
|
199
|
+
*/
|
|
51
200
|
export declare function search(parser: Parser<string>): Parser<string[]>;
|
|
52
201
|
/**
|
|
53
202
|
* seq takes an array of parsers and runs them sequentially.
|
|
@@ -68,11 +217,28 @@ export declare function search(parser: Parser<string>): Parser<string[]>;
|
|
|
68
217
|
* and pass the remaining input to the next parser yourself.
|
|
69
218
|
* seq also does some backtracking for you that you will need to do yourself.
|
|
70
219
|
*
|
|
220
|
+
* Also see `seqR` and `seqC` for convenience functions that return the results or captures respectively.
|
|
221
|
+
*
|
|
71
222
|
* @param parsers - parsers to run sequentially
|
|
72
223
|
* @param transform - function to transform the results and captures. The params are the results and captures
|
|
73
224
|
* @param debugName - optional name for trace debugging
|
|
74
225
|
* @returns
|
|
75
226
|
*/
|
|
76
227
|
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>;
|
|
228
|
+
/** Just like seq except it returns the results.
|
|
229
|
+
* It's like using `seq([parsers], getResults)`.
|
|
230
|
+
*/
|
|
77
231
|
export declare function seqR<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<MergedResults<T>[]>;
|
|
232
|
+
/** Just like seq except it returns the captures.
|
|
233
|
+
* It's like using `seq([parsers], getCaptures)`.
|
|
234
|
+
*/
|
|
78
235
|
export declare function seqC<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<MergedCaptures<T>>;
|
|
236
|
+
/**
|
|
237
|
+
* Match takes an input string and a parser. If the parser matches the input string
|
|
238
|
+
* and consumes the entire input string, `match` returns `true`. Otherwise it returns `false`.
|
|
239
|
+
*
|
|
240
|
+
* @param input - input string
|
|
241
|
+
* @param parser - parser to match input against
|
|
242
|
+
* @returns - true if the parser matches the input and consumes all input, false otherwise
|
|
243
|
+
*/
|
|
244
|
+
export declare function match(input: string, parser: GeneralParser<any, any>): boolean;
|
package/dist/combinators.js
CHANGED
|
@@ -9,26 +9,62 @@
|
|
|
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.
|
|
12
|
+
exports.match = exports.seqC = exports.seqR = exports.seq = exports.search = exports.map = exports.iManyTillStr = 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.exactly = 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
|
+
* @param parser - parser to run
|
|
26
|
+
* @returns - parser that runs the given parser zero to many times,
|
|
27
|
+
* and returns the result as an array
|
|
28
|
+
*/
|
|
17
29
|
function many(parser) {
|
|
18
30
|
return (0, trace_1.trace)("many", (input) => {
|
|
19
31
|
let results = [];
|
|
32
|
+
let captures = [];
|
|
20
33
|
let rest = input;
|
|
21
34
|
while (true) {
|
|
22
35
|
let parsed = parser(rest);
|
|
23
36
|
if (!parsed.success) {
|
|
24
|
-
|
|
37
|
+
if (Object.keys(captures).length) {
|
|
38
|
+
return (0, types_1.captureSuccess)(results, rest, { captures });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return (0, types_1.success)(results, rest);
|
|
42
|
+
}
|
|
25
43
|
}
|
|
26
44
|
results.push(parsed.result);
|
|
45
|
+
if ((0, types_1.isCaptureResult)(parsed)) {
|
|
46
|
+
captures.push(parsed.captures);
|
|
47
|
+
}
|
|
27
48
|
rest = parsed.rest;
|
|
49
|
+
// don't loop infinitely on empty strings
|
|
50
|
+
if (rest === "") {
|
|
51
|
+
if (Object.keys(captures).length) {
|
|
52
|
+
return (0, types_1.captureSuccess)(results, rest, { captures });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return (0, types_1.success)(results, rest);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
28
58
|
}
|
|
29
59
|
});
|
|
30
60
|
}
|
|
31
61
|
exports.many = many;
|
|
62
|
+
/**
|
|
63
|
+
* Same as `many`, but fails if the parser doesn't match at least once.
|
|
64
|
+
*
|
|
65
|
+
* @param parser - parser to run
|
|
66
|
+
* @returns a parser that runs the given parser one to many times,
|
|
67
|
+
*/
|
|
32
68
|
function many1(parser) {
|
|
33
69
|
return (0, trace_1.trace)(`many1`, (input) => {
|
|
34
70
|
let result = many(parser)(input);
|
|
@@ -44,6 +80,21 @@
|
|
|
44
80
|
});
|
|
45
81
|
}
|
|
46
82
|
exports.many1 = many1;
|
|
83
|
+
/**
|
|
84
|
+
* Takes a parser, runs it, and returns the number of times it succeeded.
|
|
85
|
+
* @param parser - parser to run
|
|
86
|
+
* @returns - the number of times the parser succeeded.
|
|
87
|
+
*/
|
|
88
|
+
function count(parser) {
|
|
89
|
+
return (0, trace_1.trace)("count", (input) => {
|
|
90
|
+
const result = many(parser)(input);
|
|
91
|
+
if (result.success) {
|
|
92
|
+
return (0, types_1.success)(result.result.length, result.rest);
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
exports.count = count;
|
|
47
98
|
/**
|
|
48
99
|
* Takes a parser, runs it n times, and returns the results as an array.
|
|
49
100
|
* If it cannot run the parser n times, it fails without consuming input.
|
|
@@ -51,8 +102,8 @@
|
|
|
51
102
|
* @param parser - parser to run
|
|
52
103
|
* @returns - parser that runs the given parser `num` times and returns an array of the results
|
|
53
104
|
*/
|
|
54
|
-
function
|
|
55
|
-
return (0, trace_1.trace)("
|
|
105
|
+
function exactly(num, parser) {
|
|
106
|
+
return (0, trace_1.trace)("exactly", (input) => {
|
|
56
107
|
let results = [];
|
|
57
108
|
let rest = input;
|
|
58
109
|
for (let i = 0; i < num; i++) {
|
|
@@ -66,13 +117,39 @@
|
|
|
66
117
|
return (0, types_1.success)(results, rest);
|
|
67
118
|
});
|
|
68
119
|
}
|
|
69
|
-
exports.
|
|
120
|
+
exports.exactly = exactly;
|
|
121
|
+
/**
|
|
122
|
+
* Same as `many`, but joins the results into a single string.
|
|
123
|
+
*
|
|
124
|
+
* @param parser - parser to run. The parser must return a string as its result.
|
|
125
|
+
* @returns - parser that runs the given parser zero to many times,
|
|
126
|
+
* and returns the result as a single string
|
|
127
|
+
*/
|
|
70
128
|
function manyWithJoin(parser) {
|
|
71
|
-
return
|
|
129
|
+
return (0, trace_1.trace)("manyWithJoin", (input) => {
|
|
130
|
+
const result = many(parser)(input);
|
|
131
|
+
if (result.success) {
|
|
132
|
+
return Object.assign(Object.assign({}, result), { result: result.result.join("") });
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
});
|
|
72
136
|
}
|
|
73
137
|
exports.manyWithJoin = manyWithJoin;
|
|
138
|
+
/**
|
|
139
|
+
* Same as `many1`, but joins the results into a single string.
|
|
140
|
+
*
|
|
141
|
+
* @param parser - parser to run. The parser must return a string as its result.
|
|
142
|
+
* @returns - parser that runs the given parser one to many times,
|
|
143
|
+
* and returns the result as a single string
|
|
144
|
+
*/
|
|
74
145
|
function many1WithJoin(parser) {
|
|
75
|
-
return
|
|
146
|
+
return (0, trace_1.trace)("many1WithJoin", (input) => {
|
|
147
|
+
const result = many1(parser)(input);
|
|
148
|
+
if (result.success) {
|
|
149
|
+
return Object.assign(Object.assign({}, result), { result: result.result.join("") });
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
});
|
|
76
153
|
}
|
|
77
154
|
exports.many1WithJoin = many1WithJoin;
|
|
78
155
|
/**
|
|
@@ -117,6 +194,14 @@
|
|
|
117
194
|
});
|
|
118
195
|
}
|
|
119
196
|
exports.or = or;
|
|
197
|
+
/**
|
|
198
|
+
* Takes a parser and runs it. If the parser fails,
|
|
199
|
+
* optional returns a success with a null result.
|
|
200
|
+
*
|
|
201
|
+
* @param parser - parser to run
|
|
202
|
+
* @returns - a parser that runs the given parser.
|
|
203
|
+
* If it fails, returns a success with a null result.
|
|
204
|
+
*/
|
|
120
205
|
function optional(parser) {
|
|
121
206
|
return (0, trace_1.trace)("optional", (input) => {
|
|
122
207
|
let result = parser(input);
|
|
@@ -127,6 +212,16 @@
|
|
|
127
212
|
});
|
|
128
213
|
}
|
|
129
214
|
exports.optional = optional;
|
|
215
|
+
/**
|
|
216
|
+
* Takes a parser and runs it. If the parser fails,
|
|
217
|
+
* `not` returns a success with a `null` result.
|
|
218
|
+
* If the parser succeeds, `not` returns a failure.
|
|
219
|
+
*
|
|
220
|
+
* @param parser - parser to run
|
|
221
|
+
* @returns - a parser that runs the given parser.
|
|
222
|
+
* If it fails, returns a success with a `null` result.
|
|
223
|
+
* If it succeeds, returns a failure.
|
|
224
|
+
*/
|
|
130
225
|
function not(parser) {
|
|
131
226
|
return (0, trace_1.trace)("not", (input) => {
|
|
132
227
|
let result = parser(input);
|
|
@@ -134,13 +229,24 @@
|
|
|
134
229
|
return {
|
|
135
230
|
success: false,
|
|
136
231
|
rest: input,
|
|
137
|
-
message: "
|
|
232
|
+
message: "expected parser not to succeed",
|
|
138
233
|
};
|
|
139
234
|
}
|
|
140
235
|
return (0, types_1.success)(null, input);
|
|
141
236
|
});
|
|
142
237
|
}
|
|
143
238
|
exports.not = not;
|
|
239
|
+
/**
|
|
240
|
+
* Takes three parsers, `open`, `close`, and `parser`.
|
|
241
|
+
* `between` matches something that matches `parser`,
|
|
242
|
+
* surrounded by `open` and `close`. It returns the result of `parser`.
|
|
243
|
+
* If any of the parsers fail, `between` fails.
|
|
244
|
+
*
|
|
245
|
+
* @param open - parser for the opening delimiter
|
|
246
|
+
* @param close - parser for the closing delimiter
|
|
247
|
+
* @param parser - parser for the content
|
|
248
|
+
* @returns a parser that returns the result of `parser`.
|
|
249
|
+
*/
|
|
144
250
|
function between(open, close, parser) {
|
|
145
251
|
return (input) => {
|
|
146
252
|
const result1 = open(input);
|
|
@@ -159,6 +265,12 @@
|
|
|
159
265
|
};
|
|
160
266
|
}
|
|
161
267
|
exports.between = between;
|
|
268
|
+
/**
|
|
269
|
+
* Parses many instances of the parser separated by separator.
|
|
270
|
+
* @param separator
|
|
271
|
+
* @param parser
|
|
272
|
+
* @returns a parser that runs the given parser zero to many times, separated by the separator parser.
|
|
273
|
+
*/
|
|
162
274
|
function sepBy(separator, parser) {
|
|
163
275
|
return (input) => {
|
|
164
276
|
let results = [];
|
|
@@ -179,14 +291,36 @@
|
|
|
179
291
|
};
|
|
180
292
|
}
|
|
181
293
|
exports.sepBy = sepBy;
|
|
294
|
+
/**
|
|
295
|
+
* Convenience function to use as the second argument to `seq` to get all the results from `seq`
|
|
296
|
+
* @param results
|
|
297
|
+
* @param captures
|
|
298
|
+
* @returns `results`
|
|
299
|
+
*/
|
|
182
300
|
function getResults(results, captures) {
|
|
183
301
|
return results;
|
|
184
302
|
}
|
|
185
303
|
exports.getResults = getResults;
|
|
304
|
+
/**
|
|
305
|
+
* Convenience function to use as the second argument to seq to get all the captures.
|
|
306
|
+
* @param results
|
|
307
|
+
* @param captures
|
|
308
|
+
* @returns `captures`
|
|
309
|
+
*/
|
|
186
310
|
function getCaptures(results, captures) {
|
|
187
311
|
return captures;
|
|
188
312
|
}
|
|
189
313
|
exports.getCaptures = getCaptures;
|
|
314
|
+
/**
|
|
315
|
+
* `capture` is the only way to create a capture. Given a parser and a name,
|
|
316
|
+
* `capture` runs the parser and saves its result in a captures object
|
|
317
|
+
* with the given name as the key. It returns the result from the parser,
|
|
318
|
+
* and attaches the captures object along with it.
|
|
319
|
+
*
|
|
320
|
+
* @param parser - parser to run
|
|
321
|
+
* @param name - name of the capture
|
|
322
|
+
* @returns - the results of the parser, with the captures object attached.
|
|
323
|
+
*/
|
|
190
324
|
function capture(parser, name) {
|
|
191
325
|
return (0, trace_1.trace)(`capture(${(0, utils_1.escape)(name)})`, (input) => {
|
|
192
326
|
let result = parser(input);
|
|
@@ -200,18 +334,13 @@
|
|
|
200
334
|
});
|
|
201
335
|
}
|
|
202
336
|
exports.capture = capture;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
return result;
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
exports.wrap = wrap;
|
|
337
|
+
/**
|
|
338
|
+
* Returns a parser that consumes input till the given parser succeeds.
|
|
339
|
+
* @param parser - the stop parser
|
|
340
|
+
* @returns a parser that consumes the input string until the stop parser succeeds.
|
|
341
|
+
* Then it returns the consumed input as a string.
|
|
342
|
+
* The stop parser's match is not included in the result.
|
|
343
|
+
*/
|
|
215
344
|
function manyTill(parser) {
|
|
216
345
|
return (input) => {
|
|
217
346
|
let current = 0;
|
|
@@ -226,16 +355,95 @@
|
|
|
226
355
|
};
|
|
227
356
|
}
|
|
228
357
|
exports.manyTill = manyTill;
|
|
229
|
-
|
|
230
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Just like `manyTill`, but fails unless at least one character of input is consumed.
|
|
360
|
+
* @param parser - the stop parser
|
|
361
|
+
* @returns a parser that consumes the input string until the stop parser succeeds.
|
|
362
|
+
*/
|
|
363
|
+
function many1Till(parser) {
|
|
364
|
+
return (input) => {
|
|
365
|
+
let current = 0;
|
|
366
|
+
while (current < input.length) {
|
|
367
|
+
const parsed = parser(input.slice(current));
|
|
368
|
+
if (parsed.success) {
|
|
369
|
+
if (current === 0) {
|
|
370
|
+
return (0, types_1.failure)("expected to consume at least one character of input", input);
|
|
371
|
+
}
|
|
372
|
+
return (0, types_1.success)(input.slice(0, current), input.slice(current));
|
|
373
|
+
}
|
|
374
|
+
current++;
|
|
375
|
+
}
|
|
376
|
+
if (current === 0) {
|
|
377
|
+
return (0, types_1.failure)("expected to consume at least one character of input", input);
|
|
378
|
+
}
|
|
379
|
+
return (0, types_1.success)(input, "");
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
exports.many1Till = many1Till;
|
|
383
|
+
/**
|
|
384
|
+
* `manyTillStr` is an optimized version of `manyTill`.
|
|
385
|
+
* The `manyTill` combinator is slow because it runs the given parser
|
|
386
|
+
* on every character of the string until it succeeds. However, if you
|
|
387
|
+
* just want to consume input until you get to a substring,
|
|
388
|
+
* use `manyTillStr`. It uses `indexOf`, which is significantly faster
|
|
389
|
+
* than running a parser over every character.
|
|
390
|
+
*
|
|
391
|
+
* @param str - the string to stop at
|
|
392
|
+
* @param options - object of optional parameters. { caseInsensitive: boolean }
|
|
393
|
+
* @returns a parser that consumes the input string until the given string is found.
|
|
394
|
+
*/
|
|
395
|
+
function manyTillStr(str, { caseInsensitive = false } = {}) {
|
|
396
|
+
return (0, trace_1.trace)(`manyTillStr(${str})`, (input) => {
|
|
397
|
+
const index = caseInsensitive
|
|
398
|
+
? input.toLocaleLowerCase().indexOf(str.toLocaleLowerCase())
|
|
399
|
+
: input.indexOf(str);
|
|
400
|
+
if (index === -1) {
|
|
401
|
+
return (0, types_1.success)(input, "");
|
|
402
|
+
}
|
|
403
|
+
return (0, types_1.success)(input.slice(0, index), input.slice(index));
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
exports.manyTillStr = manyTillStr;
|
|
407
|
+
/**
|
|
408
|
+
* Like `manyTillStr`, but case insensitive.
|
|
409
|
+
* @param str - the string to stop at
|
|
410
|
+
* @returns a parser that consumes the input string until the given string is found.
|
|
411
|
+
*/
|
|
412
|
+
function iManyTillStr(str) {
|
|
413
|
+
return manyTillStr(str, { caseInsensitive: true });
|
|
414
|
+
}
|
|
415
|
+
exports.iManyTillStr = iManyTillStr;
|
|
416
|
+
/**
|
|
417
|
+
* `map` is a parser combinator that takes a parser and a mapper function.
|
|
418
|
+
* If the parser succeeds, it maps its result using the mapper function.
|
|
419
|
+
* You can think of map as a general `map`, like for functors, applied to a parser.
|
|
420
|
+
* Since `map` itself is a parser, you can use it in `seq` or other combinators.
|
|
421
|
+
*
|
|
422
|
+
* @param parser - parser to run
|
|
423
|
+
* @param mapperFunc - function to map the result of the parser
|
|
424
|
+
* @returns
|
|
425
|
+
*/
|
|
426
|
+
function map(parser, mapperFunc) {
|
|
427
|
+
return (0, trace_1.trace)(`map(${mapperFunc})`, (input) => {
|
|
231
428
|
let parsed = parser(input);
|
|
232
429
|
if (parsed.success) {
|
|
233
|
-
return Object.assign(Object.assign({}, parsed), { result:
|
|
430
|
+
return Object.assign(Object.assign({}, parsed), { result: mapperFunc(parsed.result) });
|
|
234
431
|
}
|
|
235
432
|
return parsed;
|
|
236
433
|
});
|
|
237
434
|
}
|
|
238
|
-
exports.
|
|
435
|
+
exports.map = map;
|
|
436
|
+
/**
|
|
437
|
+
* Given a parser that returns a string, `search` looks for all substrings in a string that match that parser.
|
|
438
|
+
* For example, given a parser that matches quoted strings, `search` will return an array of all the quoted strings
|
|
439
|
+
* it finds in the input, as an array.
|
|
440
|
+
*
|
|
441
|
+
* The rest of the input that isn't part of the result is simply joined together and returned as a string.
|
|
442
|
+
* If you need a more structured result + rest, you can use `within` instead.
|
|
443
|
+
*
|
|
444
|
+
* @param parser - a parser that returns a string
|
|
445
|
+
* @returns - a parser that returns an array of strings
|
|
446
|
+
*/
|
|
239
447
|
function search(parser) {
|
|
240
448
|
return (0, trace_1.trace)("search", (input) => {
|
|
241
449
|
let parsed = (0, within_1.within)(parser)(input);
|
|
@@ -330,6 +538,8 @@
|
|
|
330
538
|
* and pass the remaining input to the next parser yourself.
|
|
331
539
|
* seq also does some backtracking for you that you will need to do yourself.
|
|
332
540
|
*
|
|
541
|
+
* Also see `seqR` and `seqC` for convenience functions that return the results or captures respectively.
|
|
542
|
+
*
|
|
333
543
|
* @param parsers - parsers to run sequentially
|
|
334
544
|
* @param transform - function to transform the results and captures. The params are the results and captures
|
|
335
545
|
* @param debugName - optional name for trace debugging
|
|
@@ -384,19 +594,31 @@
|
|
|
384
594
|
});
|
|
385
595
|
}
|
|
386
596
|
exports.seq = seq;
|
|
597
|
+
/** Just like seq except it returns the results.
|
|
598
|
+
* It's like using `seq([parsers], getResults)`.
|
|
599
|
+
*/
|
|
387
600
|
function seqR(...parsers) {
|
|
388
601
|
return seq(parsers, getResults);
|
|
389
602
|
}
|
|
390
603
|
exports.seqR = seqR;
|
|
604
|
+
/** Just like seq except it returns the captures.
|
|
605
|
+
* It's like using `seq([parsers], getCaptures)`.
|
|
606
|
+
*/
|
|
391
607
|
function seqC(...parsers) {
|
|
392
608
|
return seq(parsers, getCaptures);
|
|
393
609
|
}
|
|
394
610
|
exports.seqC = seqC;
|
|
611
|
+
/**
|
|
612
|
+
* Match takes an input string and a parser. If the parser matches the input string
|
|
613
|
+
* and consumes the entire input string, `match` returns `true`. Otherwise it returns `false`.
|
|
614
|
+
*
|
|
615
|
+
* @param input - input string
|
|
616
|
+
* @param parser - parser to match input against
|
|
617
|
+
* @returns - true if the parser matches the input and consumes all input, false otherwise
|
|
618
|
+
*/
|
|
619
|
+
function match(input, parser) {
|
|
620
|
+
const result = parser(input);
|
|
621
|
+
return result.success && result.rest === "";
|
|
622
|
+
}
|
|
623
|
+
exports.match = match;
|
|
395
624
|
});
|
|
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
|
-
*/
|
package/dist/parsers/within.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function within(parser: Parser<string>): Parser<
|
|
1
|
+
import { WithinResult, Parser } from "../types";
|
|
2
|
+
export declare function within(parser: Parser<string>): Parser<WithinResult[]>;
|
package/dist/parsers.d.ts
CHANGED
|
@@ -10,10 +10,16 @@ export declare function char<const S extends string>(c: S): Parser<S>;
|
|
|
10
10
|
/**
|
|
11
11
|
* Takes a string. Returns a parser that parses that string.
|
|
12
12
|
*
|
|
13
|
-
* @param s - string to
|
|
13
|
+
* @param s - string to match on
|
|
14
14
|
* @returns - parser that parses the given string
|
|
15
15
|
*/
|
|
16
16
|
export declare function str<const S extends string>(s: S): Parser<S>;
|
|
17
|
+
/**
|
|
18
|
+
* Like `str`, but case insensitive.
|
|
19
|
+
* @param s - string to match on, case insensitive
|
|
20
|
+
* @returns - parser that matches the given string, case insensitive
|
|
21
|
+
*/
|
|
22
|
+
export declare function istr<const S extends string>(s: S): Parser<S>;
|
|
17
23
|
/**
|
|
18
24
|
* Takes a string. Returns a parser that parses
|
|
19
25
|
* one of the characters in that string.
|
|
@@ -38,15 +44,39 @@ export declare function noneOf(chars: string): Parser<string>;
|
|
|
38
44
|
* @returns - ParserResult
|
|
39
45
|
*/
|
|
40
46
|
export declare const anyChar: any;
|
|
47
|
+
/** A parser that matches one of " \t\n\r". */
|
|
41
48
|
export declare const space: Parser<string>;
|
|
49
|
+
/** A parser that matches one or more spaces. */
|
|
42
50
|
export declare const spaces: Parser<string>;
|
|
51
|
+
/** A parser that matches one digit. */
|
|
43
52
|
export declare const digit: Parser<string>;
|
|
53
|
+
/** A parser that matches one letter, case insensitive. */
|
|
44
54
|
export declare const letter: Parser<string>;
|
|
55
|
+
/** A parser that matches one digit or letter, case insensitive. */
|
|
45
56
|
export declare const alphanum: Parser<string>;
|
|
57
|
+
/** A parser that matches one word, case insensitive. */
|
|
46
58
|
export declare const word: Parser<string>;
|
|
59
|
+
/** A parser that matches one or more digits. */
|
|
47
60
|
export declare const num: Parser<string>;
|
|
61
|
+
/** A parser that matches one single or double quote. */
|
|
48
62
|
export declare const quote: Parser<string>;
|
|
63
|
+
/** A parser that matches one tab character. */
|
|
49
64
|
export declare const tab: Parser<string>;
|
|
65
|
+
/** A parser that matches one newline ("\n" only) character. */
|
|
50
66
|
export declare const newline: Parser<string>;
|
|
67
|
+
/** A parser that succeeds on an empty string. Returns `null` as the result. */
|
|
51
68
|
export declare const eof: Parser<null>;
|
|
69
|
+
/** A parser that matches a quoted string, in single or double quotes.
|
|
70
|
+
* Returns the string as the result, including the quotes.
|
|
71
|
+
*/
|
|
52
72
|
export declare const quotedString: Parser<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Returns a parser that matches a regex. If you pass in a string,
|
|
75
|
+
* it will get converted to a regex. The regex should always match from the start of the input.
|
|
76
|
+
* If you pass in a string, a `^` will get prepended to it.
|
|
77
|
+
*
|
|
78
|
+
* @param str - regex string or RegExp instance to match
|
|
79
|
+
* @param options - regex options (i = ignore case, g = global, m = multiline, u = unicode)
|
|
80
|
+
* @returns - parser that matches the given regex
|
|
81
|
+
*/
|
|
82
|
+
export declare function regexParser(str: string | RegExp, options?: string): 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.istr = 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");
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
/**
|
|
42
42
|
* Takes a string. Returns a parser that parses that string.
|
|
43
43
|
*
|
|
44
|
-
* @param s - string to
|
|
44
|
+
* @param s - string to match on
|
|
45
45
|
* @returns - parser that parses the given string
|
|
46
46
|
*/
|
|
47
47
|
function str(s) {
|
|
@@ -53,6 +53,20 @@
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
exports.str = str;
|
|
56
|
+
/**
|
|
57
|
+
* Like `str`, but case insensitive.
|
|
58
|
+
* @param s - string to match on, case insensitive
|
|
59
|
+
* @returns - parser that matches the given string, case insensitive
|
|
60
|
+
*/
|
|
61
|
+
function istr(s) {
|
|
62
|
+
return (0, trace_1.trace)(`istr(${(0, utils_1.escape)(s)})`, (input) => {
|
|
63
|
+
if (input.substring(0, s.length).toLocaleLowerCase() === s.toLocaleLowerCase()) {
|
|
64
|
+
return (0, types_1.success)(input.substring(0, s.length), input.slice(s.length));
|
|
65
|
+
}
|
|
66
|
+
return (0, types_1.failure)(`expected ${s}, got ${input.substring(0, s.length)}`, input);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
exports.istr = istr;
|
|
56
70
|
/**
|
|
57
71
|
* Takes a string. Returns a parser that parses
|
|
58
72
|
* one of the characters in that string.
|
|
@@ -105,16 +119,27 @@
|
|
|
105
119
|
}
|
|
106
120
|
return (0, types_1.success)(input[0], input.slice(1));
|
|
107
121
|
});
|
|
122
|
+
/** A parser that matches one of " \t\n\r". */
|
|
108
123
|
exports.space = oneOf(" \t\n\r");
|
|
124
|
+
/** A parser that matches one or more spaces. */
|
|
109
125
|
exports.spaces = (0, combinators_1.many1WithJoin)(exports.space);
|
|
126
|
+
/** A parser that matches one digit. */
|
|
110
127
|
exports.digit = oneOf("0123456789");
|
|
111
|
-
|
|
112
|
-
exports.
|
|
113
|
-
|
|
114
|
-
exports.
|
|
128
|
+
/** A parser that matches one letter, case insensitive. */
|
|
129
|
+
exports.letter = oneOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
130
|
+
/** A parser that matches one digit or letter, case insensitive. */
|
|
131
|
+
exports.alphanum = oneOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
|
|
132
|
+
/** A parser that matches one word, case insensitive. */
|
|
133
|
+
exports.word = regexParser("^[a-z]+", "ui");
|
|
134
|
+
/** A parser that matches one or more digits. */
|
|
135
|
+
exports.num = regexParser("^[0-9]+");
|
|
136
|
+
/** A parser that matches one single or double quote. */
|
|
115
137
|
exports.quote = oneOf(`'"`);
|
|
138
|
+
/** A parser that matches one tab character. */
|
|
116
139
|
exports.tab = char("\t");
|
|
140
|
+
/** A parser that matches one newline ("\n" only) character. */
|
|
117
141
|
exports.newline = char("\n");
|
|
142
|
+
/** A parser that succeeds on an empty string. Returns `null` as the result. */
|
|
118
143
|
const eof = (input) => {
|
|
119
144
|
if (input === "") {
|
|
120
145
|
return (0, types_1.success)(null, input);
|
|
@@ -122,5 +147,34 @@
|
|
|
122
147
|
return (0, types_1.failure)("expected end of input", input);
|
|
123
148
|
};
|
|
124
149
|
exports.eof = eof;
|
|
150
|
+
/** A parser that matches a quoted string, in single or double quotes.
|
|
151
|
+
* Returns the string as the result, including the quotes.
|
|
152
|
+
*/
|
|
125
153
|
exports.quotedString = (0, combinators_1.seq)([exports.quote, (0, combinators_1.manyWithJoin)(noneOf(`"'`)), exports.quote], (results) => results.join(""));
|
|
154
|
+
/**
|
|
155
|
+
* Returns a parser that matches a regex. If you pass in a string,
|
|
156
|
+
* it will get converted to a regex. The regex should always match from the start of the input.
|
|
157
|
+
* If you pass in a string, a `^` will get prepended to it.
|
|
158
|
+
*
|
|
159
|
+
* @param str - regex string or RegExp instance to match
|
|
160
|
+
* @param options - regex options (i = ignore case, g = global, m = multiline, u = unicode)
|
|
161
|
+
* @returns - parser that matches the given regex
|
|
162
|
+
*/
|
|
163
|
+
function regexParser(str, options = "") {
|
|
164
|
+
let re;
|
|
165
|
+
if (typeof str === "string") {
|
|
166
|
+
re = new RegExp(str.startsWith("^") ? str : `^${str}`, options);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
re = str;
|
|
170
|
+
}
|
|
171
|
+
return (0, trace_1.trace)(`regex(${str})`, (input) => {
|
|
172
|
+
const match = input.match(re);
|
|
173
|
+
if (match) {
|
|
174
|
+
return (0, types_1.success)(match[0], input.slice(match[0].length));
|
|
175
|
+
}
|
|
176
|
+
return (0, types_1.failure)(`expected ${str}, got ${input.slice(0, 10)}`, input);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
exports.regexParser = regexParser;
|
|
126
180
|
});
|
package/dist/trace.d.ts
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
1
|
import { ParserResult } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* This function is used internally by the `trace` function to create the string for each step.
|
|
4
|
+
* @param name - debug name for parser
|
|
5
|
+
* @param result - parser result
|
|
6
|
+
* @returns - A formatted string that describes the parser's result
|
|
7
|
+
*/
|
|
2
8
|
export declare function resultToString<T>(name: string, result: ParserResult<T>): string;
|
|
9
|
+
/**
|
|
10
|
+
* This function is used internally with debug mode. Given a parser and a debug name for it,
|
|
11
|
+
* when the parser is called, `trace` will:
|
|
12
|
+
* 1. Print a line when the parser starts
|
|
13
|
+
* 2. print a line when the parser ends, indicating success or failure.
|
|
14
|
+
* 3. If the parser returns any captures, print the captures.
|
|
15
|
+
* 4. Count the number of times this parser has been run.
|
|
16
|
+
* 5. Track the total time this parser has taken.
|
|
17
|
+
* 6. Track the total number of steps your parser has taken (a step equals one parser invocation).
|
|
18
|
+
* So, for example, you may find out that your parser to parse Markdown has taken 50 steps to parse that file.
|
|
19
|
+
*
|
|
20
|
+
* All this happens only if debug mode is on, which you can turn on by using `parserDebug`, or setting the env var `DEBUG` to `1`.
|
|
21
|
+
*
|
|
22
|
+
* Caveat: If you have debug mode on through an environment variable, `trace` will capture counts and times
|
|
23
|
+
* for all parsers across your entire application. If you want to profile just a particular section of code, use `parserDebug` instead.
|
|
24
|
+
* If you *do* want to track constant times for all parsers, don't use `parserDebug` as it will reset those.
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
* `trace` works with tarsec's built-in parsers out of the box. You can easily set it up to work with your custom parser too.
|
|
28
|
+
*
|
|
29
|
+
* For example, if your parser looks like this:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* const myParser = (input:string) => {
|
|
33
|
+
* if (input === "hello") {
|
|
34
|
+
* return { success: true, result: "hello", rest: "" };
|
|
35
|
+
* }
|
|
36
|
+
* return { success: false, message: "expected hello", rest: input };
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* You can wrap it in `trace` like this:
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* const myParser = trace("myParser", (input:string) => {
|
|
44
|
+
* if (input === "hello") {
|
|
45
|
+
* return { success: true, result: "hello", rest: "" };
|
|
46
|
+
* }
|
|
47
|
+
* return { success: false, message: "expected hello", rest: input };
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* Now, when you run `myParser("hello")` with debug mode on,
|
|
52
|
+
* you will see the debug output.
|
|
53
|
+
*
|
|
54
|
+
* Some parsers, like `seq`, are very general. You might have a few parser that use `seq`.
|
|
55
|
+
* So when you see `seq` debug output, you might not know which `seq` parser that means.
|
|
56
|
+
* ou can pass `seq` a debug name as an optional third argument to be used in the debug output.
|
|
57
|
+
* This name is used to track count and time, so using this name will also mean this `seq` parser's
|
|
58
|
+
* count and time are tracked separately from the other `seq` parsers, which might be useful.
|
|
59
|
+
*
|
|
60
|
+
* @param name - debug name for parser
|
|
61
|
+
* @param parser - parser to run
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
3
64
|
export declare function trace(name: string, parser: any): any;
|
|
65
|
+
/**
|
|
66
|
+
* Utility timing function. Given a callback, it times the callback
|
|
67
|
+
* and returns its runtime in milliseconds. It uses `performance.now()` to do this.
|
|
68
|
+
* If `performance.now()` is not available, it returns null.
|
|
69
|
+
*
|
|
70
|
+
* @param callback - callback to time
|
|
71
|
+
* @returns - time in milliseconds
|
|
72
|
+
*/
|
|
73
|
+
export declare function parserTime(callback: Function): number | null;
|
|
74
|
+
/**
|
|
75
|
+
* Wrapper for parser time. Instead of returning the time in milliseconds,
|
|
76
|
+
* it console.logs it, in a nicely formatted string.
|
|
77
|
+
* @param name - debug name for timing
|
|
78
|
+
* @param callback - callback to time
|
|
79
|
+
*/
|
|
80
|
+
export declare function printTime(name: string, callback: Function): void;
|
|
81
|
+
/**
|
|
82
|
+
* This is the recommended way to run a parser in debug mode.
|
|
83
|
+
* Takes a callback and turns debug mode on just for the callback.
|
|
84
|
+
* This enables `trace` to capture all sorts of information
|
|
85
|
+
* about any executed parsers and print them to console.log.
|
|
86
|
+
* `trace` tracks counts and times but they don't actually get reset to zero
|
|
87
|
+
* unless you use this function to wrap your code.
|
|
88
|
+
*
|
|
89
|
+
* @param name - debug name
|
|
90
|
+
* @param callback - callback to run in debug mode
|
|
91
|
+
*/
|
|
92
|
+
export declare function parserDebug(name: string, callback: Function): void;
|
package/dist/trace.js
CHANGED
|
@@ -9,9 +9,15 @@
|
|
|
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
|
+
/**
|
|
16
|
+
* This function is used internally by the `trace` function to create the string for each step.
|
|
17
|
+
* @param name - debug name for parser
|
|
18
|
+
* @param result - parser result
|
|
19
|
+
* @returns - A formatted string that describes the parser's result
|
|
20
|
+
*/
|
|
15
21
|
function resultToString(name, result) {
|
|
16
22
|
if (result.success) {
|
|
17
23
|
return `✅ ${name} -- match: ${(0, utils_1.escape)(result.result)}, rest: ${(0, utils_1.escape)(result.rest)}`;
|
|
@@ -20,23 +26,165 @@
|
|
|
20
26
|
}
|
|
21
27
|
exports.resultToString = resultToString;
|
|
22
28
|
let level = 0;
|
|
29
|
+
let counts = {};
|
|
30
|
+
let times = {};
|
|
31
|
+
let debugFlag = !!process.env.DEBUG;
|
|
32
|
+
let stepCount = 0;
|
|
33
|
+
let stepLimit = -1;
|
|
34
|
+
/**
|
|
35
|
+
* This function is used internally with debug mode. Given a parser and a debug name for it,
|
|
36
|
+
* when the parser is called, `trace` will:
|
|
37
|
+
* 1. Print a line when the parser starts
|
|
38
|
+
* 2. print a line when the parser ends, indicating success or failure.
|
|
39
|
+
* 3. If the parser returns any captures, print the captures.
|
|
40
|
+
* 4. Count the number of times this parser has been run.
|
|
41
|
+
* 5. Track the total time this parser has taken.
|
|
42
|
+
* 6. Track the total number of steps your parser has taken (a step equals one parser invocation).
|
|
43
|
+
* So, for example, you may find out that your parser to parse Markdown has taken 50 steps to parse that file.
|
|
44
|
+
*
|
|
45
|
+
* All this happens only if debug mode is on, which you can turn on by using `parserDebug`, or setting the env var `DEBUG` to `1`.
|
|
46
|
+
*
|
|
47
|
+
* Caveat: If you have debug mode on through an environment variable, `trace` will capture counts and times
|
|
48
|
+
* for all parsers across your entire application. If you want to profile just a particular section of code, use `parserDebug` instead.
|
|
49
|
+
* If you *do* want to track constant times for all parsers, don't use `parserDebug` as it will reset those.
|
|
50
|
+
*
|
|
51
|
+
*
|
|
52
|
+
* `trace` works with tarsec's built-in parsers out of the box. You can easily set it up to work with your custom parser too.
|
|
53
|
+
*
|
|
54
|
+
* For example, if your parser looks like this:
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* const myParser = (input:string) => {
|
|
58
|
+
* if (input === "hello") {
|
|
59
|
+
* return { success: true, result: "hello", rest: "" };
|
|
60
|
+
* }
|
|
61
|
+
* return { success: false, message: "expected hello", rest: input };
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* You can wrap it in `trace` like this:
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* const myParser = trace("myParser", (input:string) => {
|
|
69
|
+
* if (input === "hello") {
|
|
70
|
+
* return { success: true, result: "hello", rest: "" };
|
|
71
|
+
* }
|
|
72
|
+
* return { success: false, message: "expected hello", rest: input };
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* Now, when you run `myParser("hello")` with debug mode on,
|
|
77
|
+
* you will see the debug output.
|
|
78
|
+
*
|
|
79
|
+
* Some parsers, like `seq`, are very general. You might have a few parser that use `seq`.
|
|
80
|
+
* So when you see `seq` debug output, you might not know which `seq` parser that means.
|
|
81
|
+
* ou can pass `seq` a debug name as an optional third argument to be used in the debug output.
|
|
82
|
+
* This name is used to track count and time, so using this name will also mean this `seq` parser's
|
|
83
|
+
* count and time are tracked separately from the other `seq` parsers, which might be useful.
|
|
84
|
+
*
|
|
85
|
+
* @param name - debug name for parser
|
|
86
|
+
* @param parser - parser to run
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
23
89
|
function trace(name, parser) {
|
|
24
90
|
return (input) => {
|
|
25
|
-
if (
|
|
91
|
+
if (debugFlag) {
|
|
26
92
|
console.log(" ".repeat(level) + `🔍 ${name} -- input: ${(0, utils_1.escape)(input)}`);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
93
|
+
let result;
|
|
94
|
+
const time = parserTime(() => {
|
|
95
|
+
level += STEP;
|
|
96
|
+
result = parser(input);
|
|
97
|
+
level -= STEP;
|
|
98
|
+
});
|
|
99
|
+
counts[name] = counts[name] ? counts[name] + 1 : 1;
|
|
100
|
+
stepCount += 1;
|
|
101
|
+
if (time) {
|
|
102
|
+
times[name] = times[name] ? times[name] + time : time;
|
|
103
|
+
}
|
|
32
104
|
console.log(" ".repeat(level) + resultToString(name, result));
|
|
33
105
|
if (result.success && result.captures) {
|
|
34
106
|
console.log(" ".repeat(level) +
|
|
35
107
|
`⭐ ${name} -- captures: ${JSON.stringify(result.captures)}`);
|
|
36
108
|
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
return parser(input);
|
|
37
113
|
}
|
|
38
|
-
return result;
|
|
39
114
|
};
|
|
40
115
|
}
|
|
41
116
|
exports.trace = trace;
|
|
117
|
+
/**
|
|
118
|
+
* Utility timing function. Given a callback, it times the callback
|
|
119
|
+
* and returns its runtime in milliseconds. It uses `performance.now()` to do this.
|
|
120
|
+
* If `performance.now()` is not available, it returns null.
|
|
121
|
+
*
|
|
122
|
+
* @param callback - callback to time
|
|
123
|
+
* @returns - time in milliseconds
|
|
124
|
+
*/
|
|
125
|
+
function parserTime(callback) {
|
|
126
|
+
if (performance && performance.now) {
|
|
127
|
+
const start = performance.now();
|
|
128
|
+
callback();
|
|
129
|
+
const end = performance.now();
|
|
130
|
+
return end - start;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.error("performance.now not available");
|
|
134
|
+
callback();
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.parserTime = parserTime;
|
|
139
|
+
/**
|
|
140
|
+
* Wrapper for parser time. Instead of returning the time in milliseconds,
|
|
141
|
+
* it console.logs it, in a nicely formatted string.
|
|
142
|
+
* @param name - debug name for timing
|
|
143
|
+
* @param callback - callback to time
|
|
144
|
+
*/
|
|
145
|
+
function printTime(name, callback) {
|
|
146
|
+
const time = parserTime(callback);
|
|
147
|
+
if (time) {
|
|
148
|
+
console.log(`⏱ ${name} -- time: ${(0, utils_1.round)(time)}ms`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.printTime = printTime;
|
|
152
|
+
/**
|
|
153
|
+
* This is the recommended way to run a parser in debug mode.
|
|
154
|
+
* Takes a callback and turns debug mode on just for the callback.
|
|
155
|
+
* This enables `trace` to capture all sorts of information
|
|
156
|
+
* about any executed parsers and print them to console.log.
|
|
157
|
+
* `trace` tracks counts and times but they don't actually get reset to zero
|
|
158
|
+
* unless you use this function to wrap your code.
|
|
159
|
+
*
|
|
160
|
+
* @param name - debug name
|
|
161
|
+
* @param callback - callback to run in debug mode
|
|
162
|
+
*/
|
|
163
|
+
function parserDebug(name, callback) {
|
|
164
|
+
debugFlag = true;
|
|
165
|
+
stepCount = 0;
|
|
166
|
+
counts = {};
|
|
167
|
+
times = {};
|
|
168
|
+
printTime(name, callback);
|
|
169
|
+
debugFlag = false;
|
|
170
|
+
console.log("\n");
|
|
171
|
+
console.log(`📊 ${name} -- counts:`);
|
|
172
|
+
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
|
|
173
|
+
for (const [name, count] of sorted) {
|
|
174
|
+
console.log(` ${name}: ${count}`);
|
|
175
|
+
}
|
|
176
|
+
console.log("\n");
|
|
177
|
+
console.log(`📊 ${name} -- times:`);
|
|
178
|
+
const sortedTimes = Object.entries(times).sort((a, b) => b[1] - a[1]);
|
|
179
|
+
for (const [name, time] of sortedTimes) {
|
|
180
|
+
console.log(` ${name}: ${(0, utils_1.round)(time)}ms`);
|
|
181
|
+
}
|
|
182
|
+
console.log("\n");
|
|
183
|
+
console.log(`📊 ${name} -- step count: ${stepCount}`);
|
|
184
|
+
console.log("\n\n");
|
|
185
|
+
stepCount = 0;
|
|
186
|
+
counts = {};
|
|
187
|
+
times = {};
|
|
188
|
+
}
|
|
189
|
+
exports.parserDebug = parserDebug;
|
|
42
190
|
});
|
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?:
|
|
8
|
+
nextParser?: Parser<any>;
|
|
7
9
|
};
|
|
8
|
-
|
|
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
|
|
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.
|
|
3
|
+
"version": "0.0.14",
|
|
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",
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
"require": "./dist/index.js"
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
|
-
"
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"keywords": ["parser", "parser combinator", "parsec"],
|
|
24
26
|
"author": "",
|
|
25
27
|
"license": "ISC",
|
|
26
28
|
"devDependencies": {
|