@mattwca/little-parser-lib 1.0.2 → 1.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattwca/little-parser-lib",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -6,8 +6,11 @@ import { FailedParserResult, isFailedResult, isSuccessfulResult, ParseFn, Parser
6
6
  /**
7
7
  * Combines multiple parsers in sequence, returning an array of their results.
8
8
  * If one of the parsers fails, the entire sequence fails.
9
+ * @param parsers The parsers to run in sequence.
9
10
  */
10
- export function and(...parsers: ParseFn<any>[]): ParseFn<any[]> {
11
+ export function and<Parsers extends ParseFn<any>[]>(
12
+ ...parsers: Parsers
13
+ ): ParseFn<{ [K in keyof Parsers]: Parsers[K] extends ParseFn<infer R> ? R : never }> {
11
14
  return (tokenStream: TokenStream) => {
12
15
  const results: any[] = [];
13
16
 
@@ -21,7 +24,7 @@ export function and(...parsers: ParseFn<any>[]): ParseFn<any[]> {
21
24
  results.push(parseResult.result);
22
25
  }
23
26
 
24
- return { result: results };
27
+ return { result: results } as any;
25
28
  };
26
29
  }
27
30
 
@@ -92,20 +95,30 @@ export function or<T>(...parsers: ParseFn<T>[]): ParseFn<T> {
92
95
  }
93
96
 
94
97
  /**
95
- * Applies a parser repeatedly until it fails, collecting all successful results into an array.
96
- * If the parser fails on the first attempt, returns a failure.
98
+ * Applies a parser repeatedly until it fails or doesn't make progress (move the position), collecting
99
+ * all successful results into an array. If the parser fails on the first attempt, returns a failure.
97
100
  */
98
- export function many(parser: ParseFn<any>): ParseFn<any> {
101
+ export function many<T>(parser: ParseFn<T>): ParseFn<T[]> {
99
102
  return (tokenStream: TokenStream) => {
100
- const results: any[] = [];
103
+ const results: T[] = [];
101
104
 
102
105
  let parseFailure = null;
103
106
 
104
107
  while (true) {
108
+ const positionBefore = tokenStream.position;
105
109
  tokenStream.storePosition();
106
110
 
107
111
  const result = parser(tokenStream);
112
+
108
113
  if (isSuccessfulResult(result)) {
114
+ const positionAfter = tokenStream.position;
115
+
116
+ // Check if the parser made any progress - if it didn't, we break out to avoid infinite loops.
117
+ if (positionAfter === positionBefore) {
118
+ tokenStream.restorePosition();
119
+ break;
120
+ }
121
+
109
122
  results.push(result.result);
110
123
  tokenStream.clearPosition();
111
124
  } else {