tarsec 0.1.0 → 0.1.2

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.
@@ -142,12 +142,22 @@ export declare function between<O, C, P>(open: Parser<O>, close: Parser<C>, pars
142
142
  export declare function between1<O, C, P>(open: Parser<O>, close: Parser<C>, parser: Parser<P>): Parser<P[]>;
143
143
  /**
144
144
  * Parses many instances of the parser separated by separator.
145
- * The parser needs to succeed at least once, otherwise sepBy fails.
145
+ * The parser will succeed even if it consumes zero instances.
146
+ * To require at least one instance, use `sepBy1`.
146
147
  * @param separator
147
148
  * @param parser
148
149
  * @returns a parser that runs the given parser zero to many times, separated by the separator parser.
149
150
  */
150
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[]>;
151
161
  /**
152
162
  * Convenience function to use as the second argument to `seq` to get all the results from `seq`
153
163
  * @param results
@@ -390,3 +400,41 @@ export declare function manyParsers<const T extends readonly GeneralParser<any,
390
400
  * @returns - An array of results, or a failure.
391
401
  */
392
402
  export declare function and<const T extends readonly GeneralParser<any, any>[]>(...parsers: T): PickParserType<T>;
403
+ /**
404
+ * If any of the parsers fail, this parser throws an error. This can be handy for showing
405
+ * more specific error messages to users. For example, if you have a parser that parses these two statements:
406
+ *
407
+ * ```
408
+ * import "foo";
409
+ * greet "name"
410
+ * ```
411
+ *
412
+ * And the user gives this input:
413
+ *
414
+ * ```
415
+ * import;
416
+ * ```
417
+ *
418
+ * You don't want the parser to fail with a generic message, such as 'all parsers have failed'.
419
+ * You want a more specific error, such as 'expected string after `import` keyword'.
420
+ *
421
+ * You could accomplish that with `throwErrorUnless`:
422
+ *
423
+ * ```ts
424
+ * const parser = seqC(
425
+ * str("import"),
426
+ * captureCaptures(
427
+ * throwErrorUnless(
428
+ * "expected string after `import` keyword",
429
+ * spaces,
430
+ * capture(quotedString, "moduleName")
431
+ * )
432
+ * )
433
+ * )
434
+ *```
435
+
436
+ * @param _message message to fail with
437
+ * @param parsers parsers to run
438
+ * @returns
439
+ */
440
+ export declare function parseError<const T extends readonly GeneralParser<any, any>[]>(_message: string, ...parsers: T): Parser<MergedCaptures<T>>;
@@ -1,5 +1,6 @@
1
1
  import { within } from "./parsers/within.js";
2
- import { trace } from "./trace.js";
2
+ import { TarsecError } from "./tarsecError.js";
3
+ import { getInputStr, trace } from "./trace.js";
3
4
  import { captureSuccess, createTree, failure, isCaptureResult, isSuccess, success, } from "./types.js";
4
5
  import { escape, findAncestorWithNextParser, popMany } from "./utils.js";
5
6
  /**
@@ -300,7 +301,8 @@ export function between1(open, close, parser) {
300
301
  }
301
302
  /**
302
303
  * Parses many instances of the parser separated by separator.
303
- * The parser needs to succeed at least once, otherwise sepBy fails.
304
+ * The parser will succeed even if it consumes zero instances.
305
+ * To require at least one instance, use `sepBy1`.
304
306
  * @param separator
305
307
  * @param parser
306
308
  * @returns a parser that runs the given parser zero to many times, separated by the separator parser.
@@ -312,28 +314,37 @@ export function sepBy(separator, parser) {
312
314
  while (true) {
313
315
  const result = parser(rest);
314
316
  if (!result.success) {
315
- if (results.length === 0) {
316
- return failure(result.message, input);
317
- }
318
- else {
319
- return success(results, rest);
320
- }
317
+ return success(results, rest);
321
318
  }
322
319
  results.push(result.result);
323
320
  rest = result.rest;
324
321
  const sepResult = separator(rest);
325
322
  if (!sepResult.success) {
326
- if (results.length === 0) {
327
- return failure(sepResult.message, input);
328
- }
329
- else {
330
- return success(results, rest);
331
- }
323
+ return success(results, rest);
332
324
  }
333
325
  rest = sepResult.rest;
334
326
  }
335
327
  };
336
328
  }
329
+ /**
330
+ * Parses many instances of the parser separated by separator.
331
+ * The parser needs to succeed at least once, otherwise sepBy fails.
332
+ * To not require at least one instance, use `sepBy`.
333
+ * @param separator
334
+ * @param parser
335
+ * @returns a parser that runs the given parser one to many times, separated by the separator parser.
336
+ */
337
+ export function sepBy1(separator, parser) {
338
+ return (input) => {
339
+ const result = sepBy(separator, parser)(input);
340
+ if (result.success) {
341
+ if (result.result.length === 0) {
342
+ return failure("expected at least one match", input);
343
+ }
344
+ }
345
+ return result;
346
+ };
347
+ }
337
348
  /**
338
349
  * Convenience function to use as the second argument to `seq` to get all the results from `seq`
339
350
  * @param results
@@ -816,3 +827,66 @@ export function and(...parsers) {
816
827
  return results;
817
828
  });
818
829
  }
830
+ /**
831
+ * If any of the parsers fail, this parser throws an error. This can be handy for showing
832
+ * more specific error messages to users. For example, if you have a parser that parses these two statements:
833
+ *
834
+ * ```
835
+ * import "foo";
836
+ * greet "name"
837
+ * ```
838
+ *
839
+ * And the user gives this input:
840
+ *
841
+ * ```
842
+ * import;
843
+ * ```
844
+ *
845
+ * You don't want the parser to fail with a generic message, such as 'all parsers have failed'.
846
+ * You want a more specific error, such as 'expected string after `import` keyword'.
847
+ *
848
+ * You could accomplish that with `throwErrorUnless`:
849
+ *
850
+ * ```ts
851
+ * const parser = seqC(
852
+ * str("import"),
853
+ * captureCaptures(
854
+ * throwErrorUnless(
855
+ * "expected string after `import` keyword",
856
+ * spaces,
857
+ * capture(quotedString, "moduleName")
858
+ * )
859
+ * )
860
+ * )
861
+ *```
862
+
863
+ * @param _message message to fail with
864
+ * @param parsers parsers to run
865
+ * @returns
866
+ */
867
+ export function parseError(_message, ...parsers) {
868
+ return (input) => {
869
+ const result = seqC(...parsers)(input);
870
+ if (result.success) {
871
+ return result;
872
+ }
873
+ else {
874
+ const inputStr = getInputStr();
875
+ const messages = [];
876
+ const prefix = "Near: ";
877
+ if (input.length > 0) {
878
+ const index = inputStr.length - input.length;
879
+ const start = Math.max(0, input.length - 20);
880
+ const end = Math.min(inputStr.length, input.length + 20);
881
+ messages.push(`${prefix}${inputStr.substring(start, end)}`);
882
+ messages.push(`${" ".repeat(index + prefix.length)}^`);
883
+ }
884
+ else {
885
+ messages.push(`${prefix}${input.substring(1, 100)}`);
886
+ }
887
+ messages.push(_message);
888
+ const message = messages.join("\n");
889
+ throw new TarsecError(message);
890
+ }
891
+ };
892
+ }
@@ -0,0 +1,3 @@
1
+ export declare class TarsecError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,6 @@
1
+ export class TarsecError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "TarsecError";
5
+ }
6
+ }
package/dist/trace.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ParserResult, Parser, PlainObject, CaptureParser } from "./types.js";
4
4
  * @param name - debug name for parser
