tarsec 0.0.22 → 0.1.1
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/dist/combinators.d.ts +49 -6
- package/dist/combinators.js +84 -11
- package/dist/parsers/within.d.ts +58 -1
- package/dist/parsers/within.js +58 -1
- package/dist/parsers.d.ts +1 -1
- package/dist/parsers.js +1 -1
- package/dist/types.d.ts +3 -3
- package/package.json +2 -2
package/dist/combinators.d.ts
CHANGED
|
@@ -98,23 +98,66 @@ export declare function optional<T>(parser: Parser<T>): Parser<T | null>;
|
|
|
98
98
|
export declare function not(parser: Parser<any>): Parser<null>;
|
|
99
99
|
/**
|
|
100
100
|
* Takes three parsers, `open`, `close`, and `parser`.
|
|
101
|
-
* `between` matches
|
|
101
|
+
* `between` matches multiple instances of `parser`,
|
|
102
102
|
* surrounded by `open` and `close`. It returns the result of `parser`.
|
|
103
|
-
* If any of the parsers fail, `between` fails.
|
|
104
103
|
*
|
|
104
|
+
* This is a look-ahead combinator. It keeps trying the `close` parser until it succeeds.
|
|
105
|
+
* That means you can use it like this, and `parser` won't consume the ending quote:
|
|
106
|
+
*
|
|
107
|
+
* ```ts
|
|
108
|
+
* const quotedString = between(quote, quote, anyChar);
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* This combinator succeeds even if it parses zero instances of `parser`.
|
|
112
|
+
* So for the above parser, this input would succeed: `""`.
|
|
113
|
+
*
|
|
114
|
+
* If you want it to consume at least one instance of `parser`, use `between1`.
|
|
115
|
+
*
|
|
116
|
+
* Even though this is a look-ahead combinator, it won't work if you supply a greedy parser.
|
|
117
|
+
* You can make the above parser fail simply by adding `many1`:
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* const quotedString = between(quote, quote, many1(anyChar));
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* `many1(anyChar)` will consume all input until the end of the string,
|
|
124
|
+
* not giving the `close` parser a chance to succeed.
|
|
125
|
+
*
|
|
126
|
+
* This combinator is obviously expensive, as it applies the `close` parser at every step.
|
|
127
|
+
* This combinator can also get into an infinite loop if `parser` succeeds without consuming any input.
|
|
128
|
+
*
|
|
129
|
+
* @param open - parser for the opening delimiter
|
|
130
|
+
* @param close - parser for the closing delimiter
|
|
131
|
+
* @param parser - parser for the content
|
|
132
|
+
* @returns the result of `parser`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function between<O, C, P>(open: Parser<O>, close: Parser<C>, parser: Parser<P>): Parser<P[]>;
|
|
135
|
+
/**
|
|
136
|
+
* Like `between`, but fails unless it consumes at least one instance of the `parser`.
|
|
105
137
|
* @param open - parser for the opening delimiter
|
|
106
138
|
* @param close - parser for the closing delimiter
|
|
107
139
|
* @param parser - parser for the content
|
|
108
|
-
* @returns
|
|
140
|
+
* @returns the result of `parser`.
|
|
109
141
|
*/
|
|
110
|
-
export declare function
|
|
142
|
+
export declare function between1<O, C, P>(open: Parser<O>, close: Parser<C>, parser: Parser<P>): Parser<P[]>;
|
|
111
143
|
/**
|
|
112
144
|
* Parses many instances of the parser separated by separator.
|
|
145
|
+
* The parser will succeed even if it consumes zero instances.
|
|
146
|
+
* To require at least one instance, use `sepBy1`.
|
|
113
147
|
* @param separator
|
|
114
148
|
* @param parser
|
|
115
149
|
* @returns a parser that runs the given parser zero to many times, separated by the separator parser.
|
|
116
150
|
*/
|
|
117
151
|
export declare function sepBy<S, P>(separator: Parser<S>, parser: Parser<P>): Parser<P[]>;
|
|
152
|
+
/**
|
|
153
|
+
* Parses many instances of the parser separated by separator.
|
|
154
|
+
* The parser needs to succeed at least once, otherwise sepBy fails.
|
|
155
|
+
* To not require at least one instance, use `sepBy`.
|
|
156
|
+
* @param separator
|
|
157
|
+
* @param parser
|
|
158
|
+
* @returns a parser that runs the given parser one to many times, separated by the separator parser.
|
|
159
|
+
*/
|
|
160
|
+
export declare function sepBy1<S, P>(separator: Parser<S>, parser: Parser<P>): Parser<P[]>;
|
|
118
161
|
/**
|
|
119
162
|
* Convenience function to use as the second argument to `seq` to get all the results from `seq`
|
|
120
163
|
* @param results
|
|
@@ -293,10 +336,10 @@ export declare function map<R, C extends PlainObject, X>(parser: GeneralParser<R
|
|
|
293
336
|
* The rest of the input that isn't part of the result is simply joined together and returned as a string.
|
|
294
337
|
* If you need a more structured result + rest, you can use `within` instead.
|
|
295
338
|
*
|
|
296
|
-
* @param parser -
|
|
339
|
+
* @param parser - the parser to search for
|
|
297
340
|
* @returns - a parser that returns an array of strings
|
|
298
341
|
*/
|
|
299
|
-
export declare function search(parser: Parser<
|
|
342
|
+
export declare function search<T>(parser: Parser<T>): Parser<T[]>;
|
|
300
343
|
/**
|
|
301
344
|
* seq takes an array of parsers and runs them sequentially.
|
|
302
345
|
* If any of the parsers fail, seq fails without consuming any input.
|
package/dist/combinators.js
CHANGED
|
@@ -221,14 +221,38 @@ export function not(parser) {
|
|
|
221
221
|
}
|
|
222
222
|
/**
|
|
223
223
|
* Takes three parsers, `open`, `close`, and `parser`.
|
|
224
|
-
* `between` matches
|
|
224
|
+
* `between` matches multiple instances of `parser`,
|
|
225
225
|
* surrounded by `open` and `close`. It returns the result of `parser`.
|
|
226
|
-
*
|
|
226
|
+
*
|
|
227
|
+
* This is a look-ahead combinator. It keeps trying the `close` parser until it succeeds.
|
|
228
|
+
* That means you can use it like this, and `parser` won't consume the ending quote:
|
|
229
|
+
*
|
|
230
|
+
* ```ts
|
|
231
|
+
* const quotedString = between(quote, quote, anyChar);
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* This combinator succeeds even if it parses zero instances of `parser`.
|
|
235
|
+
* So for the above parser, this input would succeed: `""`.
|
|
236
|
+
*
|
|
237
|
+
* If you want it to consume at least one instance of `parser`, use `between1`.
|
|
238
|
+
*
|
|
239
|
+
* Even though this is a look-ahead combinator, it won't work if you supply a greedy parser.
|
|
240
|
+
* You can make the above parser fail simply by adding `many1`:
|
|
241
|
+
*
|
|
242
|
+
* ```ts
|
|
243
|
+
* const quotedString = between(quote, quote, many1(anyChar));
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* `many1(anyChar)` will consume all input until the end of the string,
|
|
247
|
+
* not giving the `close` parser a chance to succeed.
|
|
248
|
+
*
|
|
249
|
+
* This combinator is obviously expensive, as it applies the `close` parser at every step.
|
|
250
|
+
* This combinator can also get into an infinite loop if `parser` succeeds without consuming any input.
|
|
227
251
|
*
|
|
228
252
|
* @param open - parser for the opening delimiter
|
|
229
253
|
* @param close - parser for the closing delimiter
|
|
230
254
|
* @param parser - parser for the content
|
|
231
|
-
* @returns
|
|
255
|
+
* @returns the result of `parser`.
|
|
232
256
|
*/
|
|
233
257
|
export function between(open, close, parser) {
|
|
234
258
|
return (input) => {
|
|
@@ -236,19 +260,48 @@ export function between(open, close, parser) {
|
|
|
236
260
|
if (!result1.success) {
|
|
237
261
|
return result1;
|
|
238
262
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
263
|
+
let rest = result1.rest;
|
|
264
|
+
// keep looking for close
|
|
265
|
+
let result2 = close(rest);
|
|
266
|
+
let successResult = [];
|
|
267
|
+
// while we don't succeed, keep trying to parse with parser
|
|
268
|
+
while (!result2.success) {
|
|
269
|
+
const parserResult = parser(rest);
|
|
270
|
+
// the parser should keep succeeding until we find the closer
|
|
271
|
+
// if it doesn't, we fail
|
|
272
|
+
if (!parserResult.success) {
|
|
273
|
+
return failure(parserResult.message, input);
|
|
274
|
+
}
|
|
275
|
+
successResult.push(parserResult.result);
|
|
276
|
+
rest = parserResult.rest;
|
|
277
|
+
result2 = close(rest);
|
|
242
278
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
279
|
+
// we found the closer. Note we consume the closer and return the rest
|
|
280
|
+
return success(successResult, result2.rest);
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Like `between`, but fails unless it consumes at least one instance of the `parser`.
|
|
285
|
+
* @param open - parser for the opening delimiter
|
|
286
|
+
* @param close - parser for the closing delimiter
|
|
287
|
+
* @param parser - parser for the content
|
|
288
|
+
* @returns the result of `parser`.
|
|
289
|
+
*/
|
|
290
|
+
export function between1(open, close, parser) {
|
|
291
|
+
return (input) => {
|
|
292
|
+
const result = between(open, close, parser)(input);
|
|
293
|
+
if (result.success) {
|
|
294
|
+
if (result.result.length === 0) {
|
|
295
|
+
return failure("expected at least one match", input);
|
|
296
|
+
}
|
|
246
297
|
}
|
|
247
|
-
return
|
|
298
|
+
return result;
|
|
248
299
|
};
|
|
249
300
|
}
|
|
250
301
|
/**
|
|
251
302
|
* Parses many instances of the parser separated by separator.
|
|
303
|
+
* The parser will succeed even if it consumes zero instances.
|
|
304
|
+
* To require at least one instance, use `sepBy1`.
|
|
252
305
|
* @param separator
|
|
253
306
|
* @param parser
|
|
254
307
|
* @returns a parser that runs the given parser zero to many times, separated by the separator parser.
|
|
@@ -272,6 +325,25 @@ export function sepBy(separator, parser) {
|
|
|
272
325
|
}
|
|
273
326
|
};
|
|
274
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Parses many instances of the parser separated by separator.
|
|
330
|
+
* The parser needs to succeed at least once, otherwise sepBy fails.
|
|
331
|
+
* To not require at least one instance, use `sepBy`.
|
|
332
|
+
* @param separator
|
|
333
|
+
* @param parser
|
|
334
|
+
* @returns a parser that runs the given parser one to many times, separated by the separator parser.
|
|
335
|
+
*/
|
|
336
|
+
export function sepBy1(separator, parser) {
|
|
337
|
+
return (input) => {
|
|
338
|
+
const result = sepBy(separator, parser)(input);
|
|
339
|
+
if (result.success) {
|
|
340
|
+
if (result.result.length === 0) {
|
|
341
|
+
return failure("expected at least one match", input);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
};
|
|
346
|
+
}
|
|
275
347
|
/**
|
|
276
348
|
* Convenience function to use as the second argument to `seq` to get all the results from `seq`
|
|
277
349
|
* @param results
|
|
@@ -531,7 +603,7 @@ export function map(parser, mapperFunc) {
|
|
|
531
603
|
* The rest of the input that isn't part of the result is simply joined together and returned as a string.
|
|
532
604
|
* If you need a more structured result + rest, you can use `within` instead.
|
|
533
605
|
*
|
|
534
|
-
* @param parser -
|
|
606
|
+
* @param parser - the parser to search for
|
|
535
607
|
* @returns - a parser that returns an array of strings
|
|
536
608
|
*/
|
|
537
609
|
export function search(parser) {
|
|
@@ -545,6 +617,7 @@ export function search(parser) {
|
|
|
545
617
|
.filter((x) => x.type === "unmatched")
|
|
546
618
|
.map((x) => x.value)
|
|
547
619
|
.join(" ");
|
|
620
|
+
// @ts-ignore
|
|
548
621
|
return success(result, rest);
|
|
549
622
|
}
|
|
550
623
|
return success([], input);
|
package/dist/parsers/within.d.ts
CHANGED
|
@@ -1,2 +1,59 @@
|
|
|
1
1
|
import { WithinResult, Parser } from "../types.js";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `within` is a funny combinator. It finds zero or more instances of `parser` within the input.
|
|
4
|
+
* It always succeeds and returns an array of results, each one being a matched or unmatched string.
|
|
5
|
+
* You could think of "within" as a glorified search. For example, you can use it to
|
|
6
|
+
* look for quoted text or multiline comments within an input.
|
|
7
|
+
*
|
|
8
|
+
* Example:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const multilineComments = seq(
|
|
12
|
+
* [str("/*"), manyWithJoin(noneOf(`*\/`)), str("*\/")],
|
|
13
|
+
* (results: string[]) => results.join("")
|
|
14
|
+
* );
|
|
15
|
+
* const parser = within(multilineComments);
|
|
16
|
+
* const input = `code before /* this is a comment *\/ code after /* another comment *\/ end`;
|
|
17
|
+
*
|
|
18
|
+
* const result = parser(input);
|
|
19
|
+
* console.log(JSON.stringify(result, null, 2));
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Result:
|
|
23
|
+
*
|
|
24
|
+
* ```json
|
|
25
|
+
* {
|
|
26
|
+
* "success": true,
|
|
27
|
+
* "result": [
|
|
28
|
+
* {
|
|
29
|
+
* "type": "unmatched",
|
|
30
|
+
* "value": "code before "
|
|
31
|
+
* },
|
|
32
|
+
* {
|
|
33
|
+
* "type": "matched",
|
|
34
|
+
* "value": "/* this is a comment *\/"
|
|
35
|
+
* },
|
|
36
|
+
* {
|
|
37
|
+
* "type": "unmatched",
|
|
38
|
+
* "value": " code after "
|
|
39
|
+
* },
|
|
40
|
+
* {
|
|
41
|
+
* "type": "matched",
|
|
42
|
+
* "value": "/* another comment *\/"
|
|
43
|
+
* },
|
|
44
|
+
* {
|
|
45
|
+
* "type": "unmatched",
|
|
46
|
+
* "value": " end"
|
|
47
|
+
* }
|
|
48
|
+
* ],
|
|
49
|
+
* "rest": ""
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* This parser is somewhat expensive, as it will go down the input string one character
|
|
54
|
+
* at a time, applying the given parser each time.
|
|
55
|
+
*
|
|
56
|
+
* @param parser - parser to find within the input
|
|
57
|
+
* @returns - an array of matched and unmatched strings
|
|
58
|
+
*/
|
|
59
|
+
export declare function within<T>(parser: Parser<T>): Parser<WithinResult<T>[]>;
|
package/dist/parsers/within.js
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
import { trace } from "../trace.js";
|
|
2
2
|
import { success } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* `within` is a funny combinator. It finds zero or more instances of `parser` within the input.
|
|
5
|
+
* It always succeeds and returns an array of results, each one being a matched or unmatched string.
|
|
6
|
+
* You could think of "within" as a glorified search. For example, you can use it to
|
|
7
|
+
* look for quoted text or multiline comments within an input.
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const multilineComments = seq(
|
|
13
|
+
* [str("/*"), manyWithJoin(noneOf(`*\/`)), str("*\/")],
|
|
14
|
+
* (results: string[]) => results.join("")
|
|
15
|
+
* );
|
|
16
|
+
* const parser = within(multilineComments);
|
|
17
|
+
* const input = `code before /* this is a comment *\/ code after /* another comment *\/ end`;
|
|
18
|
+
*
|
|
19
|
+
* const result = parser(input);
|
|
20
|
+
* console.log(JSON.stringify(result, null, 2));
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Result:
|
|
24
|
+
*
|
|
25
|
+
* ```json
|
|
26
|
+
* {
|
|
27
|
+
* "success": true,
|
|
28
|
+
* "result": [
|
|
29
|
+
* {
|
|
30
|
+
* "type": "unmatched",
|
|
31
|
+
* "value": "code before "
|
|
32
|
+
* },
|
|
33
|
+
* {
|
|
34
|
+
* "type": "matched",
|
|
35
|
+
* "value": "/* this is a comment *\/"
|
|
36
|
+
* },
|
|
37
|
+
* {
|
|
38
|
+
* "type": "unmatched",
|
|
39
|
+
* "value": " code after "
|
|
40
|
+
* },
|
|
41
|
+
* {
|
|
42
|
+
* "type": "matched",
|
|
43
|
+
* "value": "/* another comment *\/"
|
|
44
|
+
* },
|
|
45
|
+
* {
|
|
46
|
+
* "type": "unmatched",
|
|
47
|
+
* "value": " end"
|
|
48
|
+
* }
|
|
49
|
+
* ],
|
|
50
|
+
* "rest": ""
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* This parser is somewhat expensive, as it will go down the input string one character
|
|
55
|
+
* at a time, applying the given parser each time.
|
|
56
|
+
*
|
|
57
|
+
* @param parser - parser to find within the input
|
|
58
|
+
* @returns - an array of matched and unmatched strings
|
|
59
|
+
*/
|
|
3
60
|
export function within(parser) {
|
|
4
61
|
return trace("within", (input) => {
|
|
5
62
|
let start = 0;
|
|
@@ -19,7 +76,7 @@ export function within(parser) {
|
|
|
19
76
|
type: "matched",
|
|
20
77
|
value: parsed.result,
|
|
21
78
|
});
|
|
22
|
-
current
|
|
79
|
+
current = input.length - parsed.rest.length;
|
|
23
80
|
start = current;
|
|
24
81
|
}
|
|
25
82
|
else {
|
package/dist/parsers.d.ts
CHANGED
package/dist/parsers.js
CHANGED
|
@@ -2,7 +2,7 @@ import { many1WithJoin, manyWithJoin, seq } from "./combinators.js";
|
|
|
2
2
|
import { trace } from "./trace.js";
|
|
3
3
|
import { captureSuccess, failure, success, } from "./types.js";
|
|
4
4
|
import { escape } from "./utils.js";
|
|
5
|
-
export { within
|
|
5
|
+
export { within } from "./parsers/within.js";
|
|
6
6
|
/**
|
|
7
7
|
* Takes a character. Returns a parser that parses that character.
|
|
8
8
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -116,13 +116,13 @@ export declare function createNode(parent: Node | null, parser: GeneralParser<an
|
|
|
116
116
|
*/
|
|
117
117
|
export declare function createTree(parsers: readonly GeneralParser<any, any>[]): Node;
|
|
118
118
|
/** Used by `within`. */
|
|
119
|
-
export type Matched = {
|
|
119
|
+
export type Matched<T> = {
|
|
120
120
|
type: "matched";
|
|
121
|
-
value:
|
|
121
|
+
value: T;
|
|
122
122
|
};
|
|
123
123
|
export type Unmatched = {
|
|
124
124
|
type: "unmatched";
|
|
125
125
|
value: string;
|
|
126
126
|
};
|
|
127
|
-
export type WithinResult = Matched | Unmatched;
|
|
127
|
+
export type WithinResult<T> = Matched<T> | Unmatched;
|
|
128
128
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tarsec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A parser combinator library for TypeScript, inspired by Parsec.",
|
|
5
5
|
"homepage": "https://github.com/egonSchiele/tarsec",
|
|
6
6
|
"scripts": {
|
|
@@ -38,4 +38,4 @@
|
|
|
38
38
|
"typescript": "^5.4.2",
|
|
39
39
|
"vitest": "^1.4.0"
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|