tarsec 0.0.15 → 0.0.16

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.
@@ -1,4 +1,4 @@
1
- import { CaptureParser, GeneralParser, InferManyReturnType, MergedCaptures, MergedResults, Parser, PickParserType, PlainObject } from "./types.js";
1
+ import { CaptureParser, GeneralParser, InferManyReturnType, MergedCaptures, MergedResults, Parser, ParserResult, PickParserType, PickParserTypeArray, PlainObject } from "./types.js";
2
2
  /**
3
3
  * Takes a parser and runs it zero or more times, returning the results as an array.
4
4
  * If the parser is a capture parser, it returns the captures as an array in this form:
@@ -260,3 +260,19 @@ export declare function seqC<const T extends readonly GeneralParser<any, any>[]>
260
260
  * @returns - true if the parser matches the input and consumes all input, false otherwise
261
261
  */
262
262
  export declare function match(input: string, parser: GeneralParser<any, any>): boolean;
263
+ export declare function ifElse<const IF extends GeneralParser<any, any, I>, const ELSE extends GeneralParser<any, any, I>, I = string>(condition: boolean, ifParser: IF, elseParser: ELSE): IF | ELSE;
264
+ /**
265
+ * Apply multiple parsers to the same input and collect all the results.
266
+ * Consumes no input.
267
+ *
268
+ * @param parsers - parsers to try
269
+ * @returns
270
+ */
271
+ export declare function manyParsers<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): Parser<ParserResult<MergedResults<T>>[]>;
272
+ /**
273
+ * Runs all the given parsers. If they all succeed, returns their results as an array.
274
+ * Otherwise fails. Consumes no input.
275
+ * @param parsers - parsers to try
276
+ * @returns - An array of results, or a failure.
277
+ */
278
+ export declare function and<const I, const T extends readonly GeneralParser<any, any>[]>(...parsers: T): PickParserTypeArray<T, I>;
@@ -1,6 +1,6 @@
1
1
  import { within } from "./parsers/within.js";
2
2
  import { trace } from "./trace.js";
3
- import { captureSuccess, createTree, failure, isCaptureResult, success, } from "./types.js";
3
+ import { captureSuccess, createTree, failure, isCaptureResult, isSuccess, success, } from "./types.js";
4
4
  import { escape, findAncestorWithNextParser, popMany } from "./utils.js";