5
5
  * @param result - parser result
6
6
  * @returns - A formatted string that describes the parser's result
7
- */
7
+ */
8
8
  export declare function resultToString<T>(name: string, result: ParserResult<T>): string;
9
9
  /**
10
10
  * This function is used internally with debug mode. Given a parser and a debug name for it,
@@ -111,3 +111,11 @@ export declare function parserDebug(name: string, callback: Function): void;
111
111
  * @param callback - callback to run
112
112
  */
113
113
  export declare function limitSteps(limit: number, callback: Function): void;
114
+ /**
115
+ * Use this function in conjunction with the parseError combinator. Before running your parser,
116
+ * call this function, giving it the entire input. Then, if the parseError combinator throws an error,
117
+ * it will print out a nice message showing exactly what part of the string triggered the error.
118
+ * @param s full string to parse
119
+ */
120
+ export declare function setInputStr(s: string): void;
121
+ export declare function getInputStr(): string;
package/dist/trace.js CHANGED
@@ -16,7 +16,7 @@ let debugMessages = [];
16
16
  * @param name - debug name for parser
17
17
  * @param result - parser result
18
18
  * @returns - A formatted string that describes the parser's result
19
- */
19
+ */
20
20
  export function resultToString(name, result) {
21
21
  if (result.success) {
22
22
  return `✅ ${name} -- match: ${escape(result.result)}, rest: ${escape(result.rest)}`;
@@ -152,3 +152,16 @@ export function limitSteps(limit, callback) {
152
152
  callback();
153
153
  stepLimit = -1;
154
154
  }
155
+ let inputStr = "";
156
+ /**
157
+ * Use this function in conjunction with the parseError combinator. Before running your parser,
158
+ * call this function, giving it the entire input. Then, if the parseError combinator throws an error,
159
+ * it will print out a nice message showing exactly what part of the string triggered the error.
160
+ * @param s full string to parse
161
+ */
162
+ export function setInputStr(s) {
163
+ inputStr = s;
164
+ }
165
+ export function getInputStr() {
166
+ return inputStr;
167
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tarsec",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A parser combinator library for TypeScript, inspired by Parsec.",
5
5
  "homepage": "https://github.com/egonSchiele/tarsec",
6
6
  "scripts": {