tarsec 0.0.17 → 0.0.19
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 +3 -3
- package/dist/combinators/seq.d.ts +1 -0
- package/dist/combinators/seq.js +1 -0
- package/dist/combinators.d.ts +359 -0
- package/dist/combinators.js +753 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/parsers/within.d.ts +2 -0
- package/dist/parsers/within.js +37 -0
- package/dist/parsers.d.ts +172 -0
- package/dist/parsers.js +297 -0
- package/dist/trace.d.ts +99 -0
- package/dist/trace.js +187 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.js +57 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +57 -0
- package/package.json +7 -3
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { trace } from "../trace.js";
|
|
2
|
+
import { success } from "../types.js";
|
|
3
|
+
export function within(parser) {
|
|
4
|
+
return trace("within", (input) => {
|
|
5
|
+
let start = 0;
|
|
6
|
+
let current = 0;
|
|
7
|
+
const results = [];
|
|
8
|
+
while (current < input.length) {
|
|
9
|
+
const parsed = parser(input.slice(current));
|
|
10
|
+
if (parsed.success) {
|
|
11
|
+
const unmatchedValue = input.slice(start, current);
|
|
12
|
+
if (unmatchedValue.length > 0) {
|
|
13
|
+
results.push({
|
|
14
|
+
type: "unmatched",
|
|
15
|
+
value: unmatchedValue,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
results.push({
|
|
19
|
+
type: "matched",
|
|
20
|
+
value: parsed.result,
|
|
21
|
+
});
|
|
22
|
+
current += parsed.result.length;
|
|
23
|
+
start = current;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
current += 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (start < current) {
|
|
30
|
+
results.push({
|
|
31
|
+
type: "unmatched",
|
|
32
|
+
value: input.slice(start, current),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return success(results, "");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { CaptureParser, Parser, Prettify } from "./types.js";
|
|
2
|
+
export { within as betweenWithin } from "./parsers/within.js";
|
|
3
|
+
/**
|
|
4
|
+
* Takes a character. Returns a parser that parses that character.
|
|
5
|
+
*
|
|
6
|
+
* @param c - character to parse
|
|
7
|
+
* @returns - parser that parses the given character
|
|
8
|
+
*/
|
|
9
|
+
export declare function char<const S extends string>(c: S): Parser<S>;
|
|
10
|
+
/**
|
|
11
|
+
* Takes a string. Returns a parser that parses that string.
|
|
12
|
+
*
|
|
13
|
+
* @param s - string to match on
|
|
14
|
+
* @returns - parser that parses the given string
|
|
15
|
+
*/
|
|
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>;
|
|
23
|
+
/**
|
|
24
|
+
* Takes a string. Returns a parser that parses
|
|
25
|
+
* one of the characters in that string.
|
|
26
|
+
*
|
|
27
|
+
* @param chars - string of possible characters
|
|
28
|
+
* @returns - parser that parses one of the given characters
|
|
29
|
+
*/
|
|
30
|
+
export declare function oneOf(chars: string): Parser<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Takes a string. Returns a parser that parses one character
|
|
33
|
+
* that's not any of the characters in the given string
|
|
34
|
+
*
|
|
35
|
+
* @param chars - string of characters to avoid
|
|
36
|
+
* @returns - parser that parses a character that is not in the given string
|
|
37
|
+
*/
|
|
38
|
+
export declare function noneOf(chars: string): Parser<string>;
|
|
39
|
+
/**
|
|
40
|
+
* A parser that parses any one character.
|
|
41
|
+
* Fails on empty strings, succeeds otherwise.
|
|
42
|
+
*
|
|
43
|
+
* @param input - input string
|
|
44
|
+
* @returns - ParserResult
|
|
45
|
+
*/
|
|
46
|
+
export declare const anyChar: any;
|
|
47
|
+
/** A parser that matches one of " \t\n\r". */
|
|
48
|
+
export declare const space: Parser<string>;
|
|
49
|
+
/** A parser that matches one or more spaces. */
|
|
50
|
+
export declare const spaces: Parser<string>;
|
|
51
|
+
/** A parser that matches one digit. */
|
|
52
|
+
export declare const digit: Parser<string>;
|
|
53
|
+
/** A parser that matches one letter, case insensitive. */
|
|
54
|
+
export declare const letter: Parser<string>;
|
|
55
|
+
/** A parser that matches one digit or letter, case insensitive. */
|
|
56
|
+
export declare const alphanum: Parser<string>;
|
|
57
|
+
/** A parser that matches one word, case insensitive. */
|
|
58
|
+
export declare const word: Parser<string>;
|
|
59
|
+
/** A parser that matches one or more digits. */
|
|
60
|
+
export declare const num: Parser<string>;
|
|
61
|
+
/** A parser that matches one single or double quote. */
|
|
62
|
+
export declare const quote: Parser<string>;
|
|
63
|
+
/** A parser that matches one tab character. */
|
|
64
|
+
export declare const tab: Parser<string>;
|
|
65
|
+
/** A parser that matches one newline ("\n" only) character. */
|
|
66
|
+
export declare const newline: Parser<string>;
|
|
67
|
+
/** A parser that succeeds on an empty string. Returns `null` as the result. */
|
|
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
|
+
*/
|
|
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>;
|
|
83
|
+
/**
|
|
84
|
+
* Like `regexParser`, but you can name your capture groups
|
|
85
|
+
* and get them back as the result instead.
|
|
86
|
+
* Fails if it doesn't have the same number of names as capture groups.
|
|
87
|
+
*
|
|
88
|
+
* @param str - regex string or RegExp instance to match
|
|
89
|
+
* @param options - string of regex options (i = ignore case, g = global, m = multiline, u = unicode)
|
|
90
|
+
* @param captureNames - names of the captures
|
|
91
|
+
* @returns - parser that matches the given regex
|
|
92
|
+
*/
|
|
93
|
+
export declare function captureRegex<const T extends string[]>(str: string | RegExp, options?: string, ...captureNames: T): Parser<Prettify<Record<(typeof captureNames)[number], string>>>;
|
|
94
|
+
/**
|
|
95
|
+
* Return a parser that takes a key and a value.
|
|
96
|
+
* The parser consumes no input and always succeeds,
|
|
97
|
+
* and returns `null` as the result. It also returns a captures object
|
|
98
|
+
* with that key-value pair set. This is useful when you need to inject
|
|
99
|
+
* a key-value pair into captures for a `seq`.
|
|
100
|
+
*
|
|
101
|
+
* For example, here is a Markdown heading parser.
|
|
102
|
+
|
|
103
|
+
* ```ts
|
|
104
|
+
* export const headingParser: Parser<Heading> = seqC(
|
|
105
|
+
* capture(count(char("#")), "level"),
|
|
106
|
+
* spaces,
|
|
107
|
+
* capture(many1Till(or(char("\n"), eof)), "content")
|
|
108
|
+
* );
|
|
109
|
+
```
|
|
110
|
+
*
|
|
111
|
+
* This parser returns
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* {
|
|
115
|
+
* level: number,
|
|
116
|
+
* content: string
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
* but the type of heading is actually
|
|
120
|
+
*
|
|
121
|
+
* ```ts
|
|
122
|
+
* type Heading = {
|
|
123
|
+
* type: "heading";
|
|
124
|
+
* level: number;
|
|
125
|
+
* content: string;
|
|
126
|
+
* };
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* The `type` key is missing. You can use `set` to inject the `type`
|
|
130
|
+
* key-value pair into captures:
|
|
131
|
+
*
|
|
132
|
+
* ```ts
|
|
133
|
+
* export const headingParser: Parser<Heading> = seqC(
|
|
134
|
+
* set("type", "heading"),
|
|
135
|
+
* capture(count(char("#")), "level"),
|
|
136
|
+
* spaces,
|
|
137
|
+
* capture(many1Till(or(char("\n"), eof)), "content")
|
|
138
|
+
* );
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @param key - key to set on captures object
|
|
142
|
+
* @param value - value to set on captures object
|
|
143
|
+
* @returns
|
|
144
|
+
*/
|
|
145
|
+
export declare function set<const K extends string, const V>(key: K, value: V): CaptureParser<null, Record<K, V>>;
|
|
146
|
+
/**
|
|
147
|
+
* A parser that always succeeds with the given value.
|
|
148
|
+
* @param value - value to succeed with
|
|
149
|
+
* @returns value
|
|
150
|
+
*/
|
|
151
|
+
export declare function succeed<T>(value: T): Parser<T>;
|
|
152
|
+
/**
|
|
153
|
+
* A parser that always fails with the given message.
|
|
154
|
+
* @param message - message to fail with
|
|
155
|
+
* @returns failure
|
|
156
|
+
*/
|
|
157
|
+
export declare function fail(message: string): Parser<never>;
|
|
158
|
+
/**
|
|
159
|
+
* Takes a string. Succeeds if the given input contains that string.
|
|
160
|
+
* Consumes no input.
|
|
161
|
+
*
|
|
162
|
+
* @param substr - substring to find
|
|
163
|
+
* @returns - parser that succeeds if the given input contains that string
|
|
164
|
+
*/
|
|
165
|
+
export declare function includes<const S extends string>(substr: S): Parser<S>;
|
|
166
|
+
/**
|
|
167
|
+
* Like `includes`, but case-insensitive.
|
|
168
|
+
*
|
|
169
|
+
* @param substr - substring to find
|
|
170
|
+
* @returns - parser that succeeds if the given input contains that string
|
|
171
|
+
*/
|
|
172
|
+
export declare function iIncludes<const S extends string>(substr: S): Parser<S>;
|
package/dist/parsers.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { many1WithJoin, manyWithJoin, seq } from "./combinators.js";
|
|
2
|
+
import { trace } from "./trace.js";
|
|
3
|
+
import { captureSuccess, failure, success, } from "./types.js";
|
|
4
|
+
import { escape } from "./utils.js";
|
|
5
|
+
export { within as betweenWithin } from "./parsers/within.js";
|
|
6
|
+
/**
|
|
7
|
+
* Takes a character. Returns a parser that parses that character.
|
|
8
|
+
*
|
|
9
|
+
* @param c - character to parse
|
|
10
|
+
* @returns - parser that parses the given character
|
|
11
|
+
*/
|
|
12
|
+
export function char(c) {
|
|
13
|
+
return trace(`char(${escape(c)})`, (input) => {
|
|
14
|
+
if (input.length === 0) {
|
|
15
|
+
return {
|
|
16
|
+
success: false,
|
|
17
|
+
rest: input,
|
|
18
|
+
message: "unexpected end of input",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (input[0] === c) {
|
|
22
|
+
return success(c, input.slice(1));
|
|
23
|
+
}
|
|
24
|
+
return failure(`expected ${escape(c)}, got ${escape(input[0])}`, input);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Takes a string. Returns a parser that parses that string.
|
|
29
|
+
*
|
|
30
|
+
* @param s - string to match on
|
|
31
|
+
* @returns - parser that parses the given string
|
|
32
|
+
*/
|
|
33
|
+
export function str(s) {
|
|
34
|
+
return trace(`str(${escape(s)})`, (input) => {
|
|
35
|
+
if (input.substring(0, s.length) === s) {
|
|
36
|
+
return success(s, input.slice(s.length));
|
|
37
|
+
}
|
|
38
|
+
return failure(`expected ${s}, got ${input.substring(0, s.length)}`, input);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Like `str`, but case insensitive.
|
|
43
|
+
* @param s - string to match on, case insensitive
|
|
44
|
+
* @returns - parser that matches the given string, case insensitive
|
|
45
|
+
*/
|
|
46
|
+
export function istr(s) {
|
|
47
|
+
return trace(`istr(${escape(s)})`, (input) => {
|
|
48
|
+
if (input.substring(0, s.length).toLocaleLowerCase() === s.toLocaleLowerCase()) {
|
|
49
|
+
return success(input.substring(0, s.length), input.slice(s.length));
|
|
50
|
+
}
|
|
51
|
+
return failure(`expected ${s}, got ${input.substring(0, s.length)}`, input);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Takes a string. Returns a parser that parses
|
|
56
|
+
* one of the characters in that string.
|
|
57
|
+
*
|
|
58
|
+
* @param chars - string of possible characters
|
|
59
|
+
* @returns - parser that parses one of the given characters
|
|
60
|
+
*/
|
|
61
|
+
export function oneOf(chars) {
|
|
62
|
+
return trace(`oneOf(${escape(chars)})`, (input) => {
|
|
63
|
+
if (input.length === 0) {
|
|
64
|
+
return failure("unexpected end of input", input);
|
|
65
|
+
}
|
|
66
|
+
const c = input[0];
|
|
67
|
+
if (chars.includes(c)) {
|
|
68
|
+
return char(c)(input);
|
|
69
|
+
}
|
|
70
|
+
return failure(`expected one of ${escape(chars)}, got ${c}`, input);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Takes a string. Returns a parser that parses one character
|
|
75
|
+
* that's not any of the characters in the given string
|
|
76
|
+
*
|
|
77
|
+
* @param chars - string of characters to avoid
|
|
78
|
+
* @returns - parser that parses a character that is not in the given string
|
|
79
|
+
*/
|
|
80
|
+
export function noneOf(chars) {
|
|
81
|
+
return trace(`noneOf(${escape(chars)})`, (input) => {
|
|
82
|
+
if (input.length === 0) {
|
|
83
|
+
return failure("unexpected end of input", input);
|
|
84
|
+
}
|
|
85
|
+
if (chars.includes(input[0])) {
|
|
86
|
+
return failure(`expected none of ${escape(chars)}, got ${input[0]}`, input);
|
|
87
|
+
}
|
|
88
|
+
return char(input[0])(input);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A parser that parses any one character.
|
|
93
|
+
* Fails on empty strings, succeeds otherwise.
|
|
94
|
+
*
|
|
95
|
+
* @param input - input string
|
|
96
|
+
* @returns - ParserResult
|
|
97
|
+
*/
|
|
98
|
+
export const anyChar = trace("anyChar", (input) => {
|
|
99
|
+
if (input.length === 0) {
|
|
100
|
+
return failure("unexpected end of input", input);
|
|
101
|
+
}
|
|
102
|
+
return success(input[0], input.slice(1));
|
|
103
|
+
});
|
|
104
|
+
/** A parser that matches one of " \t\n\r". */
|
|
105
|
+
export const space = oneOf(" \t\n\r");
|
|
106
|
+
/** A parser that matches one or more spaces. */
|
|
107
|
+
export const spaces = many1WithJoin(space);
|
|
108
|
+
/** A parser that matches one digit. */
|
|
109
|
+
export const digit = oneOf("0123456789");
|
|
110
|
+
/** A parser that matches one letter, case insensitive. */
|
|
111
|
+
export const letter = oneOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
112
|
+
/** A parser that matches one digit or letter, case insensitive. */
|
|
113
|
+
export const alphanum = oneOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
|
|
114
|
+
/** A parser that matches one word, case insensitive. */
|
|
115
|
+
export const word = regexParser("^[a-z]+", "ui");
|
|
116
|
+
/** A parser that matches one or more digits. */
|
|
117
|
+
export const num = regexParser("^[0-9]+");
|
|
118
|
+
/** A parser that matches one single or double quote. */
|
|
119
|
+
export const quote = oneOf(`'"`);
|
|
120
|
+
/** A parser that matches one tab character. */
|
|
121
|
+
export const tab = char("\t");
|
|
122
|
+
/** A parser that matches one newline ("\n" only) character. */
|
|
123
|
+
export const newline = char("\n");
|
|
124
|
+
/** A parser that succeeds on an empty string. Returns `null` as the result. */
|
|
125
|
+
export const eof = (input) => {
|
|
126
|
+
if (input === "") {
|
|
127
|
+
return success(null, input);
|
|
128
|
+
}
|
|
129
|
+
return failure("expected end of input", input);
|
|
130
|
+
};
|
|
131
|
+
/** A parser that matches a quoted string, in single or double quotes.
|
|
132
|
+
* Returns the string as the result, including the quotes.
|
|
133
|
+
*/
|
|
134
|
+
export const quotedString = seq([quote, manyWithJoin(noneOf(`"'`)), quote], (results) => results.join(""));
|
|
135
|
+
/**
|
|
136
|
+
* Returns a parser that matches a regex. If you pass in a string,
|
|
137
|
+
* it will get converted to a regex. The regex should always match from the start of the input.
|
|
138
|
+
* If you pass in a string, a `^` will get prepended to it.
|
|
139
|
+
*
|
|
140
|
+
* @param str - regex string or RegExp instance to match
|
|
141
|
+
* @param options - regex options (i = ignore case, g = global, m = multiline, u = unicode)
|
|
142
|
+
* @returns - parser that matches the given regex
|
|
143
|
+
*/
|
|
144
|
+
export function regexParser(str, options = "") {
|
|
145
|
+
let re;
|
|
146
|
+
if (typeof str === "string") {
|
|
147
|
+
re = new RegExp(str.startsWith("^") ? str : `^${str}`, options);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
re = str;
|
|
151
|
+
}
|
|
152
|
+
return trace(`regex(${str})`, (input) => {
|
|
153
|
+
const match = input.match(re);
|
|
154
|
+
if (match) {
|
|
155
|
+
return success(match[0], input.slice(match[0].length));
|
|
156
|
+
}
|
|
157
|
+
return failure(`expected ${str}, got ${input.slice(0, 10)}`, input);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Like `regexParser`, but you can name your capture groups
|
|
162
|
+
* and get them back as the result instead.
|
|
163
|
+
* Fails if it doesn't have the same number of names as capture groups.
|
|
164
|
+
*
|
|
165
|
+
* @param str - regex string or RegExp instance to match
|
|
166
|
+
* @param options - string of regex options (i = ignore case, g = global, m = multiline, u = unicode)
|
|
167
|
+
* @param captureNames - names of the captures
|
|
168
|
+
* @returns - parser that matches the given regex
|
|
169
|
+
*/
|
|
170
|
+
export function captureRegex(str, options = "", ...captureNames) {
|
|
171
|
+
let re;
|
|
172
|
+
if (typeof str === "string") {
|
|
173
|
+
re = new RegExp(str.startsWith("^") ? str : `^${str}`, options);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
re = str;
|
|
177
|
+
}
|
|
178
|
+
return trace(`captureRegex(${str})`, (input) => {
|
|
179
|
+
const match = input.match(re);
|
|
180
|
+
if (match) {
|
|
181
|
+
if (match.slice(1).length > captureNames.length) {
|
|
182
|
+
return failure(`more capture groups than names. ${match.slice(1).length} capture groups, ${captureNames.length} names`, input);
|
|
183
|
+
}
|
|
184
|
+
if (match.slice(1).length < captureNames.length) {
|
|
185
|
+
return failure(`fewer capture groups than names. ${match.slice(1).length} capture groups, ${captureNames.length} names`, input);
|
|
186
|
+
}
|
|
187
|
+
const captures = Object.assign({}, Object.fromEntries(match.slice(1).map((value, index) => [captureNames[index], value])));
|
|
188
|
+
return success(captures, input.slice(match[0].length));
|
|
189
|
+
}
|
|
190
|
+
return failure(`expected ${str}, got ${input.slice(0, 10)}`, input);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Return a parser that takes a key and a value.
|
|
195
|
+
* The parser consumes no input and always succeeds,
|
|
196
|
+
* and returns `null` as the result. It also returns a captures object
|
|
197
|
+
* with that key-value pair set. This is useful when you need to inject
|
|
198
|
+
* a key-value pair into captures for a `seq`.
|
|
199
|
+
*
|
|
200
|
+
* For example, here is a Markdown heading parser.
|
|
201
|
+
|
|
202
|
+
* ```ts
|
|
203
|
+
* export const headingParser: Parser<Heading> = seqC(
|
|
204
|
+
* capture(count(char("#")), "level"),
|
|
205
|
+
* spaces,
|
|
206
|
+
* capture(many1Till(or(char("\n"), eof)), "content")
|
|
207
|
+
* );
|
|
208
|
+
```
|
|
209
|
+
*
|
|
210
|
+
* This parser returns
|
|
211
|
+
*
|
|
212
|
+
* ```ts
|
|
213
|
+
* {
|
|
214
|
+
* level: number,
|
|
215
|
+
* content: string
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
* but the type of heading is actually
|
|
219
|
+
*
|
|
220
|
+
* ```ts
|
|
221
|
+
* type Heading = {
|
|
222
|
+
* type: "heading";
|
|
223
|
+
* level: number;
|
|
224
|
+
* content: string;
|
|
225
|
+
* };
|
|
226
|
+
* ```
|
|
227
|
+
*
|
|
228
|
+
* The `type` key is missing. You can use `set` to inject the `type`
|
|
229
|
+
* key-value pair into captures:
|
|
230
|
+
*
|
|
231
|
+
* ```ts
|
|
232
|
+
* export const headingParser: Parser<Heading> = seqC(
|
|
233
|
+
* set("type", "heading"),
|
|
234
|
+
* capture(count(char("#")), "level"),
|
|
235
|
+
* spaces,
|
|
236
|
+
* capture(many1Till(or(char("\n"), eof)), "content")
|
|
237
|
+
* );
|
|
238
|
+
* ```
|
|
239
|
+
*
|
|
240
|
+
* @param key - key to set on captures object
|
|
241
|
+
* @param value - value to set on captures object
|
|
242
|
+
* @returns
|
|
243
|
+
*/
|
|
244
|
+
export function set(key, value) {
|
|
245
|
+
return trace(`set(${key}, ${value})`, (input) => {
|
|
246
|
+
return captureSuccess(null, input, { [key]: value });
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* A parser that always succeeds with the given value.
|
|
251
|
+
* @param value - value to succeed with
|
|
252
|
+
* @returns value
|
|
253
|
+
*/
|
|
254
|
+
export function succeed(value) {
|
|
255
|
+
return trace(`succeed(${value})`, (input) => {
|
|
256
|
+
return success(value, input);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* A parser that always fails with the given message.
|
|
261
|
+
* @param message - message to fail with
|
|
262
|
+
* @returns failure
|
|
263
|
+
*/
|
|
264
|
+
export function fail(message) {
|
|
265
|
+
return trace(`fail(${message})`, (input) => {
|
|
266
|
+
return failure(message, input);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Takes a string. Succeeds if the given input contains that string.
|
|
271
|
+
* Consumes no input.
|
|
272
|
+
*
|
|
273
|
+
* @param substr - substring to find
|
|
274
|
+
* @returns - parser that succeeds if the given input contains that string
|
|
275
|
+
*/
|
|
276
|
+
export function includes(substr) {
|
|
277
|
+
return trace(`includes(${substr})`, (input) => {
|
|
278
|
+
if (input.includes(substr)) {
|
|
279
|
+
return success(substr, input);
|
|
280
|
+
}
|
|
281
|
+
return failure(`expected ${escape(input)} to include ${escape(substr)}`, input);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Like `includes`, but case-insensitive.
|
|
286
|
+
*
|
|
287
|
+
* @param substr - substring to find
|
|
288
|
+
* @returns - parser that succeeds if the given input contains that string
|
|
289
|
+
*/
|
|
290
|
+
export function iIncludes(substr) {
|
|
291
|
+
return trace(`iIncludes(${substr})`, (input) => {
|
|
292
|
+
if (input.toLowerCase().includes(substr.toLowerCase())) {
|
|
293
|
+
return success(substr, input);
|
|
294
|
+
}
|
|
295
|
+
return failure(`expected "${input}" to include "${substr}" (case-insensitive)`, input);
|
|
296
|
+
});
|
|
297
|
+
}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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;
|
|
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;
|