@ptolemy2002/rgx 7.1.0 → 7.3.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/README.md CHANGED
@@ -24,6 +24,7 @@ type RGXToken = RGXNativeToken | RGXLiteralToken | RGXConvertibleToken | RGXToke
24
24
  type RGXClassTokenConstructor = new (...args: unknown[]) => RGXClassToken;
25
25
  type RGXGroupedToken = RGXToken[] | RGXLiteralToken | RGXGroupedConvertibleToken;
26
26
  type RGXGroupedConvertibleToken = (RGXConvertibleToken & { readonly rgxIsGroup: true }) | (Omit<RGXConvertibleToken, "toRgx"> & { toRgx: () => RGXGroupedToken, readonly rgxGroupWrap: true });
27
+ type RGXRepeatableConvertibleToken = RGXConvertibleToken & { readonly rgxIsRepeatable: true | undefined };
27
28
 
28
29
  const validRegexSymbol = Symbol('rgx.ValidRegex');
29
30
  type ValidRegexBrandSymbol = typeof validRegexSymbol;
@@ -45,7 +46,7 @@ type ValidIdentifier = Branded<string, [ValidIdentifierBrandSymbol]>;
45
46
 
46
47
  type RGXTokenType = 'no-op' | 'literal' | 'native' | 'convertible' | 'class' | RGXTokenType[];
47
48
  type RGXTokenTypeFlat = Exclude<RGXTokenType, RGXTokenType[]> | "array";
48
- type RGXTokenTypeGuardInput = RGXTokenTypeFlat | null | RGXClassTokenConstructor | typeof RegExp | typeof ExtRegExp | typeof RGXTokenCollection | RGXTokenTypeGuardInput[];
49
+ type RGXTokenTypeGuardInput = "repeatable" | RGXTokenTypeFlat | null | RGXClassTokenConstructor | typeof RegExp | typeof ExtRegExp | typeof RGXTokenCollection | RGXTokenTypeGuardInput[];
49
50
  type RGXTokenFromType<T extends RGXTokenTypeGuardInput> =
50
51
  // Maps token type strings to their corresponding types, e.g.:
51
52
  // 'no-op' -> RGXNoOpToken, 'literal' -> RGXLiteralToken, etc.
