@zigsterz/parzing 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-2022 Eldan Ben-Haim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,431 @@
1
+ # Parzing: TypesSript Parser Combinator Library
2
+
3
+ ## Overview
4
+ This package implements a parser combinator system, allowing client code to easily create parsers in JavaScript or TypeScript. When used with TypeScript accurate types are computed for parsed and intermediate results, allowing quick and safe implementation and utilization of parsers.
5
+
6
+ ## Installation
7
+
8
+ ```
9
+ npm install --save @zigsterz/parzing
10
+ ```
11
+
12
+ ## Parsing Basics
13
+
14
+ Parzing exposes a `parse` function for invoking a parser on content. To use it, we first construct a parser, and then pass the parser along with the content to parse. `parse` will either return a the result of succesfuly parsing the content, or throw an error describing a failure to parse.
15
+
16
+ The `ParserBuilder` class exposes a set of helper factory functions for constructing parser.
17
+
18
+ The example below demonstrates how to parse a sequence of 1 to 3 digits by first constructing a `ParserBuilder`, then using it to create an `AnyOfParser` parser and finally running the parser using `parse`.
19
+
20
+ ```typescript
21
+ import { ParserBuilder, parse } from 'parzing';
22
+
23
+ const pb = new ParserBuilder();
24
+
25
+ const result = parse(pb.anyOf("0123456789", 1, 3), "123", true);
26
+ assert(result == "123");
27
+
28
+ ```
29
+
30
+ ### Parsing Results
31
+ The result of parsing content may be a value of any type. A Parzing parser has an associated result type that describes the type of the parsing result returned by that parser.
32
+
33
+
34
+ ### Basic Parsers
35
+
36
+ `pb.anyOf` creates the Any Of *basic parser*. Basic parsers are the atomic building blocks for parsing. They may be combined using [*parser combinators*](#parser-combinators) to construct more complex parsers.
37
+
38
+ Parzing offers the following basic parsers out of the box:
39
+
40
+ | Parser constructor | Description | Result type |
41
+ | ------------------ | ----------- | ----------- |
42
+ | `ParserBuilder.anyOf(chars, min, max)` | Parses a minimum of *min* characters, and up to *max* characters, all out the characters listed in *chars*. | `string` |
43
+ | `ParserBuilder.token(token)` | Parses the exact string specified by *token*. | `string` |
44
+ | `ParserBuilder.pass()` | This is a no-op parser. It consumes no input and always succeeds. | `void` |
45
+ | `ParserBuilder.fail(message)` | Fail parsing with the error message provided in *message*. | `void` |
46
+
47
+ In addition to these parsers, you can create [custom parsers](#custom-parsers) to parse arbitrary complex "atoms". Customer parsers may provide any result type.
48
+
49
+ ## Creating Complex Parsers
50
+
51
+ ### Parser Combinators
52
+ *Parser Combinators* are parsers that are constructed based on other parsers, and combine these parsers in some form to generate a more complex parser.
53
+
54
+ Perhaps the simplest example of a paser combinator is the Sequence combinator.
55
+ The sequence combinator is constructed based on a sequence of underlying parsers, using the `ParserBuilder.sequence(...)` factory method.
56
+ When parsing input content, the combinator will invoke each of the underlying parsers to parse consecutive fragments of the content.
57
+ If any underlying parser fails, the sequence parser fails as well.
58
+ If all underlying parsers succeed, the parse result is an array that consists of the underlying parsers' parse results.
59
+
60
+ The following example shows how to construct a parser that expects three digits separated by a dash:
61
+
62
+ ```typescript
63
+ import { ParserBuilder, parse } from 'parzing';
64
+ import { strict as assert } from 'node:assert';
65
+
66
+ const pb = new ParserBuilder();
67
+ const sample_parser = pb.sequence(
68
+ pb.anyOf("0123456789"),
69
+ pb.token("-"),
70
+ pb.anyOf("0123456789"),
71
+ pb.token("-"),
72
+ pb.anyOf("0123456789")
73
+ );
74
+
75
+ const result = parse(sample_parser, "1-2-3", true);
76
+ assert.deepEqual(result, ["1", "-", "2", "-", "3"]);
77
+ ```
78
+
79
+ In addition to the `sequence` parser combinator, the following parser combinators offered by Parzing out-of-the box:
80
+
81
+ | Parser constructor | Description | Result type |
82
+ | ------------------ | ----------- | ----------- |
83
+ | `ParserBuilder.sequence(parser,...)`| Parse a sequence of elements: Invoke the provided parsers one after the other on consecutive fragments of the input. Return a sequence made of the results returned by the various parsers if all were succesful. Fail if any underlying parser failed. | A typed tuple (array) with each element typed as the corresponding underlying parser's result type.
84
+ | `ParserBuilder.choice(parser, ...))` | Parse one of several alternatives: Attempt invoking each of the provided parsers one after the other on the input, until a parser succeeds in parsing the input. Return the result obtained from that parser on the input. If no parser was able to parse, fail parsing. | Union type of the result types of underlying parsers.
85
+ |
86
+ | `ParserBuilder.many(parser, separator?, min?, max?)` | Parse multiple occurences of the underlying `parser`. Optionally require at least `min` occurrences (fail otherwise). You can limit the number of parsed occurences to `max`; note that any additional occurences won't be parsed (may be processed by whatever parsers follow) but their existence won't fail the parser. If a `separator` parser is provided, it will be used to parse content between occurences. | An array of items of the same type as the result type of the underlying `parser`. |
87
+ | `ParserBuilder.optional(parser)` | Optionally parse: Attempt to parse using the underlying parser. If parsing is succesful, return the parsed value. Otherwise return null (instead of failure). | A union between the underlying parser type and `null`. |
88
+ |
89
+ | `ParserBuilder.map(parser, mapper)` | Map parser results: Attempt to parse content using the underlying `parser`. If parsing is succesful, return the result of applying the `mapper` on the underlying parser result. Otherwise indicate failure. | Return type of the `mapper` function. |
90
+ |
91
+
92
+ ### Whitespace Support
93
+
94
+ Parsers that derive from `ParserWithInternalWhitespaceSupport` support ignoring whitespace within the content. Exactly where whitespace is ignored depends on the specific parser (see table below).
95
+
96
+ For all of these parsers, the ignored "whitespace" is defined as content that can be parsed by the *whitespace parser*. The whitespace parser can be set by invoking `target_parser.whitespace(whitespace_parser)`.
97
+ If this method is not invoked on the parser, and the parser was created using `ParserBuilder`, then the whitespace parser is set as the default whitespace parser for the builder. The default whitespace parser for a `ParserBuilder` can be set by passing it on construction.
98
+
99
+ If there's no set whitespace parser on a `ParserWithInternalWhitespaceSupport`, no whitespace will be ignored.
100
+
101
+ The `WhitespaceParser` class implements a parser that accepts common whitespace patterns.
102
+
103
+ The following code example illustrates a few ways to set whitespace parsers:
104
+
105
+ ```typescript
106
+ import { ParserBuilder, WhitespaceParser } from 'parzing';
107
+
108
+ // Builder without default whitespace parser
109
+ const pb1 = new ParserBuilder();
110
+
111
+ // Builder with default whitespace parser
112
+ const pb2 = new ParserBuilder(new WhitespaceParser(false));
113
+
114
+ // No whitespace parser; will accept 'hellohello' but not 'hello hello'
115
+ const p1 = pb1.many(pb1.token('hello'));
116
+
117
+ // Default whitespace parser from pb2; will accept 'hello hello' but not 'hellohello'
118
+ const p2 = pb2.many(pb2.token('hello'));
119
+
120
+ // Specified whitespace parser; will accept 'hello hello' but not 'hellohello'
121
+ const p3 = pb1.many(pb1.token('hello')).whitespace(new WhitespaceParser(false));
122
+ ```
123
+
124
+ Parzing parsers that support whitespace (derived from `ParserWithInternalWhitespaceSupport`) are listed below:
125
+
126
+ | Parser | Builder factory method | Whitespace behavior |
127
+ | ------ | ----------------------- | ------------------- |
128
+ | `ManyCombinator` | `ParserBuilder.many` | Invoke whitespace parser between occurrences of underlying parser. |
129
+ | `SequenceCombinator` | `ParserBuilder.sequence` | Invoke whitespace parser among between underlying parsers. |
130
+
131
+ ### Cuts
132
+
133
+ Some parser combinators may recover from parsing errors by offering alternative parsing options:
134
+
135
+ - The `ChooseCombinator` (typically constructed through `ParserBuilder.choose`) combinator may attempt a different parser if a parser alternative failed.
136
+ - The `OptionalCombinator` (typically constructed through `ParserBuilder.optional`) combinator may pass on parsing content if the underlying parser fails.
137
+ - The `ManyCombinator` (typically constructed through `ParserBuilder.many`) may backtrack a failed parse, realizing that the sequence of "many" occurences was terminated.
138
+
139
+ Such recovery and backtracking behavior may lead to hard to understand parsing errors. For example, consider the following parser:
140
+
141
+
142
+ ```typescript
143
+ import { assert } from 'console';
144
+ import { parse, ParserBuilder, WhitespaceParser } from 'parzing';
145
+
146
+ // Builder without default whitespace parser
147
+ const pb = new ParserBuilder(new WhitespaceParser(false));
148
+
149
+ const parser = pb.choice(
150
+ pb.sequence(
151
+ pb.token('number'),
152
+ pb.anyOf('0123456789', 1)
153
+ ),
154
+
155
+ pb.sequence(
156
+ pb.anyOf('abcdefghijklmnopqrstuvwxyz', 1),
157
+ pb.anyOf('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 1)
158
+ )
159
+ )
160
+
161
+ assert(parse(parser, 'number a123', true));
162
+ ```
163
+
164
+ This parser will fail, but the failure will be reported by the `choice` combinator after exhausting all of its options. Indeed running this code will result in the following exception:
165
+
166
+ ```
167
+ ParseError { message: 'Parser rejected input' }
168
+ ```
169
+
170
+ Clearly, for the input `number a123` a more approach would be to not even try the second alternative in the `choice` combinator above, and immediately bail out if we've encountered the `number` token.
171
+ This kind of behavior can be achieved using cuts. A cut is a special parser, constructed using `ParserBuilder.cut`, that doesn't attempt to consume any input. Rather,
172
+ When a cut 'parses', the fact that it was encountered is recored in the parsing context. Backtracking parsers, such as the ones listed above, will not attempt to backtrack parsing if a cut was encountered by one of their underlying parsers. Rather they will immediate fail with whatever failure that would have caused them to backtrack.
173
+
174
+ Fixing the example above using cuts, we can write:
175
+
176
+ ```typescript
177
+ import { assert } from 'console';
178
+ import { parse, ParserBuilder, WhitespaceParser } from 'parzing';
179
+
180
+ // Builder without default whitespace parser
181
+ const pb = new ParserBuilder(new WhitespaceParser(false));
182
+
183
+ const parser = pb.choice(
184
+ pb.sequence(
185
+ pb.token('number'),
186
+ pb.cut(),
187
+ pb.anyOf('0123456789', 1)
188
+ ),
189
+
190
+ pb.sequence(
191
+ pb.anyOf('abcdefghijklmnopqrstuvwxyz', 1),
192
+ pb.anyOf('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 1)
193
+ )
194
+ )
195
+
196
+ assert(parse(parser, 'number a123', true));
197
+ ```
198
+
199
+ which would now result in the following exception:
200
+
201
+ ```
202
+ ParseError { message: "Expecting AnyOf 0123456789 at 7 ('a123')" }
203
+ ```
204
+
205
+ Clearly a more useful error message. Note that in addition to yielding clearer errors, cuts may also improve parsing performance since by preventing backtracks.
206
+
207
+ There are cases where you may want to reuse the same parser in different contexts -- where in some contexts you want the cut to appear but in others you want the cut to be ignored. This is achieved by invoking ``ParserBuilder.attempt`` on the parser which will return an ``AttemptCombinator``. This combinator parser will "swallow" any cut encountered indication within the underlying parser.
208
+
209
+ ### Parser Operators
210
+
211
+ Parser operators allow specifying transformations on parsers in postfix notation. This notation makes chaining transformations more readable.
212
+
213
+ For example, consider the following two equivalent examples for creating a parser that reads "true" or "false" and converts to boolean:
214
+
215
+ ```typescript
216
+
217
+ import { ParserBuilder, ParserOperators } from 'parzing';
218
+
219
+ const pb = new ParserBuilder();
220
+
221
+ const noOperators = pb.choice(
222
+ pb.map(pb.token("true"), (s) => true),
223
+ pb.map(pb.token("false"), (s) => false)
224
+ );
225
+
226
+ const useOperators = pb.choice(
227
+ pb.token("true")._(ParserOperators.map((s) => true)),
228
+ pb.token("false")._(ParserOperators.map((s) => false))
229
+ );
230
+
231
+ ```
232
+
233
+ To apply an operator on a parser created using `ParserBuilder`, invoke the `_` method of the parser and pass it the operator. You can chain such operator applications:
234
+
235
+ ```typescript
236
+
237
+ const optionalInt = pb.anyOf("0123456789")._(ParserOperators.map((s) => Number.parseInt(s)))._(ParserOperators.optional())
238
+ ```
239
+
240
+ Parzing offers the following operators out of the box. Note that you can also create your own [custom operators](#custom-operators).
241
+
242
+ | Operator | Description |
243
+ | -------- | ----------- |
244
+ | `ParserOperators.map(mapper)` | Equivalent to `ParserBuilder.map(parser, mapper)`. |
245
+ | `ParserOperators.optional()` | Equivalent to `ParserBuilder.optional(parser)`. |
246
+ | `ParserOperators.build(ctor)` | Transforms the parser to a parser that will construct an object by invoking constructor `ctor` with the parse result of the transformed parser. |
247
+ | `ParserOperators.omit()` | Maps the result of the parser to `void`. If this is included in a sequence parser combinator, the parser will not contribute to the sequence. |
248
+ | `ParserOperators.whitespace(whitespaceParser)` | Only applicable to parsers with whitespace support; sets the whitespace of the parser. |
249
+ | `ParserOperators.withIndices()` | Transforms the parser to a parser that returns the original parser result wrapped in a struct that includes the start and end offsets into the parsed input that were processed by the parser. |
250
+
251
+ ### Recursive Parsers
252
+
253
+ In many cases a language may include recursive grammar definitions. Consider for example the following simple expresion parser grammer:
254
+
255
+ expression := addition | subtraction
256
+ addition := term '+' term
257
+ subtraction := term '-' term
258
+ term := number | '(' expression ')'
259
+
260
+ How would we define this using Parzing?
261
+
262
+ ```typescript
263
+ import { ParserBuilder } from 'parzing';
264
+
265
+ const pb = new ParserBuilder();
266
+
267
+ const term = pb.choice(
268
+ pb.anyOf("0123456789"),
269
+ pb.sequence(
270
+ pb.token("("),
271
+ expression, /// Ooops!
272
+ pb.token(")")
273
+ )
274
+ );
275
+
276
+ const addition = pb.sequence(
277
+ term,
278
+ pb.token("+"),
279
+ term
280
+ );
281
+
282
+
283
+ const subtraction = pb.sequence(
284
+ term,
285
+ pb.token("-"),
286
+ term
287
+ );
288
+
289
+ const expression = pb.choice(addition, subtraction);
290
+ ```
291
+
292
+ Note the comment "Ooops!" above. The recursive nature of the parser creates a circular declaration in Typescript, which is disallowed.
293
+
294
+ The `ParserBuilder.ref` method allows coping with this situation by receiving a parameterless function returning a parser, and creating a parser that lazily resolves to the function's return value.
295
+ Using this mechanism, our recursive parser becomes possible by modifying the code above as follows:
296
+
297
+
298
+ ```typescript
299
+ import { ParserBuilder } from 'parzing';
300
+
301
+ const pb = new ParserBuilder();
302
+
303
+ const term = pb.choice(
304
+ pb.anyOf("0123456789"),
305
+ pb.sequence(
306
+ pb.token("("),
307
+ pb.ref(expression), /// Now we're good!
308
+ pb.token(")")
309
+ )
310
+ );
311
+
312
+ const addition = pb.sequence(
313
+ term,
314
+ pb.token("+"),
315
+ term
316
+ );
317
+
318
+
319
+ const subtraction = pb.sequence(
320
+ term,
321
+ pb.token("-"),
322
+ term
323
+ );
324
+
325
+ const expression = pb.choice(addition, subtraction);
326
+ ```
327
+
328
+ ## Extending Parzing
329
+
330
+ ### Custom Parsers
331
+
332
+ Custom parsers can be created by implementing the `Parser<T>` interface:
333
+
334
+ ```typescript
335
+ export interface Parser<T> {
336
+ parse(parserContext: ParserContext): ParseResult<T>;
337
+ }
338
+ ```
339
+
340
+ The `T` type parameter represents the parser result type.
341
+
342
+ The `ParserContext` class provides the parser with access to the content to parse, as well as additional context that flows through the parsing process.
343
+
344
+ `ParseResult<T>` is a type that represents either succesful parsing or failed parsing accompanied with a result.
345
+
346
+ Your parser implementation will typically use `ParserContext.input` to obtain access to the parsed input (implementing `ParserInput`). The input is represented as a stream with an option to take bookmarks and seek back to them.
347
+
348
+ To read information from the input stream use `ParserInput.read()`. To peek at information without taking it out of the stream, use `ParserInput.peek()`. On entry to your parser's `parse` method, the stream will be positioned at the first character that your parser is requested to parse. At the end of parsing the input stream must be positioned on the character past the last character that was succesfuly parsed by your parser.
349
+
350
+ #### Backtracking and handling Cuts
351
+
352
+ Some parsers may need to backtrack -- go back to a position in the stream that they have already visited. This is done by invoking `ParserInput.getBookmark()` to obtain an opaque bookmark to the current stream position, and `ParserInput.seekToBookmark()` to seek back to the previosuly fetched position.
353
+
354
+ When implemeneting a parser combinator that may backtrack, be mindful of [cuts](#cuts). When a `cut` parser is invoked, it turns on `ParserContext.cutEncountered`. You should avoid backtracking if during the excution of an underlying parser this flag was turned on. To use this flag effectively, you should follow this pattern:
355
+
356
+ - Save the current state of the `cutEncountered` flag before invoking an underlying parser
357
+ - Set the `cutEncountered` flag to `false`
358
+ - Invoke an underlying parser
359
+ - Check if `cutEncountered` is true. If it is -- avoid further backtraces
360
+ - Restore previous value of `cutEncountered`.
361
+
362
+ The following implementation of the `optional` combinator illustrates how to handle backtracking and cuts:
363
+
364
+ ```typescript
365
+ export class OptionalCombinator<T> implements Parser<T | null> {
366
+ constructor(private _parser: Parser<T>) {
367
+ }
368
+
369
+ parse(parserContext: ParserContext): ParseResult<T | null> {
370
+ const input = parserContext.input;
371
+
372
+ // Take a bookmark to the current position;
373
+ // if our underlying parser fails we'll
374
+ // restore that position (as if we didn't
375
+ // read anything).
376
+ const bm = input.getBookmark();
377
+
378
+ // Remember whether a cut was encountered before
379
+ // invoking the underlying parser.
380
+ // If underlying parser will fail, we will
381
+ // restore this value.
382
+ const pce = parserContext.cutEncountered;
383
+
384
+ // Clear the 'cut encountered' flag to
385
+ // trace whether a cut is encountered strictly
386
+ // within our underlying parser's operation
387
+ parserContext.cutEncountered = false;
388
+ let ret = this._parser.parse(parserContext);
389
+
390
+ // If underlyilng parser failed --
391
+ // we return a null result and rewind the
392
+ // input stream to where it was before our parse operation.
393
+ if(!ret.successful && !parserContext.cutEncountered) {
394
+ input.seekToBookmark(bm);
395
+ ret = ParseResult.successful(null);
396
+ }
397
+
398
+ // Restore cut encountered flag
399
+ parserContext.cutEncountered = pce;
400
+ return ret;
401
+ }
402
+ }
403
+ ```
404
+
405
+ #### Whitespace and Operator Support
406
+
407
+ By deriving your parser from `ParserWithInternalWhitespaceSupport` you can benefit from API-consistent whitespace handling. If you derive from this class, invoke `ParserWithInternalWhitespaceSupport.parseWhitespace` from your parser whenever you want to skip whitespace.
408
+
409
+ To support `ParserBuilder` integration, when after constructing your parser pass it to `ParserBuilder.parser`. This will:
410
+
411
+ - Apply default whitespace handling as set for the parser builder if your parser is derived from `ParserWithInternalWhitespaceSupport`
412
+ - Add support for applying operators on your parser.
413
+
414
+ ### Custom Operators
415
+
416
+ Implementing custom operators is easy. Just implement a function that returns a function that receives the source parser and returns the target parser. You can restrict the source parser type if you need to.
417
+
418
+ As an example, here's the implementation of the `ParserOperators.map` operator:
419
+
420
+ ```typescript
421
+
422
+ function map<S, T>(mapper: (s: S) => T) {
423
+ return (p: Parser<S>) => {
424
+ return new MapParser(p, mapper);
425
+ }
426
+ }
427
+ ```
428
+
429
+ ## License and Credits
430
+ Parzing is Copyright (c) 2021, 2022 Eldan Ben-Haim.
431
+ Licensed under MIT license.
@@ -0,0 +1,32 @@
1
+ import { AttemptParser } from "./combinators/AttemptParser";
2
+ import { ChooseCombinator } from "./combinators/ChooseCombinator";
3
+ import { ManyCombinator } from "./combinators/ManyCombinator";
4
+ import { MapParser } from "./combinators/MapParser";
5
+ import { OptionalCombinator } from "./combinators/OptionalCombinator";
6
+ import { SequenceCombinator } from "./combinators/SequenceCombinator";
7
+ import { CutParser, FailParser, Parser, ParserType, PassParser, RefParser } from "./core";
8
+ import { AnyOfParser } from "./parsers/AnyOfParser";
9
+ import { TokenParser } from "./parsers/TokenParser";
10
+ declare type WithPostfixSupport<T> = T & {
11
+ _<R>(f: (target: WithPostfixSupport<T>) => R): WithPostfixSupport<R>;
12
+ };
13
+ export declare function addPostfixSupport<T>(who: T): WithPostfixSupport<T>;
14
+ export declare class ParserBuilder {
15
+ constructor(whitespaceParser?: Parser<unknown> | null);
16
+ postProcessParser<T extends Parser<any>>(parser: T): WithPostfixSupport<T>;
17
+ parser<T extends Parser<any>>(p: T): WithPostfixSupport<T>;
18
+ token(tok: string): WithPostfixSupport<TokenParser>;
19
+ anyOf(alts: string, minLen?: number | null, maxLen?: number | null): WithPostfixSupport<AnyOfParser>;
20
+ fail(message?: string): WithPostfixSupport<FailParser>;
21
+ pass(): WithPostfixSupport<PassParser>;
22
+ cut(): WithPostfixSupport<CutParser>;
23
+ ref<T>(f: () => Parser<T>): WithPostfixSupport<RefParser<T>>;
24
+ attempt<T>(p: Parser<T>): WithPostfixSupport<AttemptParser<T>>;
25
+ map<V extends Parser<unknown>, T>(parser: V, mapper: (i: ParserType<V>) => T): WithPostfixSupport<MapParser<V, T>>;
26
+ sequence<TS extends Parser<unknown>[]>(...s: TS): WithPostfixSupport<SequenceCombinator<TS>>;
27
+ choice<T extends Parser<any>[]>(...parsers: T): ChooseCombinator<T>;
28
+ many<T>(parser: Parser<T>, sep?: Parser<unknown> | null, min?: number, max?: number): WithPostfixSupport<ManyCombinator<T>>;
29
+ optional<T>(parser: Parser<T>): WithPostfixSupport<OptionalCombinator<T>>;
30
+ private _ws;
31
+ }
32
+ export {};
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParserBuilder = exports.addPostfixSupport = void 0;
4
+ var AttemptParser_1 = require("./combinators/AttemptParser");
5
+ var ChooseCombinator_1 = require("./combinators/ChooseCombinator");
6
+ var ManyCombinator_1 = require("./combinators/ManyCombinator");
7
+ var MapParser_1 = require("./combinators/MapParser");
8
+ var OptionalCombinator_1 = require("./combinators/OptionalCombinator");
9
+ var SequenceCombinator_1 = require("./combinators/SequenceCombinator");
10
+ var core_1 = require("./core");
11
+ var AnyOfParser_1 = require("./parsers/AnyOfParser");
12
+ var TokenParser_1 = require("./parsers/TokenParser");
13
+ function addPostfixSupport(who) {
14
+ var ret = who;
15
+ ret._ = function (f) {
16
+ return addPostfixSupport(f(ret));
17
+ };
18
+ return ret;
19
+ }
20
+ exports.addPostfixSupport = addPostfixSupport;
21
+ var ParserBuilder = /** @class */ (function () {
22
+ function ParserBuilder(whitespaceParser) {
23
+ if (whitespaceParser === void 0) { whitespaceParser = null; }
24
+ this._ws = whitespaceParser;
25
+ }
26
+ ParserBuilder.prototype.postProcessParser = function (parser) {
27
+ var p = parser;
28
+ if (p instanceof core_1.ParserWithInternalWhitespaceSupport && this._ws) {
29
+ p = p.whitespace(this._ws);
30
+ }
31
+ return addPostfixSupport(p);
32
+ };
33
+ ParserBuilder.prototype.parser = function (p) {
34
+ return this.postProcessParser(p);
35
+ };
36
+ ParserBuilder.prototype.token = function (tok) {
37
+ return this.postProcessParser(new TokenParser_1.TokenParser(tok));
38
+ };
39
+ ParserBuilder.prototype.anyOf = function (alts, minLen, maxLen) {
40
+ if (minLen === void 0) { minLen = 1; }
41
+ if (maxLen === void 0) { maxLen = null; }
42
+ return this.postProcessParser(new AnyOfParser_1.AnyOfParser(alts, minLen, maxLen));
43
+ };
44
+ ParserBuilder.prototype.fail = function (message) {
45
+ if (message === void 0) { message = null; }
46
+ return this.postProcessParser(new core_1.FailParser(message));
47
+ };
48
+ ParserBuilder.prototype.pass = function () {
49
+ return this.postProcessParser(new core_1.PassParser());
50
+ };
51
+ ParserBuilder.prototype.cut = function () {
52
+ return this.postProcessParser(new core_1.CutParser());
53
+ };
54
+ ParserBuilder.prototype.ref = function (f) {
55
+ return this.postProcessParser(new core_1.RefParser(f));
56
+ };
57
+ ParserBuilder.prototype.attempt = function (p) {
58
+ return this.postProcessParser(new AttemptParser_1.AttemptParser(p));
59
+ };
60
+ ParserBuilder.prototype.map = function (parser, mapper) {
61
+ return this.postProcessParser(new MapParser_1.MapParser(parser, mapper));
62
+ };
63
+ ParserBuilder.prototype.sequence = function () {
64
+ var s = [];
65
+ for (var _i = 0; _i < arguments.length; _i++) {
66
+ s[_i] = arguments[_i];
67
+ }
68
+ return this.postProcessParser(new SequenceCombinator_1.SequenceCombinator(s));
69
+ };
70
+ ParserBuilder.prototype.choice = function () {
71
+ var parsers = [];
72
+ for (var _i = 0; _i < arguments.length; _i++) {
73
+ parsers[_i] = arguments[_i];
74
+ }
75
+ return new ChooseCombinator_1.ChooseCombinator(parsers);
76
+ };
77
+ ParserBuilder.prototype.many = function (parser, sep, min, max) {
78
+ if (sep === void 0) { sep = null; }
79
+ if (min === void 0) { min = 0; }
80
+ if (max === void 0) { max = 0; }
81
+ return this.postProcessParser(new ManyCombinator_1.ManyCombinator(parser, sep, min, max));
82
+ };
83
+ ParserBuilder.prototype.optional = function (parser) {
84
+ return this.postProcessParser(new OptionalCombinator_1.OptionalCombinator(parser));
85
+ };
86
+ return ParserBuilder;
87
+ }());
88
+ exports.ParserBuilder = ParserBuilder;
@@ -0,0 +1,7 @@
1
+ import { Parser, ParserContext, ParseResult } from "../core";
2
+ export declare class AstBuilder<Args extends [...any], Ctor extends new (...args: Args) => any> implements Parser<InstanceType<Ctor>> {
3
+ private _parser;
4
+ private _ctor;
5
+ constructor(_parser: Parser<Args>, _ctor: Ctor);
6
+ parse(parserContext: ParserContext): ParseResult<InstanceType<Ctor>>;
7
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
3
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
4
+ if (ar || !(i in from)) {
5
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
6
+ ar[i] = from[i];
7
+ }
8
+ }
9
+ return to.concat(ar || Array.prototype.slice.call(from));
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AstBuilder = void 0;
13
+ var core_1 = require("../core");
14
+ var AstBuilder = /** @class */ (function () {
15
+ function AstBuilder(_parser, _ctor) {
16
+ this._parser = _parser;
17
+ this._ctor = _ctor;
18
+ }
19
+ AstBuilder.prototype.parse = function (parserContext) {
20
+ var _a;
21
+ var s = this._parser.parse(parserContext);
22
+ if (!s.successful) {
23
+ return s;
24
+ }
25
+ return core_1.ParseResult.successful(new ((_a = this._ctor).bind.apply(_a, __spreadArray([void 0], s.result, false)))());
26
+ };
27
+ return AstBuilder;
28
+ }());
29
+ exports.AstBuilder = AstBuilder;
@@ -0,0 +1,6 @@
1
+ import { Parser, ParserContext, ParseResult } from "../core";
2
+ export declare class AttemptParser<T> implements Parser<T> {
3
+ private _parser;
4
+ constructor(_parser: Parser<T>);
5
+ parse(parserContext: ParserContext): ParseResult<T>;
6
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AttemptParser = void 0;
4
+ var AttemptParser = /** @class */ (function () {
5
+ function AttemptParser(_parser) {
6
+ this._parser = _parser;
7
+ }
8
+ AttemptParser.prototype.parse = function (parserContext) {
9
+ var s = this._parser.parse(parserContext);
10
+ parserContext.cutEncountered = false;
11
+ return s;
12
+ };
13
+ return AttemptParser;
14
+ }());
15
+ exports.AttemptParser = AttemptParser;
@@ -0,0 +1,8 @@
1
+ import { Parser, ParserContext, ParseResult, ParserType } from "../core";
2
+ declare type ChooseResult<E> = E extends [infer Head, ...infer Tails] ? ParserType<Head> | ChooseResult<Tails> : never;
3
+ export declare class ChooseCombinator<E extends Parser<any>[]> implements Parser<ChooseResult<E>> {
4
+ private _parsers;
5
+ constructor(_parsers: E);
6
+ parse(parserContext: ParserContext): ParseResult<ChooseResult<E>>;
7
+ }
8
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChooseCombinator = void 0;
4
+ var core_1 = require("../core");
5
+ var ChooseCombinator = /** @class */ (function () {
6
+ function ChooseCombinator(_parsers) {
7
+ this._parsers = _parsers;
8
+ }
9
+ ChooseCombinator.prototype.parse = function (parserContext) {
10
+ var input = parserContext.input;
11
+ var bm = input.getBookmark();
12
+ for (var i = 0; i < this._parsers.length; i++) {
13
+ var combOpt = this._parsers[i].parse(parserContext);
14
+ if (combOpt.successful || parserContext.cutEncountered) {
15
+ return combOpt;
16
+ }
17
+ else {
18
+ input.seekToBookmark(bm);
19
+ }
20
+ }
21
+ return core_1.ParseResult.failed(core_1.ParseError.parserRejected(this, parserContext));
22
+ };
23
+ return ChooseCombinator;
24
+ }());
25
+ exports.ChooseCombinator = ChooseCombinator;