@mattwca/little-parser-lib 1.0.2 → 1.0.3

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.3",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -92,8 +92,8 @@ export function or<T>(...parsers: ParseFn<T>[]): ParseFn<T> {
92
92
  }
93
93
 
94
94
  /**
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.
95
+ * Applies a parser repeatedly until it fails or doesn't make progress (move the position), collecting
96
+ * all successful results into an array. If the parser fails on the first attempt, returns a failure.
97
97
  */
98
98
  export function many(parser: ParseFn<any>): ParseFn<any> {
99
99
  return (tokenStream: TokenStream) => {
@@ -102,10 +102,20 @@ export function many(parser: ParseFn<any>): ParseFn<any> {
102
102
  let parseFailure = null;
103
103
 
104
104
  while (true) {
105
+ const positionBefore = tokenStream.position;
105
106
  tokenStream.storePosition();
106
107
 
107
108
  const result = parser(tokenStream);
109
+
108
110
  if (isSuccessfulResult(result)) {
111
+ const positionAfter = tokenStream.position;
112
+
113
+ // Check if the parser made any progress - if it didn't, we break out to avoid infinite loops.
114
+ if (positionAfter === positionBefore) {
115
+ tokenStream.restorePosition();
116
+ break;
117
+ }
118
+
109
119
  results.push(result.result);
110
120
  tokenStream.clearPosition();
111
121
  } else {