@@ -100,6 +101,8 @@ type RGXPartOptions<R, T=string> = {
100
101
  type RGXWalkerOptions<R> = {
101
102
  startingSourcePosition?: number;
102
103
  reduced?: R;
104
+ infinite?: boolean;
105
+ looping?: boolean;
103
106
  };
104
107
 
105
108
  type RGXWOptions<R = unknown> = Omit<RGXWalkerOptions<R>, "startingSourcePosition"> & {
@@ -657,6 +660,8 @@ constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalker
657
660
  - `options` (`RGXWalkerOptions<R>`, optional): Configuration options. Defaults to `{}`.
658
661
  - `startingSourcePosition` (`number`, optional): The starting index in the source string. Defaults to `0`.
659
662
  - `reduced` (`R`, optional): The initial value for the `reduced` accumulator. Defaults to `null`.
663
+ - `infinite` (`boolean`, optional): When `true`, the walker stays at the last token indefinitely rather than stopping when the token collection is exhausted, continuing to match the last token until the source is consumed. Defaults to `false`.
664
+ - `looping` (`boolean`, optional): When `true`, the walker loops back to token position `0` when the token collection is exhausted, continuing to match from the start until the source is consumed. Defaults to `false`.
660
665
 
661
666
  #### Properties
662
667
  - `source` (`string`): The source string being walked (readonly).
@@ -666,6 +671,8 @@ constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalker
666
671
  - `reduced` (`R`): A user-defined accumulator value, typically updated by `RGXPart` callbacks during walking.
667
672
  - `captures` (`RGXCapture[]`): An array of structured capture results recorded during walking. Each entry has a `raw` string, a `value` (the transform result for Parts, or the raw string for plain tokens), `start` and `end` indices in the source string, and an `ownerId` that is the `id` of the Part that produced it (or `null` for captures from plain tokens or parts without ids).
668
673
  - `namedCaptures` (`Record<string, RGXCapture[]>`): An object mapping capture IDs to their corresponding `RGXCapture` results. Only Parts with non-null IDs are included. The captures occur in the same order as they appear in the `captures` array.
674
+ - `infinite` (`boolean`): Whether the walker is in infinite mode — stays at the last token when the token collection is exhausted until the source is consumed.
675
+ - `looping` (`boolean`): Whether the walker is in looping mode — loops back to token position `0` when the token collection is exhausted until the source is consumed.
669
676
  - `stopped` (`boolean`, readonly): Whether the walker has been stopped, either by a Part's `beforeCapture` returning `"stop"` or by calling `stop()` in an `afterCapture` callback.
670
677
 
671
678
  #### Methods
@@ -678,12 +685,12 @@ constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalker
678
685
  - `currentToken() => RGXToken | null`: Returns the token at the current token position, or `null` if at the end.
679
686
  - `remainingSource() => string | null`: Returns the remaining source string from the current position onward, or `null` if the source is fully consumed.
680
687
  - `capture(token: RGXToken) => string`: Resolves the token to a regex, asserts that it matches at the current source position (throwing `RGXRegexNotMatchedAtPositionError` if not), and advances the source position by the match length. Returns the matched string.
681
- - `step() => RGXCapture | null`: Steps through the next token in the collection. If the token is an `RGXPart`, calls `beforeCapture` first — if it returns `"stop"`, sets `stopped` and returns `null` without advancing; if `"skip"`, advances the token position and returns `null` without capturing; if `"silent"`, captures but does not add to `captures` or `namedCaptures`. After capturing, validates. After validating, calls `afterCapture` if present. Returns the `RGXCapture` result, or `null` if there are no more tokens, the step was skipped, or the walker was stopped.
688
+ - `step() => RGXCapture | null`: Steps through the next token in the collection. If the token is an `RGXPart`, calls `beforeCapture` first — if it returns `"stop"`, sets `stopped` and returns `null` without advancing; if `"skip"`, advances the token position and returns `null` without capturing; if `"silent"`, captures but does not add to `captures` or `namedCaptures`. After capturing, validates. After validating, calls `afterCapture` if present. Returns the `RGXCapture` result, or `null` if there are no more tokens (or no more source in `infinite`/`looping` mode), the step was skipped, or the walker was stopped.
682
689
  - `stepToToken(predicate: (token: RGXToken) => boolean) => void`: Steps through tokens until the predicate returns `true` for the current token or the walker is stopped. The matching token is not consumed.
683
690
  - `stepToPart(predicate?: (part: RGXPart<R>) => boolean) => void`: Steps through tokens until the next `RGXPart` satisfying the predicate is reached. If already at a Part, steps once first to move past it. The matching Part is not consumed.
684
- - `walk() => void`: Steps through all remaining tokens until the end of the token collection or the walker is stopped.
691
+ - `walk() => void`: Steps through all remaining tokens until the end of the token collection (or until the source is consumed in `infinite`/`looping` mode) or the walker is stopped.
685
692
  - `toRgx() => RGXToken`: Returns the internal `RGXTokenCollection`, allowing the walker to be used as a convertible token.
686
- - `clone(depth: CloneDepth = "max") => RGXWalker`: Creates a clone of the walker. When `depth` is `0`, returns `this`; otherwise, creates a new `RGXWalker` with cloned tokens, source position, reduced value, captures, and stopped state.
693
+ - `clone(depth: CloneDepth = "max") => RGXWalker`: Creates a clone of the walker. When `depth` is `0`, returns `this`; otherwise, creates a new `RGXWalker` with cloned tokens, source position, reduced value, captures, stopped state, and the `infinite`/`looping` flags.
687
694
 
688
695
  ## Functions
689
696
  The following functions are exported by the library:
@@ -905,7 +912,7 @@ Converts an `RGXTokenType` to its flat equivalent `RGXTokenTypeFlat`. If the typ
905
912
  function rgxTokenTypeGuardInputToFlat(type: RGXTokenTypeGuardInput): RGXTokenTypeFlat | null
906
913
  ```
907
914
 
908
- Converts an `RGXTokenTypeGuardInput` to its flat equivalent. If the type is `null`, it returns `null`; if it is an array, it returns `'array'`; if it is an `RGXClassTokenConstructor` (a constructor for an `RGXClassToken` subclass), it returns `'class'` (making it slightly lossy in that case); otherwise, it returns the type as-is.
915
+ Converts an `RGXTokenTypeGuardInput` to its flat equivalent. If the type is `null`, it returns `null`; if it is an array, it returns `'array'`; if it is a RegEx constructor, it returns `'literal`; if it is the `RGXTokenCollection` constructor, it returns `'convertible'`; if it is an `RGXClassTokenConstructor` (a constructor for an `RGXClassToken` subclass), it returns `'class'` (making it slightly lossy in that case); otherwise, it returns the type as-is.
909
916
 
910
917
  #### Parameters
911
918
  - `type` (`RGXTokenTypeGuardInput`): The type guard input to convert.
@@ -924,6 +931,8 @@ When `type` is a constructor, it performs an `instanceof` check against that spe
924
931
 
925
932
  When `type` is an array, it checks that every element of the value array is a valid RGX token matching the corresponding type in the `type` array. If `matchLength` is `true` (the default), it also requires that the value array has the same length as the type array; if `false`, it allows the value array to be longer than the type array, as long as all elements up to the length of the type array match and all elements after that are still valid RGX tokens of any type.
926
933
 
934
+ When `type` is `"repeatable"`, it passes for any token that is not convertible, then checks if `rgxIsRepeatable` is `true | undefined` for convertible tokens.
935
+
927
936
  #### Parameters
928
937
  - `value` (`unknown`): The value to check.
929
938
  - `type` (`T`, optional): The token type to check against. Can be a token type string, `null` (checks against all token types), an `RGXClassTokenConstructor` (checks via `instanceof`), or an array of these. Defaults to `null`.
@@ -32,7 +32,7 @@ class RGXRepeatToken extends base_1.RGXClassToken {
32
32
  return this._token;
33
33
  }
34
34
  set token(value) {
35
- if ((0, typeGuards_1.isRGXToken)(value, "convertible") && !value.rgxIsRepeatable) {
35
+ if ((0, typeGuards_1.isRGXToken)(value, "convertible") && !(0, typeGuards_1.isRGXToken)(value, "repeatable")) {
36
36
  if ((0, typeGuards_1.isRGXToken)(value, "class")) {
37
37
  throw new errors_1.RGXNotSupportedError(`Repeating ${value.constructor.name} tokens`, "The token was manually marked as non-repeatable.");
38
38
  }
package/dist/constants.js CHANGED
@@ -61,6 +61,13 @@ defineRGXConstant("start", {
61
61
  return /^/;
62
62
  }
63
63
  });
64
+ defineRGXConstant("line-start", {
65
+ rgxGroupWrap: false,
66
+ rgxIsRepeatable: false,
67
+ toRgx() {
68
+ return /^/m;
69
+ }
70
+ });
64
71
  defineRGXConstant("end", {
65
72
  rgxGroupWrap: false,
66
73
  rgxIsRepeatable: false,
@@ -68,6 +75,13 @@ defineRGXConstant("end", {
68
75
  return /$/;
69
76
  }
70
77
  });
78
+ defineRGXConstant("line-end", {
79
+ rgxGroupWrap: false,
80
+ rgxIsRepeatable: false,
81
+ toRgx() {
82
+ return /$/m;
83
+ }
84
+ });
71
85
  defineRGXConstant("word-bound", {
72
86
  rgxGroupWrap: false,
73
87
  rgxIsRepeatable: false,
@@ -65,6 +65,8 @@ const e = __importStar(require("./errors"));
65
65
  const is_callable_1 = __importDefault(require("is-callable"));
66
66
  const internal_1 = require("./internal");
67
67
  const class_1 = require("./class");
68
+ const ExtRegExp_1 = require("./ExtRegExp");
69
+ const collection_1 = require("./collection");
68
70
  function isRGXNoOpToken(value) {
69
71
  return value === null || value === undefined;
70
72
  }
@@ -171,8 +173,14 @@ function rgxTokenTypeGuardInputToFlat(type) {
171
173
  return null;
172
174
  if (Array.isArray(type))
173
175
  return 'array';
176
+ if (type === RegExp || type === ExtRegExp_1.ExtRegExp)
177
+ return 'literal';
178
+ if (type === collection_1.RGXTokenCollection)
179
+ return 'convertible';
174
180
  if ((0, internal_1.isConstructor)(type))
175
181
  return 'class';
182
+ if (type === "repeatable")
183
+ return null;
176
184
  return type;
177
185
  }
178
186
  function isRGXToken(value, type = null, matchLength = true) {
@@ -192,6 +200,11 @@ function isRGXToken(value, type = null, matchLength = true) {
192
200
  return true;
193
201
  if (typeMatches('convertible') && isRGXConvertibleToken(value))
194
202
  return true;
203
+ // Non-covertible tokens are repeatable by default. Convertible tokens are only non-repeatable if they have the rgxIsRepeatable property set to false.
204
+ if (type === 'repeatable' && !isRGXConvertibleToken(value, false))
205
+ return true;
206
+ if (type === 'repeatable' && isRGXConvertibleToken(value))
207
+ return value.rgxIsRepeatable ?? true;
195
208
  if (typeMatches('array') && isRGXArrayToken(value))
196
209
  return true;
197
210
  if (Array.isArray(type) && Array.isArray(value) && (!matchLength || type.length === value.length)) {
package/dist/types.d.ts CHANGED
@@ -22,10 +22,13 @@ export type RGXGroupedConvertibleToken = (RGXConvertibleToken & {
22
22
  toRgx: () => RGXGroupedToken;
23
23
  readonly rgxGroupWrap: true;
24
24
  });
25
+ export type RGXRepeatableConvertibleToken = RGXConvertibleToken & {
26
+ readonly rgxIsRepeatable: true | undefined;
27
+ };
25
28
  export type RGXTokenType = 'no-op' | 'literal' | 'native' | 'convertible' | 'class' | RGXTokenType[];
26
29
  export type RGXTokenTypeFlat = Exclude<RGXTokenType, RGXTokenType[]> | "array";
27
- export type RGXTokenTypeGuardInput = RGXTokenTypeFlat | null | RGXClassTokenConstructor | typeof RegExp | typeof ExtRegExp | typeof RGXTokenCollection | RGXTokenTypeGuardInput[];
28
- export type RGXTokenFromType<T extends RGXTokenTypeGuardInput> = T extends null ? RGXToken : T extends 'no-op' ? RGXNoOpToken : T extends 'literal' ? RGXLiteralToken : T extends 'native' ? RGXNativeToken : T extends 'convertible' ? RGXConvertibleToken : T extends 'class' ? RGXClassToken : T extends 'array' ? RGXToken[] : T extends new (...args: unknown[]) => infer R ? R : T extends RGXTokenTypeGuardInput[] ? {
30
+ export type RGXTokenTypeGuardInput = "repeatable" | RGXTokenTypeFlat | null | RGXClassTokenConstructor | typeof RegExp | typeof ExtRegExp | typeof RGXTokenCollection | RGXTokenTypeGuardInput[];
31
+ export type RGXTokenFromType<T extends RGXTokenTypeGuardInput> = T extends null ? RGXToken : T extends 'no-op' ? RGXNoOpToken : T extends 'literal' ? RGXLiteralToken : T extends 'native' ? RGXNativeToken : T extends 'convertible' ? RGXConvertibleToken : T extends 'class' ? RGXClassToken : T extends 'array' ? RGXToken[] : T extends 'repeatable' ? Exclude<RGXToken, RGXConvertibleToken> | RGXRepeatableConvertibleToken : T extends new (...args: unknown[]) => infer R ? R : T extends RGXTokenTypeGuardInput[] ? {
29
32
  [K in keyof T]: T[K] extends RGXTokenTypeGuardInput ? RGXTokenFromType<T[K]> : never;
30
33
  } : never;
31
34
  export type RangeObject = {
@@ -5,6 +5,8 @@ import { RGXPart, RGXCapture } from "./part";
5
5
  export type RGXWalkerOptions<R> = {
6
6
  startingSourcePosition?: number;
7
7
  reduced?: R;
8
+ infinite?: boolean;
9
+ looping?: boolean;
8
10
  };
9
11
  export declare class RGXWalker<R> implements RGXConvertibleToken {
10
12
  readonly source: string;
@@ -14,6 +16,8 @@ export declare class RGXWalker<R> implements RGXConvertibleToken {
14
16
  reduced: R;
15
17
  captures: RGXCapture[];
16
18
  namedCaptures: Record<string, RGXCapture[]>;
19
+ infinite: boolean;
20
+ looping: boolean;
17
21
  private _stopped;
18
22
  static check: (value: unknown) => value is RGXWalker<unknown>;
19
23
  static assert: (value: unknown) => asserts value is RGXWalker<unknown>;
@@ -35,6 +35,8 @@ class RGXWalker {
35
35
  this.tokens = new collection_1.RGXTokenCollection(tokens, "concat");
36
36
  this.tokenPosition = 0;
37
37
  this.reduced = options.reduced ?? null;
38
+ this.infinite = options.infinite ?? false;
39
+ this.looping = options.looping ?? false;
38
40
  }
39
41
  stop() {
40
42
  this._stopped = true;
@@ -73,8 +75,15 @@ class RGXWalker {
73
75
  return match;
74
76
  }
75
77
  step() {
76
- if (this.atTokenEnd())
78
+ if (!this.infinite && !this.looping && this.atTokenEnd()) {
79
+ this._stopped = true;
80
+ return null;
81
+ }
82
+ // If we're infinite, we need to manually stop when all is exhausted.
83
+ if ((this.infinite || this.looping) && this.atSourceEnd()) {
84
+ this._stopped = true;
77
85
  return null;
86
+ }
78
87
  const token = this.currentToken();
79
88
  const isPart = token instanceof part_1.RGXPart;
80
89
  let silent = false;
@@ -116,7 +125,12 @@ class RGXWalker {
116
125
  if (isPart) {
117
126
  token.afterCapture?.(captureResult, token, this);
118
127
  }
119
- this.tokenPosition++;
128
+ if (!this.infinite || this.tokenPosition < this.tokens.length - 1) {
129
+ this.tokenPosition++;
130
+ if (this.looping && this.atTokenEnd()) {
131
+ this.tokenPosition = 0;
132
+ }
133
+ }
120
134
  return captureResult;
121
135
  }
122
136
  stepToToken(predicate) {
@@ -153,7 +167,9 @@ class RGXWalker {
153
167
  return this;
154
168
  const clone = new RGXWalker(this.source, this.tokens.clone((0, immutability_utils_1.depthDecrement)(1)), {
155
169
  startingSourcePosition: this.sourcePosition,
156
- reduced: (0, immutability_utils_1.extClone)(this.reduced, (0, immutability_utils_1.depthDecrement)(1))
170
+ reduced: (0, immutability_utils_1.extClone)(this.reduced, (0, immutability_utils_1.depthDecrement)(1)),
171
+ infinite: this.infinite,
172
+ looping: this.looping
157
173
  });
158
174
  clone._tokenPosition = this.tokenPosition;
159
175
  clone.captures = (0, immutability_utils_1.extClone)(this.captures, (0, immutability_utils_1.depthDecrement)(1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptolemy2002/rgx",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",