tarsec 0.0.13 → 0.0.15
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 +10 -2
- package/dist/combinators/seq.js +1 -12
- package/dist/combinators.d.ts +116 -5
- package/dist/combinators.js +593 -495
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -30
- package/dist/parsers/within.d.ts +1 -1
- package/dist/parsers/within.js +31 -45
- package/dist/parsers.d.ts +85 -7
- package/dist/parsers.js +239 -142
- package/dist/trace.d.ts +94 -1
- package/dist/trace.js +177 -94
- package/dist/types.d.ts +1 -1
- package/dist/types.js +40 -59
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +53 -66
- package/package.json +3 -2
package/dist/trace.d.ts
CHANGED
|
@@ -1,6 +1,99 @@
|
|
|
1
|
-
import { ParserResult } from "./types";
|
|
1
|
+
import { ParserResult } from "./types.js";
|
|
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
|
+
*/
|
|
4
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
|
+
*/
|
|
5
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
|
+
*/
|
|
6
92
|
export declare function parserDebug(name: string, callback: Function): void;
|
|
93
|
+
/**
|
|
94
|
+
* Utility function to limit the number of steps a parser can take.
|
|
95
|
+
* This is useful for avoiding infinite loops in your parser.
|
|
96
|
+
* @param limit - number of steps to limit the parser to
|
|
97
|
+
* @param callback - callback to run
|
|
98
|
+
*/
|
|
99
|
+
export declare function limitSteps(limit: number, callback: Function): void;
|
package/dist/trace.js
CHANGED
|
@@ -1,104 +1,187 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { escape, round, shorten } from "./utils.js";
|
|
2
|
+
import process from "process";
|
|
3
|
+
const STEP = 2;
|
|
4
|
+
/**
|
|
5
|
+
* This function is used internally by the `trace` function to create the string for each step.
|
|
6
|
+
* @param name - debug name for parser
|
|
7
|
+
* @param result - parser result
|
|
8
|
+
* @returns - A formatted string that describes the parser's result
|
|
9
|
+
*/
|
|
10
|
+
export function resultToString(name, result) {
|
|
11
|
+
if (result.success) {
|
|
12
|
+
return `✅ ${name} -- match: ${escape(result.result)}, rest: ${escape(result.rest)}`;
|
|
5
13
|
}
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
return `❌ ${name} -- message: ${escape(result.message)}, rest: ${escape(result.rest)}`;
|
|
15
|
+
}
|
|
16
|
+
let level = 0;
|
|
17
|
+
let counts = {};
|
|
18
|
+
let times = {};
|
|
19
|
+
let debugFlag = !!process.env.DEBUG;
|
|
20
|
+
let stepCount = 0;
|
|
21
|
+
let stepLimit = -1;
|
|
22
|
+
/**
|
|
23
|
+
* This function is used internally with debug mode. Given a parser and a debug name for it,
|
|
24
|
+
* when the parser is called, `trace` will:
|
|
25
|
+
* 1. Print a line when the parser starts
|
|
26
|
+
* 2. print a line when the parser ends, indicating success or failure.
|
|
27
|
+
* 3. If the parser returns any captures, print the captures.
|
|
28
|
+
* 4. Count the number of times this parser has been run.
|
|
29
|
+
* 5. Track the total time this parser has taken.
|
|
30
|
+
* 6. Track the total number of steps your parser has taken (a step equals one parser invocation).
|
|
31
|
+
* So, for example, you may find out that your parser to parse Markdown has taken 50 steps to parse that file.
|
|
32
|
+
*
|
|
33
|
+
* 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`.
|
|
34
|
+
*
|
|
35
|
+
* Caveat: If you have debug mode on through an environment variable, `trace` will capture counts and times
|
|
36
|
+
* for all parsers across your entire application. If you want to profile just a particular section of code, use `parserDebug` instead.
|
|
37
|
+
* If you *do* want to track constant times for all parsers, don't use `parserDebug` as it will reset those.
|
|
38
|
+
*
|
|
39
|
+
*
|
|
40
|
+
* `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.
|
|
41
|
+
*
|
|
42
|
+
* For example, if your parser looks like this:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* const myParser = (input:string) => {
|
|
46
|
+
* if (input === "hello") {
|
|
47
|
+
* return { success: true, result: "hello", rest: "" };
|
|
48
|
+
* }
|
|
49
|
+
* return { success: false, message: "expected hello", rest: input };
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* You can wrap it in `trace` like this:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* const myParser = trace("myParser", (input:string) => {
|
|
57
|
+
* if (input === "hello") {
|
|
58
|
+
* return { success: true, result: "hello", rest: "" };
|
|
59
|
+
* }
|
|
60
|
+
* return { success: false, message: "expected hello", rest: input };
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* Now, when you run `myParser("hello")` with debug mode on,
|
|
65
|
+
* you will see the debug output.
|
|
66
|
+
*
|
|
67
|
+
* Some parsers, like `seq`, are very general. You might have a few parser that use `seq`.
|
|
68
|
+
* So when you see `seq` debug output, you might not know which `seq` parser that means.
|
|
69
|
+
* ou can pass `seq` a debug name as an optional third argument to be used in the debug output.
|
|
70
|
+
* This name is used to track count and time, so using this name will also mean this `seq` parser's
|
|
71
|
+
* count and time are tracked separately from the other `seq` parsers, which might be useful.
|
|
72
|
+
*
|
|
73
|
+
* @param name - debug name for parser
|
|
74
|
+
* @param parser - parser to run
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
export function trace(name, parser) {
|
|
78
|
+
if (stepLimit > 0 && stepCount > stepLimit) {
|
|
79
|
+
throw new Error(`parser step limit of ${stepLimit} exceeded, parser may be in an infinite loop`);
|
|
8
80
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
let level = 0;
|
|
23
|
-
let counts = {};
|
|
24
|
-
let times = {};
|
|
25
|
-
let debugFlag = !!process.env.DEBUG;
|
|
26
|
-
let stepCount = 0;
|
|
27
|
-
let stepLimit = -1;
|
|
28
|
-
function trace(name, parser) {
|
|
29
|
-
return (input) => {
|
|
30
|
-
if (debugFlag) {
|
|
31
|
-
console.log(" ".repeat(level) + `🔍 ${name} -- input: ${(0, utils_1.escape)(input)}`);
|
|
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
|
-
}
|
|
43
|
-
console.log(" ".repeat(level) + resultToString(name, result));
|
|
44
|
-
if (result.success && result.captures) {
|
|
45
|
-
console.log(" ".repeat(level) +
|
|
46
|
-
`⭐ ${name} -- captures: ${JSON.stringify(result.captures)}`);
|
|
47
|
-
}
|
|
48
|
-
return result;
|
|
81
|
+
return (input) => {
|
|
82
|
+
if (debugFlag) {
|
|
83
|
+
console.log(" ".repeat(level) + `🔍 ${name} -- input: ${shorten(escape(input))}`);
|
|
84
|
+
let result;
|
|
85
|
+
const time = parserTime(() => {
|
|
86
|
+
level += STEP;
|
|
87
|
+
result = parser(input);
|
|
88
|
+
level -= STEP;
|
|
89
|
+
});
|
|
90
|
+
counts[name] = counts[name] ? counts[name] + 1 : 1;
|
|
91
|
+
stepCount += 1;
|
|
92
|
+
if (time) {
|
|
93
|
+
times[name] = times[name] ? times[name] + time : time;
|
|
49
94
|
}
|
|
50
|
-
|
|
51
|
-
|
|
95
|
+
console.log(" ".repeat(level) + resultToString(name, result));
|
|
96
|
+
if (result.success && result.captures) {
|
|
97
|
+
console.log(" ".repeat(level) +
|
|
98
|
+
`⭐ ${name} -- captures: ${JSON.stringify(result.captures)}`);
|
|
52
99
|
}
|
|
53
|
-
|
|
54
|
-
}
|
|
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;
|
|
100
|
+
return result;
|
|
62
101
|
}
|
|
63
102
|
else {
|
|
64
|
-
|
|
65
|
-
callback();
|
|
66
|
-
return null;
|
|
103
|
+
return parser(input);
|
|
67
104
|
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Utility timing function. Given a callback, it times the callback
|
|
109
|
+
* and returns its runtime in milliseconds. It uses `performance.now()` to do this.
|
|
110
|
+
* If `performance.now()` is not available, it returns null.
|
|
111
|
+
*
|
|
112
|
+
* @param callback - callback to time
|
|
113
|
+
* @returns - time in milliseconds
|
|
114
|
+
*/
|
|
115
|
+
export function parserTime(callback) {
|
|
116
|
+
if (performance && performance.now) {
|
|
117
|
+
const start = performance.now();
|
|
118
|
+
callback();
|
|
119
|
+
const end = performance.now();
|
|
120
|
+
return end - start;
|
|
68
121
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log(`⏱ ${name} -- time: ${(0, utils_1.round)(time)}ms`);
|
|
74
|
-
}
|
|
122
|
+
else {
|
|
123
|
+
console.error("performance.now not available");
|
|
124
|
+
callback();
|
|
125
|
+
return null;
|
|
75
126
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Wrapper for parser time. Instead of returning the time in milliseconds,
|
|
130
|
+
* it console.logs it, in a nicely formatted string.
|
|
131
|
+
* @param name - debug name for timing
|
|
132
|
+
* @param callback - callback to time
|
|
133
|
+
*/
|
|
134
|
+
export function printTime(name, callback) {
|
|
135
|
+
const time = parserTime(callback);
|
|
136
|
+
if (time) {
|
|
137
|
+
console.log(`⏱ ${name} -- time: ${round(time)}ms`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* This is the recommended way to run a parser in debug mode.
|
|
142
|
+
* Takes a callback and turns debug mode on just for the callback.
|
|
143
|
+
* This enables `trace` to capture all sorts of information
|
|
144
|
+
* about any executed parsers and print them to console.log.
|
|
145
|
+
* `trace` tracks counts and times but they don't actually get reset to zero
|
|
146
|
+
* unless you use this function to wrap your code.
|
|
147
|
+
*
|
|
148
|
+
* @param name - debug name
|
|
149
|
+
* @param callback - callback to run in debug mode
|
|
150
|
+
*/
|
|
151
|
+
export function parserDebug(name, callback) {
|
|
152
|
+
debugFlag = true;
|
|
153
|
+
stepCount = 0;
|
|
154
|
+
counts = {};
|
|
155
|
+
times = {};
|
|
156
|
+
printTime(name, callback);
|
|
157
|
+
debugFlag = false;
|
|
158
|
+
console.log("\n");
|
|
159
|
+
console.log(`📊 ${name} -- counts:`);
|
|
160
|
+
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
|
|
161
|
+
for (const [name, count] of sorted) {
|
|
162
|
+
console.log(` ${name}: ${count}`);
|
|
163
|
+
}
|
|
164
|
+
console.log("\n");
|
|
165
|
+
console.log(`📊 ${name} -- times:`);
|
|
166
|
+
const sortedTimes = Object.entries(times).sort((a, b) => b[1] - a[1]);
|
|
167
|
+
for (const [name, time] of sortedTimes) {
|
|
168
|
+
console.log(` ${name}: ${round(time)}ms`);
|
|
102
169
|
}
|
|
103
|
-
|
|
104
|
-
});
|
|
170
|
+
console.log("\n");
|
|
171
|
+
console.log(`📊 ${name} -- step count: ${stepCount}`);
|
|
172
|
+
console.log("\n\n");
|
|
173
|
+
stepCount = 0;
|
|
174
|
+
counts = {};
|
|
175
|
+
times = {};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Utility function to limit the number of steps a parser can take.
|
|
179
|
+
* This is useful for avoiding infinite loops in your parser.
|
|
180
|
+
* @param limit - number of steps to limit the parser to
|
|
181
|
+
* @param callback - callback to run
|
|
182
|
+
*/
|
|
183
|
+
export function limitSteps(limit, callback) {
|
|
184
|
+
stepLimit = limit;
|
|
185
|
+
callback();
|
|
186
|
+
stepLimit = -1;
|
|
187
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ export type PickParserType<T extends readonly GeneralParser<any, any>[]> = HasCa
|
|
|
81
81
|
*/
|
|
82
82
|
export type InferManyReturnType<T extends GeneralParser<any, any>> = T extends CaptureParser<infer R, infer C> ? CaptureParser<R[], {
|
|
83
83
|
captures: C[];
|
|
84
|
-
}> : Parser<
|
|
84
|
+
}> : T extends Parser<infer R> ? Parser<R[]> : never;
|
|
85
85
|
export type MergedResults<T extends readonly GeneralParser<any, any>[]> = ExtractResults<T[number]>;
|
|
86
86
|
/** Used to create a parser tree for backtracking. */
|
|
87
87
|
export type Node = ParserNode | EmptyNode;
|
package/dist/types.js
CHANGED
|
@@ -1,59 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
*/
|
|
46
|
-
function createTree(parsers) {
|
|
47
|
-
if (parsers.length === 0) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const rootNode = createNode(null, parsers[0]);
|
|
51
|
-
let currentNode = rootNode;
|
|
52
|
-
for (let i = 1; i < parsers.length; i++) {
|
|
53
|
-
currentNode.child = createNode(currentNode, parsers[i]);
|
|
54
|
-
currentNode = currentNode.child;
|
|
55
|
-
}
|
|
56
|
-
return rootNode;
|
|
57
|
-
}
|
|
58
|
-
exports.createTree = createTree;
|
|
59
|
-
});
|
|
1
|
+
export function isCaptureResult(result) {
|
|
2
|
+
return "captures" in result;
|
|
3
|
+
}
|
|
4
|
+
/** Convenience function to return a ParserSuccess */
|
|
5
|
+
export function success(result, rest) {
|
|
6
|
+
return { success: true, result, rest };
|
|
7
|
+
}
|
|
8
|
+
/** Convenience function to return a CaptureParserSuccess */
|
|
9
|
+
export function captureSuccess(result, rest, captures) {
|
|
10
|
+
return { success: true, result, rest, captures };
|
|
11
|
+
}
|
|
12
|
+
/** Convenience function to return a ParserFailure */
|
|
13
|
+
export function failure(message, rest) {
|
|
14
|
+
return { success: false, message, rest };
|
|
15
|
+
}
|
|
16
|
+
/** Convenience function to create a ParserNode. */
|
|
17
|
+
export function createNode(parent, parser) {
|
|
18
|
+
return {
|
|
19
|
+
parent,
|
|
20
|
+
parser,
|
|
21
|
+
child: null,
|
|
22
|
+
closed: false,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Convenience function where, given an array of parsers, it creates a tree we can use for backtracking.
|
|
26
|
+
* This tree is what `seq` use. It's used to keep track of the parsers we've tried so far,
|
|
27
|
+
* so we can backtrack if we need to.
|
|
28
|
+
*/
|
|
29
|
+
export function createTree(parsers) {
|
|
30
|
+
if (parsers.length === 0) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const rootNode = createNode(null, parsers[0]);
|
|
34
|
+
let currentNode = rootNode;
|
|
35
|
+
for (let i = 1; i < parsers.length; i++) {
|
|
36
|
+
currentNode.child = createNode(currentNode, parsers[i]);
|
|
37
|
+
currentNode = currentNode.child;
|
|
38
|
+
}
|
|
39
|
+
return rootNode;
|
|
40
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Node } from "./types";
|
|
1
|
+
import { Node } from "./types.js";
|
|
2
2
|
export declare function escape(str: any): string;
|
|
3
3
|
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
7
|
export declare function round(num: number, places?: number): number;
|
|
8
|
+
export declare function shorten(str: string, length?: number): string;
|
package/dist/utils.js
CHANGED
|
@@ -1,70 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
export function escape(str) {
|
|
2
|
+
return JSON.stringify(str);
|
|
3
|
+
}
|
|
4
|
+
export function merge(a, b) {
|
|
5
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
6
|
+
return [...a, ...b];
|
|
7
|
+
}
|
|
8
|
+
else if (Array.isArray(a)) {
|
|
9
|
+
return [...a, b];
|
|
10
|
+
}
|
|
11
|
+
else if (Array.isArray(b)) {
|
|
12
|
+
return [a, ...b];
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
return [a, b];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function mergeCaptures(a, b) {
|
|
19
|
+
const result = {};
|
|
20
|
+
Object.keys(a).forEach((key) => {
|
|
21
|
+
result[key] = a[key];
|
|
22
|
+
});
|
|
23
|
+
Object.keys(b).forEach((key) => {
|
|
24
|
+
if (result[key]) {
|
|
25
|
+
result[key] = merge(result[key], b[key]);
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
exports.merge = merge;
|
|
32
|
-
function mergeCaptures(a, b) {
|
|
33
|
-
const result = {};
|
|
34
|
-
Object.keys(a).forEach((key) => {
|
|
35
|
-
result[key] = a[key];
|
|
36
|
-
});
|
|
37
|
-
Object.keys(b).forEach((key) => {
|
|
38
|
-
if (result[key]) {
|
|
39
|
-
result[key] = merge(result[key], b[key]);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
result[key] = b[key];
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
exports.mergeCaptures = mergeCaptures;
|
|
48
|
-
function findAncestorWithNextParser(node, count = 0) {
|
|
49
|
-
if (node === null)
|
|
50
|
-
return [null, count];
|
|
51
|
-
if (!node.closed) {
|
|
52
|
-
return [node, count];
|
|
53
|
-
}
|
|
54
|
-
if (node.parent) {
|
|
55
|
-
return findAncestorWithNextParser(node.parent, count + 1);
|
|
28
|
+
result[key] = b[key];
|
|
56
29
|
}
|
|
30
|
+
});
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
export function findAncestorWithNextParser(node, count = 0) {
|
|
34
|
+
if (node === null)
|
|
57
35
|
return [null, count];
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
36
|
+
if (!node.closed) {
|
|
37
|
+
return [node, count];
|
|
38
|
+
}
|
|
39
|
+
if (node.parent) {
|
|
40
|
+
return findAncestorWithNextParser(node.parent, count + 1);
|
|
41
|
+
}
|
|
42
|
+
return [null, count];
|
|
43
|
+
}
|
|
44
|
+
export function popMany(arr, count) {
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
arr.pop();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function round(num, places = 2) {
|
|
50
|
+
return Math.round(num * 10 ** places) / 10 ** places;
|
|
51
|
+
}
|
|
52
|
+
export function shorten(str, length = 250) {
|
|
53
|
+
if (str.length > length) {
|
|
54
|
+
return str.substring(0, length) + "...";
|
|
55
|
+
}
|
|
56
|
+
return str;
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tarsec",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "A parser combinator library for TypeScript, inspired by Parsec.",
|
|
5
5
|
"homepage": "https://github.com/egonSchiele/tarsec",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"require": "./dist/index.js"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
-
"
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"keywords": ["parser", "parser combinator", "parsec"],
|
|
25
26
|
"author": "",
|
|
26
27
|
"license": "ISC",
|
|
27
28
|
"devDependencies": {
|