@ptolemy2002/rgx 5.6.0 → 6.0.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 +54 -40
- package/dist/errors/base.d.ts +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/partValidationFailed.d.ts +7 -0
- package/dist/errors/partValidationFailed.js +16 -0
- package/dist/utils/regexMatchAtPosition.js +3 -2
- package/dist/walker/base.d.ts +9 -17
- package/dist/walker/base.js +68 -63
- package/dist/walker/part.d.ts +12 -6
- package/dist/walker/part.js +17 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,21 +79,23 @@ type RGXGroupTokenArgs = {
|
|
|
79
79
|
capturing?: boolean;
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
type
|
|
82
|
+
type RGXPartControl = "skip" | "stop" | "silent" | void;
|
|
83
|
+
|
|
84
|
+
type RGXCapture<T = unknown> = {
|
|
85
|
+
raw: string;
|
|
86
|
+
value: T;
|
|
87
|
+
};
|
|
88
|
+
|
|
83
89
|
type RGXPartOptions<R, T=string> = {
|
|
84
90
|
transform: (captured: string) => T;
|
|
85
|
-
|
|
91
|
+
validate: (captured: RGXCapture<T>, part: RGXPart<R, T>, walker: RGXWalker<R>) => boolean | string;
|
|
92
|
+
beforeCapture: ((part: RGXPart<R, T>, walker: RGXWalker<R>) => RGXPartControl) | null;
|
|
93
|
+
afterCapture: ((capture: RGXCapture<T>, part: RGXPart<R, T>, walker: RGXWalker<R>) => void) | null;
|
|
86
94
|
};
|
|
87
95
|
|
|
88
96
|
type RGXWalkerOptions<R> = {
|
|
89
97
|
startingSourcePosition?: number;
|
|
90
|
-
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
type RGXWalkerFlags = {
|
|
94
|
-
stopped: boolean;
|
|
95
|
-
skipped: boolean;
|
|
96
|
-
nonCapture: boolean;
|
|
98
|
+
reduced?: R;
|
|
97
99
|
};
|
|
98
100
|
```
|
|
99
101
|
|
|
@@ -325,6 +327,21 @@ constructor(message: string, got: number, { min, max, inclusiveLeft, inclusiveRi
|
|
|
325
327
|
- `failedAtMax() => boolean`: Returns `true` if the `got` value is above the maximum bound (respecting `inclusiveRight`), otherwise `false`. Returns `false` if `max` is `null`.
|
|
326
328
|
- `failedAtAny() => boolean`: Returns `true` if the value failed at either the minimum or maximum bound.
|
|
327
329
|
|
|
330
|
+
### RGXPartValidationFailedError extends RGXError
|
|
331
|
+
A specific error class for RGX part validation failures. This error is thrown when a captured value fails validation in a custom part's `validate` function. The error code is set to `PART_VALIDATION_FAILED` on instantiation.
|
|
332
|
+
|
|
333
|
+
#### Constructor
|
|
334
|
+
```typescript
|
|
335
|
+
constructor(message: string, gotRaw: string, gotTransformed: unknown)
|
|
336
|
+
```
|
|
337
|
+
- `message` (`string`): The error message.
|
|
338
|
+
- `gotRaw` (`string`): The raw captured string value that failed validation.
|
|
339
|
+
- `gotTransformed` (`unknown`): The transformed value that was produced by the part's `transform` function, which also failed validation.
|
|
340
|
+
|
|
341
|
+
#### Properties
|
|
342
|
+
- `gotRaw` (`string`): The raw captured string value that failed validation.
|
|
343
|
+
- `gotTransformed` (`unknown`): The transformed value that was produced by the part's `transform` function, which also failed validation.
|
|
344
|
+
|
|
328
345
|
### RGXTokenCollection
|
|
329
346
|
A class representing a collection of RGX tokens. This class manages collections of RGX tokens like an array, but with additional metadata about the collection mode (union or concat). Since `toRgx()` returns a `RegExp`, instances of this class satisfy the `RGXConvertibleToken` interface and can be used directly as tokens in `rgx`, `rgxa`, and other token-accepting functions.
|
|
330
347
|
|
|
@@ -579,7 +596,7 @@ constructor(pattern: string | RegExp, flags?: string)
|
|
|
579
596
|
- `[Symbol.species]` (`RegExpConstructor`): Returns `ExtRegExp`, ensuring that derived `RegExp` methods (like those returning new regex instances) produce `ExtRegExp` instances rather than plain `RegExp`.
|
|
580
597
|
|
|
581
598
|
### RGXPart\<R, T=string\>
|
|
582
|
-
A class that wraps an `RGXToken`
|
|
599
|
+
A class that wraps an `RGXToken` with optional callbacks for use within an `RGXWalker`. It implements `RGXConvertibleToken`, delegating `rgxIsGroup` and `rgxIsRepeatable` to the wrapped token. Unlike plain tokens, Parts can control walker behavior via `beforeCapture` (returning an `RGXPartControl` value) and react to captures via `afterCapture`. Parts are purely definitions and do not store capture state — all captures are stored on the walker as `RGXCapture` objects.
|
|
583
600
|
|
|
584
601
|
A function `rgxPart` is provided with the same parameters as this class' constructor, for easier instantiation without needing to use the `new` keyword.
|
|
585
602
|
|
|
@@ -594,24 +611,25 @@ constructor(token: RGXToken, options?: Partial<RGXPartOptions<R, T>>)
|
|
|
594
611
|
- `token` (`RGXToken`): The token to wrap.
|
|
595
612
|
- `options` (`Partial<RGXPartOptions<R, T>>`, optional): Configuration options. Defaults to `{}`.
|
|
596
613
|
- `transform` (`(captured: string) => T`, optional): A function that transforms the captured string into the desired type `T`. Defaults to an identity function that casts the string to `T`.
|
|
597
|
-
- `
|
|
614
|
+
- `beforeCapture` (`((part: RGXPart<R, T>, walker: RGXWalker<R>) => RGXPartControl) | null`, optional): A callback invoked before capturing this part during walking. Returns an `RGXPartControl` value to control walker behavior: `"skip"` to skip this token without capturing, `"silent"` to capture but not record in `captures`, `"stop"` to halt immediately without capturing or advancing, or `void`/`undefined` to proceed normally. Defaults to `null`.
|
|
615
|
+
- `afterCapture` (`((capture: RGXCapture<T>, part: RGXPart<R, T>, walker: RGXWalker<R>) => void) | null`, optional): A callback invoked after capturing this part during walking. Receives the typed `RGXCapture<T>` result. Can call `walker.stop()` to halt walking after this capture. Defaults to `null`.
|
|
616
|
+
- `validate` (`((capture: RGXCapture<T>, walker: RGXWalker<R>) => boolean | string) | null`, optional): A callback invoked during validation after capturing and transforming, but before `afterCapture`. Returns `true` if validation passes, `false` to fail with a generic error, or a string to fail with that string as the error message. Defaults to `null`.
|
|
598
617
|
|
|
599
618
|
#### Properties
|
|
600
619
|
- `token` (`RGXToken`): The wrapped token.
|
|
601
|
-
- `
|
|
602
|
-
- `
|
|
603
|
-
- `
|
|
604
|
-
- `onEvent` (`((part: RGXPart<R, T>, eventType: RGXPartEventType, walker: RGXWalker<R>) => void) | null`): The event callback, or `null`.
|
|
620
|
+
- `transform` (`(captured: string) => T`, readonly): The transform function used to convert captured strings to values of type `T`.
|
|
621
|
+
- `beforeCapture` (`((part: RGXPart<R, T>, walker: RGXWalker<R>) => RGXPartControl) | null`, readonly): The before-capture callback, or `null`.
|
|
622
|
+
- `afterCapture` (`((capture: RGXCapture<T>, part: RGXPart<R, T>, walker: RGXWalker<R>) => void) | null`, readonly): The after-capture callback, or `null`.
|
|
605
623
|
- `rgxIsGroup` (`boolean`): Delegates to the wrapped token's group status via `isRGXGroupedToken`.
|
|
606
624
|
- `rgxIsRepeatable` (`boolean`): If the wrapped token is an `RGXConvertibleToken`, delegates to its `rgxIsRepeatable` property (defaulting to `true` if not present). Otherwise, returns `true`.
|
|
607
625
|
|
|
608
626
|
#### Methods
|
|
609
|
-
- `triggerEvent(eventType: RGXPartEventType, walker: RGXWalker<R>) => void`: Triggers an event on this part. For `"post-capture"` events, sets `capturedString` to the walker's last captured string and `capturedValue` to the result of `transform`. Then calls the `onEvent` callback if present.
|
|
610
627
|
- `toRgx() => RGXToken`: Returns the wrapped token.
|
|
611
|
-
- `clone(depth: CloneDepth = "max") => RGXPart`: Creates a clone of this part. When `depth` is `0`, returns `this`; otherwise, returns a new `RGXPart` with a cloned token and the same `transform` and `
|
|
628
|
+
- `clone(depth: CloneDepth = "max") => RGXPart`: Creates a clone of this part. When `depth` is `0`, returns `this`; otherwise, returns a new `RGXPart` with a cloned token and the same `transform`, `beforeCapture`, and `afterCapture` references.
|
|
629
|
+
- `validate(capture: RGXCapture<T>, walker: RGXWalker<R>) => void`: A method that calls the inner passed validation logic for this part, if any. If it returns `false`, a generic `RGXPartValidationFailedError` is thrown. If it returns a string, an `RGXPartValidationFailedError` is thrown with that string as the message. If it returns `true`, validation passed. This is called internally by the walker after capturing and transforming a part, before invoking `afterCapture`.
|
|
612
630
|
|
|
613
631
|
### RGXWalker\<R\>
|
|
614
|
-
A class that walks through a sequence of RGX tokens, matching each token against a source string at the current position. It implements `RGXConvertibleToken`, delegating to its internal `RGXTokenCollection`. The walker maintains a source position and a token position, advancing through both as tokens are matched. When an `RGXPart` is encountered, its
|
|
632
|
+
A class that walks through a sequence of RGX tokens, matching each token against a source string at the current position. It implements `RGXConvertibleToken`, delegating to its internal `RGXTokenCollection`. The walker maintains a source position and a token position, advancing through both as tokens are matched. When an `RGXPart` is encountered, its `beforeCapture` callback can control behavior via return values (`RGXPartControl`), and its `afterCapture` callback is invoked with the typed capture result. All captures are stored as structured `RGXCapture` objects on the walker. The generic type `R` represents a user-defined "reduced" value that can accumulate state during walking (e.g., via `RGXPart` callbacks).
|
|
615
633
|
|
|
616
634
|
A function `rgxWalker` is provided with the same parameters as this class' constructor, for easier instantiation without needing to use the `new` keyword.
|
|
617
635
|
|
|
@@ -627,37 +645,33 @@ constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalker
|
|
|
627
645
|
- `tokens` (`RGXTokenCollectionInput`): The tokens to match sequentially. Internally stored as an `RGXTokenCollection` in 'concat' mode.
|
|
628
646
|
- `options` (`RGXWalkerOptions<R>`, optional): Configuration options. Defaults to `{}`.
|
|
629
647
|
- `startingSourcePosition` (`number`, optional): The starting index in the source string. Defaults to `0`.
|
|
630
|
-
- `
|
|
648
|
+
- `reduced` (`R`, optional): The initial value for the `reduced` accumulator. Defaults to `null`.
|
|
631
649
|
|
|
632
650
|
#### Properties
|
|
633
651
|
- `source` (`string`): The source string being walked (readonly).
|
|
634
|
-
- `sourcePosition` (`number`): The current index in the source string. Setting this validates that the value is >= 0 and
|
|
652
|
+
- `sourcePosition` (`number`): The current index in the source string. Range is `[0, source.length]` inclusive, where `source.length` represents "fully consumed". Setting this validates that the value is >= 0 and <= `source.length`, throwing `RGXOutOfBoundsError` if not.
|
|
635
653
|
- `tokens` (`RGXTokenCollection`): The internal collection of tokens in 'concat' mode (readonly).
|
|
636
654
|
- `tokenPosition` (`number`): The current index in the token collection. Setting this validates that the value is >= 0 and <= `tokens.length`, throwing `RGXOutOfBoundsError` if not.
|
|
637
|
-
- `
|
|
638
|
-
- `
|
|
639
|
-
- `
|
|
655
|
+
- `reduced` (`R`): A user-defined accumulator value, typically updated by `RGXPart` callbacks during walking.
|
|
656
|
+
- `captures` (`RGXCapture[]`): An array of structured capture results recorded during walking. Each entry has a `raw` string and a `value` (the transform result for Parts, or the raw string for plain tokens).
|
|
657
|
+
- `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.
|
|
640
658
|
|
|
641
659
|
#### Methods
|
|
642
|
-
- `
|
|
643
|
-
- `stop() => void`: Sets the `stopped` flag to `true`, causing any active `stepToToken`, `stepToPart`, or `walk` loop to halt after the current iteration.
|
|
644
|
-
- `skip() => void`: Sets the `skipped` flag to `true`, causing the current `step` call to skip capturing the current token.
|
|
645
|
-
- `preventCapture() => void`: Sets the `nonCapture` flag to `true`, causing the current `step` call to capture the token but not add the matched string to `capturedStrings`.
|
|
660
|
+
- `stop() => void`: Sets `stopped` to `true`, causing any active `stepToToken`, `stepToPart`, or `walk` loop to halt after the current iteration. Typically called from an `afterCapture` callback to stop walking after the current capture.
|
|
646
661
|
- `atTokenEnd() => boolean`: Returns `true` if the token position is at or past the end of the token collection.
|
|
647
|
-
- `hasNextToken(predicate?: (token: RGXToken) => boolean) => boolean`: Returns `true` if there is a
|
|
648
|
-
- `atSourceEnd() => boolean`: Returns `true` if the source
|
|
649
|
-
- `hasNextSource(predicate?: (rest: string) => boolean) => boolean`: Returns `true` if the source
|
|
650
|
-
- `
|
|
651
|
-
- `
|
|
652
|
-
- `
|
|
653
|
-
- `
|
|
654
|
-
- `
|
|
655
|
-
- `
|
|
656
|
-
- `
|
|
657
|
-
- `
|
|
658
|
-
- `walk() => void`: Steps through all remaining tokens until the end of the token collection.
|
|
662
|
+
- `hasNextToken(predicate?: (token: RGXToken) => boolean) => boolean`: Returns `true` if there is a current token and it satisfies the optional predicate (defaults to `() => true`).
|
|
663
|
+
- `atSourceEnd() => boolean`: Returns `true` if the source has been fully consumed (`sourcePosition >= source.length`).
|
|
664
|
+
- `hasNextSource(predicate?: (rest: string) => boolean) => boolean`: Returns `true` if the source is not fully consumed and the remaining source satisfies the optional predicate (defaults to `() => true`).
|
|
665
|
+
- `lastCapture() => RGXCapture | null`: Returns the last entry in `captures`, or `null` if empty.
|
|
666
|
+
- `currentToken() => RGXToken | null`: Returns the token at the current token position, or `null` if at the end.
|
|
667
|
+
- `remainingSource() => string | null`: Returns the remaining source string from the current position onward, or `null` if the source is fully consumed.
|
|
668
|
+
- `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.
|
|
669
|
+
- `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`. 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.
|
|
670
|
+
- `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.
|
|
671
|
+
- `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.
|
|
672
|
+
- `walk() => void`: Steps through all remaining tokens until the end of the token collection or the walker is stopped.
|
|
659
673
|
- `toRgx() => RGXToken`: Returns the internal `RGXTokenCollection`, allowing the walker to be used as a convertible token.
|
|
660
|
-
- `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,
|
|
674
|
+
- `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.
|
|
661
675
|
|
|
662
676
|
## Functions
|
|
663
677
|
The following functions are exported by the library:
|
package/dist/errors/base.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_REGEX_FLAGS' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED' | 'NOT_SUPPORTED' | 'INVALID_IDENTIFIER' | 'OUT_OF_BOUNDS' | 'INVALID_FLAG_TRANSFORMER_KEY' | 'FLAG_TRANSFORMER_CONFLICT' | 'CONSTANT_CONFLICT' | 'INVALID_CONSTANT_KEY' | 'INSERTION_REJECTED' | 'REGEX_NOT_MATCHED_AT_POSITION';
|
|
1
|
+
export type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_REGEX_FLAGS' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED' | 'NOT_SUPPORTED' | 'INVALID_IDENTIFIER' | 'OUT_OF_BOUNDS' | 'INVALID_FLAG_TRANSFORMER_KEY' | 'FLAG_TRANSFORMER_CONFLICT' | 'CONSTANT_CONFLICT' | 'INVALID_CONSTANT_KEY' | 'INSERTION_REJECTED' | 'REGEX_NOT_MATCHED_AT_POSITION' | 'PART_VALIDATION_FAILED';
|
|
2
2
|
export declare class RGXError extends Error {
|
|
3
3
|
_message: string;
|
|
4
4
|
code: RGXErrorCode;
|
package/dist/errors/index.d.ts
CHANGED
package/dist/errors/index.js
CHANGED
|
@@ -29,3 +29,4 @@ __exportStar(require("./insertionRejected"), exports);
|
|
|
29
29
|
__exportStar(require("./constantConflict"), exports);
|
|
30
30
|
__exportStar(require("./invalidConstantKey"), exports);
|
|
31
31
|
__exportStar(require("./regexNotMatchedAtPosition"), exports);
|
|
32
|
+
__exportStar(require("./partValidationFailed"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RGXPartValidationFailedError = void 0;
|
|
4
|
+
const base_1 = require("./base");
|
|
5
|
+
class RGXPartValidationFailedError extends base_1.RGXError {
|
|
6
|
+
constructor(message, gotRaw, gotTransformed) {
|
|
7
|
+
super(message, 'PART_VALIDATION_FAILED');
|
|
8
|
+
this.name = 'RGXPartValidationFailedError';
|
|
9
|
+
this.gotRaw = gotRaw;
|
|
10
|
+
this.gotTransformed = gotTransformed;
|
|
11
|
+
}
|
|
12
|
+
calcMessage(message) {
|
|
13
|
+
return `${message}; Got: ${this.gotRaw} (transformed: ${JSON.stringify(this.gotTransformed)})`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.RGXPartValidationFailedError = RGXPartValidationFailedError;
|
|
@@ -22,8 +22,9 @@ function doesRegexMatchAtPosition(regex, str, position) {
|
|
|
22
22
|
return regexMatchAtPosition(regex, str, position) !== null;
|
|
23
23
|
}
|
|
24
24
|
function assertRegexMatchesAtPosition(regex, str, position, contextSize = 10) {
|
|
25
|
-
|
|
25
|
+
const result = regexMatchAtPosition(regex, str, position);
|
|
26
|
+
if (result === null) {
|
|
26
27
|
throw new errors_1.RGXRegexNotMatchedAtPositionError("Regex not matched at index", regex, str, position, contextSize);
|
|
27
28
|
}
|
|
28
|
-
return
|
|
29
|
+
return result;
|
|
29
30
|
}
|
package/dist/walker/base.d.ts
CHANGED
|
@@ -1,45 +1,37 @@
|
|
|
1
1
|
import { CloneDepth } from "@ptolemy2002/immutability-utils";
|
|
2
2
|
import { RGXTokenCollection, RGXTokenCollectionInput } from "../collection";
|
|
3
3
|
import { RGXConvertibleToken, RGXToken } from "../types";
|
|
4
|
-
import { RGXPart } from "./part";
|
|
4
|
+
import { RGXPart, RGXCapture } from "./part";
|
|
5
5
|
export type RGXWalkerOptions<R> = {
|
|
6
6
|
startingSourcePosition?: number;
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
export type RGXWalkerFlags = {
|
|
10
|
-
stopped: boolean;
|
|
11
|
-
skipped: boolean;
|
|
12
|
-
nonCapture: boolean;
|
|
7
|
+
reduced?: R;
|
|
13
8
|
};
|
|
14
9
|
export declare class RGXWalker<R> implements RGXConvertibleToken {
|
|
15
10
|
readonly source: string;
|
|
16
11
|
_sourcePosition: number;
|
|
17
12
|
readonly tokens: RGXTokenCollection;
|
|
18
13
|
_tokenPosition: number;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
reduced: R;
|
|
15
|
+
captures: RGXCapture[];
|
|
16
|
+
private _stopped;
|
|
22
17
|
static check: (value: unknown) => value is RGXWalker<unknown>;
|
|
23
18
|
static assert: (value: unknown) => asserts value is RGXWalker<unknown>;
|
|
24
19
|
get sourcePosition(): number;
|
|
25
20
|
set sourcePosition(value: number);
|
|
26
21
|
get tokenPosition(): number;
|
|
27
22
|
set tokenPosition(value: number);
|
|
23
|
+
get stopped(): boolean;
|
|
28
24
|
constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalkerOptions<R>);
|
|
29
|
-
resetFlags(): void;
|
|
30
25
|
stop(): void;
|
|
31
|
-
skip(): void;
|
|
32
|
-
preventCapture(): void;
|
|
33
26
|
atTokenEnd(): boolean;
|
|
34
27
|
hasNextToken(predicate?: (token: RGXToken) => boolean): boolean;
|
|
35
28
|
atSourceEnd(): boolean;
|
|
36
29
|
hasNextSource(predicate?: (rest: string) => boolean): boolean;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
nextToken(): RGXToken;
|
|
30
|
+
lastCapture(): RGXCapture | null;
|
|
31
|
+
currentToken(): RGXToken;
|
|
40
32
|
remainingSource(): string | null;
|
|
41
33
|
capture(token: RGXToken): string;
|
|
42
|
-
step(
|
|
34
|
+
step(): RGXCapture | null;
|
|
43
35
|
stepToToken(predicate: (token: RGXToken) => boolean): void;
|
|
44
36
|
stepToPart(predicate?: (part: RGXPart<R>) => boolean): void;
|
|
45
37
|
walk(): void;
|
package/dist/walker/base.js
CHANGED
|
@@ -13,7 +13,7 @@ class RGXWalker {
|
|
|
13
13
|
return this._sourcePosition;
|
|
14
14
|
}
|
|
15
15
|
set sourcePosition(value) {
|
|
16
|
-
(0, errors_1.assertInRange)(value, { min: 0, max: this.source.length
|
|
16
|
+
(0, errors_1.assertInRange)(value, { min: 0, max: this.source.length }, "sourcePosition is outside the bounds of the source string");
|
|
17
17
|
this._sourcePosition = value;
|
|
18
18
|
}
|
|
19
19
|
get tokenPosition() {
|
|
@@ -23,114 +23,119 @@ class RGXWalker {
|
|
|
23
23
|
(0, errors_1.assertInRange)(value, { min: 0, max: this.tokens.length }, "tokenPosition is outside the bounds of the token collection");
|
|
24
24
|
this._tokenPosition = value;
|
|
25
25
|
}
|
|
26
|
+
get stopped() {
|
|
27
|
+
return this._stopped;
|
|
28
|
+
}
|
|
26
29
|
constructor(source, tokens, options = {}) {
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
stopped: false,
|
|
30
|
-
skipped: false,
|
|
31
|
-
nonCapture: false
|
|
32
|
-
};
|
|
30
|
+
this.captures = [];
|
|
31
|
+
this._stopped = false;
|
|
33
32
|
this.source = source;
|
|
34
33
|
this.sourcePosition = options.startingSourcePosition ?? 0;
|
|
35
34
|
this.tokens = new collection_1.RGXTokenCollection(tokens, "concat");
|
|
36
35
|
this.tokenPosition = 0;
|
|
37
|
-
this.
|
|
38
|
-
}
|
|
39
|
-
resetFlags() {
|
|
40
|
-
this.flags.stopped = false;
|
|
41
|
-
this.flags.skipped = false;
|
|
42
|
-
this.flags.nonCapture = false;
|
|
36
|
+
this.reduced = options.reduced ?? null;
|
|
43
37
|
}
|
|
44
38
|
stop() {
|
|
45
|
-
this.
|
|
46
|
-
}
|
|
47
|
-
skip() {
|
|
48
|
-
this.flags.skipped = true;
|
|
49
|
-
}
|
|
50
|
-
preventCapture() {
|
|
51
|
-
this.flags.nonCapture = true;
|
|
39
|
+
this._stopped = true;
|
|
52
40
|
}
|
|
53
41
|
atTokenEnd() {
|
|
54
42
|
return this.tokenPosition >= this.tokens.length;
|
|
55
43
|
}
|
|
56
44
|
hasNextToken(predicate = () => true) {
|
|
57
|
-
return !this.atTokenEnd() && predicate(this.
|
|
45
|
+
return !this.atTokenEnd() && predicate(this.currentToken());
|
|
58
46
|
}
|
|
59
47
|
atSourceEnd() {
|
|
60
|
-
return this.sourcePosition >= this.source.length
|
|
48
|
+
return this.sourcePosition >= this.source.length;
|
|
61
49
|
}
|
|
62
50
|
hasNextSource(predicate = () => true) {
|
|
63
51
|
return !this.atSourceEnd() && predicate(this.remainingSource());
|
|
64
52
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
getLastCapturedString() {
|
|
69
|
-
if (!this.hasCapturedStrings())
|
|
53
|
+
lastCapture() {
|
|
54
|
+
if (this.captures.length === 0)
|
|
70
55
|
return null;
|
|
71
|
-
return this.
|
|
56
|
+
return this.captures[this.captures.length - 1];
|
|
72
57
|
}
|
|
73
|
-
|
|
74
|
-
if (this.
|
|
58
|
+
currentToken() {
|
|
59
|
+
if (this.atTokenEnd())
|
|
75
60
|
return null;
|
|
76
61
|
return this.tokens.at(this.tokenPosition);
|
|
77
62
|
}
|
|
78
63
|
remainingSource() {
|
|
79
|
-
if (this.
|
|
64
|
+
if (this.atSourceEnd())
|
|
80
65
|
return null;
|
|
81
66
|
return this.source.slice(this.sourcePosition);
|
|
82
67
|
}
|
|
83
68
|
capture(token) {
|
|
84
69
|
const regex = (0, index_1.rgxa)([token]);
|
|
85
70
|
const match = (0, index_1.assertRegexMatchesAtPosition)(regex, this.source, this.sourcePosition);
|
|
86
|
-
|
|
87
|
-
this.capturedStrings.push(match);
|
|
88
|
-
if (this.sourcePosition < this.source.length - match.length)
|
|
89
|
-
this.sourcePosition += match.length;
|
|
90
|
-
else
|
|
91
|
-
this.sourcePosition = this.source.length - 1; // Move to the end of the source if the match goes beyond it
|
|
71
|
+
this.sourcePosition += match.length;
|
|
92
72
|
return match;
|
|
93
73
|
}
|
|
94
|
-
step(
|
|
95
|
-
if (
|
|
96
|
-
this.resetFlags();
|
|
97
|
-
if (!this.hasNextToken())
|
|
74
|
+
step() {
|
|
75
|
+
if (this.atTokenEnd())
|
|
98
76
|
return null;
|
|
99
|
-
const token = this.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
77
|
+
const token = this.currentToken();
|
|
78
|
+
const isPart = token instanceof part_1.RGXPart;
|
|
79
|
+
let silent = false;
|
|
80
|
+
// Ask Part what to do — control flow via return values, not flags.
|
|
81
|
+
if (isPart) {
|
|
82
|
+
const control = token.beforeCapture?.(token, this);
|
|
83
|
+
if (control === "stop") {
|
|
84
|
+
this._stopped = true;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (control === "skip") {
|
|
88
|
+
this.tokenPosition++;
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if (control === "silent") {
|
|
92
|
+
silent = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Capture the match
|
|
96
|
+
const raw = this.capture(token);
|
|
97
|
+
const value = isPart ? token.transform(raw) : raw;
|
|
98
|
+
const captureResult = { raw, value };
|
|
99
|
+
// Validate the part. If validation fails, it will throw an error, so nothing below will run.
|
|
100
|
+
if (isPart) {
|
|
101
|
+
token.validate(captureResult, this);
|
|
102
|
+
}
|
|
103
|
+
// Skip adding the capture if in silent mode.
|
|
104
|
+
if (!silent) {
|
|
105
|
+
this.captures.push(captureResult);
|
|
106
|
+
}
|
|
107
|
+
// Notify Part after capture
|
|
108
|
+
if (isPart) {
|
|
109
|
+
token.afterCapture?.(captureResult, token, this);
|
|
107
110
|
}
|
|
108
111
|
this.tokenPosition++;
|
|
109
|
-
return
|
|
112
|
+
return captureResult;
|
|
110
113
|
}
|
|
111
114
|
stepToToken(predicate) {
|
|
112
115
|
while (this.hasNextToken()) {
|
|
113
|
-
this.
|
|
114
|
-
if (predicate(this.
|
|
116
|
+
this._stopped = false;
|
|
117
|
+
if (predicate(this.currentToken()))
|
|
115
118
|
break;
|
|
116
|
-
this.step(
|
|
117
|
-
if (this.
|
|
119
|
+
this.step();
|
|
120
|
+
if (this._stopped)
|
|
118
121
|
break;
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
stepToPart(predicate = () => true) {
|
|
122
|
-
// If
|
|
123
|
-
|
|
125
|
+
// If currently at a Part, step past it first so repeated
|
|
126
|
+
// calls advance to the next Part rather than getting stuck.
|
|
127
|
+
if (this.currentToken() instanceof part_1.RGXPart) {
|
|
128
|
+
this._stopped = false;
|
|
124
129
|
this.step();
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
if (this._stopped)
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
127
133
|
this.stepToToken(token => token instanceof part_1.RGXPart && predicate(token));
|
|
128
134
|
}
|
|
129
135
|
walk() {
|
|
130
136
|
return this.stepToToken(() => false);
|
|
131
137
|
}
|
|
132
|
-
// When used as a convertible token,
|
|
133
|
-
// a collection.
|
|
138
|
+
// When used as a convertible token, treat the walker as its collection.
|
|
134
139
|
toRgx() {
|
|
135
140
|
return this.tokens;
|
|
136
141
|
}
|
|
@@ -140,11 +145,11 @@ class RGXWalker {
|
|
|
140
145
|
return this;
|
|
141
146
|
const clone = new RGXWalker(this.source, this.tokens.clone((0, immutability_utils_1.depthDecrement)(1)), {
|
|
142
147
|
startingSourcePosition: this.sourcePosition,
|
|
143
|
-
|
|
148
|
+
reduced: (0, immutability_utils_1.extClone)(this.reduced, (0, immutability_utils_1.depthDecrement)(1))
|
|
144
149
|
});
|
|
145
150
|
clone._tokenPosition = this.tokenPosition;
|
|
146
|
-
clone.
|
|
147
|
-
clone.
|
|
151
|
+
clone.captures = (0, immutability_utils_1.extClone)(this.captures, (0, immutability_utils_1.depthDecrement)(1));
|
|
152
|
+
clone._stopped = this._stopped;
|
|
148
153
|
return clone;
|
|
149
154
|
}
|
|
150
155
|
}
|
package/dist/walker/part.d.ts
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { RGXConvertibleToken, RGXToken } from "../types";
|
|
2
2
|
import type { RGXWalker } from "./base";
|
|
3
3
|
import { CloneDepth } from "@ptolemy2002/immutability-utils";
|
|
4
|
-
export type
|
|
4
|
+
export type RGXPartControl = "skip" | "stop" | "silent" | void;
|
|
5
|
+
export type RGXCapture<T = unknown> = {
|
|
6
|
+
raw: string;
|
|
7
|
+
value: T;
|
|
8
|
+
};
|
|
5
9
|
export type RGXPartOptions<R, T = string> = {
|
|
6
10
|
transform: (captured: string) => T;
|
|
7
|
-
|
|
11
|
+
validate: (captured: RGXCapture<T>, part: RGXPart<R, T>, walker: RGXWalker<R>) => boolean | string;
|
|
12
|
+
beforeCapture: ((part: RGXPart<R, T>, walker: RGXWalker<R>) => RGXPartControl) | null;
|
|
13
|
+
afterCapture: ((capture: RGXCapture<T>, part: RGXPart<R, T>, walker: RGXWalker<R>) => void) | null;
|
|
8
14
|
};
|
|
9
15
|
export declare class RGXPart<R, T = string> implements RGXConvertibleToken {
|
|
10
16
|
token: RGXToken;
|
|
11
|
-
capturedString: string | null;
|
|
12
|
-
capturedValue: T | null;
|
|
13
17
|
readonly transform: RGXPartOptions<R, T>["transform"];
|
|
14
|
-
readonly
|
|
18
|
+
private readonly _validate;
|
|
19
|
+
readonly beforeCapture: RGXPartOptions<R, T>["beforeCapture"];
|
|
20
|
+
readonly afterCapture: RGXPartOptions<R, T>["afterCapture"];
|
|
15
21
|
static check: (value: unknown) => value is RGXPart<unknown, unknown>;
|
|
16
22
|
static assert: (value: unknown) => asserts value is RGXPart<unknown, unknown>;
|
|
17
23
|
constructor(token: RGXToken, options?: Partial<RGXPartOptions<R, T>>);
|
|
18
|
-
|
|
24
|
+
validate(capture: RGXCapture<T>, walker: RGXWalker<R>): void;
|
|
19
25
|
get rgxIsGroup(): boolean;
|
|
20
26
|
get rgxIsRepeatable(): boolean;
|
|
21
27
|
toRgx(): RGXToken;
|
package/dist/walker/part.js
CHANGED
|
@@ -6,23 +6,23 @@ const typeGuards_1 = require("../typeGuards");
|
|
|
6
6
|
const internal_1 = require("../internal");
|
|
7
7
|
const clone_1 = require("../clone");
|
|
8
8
|
const immutability_utils_1 = require("@ptolemy2002/immutability-utils");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
// A Part is purely a definition: a token + optional callbacks.
|
|
11
|
+
// It does NOT store capture state — that lives on the walker.
|
|
9
12
|
class RGXPart {
|
|
10
13
|
constructor(token, options = {}) {
|
|
11
|
-
this.capturedString = null;
|
|
12
|
-
this.capturedValue = null;
|
|
13
14
|
this.token = token;
|
|
14
15
|
this.transform = options.transform ?? ((captured) => captured);
|
|
15
|
-
this.
|
|
16
|
+
this._validate = options.validate ?? (() => true);
|
|
17
|
+
this.beforeCapture = options.beforeCapture ?? null;
|
|
18
|
+
this.afterCapture = options.afterCapture ?? null;
|
|
16
19
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
this.onEvent?.(this, eventType, walker);
|
|
20
|
+
validate(capture, walker) {
|
|
21
|
+
const result = this._validate(capture, this, walker);
|
|
22
|
+
if (result === true)
|
|
23
|
+
return;
|
|
24
|
+
const message = typeof result === "string" ? result : "Part Validation Failed";
|
|
25
|
+
throw new errors_1.RGXPartValidationFailedError(message, capture.raw, capture.value);
|
|
26
26
|
}
|
|
27
27
|
// Properties used for conversion to an RGXToken
|
|
28
28
|
get rgxIsGroup() {
|
|
@@ -41,7 +41,11 @@ class RGXPart {
|
|
|
41
41
|
clone(depth = "max") {
|
|
42
42
|
if (depth === 0)
|
|
43
43
|
return this;
|
|
44
|
-
return new RGXPart((0, clone_1.cloneRGXToken)(this.token, (0, immutability_utils_1.depthDecrement)(depth, 1)), {
|
|
44
|
+
return new RGXPart((0, clone_1.cloneRGXToken)(this.token, (0, immutability_utils_1.depthDecrement)(depth, 1)), {
|
|
45
|
+
transform: this.transform,
|
|
46
|
+
beforeCapture: this.beforeCapture,
|
|
47
|
+
afterCapture: this.afterCapture,
|
|
48
|
+
});
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
exports.RGXPart = RGXPart;
|