5
5
  /**
6
6
  * Takes a parser and runs it zero or more times, returning the results as an array.
@@ -615,3 +615,50 @@ export function match(input, parser) {
615
615
  const result = parser(input);
616
616
  return result.success && result.rest === "";
617
617
  }
618
+ export function ifElse(condition, ifParser, elseParser) {
619
+ return trace(`ifElse(${escape(condition)})`, (input) => {
620
+ if (condition) {
621
+ return ifParser(input);
622
+ }
623
+ return elseParser(input);
624
+ });
625
+ }
626
+ /**
627
+ * Apply multiple parsers to the same input and collect all the results.
628
+ * Consumes no input.
629
+ *
630
+ * @param parsers - parsers to try
631
+ * @returns
632
+ */
633
+ export function manyParsers(...parsers) {
634
+ return trace(`manyParsers()`, (input) => {
635
+ const results = [];
636
+ for (let i = 0; i < parsers.length; i++) {
637
+ let result = parsers[i](input);
638
+ results.push(result);
639
+ }
640
+ if (results.some(isSuccess)) {
641
+ return success(results, input);
642
+ }
643
+ return failure("no parsers succeeded", input);
644
+ });
645
+ }
646
+ /**
647
+ * Runs all the given parsers. If they all succeed, returns their results as an array.
648
+ * Otherwise fails. Consumes no input.
649
+ * @param parsers - parsers to try
650
+ * @returns - An array of results, or a failure.
651
+ */
652
+ export function and(...parsers) {
653
+ return trace(`and()`, (input) => {
654
+ const results = manyParsers(...parsers)(input);
655
+ if (results.success) {
656
+ const successes = results.result.filter(isSuccess);
657
+ if (successes.length === results.result.length) {
658
+ return success(results.result.map((r) => r.result), input);
659
+ }
660
+ return failure("not all parsers succeeded", input);
661
+ }
662
+ return results;
663
+ });
664
+ }
package/dist/parsers.d.ts CHANGED
@@ -143,3 +143,70 @@ export declare function captureRegex<const T extends string[]>(str: string | Reg
143
143
  * @returns
144
144
  */
145
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, I = string>(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<I = string>(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>;
173
+ /**
174
+ * Returns a parser that takes some input, runs the transformer function over it,
175
+ * and returns the result as `rest`, so it can be chained to another parser.
176
+ * It always returns null as its result. Always succeeds.
177
+ *
178
+ * `shape` is useful for modifying the user's input before running parsers over it.
179
+ * For example, here is a parser that takes in a chapter
180
+ * and checks that its title starts with "Once upon a time"
181
+ *
182
+ * ```ts
183
+ * const parser = seqR(
184
+ * shape((c: Chapter) => c.title),
185
+ * istr("Once upon a time"),
186
+ * )
187
+ * );
188
+ * ```
189
+ *
190
+ * Now you might be thinking, why not just use the chapter's title as input?
191
+ * `shape` is most useful when you want to parse multiple properties.
192
+ *
193
+ * ```ts
194
+ * const titleParser = seqR(
195
+ * shape((c: Chapter) => c.title),
196
+ * istr("Once upon a time"),
197
+ * );
198
+ *
199
+ * const textParser = seqR(
200
+ * shape((c: Chapter) => c.text),
201
+ * istr("There was a princess"),
202
+ * );
203
+ *
204
+ * const parser = and(titleParser, textParser);
205
+ * ```
206
+ *
207
+ * `parser` now takes a chapter as input and parses its title and text correctly.
208
+ *
209
+ * @param transformer - function to transform the input
210
+ * @returns a parser that takes some input and runs the transformer function over it
211
+ */
212
+ export declare function shape<const X, const I>(transformer: (item: X) => I): Parser<null>;
package/dist/parsers.js CHANGED
@@ -246,3 +246,96 @@ export function set(key, value) {
246
246
  return captureSuccess(null, input, { [key]: value });
247
247
  });
248
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
+ }
298
+ /**
299
+ * Returns a parser that takes some input, runs the transformer function over it,
300
+ * and returns the result as `rest`, so it can be chained to another parser.
301
+ * It always returns null as its result. Always succeeds.
302
+ *
303
+ * `shape` is useful for modifying the user's input before running parsers over it.
304
+ * For example, here is a parser that takes in a chapter
305
+ * and checks that its title starts with "Once upon a time"
306
+ *
307
+ * ```ts
308
+ * const parser = seqR(
309
+ * shape((c: Chapter) => c.title),
310
+ * istr("Once upon a time"),
311
+ * )
312
+ * );
313
+ * ```
314
+ *
315
+ * Now you might be thinking, why not just use the chapter's title as input?
316
+ * `shape` is most useful when you want to parse multiple properties.
317
+ *
318
+ * ```ts
319
+ * const titleParser = seqR(
320
+ * shape((c: Chapter) => c.title),
321
+ * istr("Once upon a time"),
322
+ * );
323
+ *
324
+ * const textParser = seqR(
325
+ * shape((c: Chapter) => c.text),
326
+ * istr("There was a princess"),
327
+ * );
328
+ *
329
+ * const parser = and(titleParser, textParser);
330
+ * ```
331
+ *
332
+ * `parser` now takes a chapter as input and parses its title and text correctly.
333
+ *
334
+ * @param transformer - function to transform the input
335
+ * @returns a parser that takes some input and runs the transformer function over it
336
+ */
337
+ export function shape(transformer) {
338
+ return trace(`shape()`, (_input) => {
339
+ return success(null, transformer(_input));
340
+ });
341
+ }
package/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /** A generic object type. */
2
2
  export type PlainObject = Record<string, unknown>;
3
3
  /** Represents a parse success with no captures. */
4
- export type ParserSuccess<T> = {
4
+ export type ParserSuccess<T, R = string> = {
5
5
  success: true;
6
6
  result: T;
7
- rest: string;
7
+ rest: R;
8
8
  nextParser?: Parser<any>;
9
9
  };
10
10
  /** Represents a parse success with captures. Notice nextParser is also a CaptureParser. */
@@ -16,27 +16,47 @@ export type CaptureParserSuccess<T, C extends PlainObject> = {
16
16
  nextParser?: CaptureParser<any, any>;
17
17
  };
18
18
  /** Represents a parse failure. */
19
- export type ParserFailure = {
19
+ export type ParserFailure<R = string> = {
20
20
  success: false;
21
- rest: string;
21
+ rest: R;
22
22
  message: string;
23
23
  };
24
- export type ParserResult<T> = ParserSuccess<T> | ParserFailure;
25
- export type CaptureParserResult<T, C extends PlainObject> = CaptureParserSuccess<T, C> | ParserFailure;
26
- /** A parser is any function that takes a string and returns a ParserResult. */
27
- export type Parser<T> = (input: string) => ParserResult<T>;
28
- /** A capture parser is any function that takes a string and returns a CaptureParserResult.
24
+ export type ParserResult<T, R = string> = ParserSuccess<T, R> | ParserFailure<R>;
25
+ export type CaptureParserResult<T, C extends PlainObject, R = string> = CaptureParserSuccess<T, C> | ParserFailure<R>;
26
+ /** A parser is any function that takes an arg and returns a ParserResult. */
27
+ export type Parser<T, I = string> = (input: I) => ParserResult<T>;
28
+ /** A string parser is any function that takes a string and returns a ParserResult. */
29
+ export type StringParser<T> = (input: string) => ParserResult<T>;
30
+ /** A capture parser is any function that takes an arg and returns a CaptureParserResult.
29
31
  * A CaptureParserResult is the same as a ParserResult, except it also includes captures,
30
32
  * i.e. matches selected using `capture`. */
31
- export type CaptureParser<T, C extends PlainObject> = (input: string) => CaptureParserResult<T, C>;
32
- export type GeneralParser<T, C extends PlainObject> = Parser<T> | CaptureParser<T, C>;
33
+ export type CaptureParser<T, C extends PlainObject, I = string> = (input: I) => CaptureParserResult<T, C>;
34
+ /** A string capture parser is any function that takes a string and returns a CaptureParserResult. */
35
+ export type StringCaptureParser<T, C extends PlainObject> = (input: string) => CaptureParserResult<T, C>;
36
+ export type GeneralParser<T, C extends PlainObject, I = string> = Parser<T, I> | CaptureParser<T, C, I>;
37
+ export type GeneralStringParser<T, C extends PlainObject> = Parser<T, string> | CaptureParser<T, C, string>;
33
38
  export declare function isCaptureResult<T, C extends PlainObject>(result: ParserResult<T>): result is CaptureParserSuccess<T, C>;
39
+ /**
40
+ * This typed function is helpful in filtering out the successes
41
+ * from an array of results while preserving type information. For example:
42
+ *
43
+ * ```
44
+ * // type is ParserSuccess[]
45
+ * results.filter(isSuccess);
46
+ *
47
+ * // type is ParserResult[]
48
+ * results.filter(r => r.success);
49
+ * ```
50
+ * @param result - a parser result
51
+ * @returns - true if the result is a success, otherwise false
52
+ */
53
+ export declare function isSuccess<T, R = string>(result: ParserResult<T, R>): result is ParserSuccess<T, R>;
34
54
  /** Convenience function to return a ParserSuccess */
35
- export declare function success<T>(result: T, rest: string): ParserSuccess<T>;
55
+ export declare function success<T, R = string>(result: T, rest: R): ParserSuccess<T, R>;
36
56
  /** Convenience function to return a CaptureParserSuccess */
37
57
  export declare function captureSuccess<T, C extends PlainObject>(result: T, rest: string, captures: C): CaptureParserSuccess<T, C>;
38
58
  /** Convenience function to return a ParserFailure */
39
- export declare function failure(message: string, rest: string): ParserFailure;
59
+ export declare function failure<R = string>(message: string, rest: R): ParserFailure<R>;
40
60
  /** Prettify an intersected type, to make it easier to read. */
41
61
  export type Prettify<T> = {
42
62
  [K in keyof T]: T[K];
@@ -71,7 +91,15 @@ export type HasCaptureParsers<T extends readonly GeneralParser<any, any>[]> = Ex
71
91
  * the result and capture types. This is useful for a combinator like `or`
72
92
  * which is not able to infer its return type correctly.
73
93
  */
74
- export type PickParserType<T extends readonly GeneralParser<any, any>[]> = HasCaptureParsers<T> extends true ? CaptureParser<MergedResults<T>, UnionOfCaptures<T>> : Parser<MergedResults<T>>;
94
+ export type PickParserType<T extends readonly GeneralParser<any, any>[], I = string> = I extends string ? PickParserTypeString<T> : PickParserTypeGeneral<T, I>;
95
+ /** split out to make types more readable */
96
+ export type PickParserTypeGeneral<T extends readonly GeneralParser<any, any>[], I = string> = HasCaptureParsers<T> extends true ? CaptureParser<MergedResults<T>, UnionOfCaptures<T>, I> : Parser<MergedResults<T>, I>;
97
+ export type PickParserTypeString<T extends readonly GeneralParser<any, any>[]> = HasCaptureParsers<T> extends true ? StringCaptureParser<MergedResults<T>, UnionOfCaptures<T>> : StringParser<MergedResults<T>>;
98
+ /**
99
+ * Like `PickParserType`, but the result is an array of the result types,
100
+ * instead of just a union.
101
+ */
102
+ export type PickParserTypeArray<T extends readonly GeneralParser<any, any>[], I = string> = HasCaptureParsers<T> extends true ? CaptureParser<MergedResults<T>, UnionOfCaptures<T>, I> : Parser<MergedResults<T>[], I>;
75
103
  /** This is used to generate a return type for the `many` and `many1` combinators.
76
104
  * Given a parser we want to apply `many` to. Suppose its type is `Parser<string>`.
77
105
  * This will return `Parser<string[]>` as the return type.
package/dist/types.js CHANGED
@@ -1,6 +1,23 @@
1
1
  export function isCaptureResult(result) {
2
2
  return "captures" in result;
3
3
  }
4
+ /**
5
+ * This typed function is helpful in filtering out the successes
6
+ * from an array of results while preserving type information. For example:
7
+ *
8
+ * ```
9
+ * // type is ParserSuccess[]
10
+ * results.filter(isSuccess);
11
+ *
12
+ * // type is ParserResult[]
13
+ * results.filter(r => r.success);
14
+ * ```
15
+ * @param result - a parser result
16
+ * @returns - true if the result is a success, otherwise false
17
+ */
18
+ export function isSuccess(result) {
19
+ return result.success;
20
+ }
4
21
  /** Convenience function to return a ParserSuccess */
5
22
  export function success(result, rest) {
6
23
  return { success: true, result, rest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tarsec",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "A parser combinator library for TypeScript, inspired by Parsec.",
5
5
  "homepage": "https://github.com/egonSchiele/tarsec",
6
6
  "scripts": {
@@ -21,6 +21,7 @@
21
21
  "require": "./dist/index.js"
22
22
  }
23
23
  },
24
+ "type": "module",
24
25
  "types": "./dist/index.d.ts",
25
26
  "keywords": ["parser", "parser combinator", "parsec"],
26
27
  "author": "",