@ptolemy2002/rgx 5.4.0 → 5.6.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 +180 -7
- package/dist/collection.d.ts +2 -2
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +5 -2
- 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/regexNotMatchedAtPosition.d.ts +15 -0
- package/dist/errors/regexNotMatchedAtPosition.js +53 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/internal/taggedTemplateToArray.d.ts +1 -1
- package/dist/internal/taggedTemplateToArray.js +11 -3
- package/dist/utils/regexMatchAtPosition.d.ts +3 -1
- package/dist/utils/regexMatchAtPosition.js +14 -1
- package/dist/walker/base.d.ts +49 -0
- package/dist/walker/base.js +156 -0
- package/dist/walker/index.d.ts +2 -0
- package/dist/walker/index.js +18 -0
- package/dist/walker/part.d.ts +24 -0
- package/dist/walker/part.js +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ type RGXTokenFromType<T extends RGXTokenTypeGuardInput> =
|
|
|
54
54
|
// ... see source for full definition
|
|
55
55
|
;
|
|
56
56
|
|
|
57
|
-
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';
|
|
57
|
+
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';
|
|
58
58
|
|
|
59
59
|
type RangeObject = {
|
|
60
60
|
min?: number | null;
|
|
@@ -78,6 +78,23 @@ type RGXGroupTokenArgs = {
|
|
|
78
78
|
name?: string | null;
|
|
79
79
|
capturing?: boolean;
|
|
80
80
|
};
|
|
81
|
+
|
|
82
|
+
type RGXPartEventType = "pre-capture" | "post-capture";
|
|
83
|
+
type RGXPartOptions<R, T=string> = {
|
|
84
|
+
transform: (captured: string) => T;
|
|
85
|
+
onEvent: ((part: RGXPart<R, T>, eventType: RGXPartEventType, walker: RGXWalker<R>) => void) | null;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
type RGXWalkerOptions<R> = {
|
|
89
|
+
startingSourcePosition?: number;
|
|
90
|
+
reducedCurrent?: R;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
type RGXWalkerFlags = {
|
|
94
|
+
stopped: boolean;
|
|
95
|
+
skipped: boolean;
|
|
96
|
+
nonCapture: boolean;
|
|
97
|
+
};
|
|
81
98
|
```
|
|
82
99
|
|
|
83
100
|
## Classes
|
|
@@ -257,6 +274,31 @@ constructor(reason?: string | null, message?: string | null)
|
|
|
257
274
|
#### Properties
|
|
258
275
|
- `reason` (`string | null`): The reason the insertion was rejected, or `null` if no reason was provided.
|
|
259
276
|
|
|
277
|
+
### RGXRegexNotMatchedAtPositionError extends RGXError
|
|
278
|
+
A specific error class for regex match failures at a given position. This error is thrown when a regex is expected to match at a specific position in a string but does not (e.g., via `assertRegexMatchesAtPosition`). The error code is set to `REGEX_NOT_MATCHED_AT_POSITION` on instantiation.
|
|
279
|
+
|
|
280
|
+
#### Constructor
|
|
281
|
+
```typescript
|
|
282
|
+
constructor(message: string, pattern: RegExp, source: string, position: number, contextSize?: number | null)
|
|
283
|
+
```
|
|
284
|
+
- `message` (`string`): The error message.
|
|
285
|
+
- `pattern` (`RegExp`): The regex pattern that failed to match.
|
|
286
|
+
- `source` (`string`): The string that was being matched against.
|
|
287
|
+
- `position` (`number`): The zero-based index in the source string where the match was expected. Must be >= 0 and < `source.length`, or an `RGXOutOfBoundsError` will be thrown.
|
|
288
|
+
- `contextSize` (`number | null`, optional): The number of characters on each side of the position to include in contextual output. Defaults to `null` (full source shown).
|
|
289
|
+
|
|
290
|
+
#### Properties
|
|
291
|
+
- `pattern` (`RegExp`): The regex pattern that failed to match.
|
|
292
|
+
- `source` (`string`): The string that was being matched against.
|
|
293
|
+
- `position` (`number`): The position where the match was expected. Setting this validates that the value is >= 0 and < `source.length`, throwing `RGXOutOfBoundsError` if not.
|
|
294
|
+
- `contextSize` (`number | null`): The number of characters on each side of the position to include in contextual output, or `null` for the full source.
|
|
295
|
+
|
|
296
|
+
#### Methods
|
|
297
|
+
- `sourceContext() => string`: Returns the relevant portion of the source string around the position. When `contextSize` is `null` or covers the entire string, returns the full source. Otherwise, returns a substring from `max(0, position - contextSize)` to `min(source.length, position + contextSize)`.
|
|
298
|
+
- `hasLeftContext() => boolean`: Returns `true` if the context window starts after the beginning of the source string (i.e., there is truncated content on the left). Returns `false` when `contextSize` is `null`.
|
|
299
|
+
- `hasRightContext() => boolean`: Returns `true` if the context window ends before the end of the source string (i.e., there is truncated content on the right). Returns `false` when `contextSize` is `null`.
|
|
300
|
+
- `hasFullContext() => boolean`: Returns `true` when the full source is shown (neither side is truncated). This is the case when `contextSize` is `null` or when the context window covers the entire source string.
|
|
301
|
+
|
|
260
302
|
### RGXOutOfBoundsError extends RGXError
|
|
261
303
|
A specific error class for out-of-bounds values. This error is thrown when a numeric value falls outside an expected range. The error code is set to `OUT_OF_BOUNDS` on instantiation.
|
|
262
304
|
|
|
@@ -536,6 +578,87 @@ constructor(pattern: string | RegExp, flags?: string)
|
|
|
536
578
|
#### Static Properties
|
|
537
579
|
- `[Symbol.species]` (`RegExpConstructor`): Returns `ExtRegExp`, ensuring that derived `RegExp` methods (like those returning new regex instances) produce `ExtRegExp` instances rather than plain `RegExp`.
|
|
538
580
|
|
|
581
|
+
### RGXPart\<R, T=string\>
|
|
582
|
+
A class that wraps an `RGXToken` and captures the matched string when used within an `RGXWalker`. It implements `RGXConvertibleToken`, delegating `rgxIsGroup` and `rgxIsRepeatable` to the wrapped token. After a walker captures a match for this part, the `capturedString` and `capturedValue` properties are populated, with the latter produced by the `transform` function.
|
|
583
|
+
|
|
584
|
+
A function `rgxPart` is provided with the same parameters as this class' constructor, for easier instantiation without needing to use the `new` keyword.
|
|
585
|
+
|
|
586
|
+
#### Static Properties
|
|
587
|
+
- `check(value: unknown): value is RGXPart`: A type guard that checks if the given value is an instance of `RGXPart`.
|
|
588
|
+
- `assert(value: unknown): asserts value is RGXPart`: An assertion that checks if the given value is an instance of `RGXPart`. If the assertion fails, an `RGXInvalidTokenError` will be thrown.
|
|
589
|
+
|
|
590
|
+
#### Constructor
|
|
591
|
+
```typescript
|
|
592
|
+
constructor(token: RGXToken, options?: Partial<RGXPartOptions<R, T>>)
|
|
593
|
+
```
|
|
594
|
+
- `token` (`RGXToken`): The token to wrap.
|
|
595
|
+
- `options` (`Partial<RGXPartOptions<R, T>>`, optional): Configuration options. Defaults to `{}`.
|
|
596
|
+
- `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
|
+
- `onEvent` (`((part: RGXPart<R, T>, eventType: RGXPartEventType, walker: RGXWalker<R>) => void) | null`, optional): A callback invoked when an event is triggered on this part during walking. Defaults to `null`.
|
|
598
|
+
|
|
599
|
+
#### Properties
|
|
600
|
+
- `token` (`RGXToken`): The wrapped token.
|
|
601
|
+
- `capturedString` (`string | null`): The raw string captured by the walker for this part, or `null` if not yet captured.
|
|
602
|
+
- `capturedValue` (`T | null`): The transformed value produced by applying `transform` to `capturedString`, or `null` if not yet captured.
|
|
603
|
+
- `transform` (`(captured: string) => T`): The transform function used to convert captured strings to values of type `T`.
|
|
604
|
+
- `onEvent` (`((part: RGXPart<R, T>, eventType: RGXPartEventType, walker: RGXWalker<R>) => void) | null`): The event callback, or `null`.
|
|
605
|
+
- `rgxIsGroup` (`boolean`): Delegates to the wrapped token's group status via `isRGXGroupedToken`.
|
|
606
|
+
- `rgxIsRepeatable` (`boolean`): If the wrapped token is an `RGXConvertibleToken`, delegates to its `rgxIsRepeatable` property (defaulting to `true` if not present). Otherwise, returns `true`.
|
|
607
|
+
|
|
608
|
+
#### 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
|
+
- `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 `onEvent` references.
|
|
612
|
+
|
|
613
|
+
### 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 event callbacks are triggered around the capture. The generic type `R` represents a user-defined "reduced" value that can accumulate state during walking (e.g., via `RGXPart` event callbacks).
|
|
615
|
+
|
|
616
|
+
A function `rgxWalker` is provided with the same parameters as this class' constructor, for easier instantiation without needing to use the `new` keyword.
|
|
617
|
+
|
|
618
|
+
#### Static Properties
|
|
619
|
+
- `check(value: unknown): value is RGXWalker`: A type guard that checks if the given value is an instance of `RGXWalker`.
|
|
620
|
+
- `assert(value: unknown): asserts value is RGXWalker`: An assertion that checks if the given value is an instance of `RGXWalker`. If the assertion fails, an `RGXInvalidTokenError` will be thrown.
|
|
621
|
+
|
|
622
|
+
#### Constructor
|
|
623
|
+
```typescript
|
|
624
|
+
constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalkerOptions<R>)
|
|
625
|
+
```
|
|
626
|
+
- `source` (`string`): The string to walk through, matching tokens against.
|
|
627
|
+
- `tokens` (`RGXTokenCollectionInput`): The tokens to match sequentially. Internally stored as an `RGXTokenCollection` in 'concat' mode.
|
|
628
|
+
- `options` (`RGXWalkerOptions<R>`, optional): Configuration options. Defaults to `{}`.
|
|
629
|
+
- `startingSourcePosition` (`number`, optional): The starting index in the source string. Defaults to `0`.
|
|
630
|
+
- `reducedCurrent` (`R`, optional): The initial value for the `reducedCurrent` accumulator. Defaults to `null`.
|
|
631
|
+
|
|
632
|
+
#### Properties
|
|
633
|
+
- `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 < `source.length`, throwing `RGXOutOfBoundsError` if not.
|
|
635
|
+
- `tokens` (`RGXTokenCollection`): The internal collection of tokens in 'concat' mode (readonly).
|
|
636
|
+
- `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
|
+
- `reducedCurrent` (`R`): A user-defined accumulator value, typically updated by `RGXPart` event callbacks during walking.
|
|
638
|
+
- `capturedStrings` (`string[]`): An array of all strings captured during walking (excluding those captured with the `nonCapture` flag set).
|
|
639
|
+
- `flags` (`RGXWalkerFlags`): The current walker flags: `stopped` halts `stepToToken`/`stepToPart`/`walk`, `skipped` causes `step` to skip the current token without capturing, and `nonCapture` prevents the captured string from being added to `capturedStrings`.
|
|
640
|
+
|
|
641
|
+
#### Methods
|
|
642
|
+
- `resetFlags() => void`: Resets all flags (`stopped`, `skipped`, `nonCapture`) to `false`.
|
|
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`.
|
|
646
|
+
- `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 next token and it satisfies the optional predicate (defaults to `() => true`).
|
|
648
|
+
- `atSourceEnd() => boolean`: Returns `true` if the source position is at the last character of the source string.
|
|
649
|
+
- `hasNextSource(predicate?: (rest: string) => boolean) => boolean`: Returns `true` if the source position is not at the end and the remaining source satisfies the optional predicate (defaults to `() => true`).
|
|
650
|
+
- `hasCapturedStrings(minCount?: number) => boolean`: Returns `true` if `capturedStrings` has at least `minCount` entries (defaults to `1`).
|
|
651
|
+
- `getLastCapturedString() => string | null`: Returns the last entry in `capturedStrings`, or `null` if empty.
|
|
652
|
+
- `nextToken() => RGXToken | null`: Returns the token at the current token position, or `null` if at the end.
|
|
653
|
+
- `remainingSource() => string | null`: Returns the remaining source string from the current position onward, or `null` if at the end.
|
|
654
|
+
- `capture(token: RGXToken) => string`: Resolves the token to a regex, asserts that it matches at the current source position (throwing `RGXRegexNotMatchedAtPositionError` if not), captures the matched string (unless `nonCapture` is set), and advances the source position by the match length. Returns the matched string.
|
|
655
|
+
- `step(flagReset?: boolean) => string | null`: Steps through the next token in the collection. If `flagReset` is `true` (the default), resets flags first. If the token is an `RGXPart`, triggers `"pre-capture"` before capturing and `"post-capture"` after (when not skipped or non-capturing). Advances the token position and returns the captured string, or `null` if there are no more tokens or the step was skipped.
|
|
656
|
+
- `stepToToken(predicate: (token: RGXToken) => boolean) => void`: Steps through tokens until the predicate returns `true` for the next token or the walker is stopped. The matching token is not consumed.
|
|
657
|
+
- `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.
|
|
658
|
+
- `walk() => void`: Steps through all remaining tokens until the end of the token collection.
|
|
659
|
+
- `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, captured strings, and flags.
|
|
661
|
+
|
|
539
662
|
## Functions
|
|
540
663
|
The following functions are exported by the library:
|
|
541
664
|
|
|
@@ -950,11 +1073,13 @@ A helper function that resolves an array of RGX tokens and concatenates their re
|
|
|
950
1073
|
|
|
951
1074
|
### rgx
|
|
952
1075
|
```typescript
|
|
953
|
-
function rgx(flags?: string): (strings: TemplateStringsArray, ...tokens: RGXToken[]) => ExtRegExp
|
|
1076
|
+
function rgx(flags?: string, multiline?: boolean): (strings: TemplateStringsArray, ...tokens: RGXToken[]) => ExtRegExp
|
|
954
1077
|
```
|
|
955
1078
|
|
|
956
1079
|
Creates and returns a template tag function that constructs an `ExtRegExp` object from the provided template literal with the provided flags. The template literal can contain RGX tokens, which will be resolved and concatenated with the literal parts to form the final regex pattern. Before constructing the pattern, any convertible token that defines `rgxAcceptInsertion` is checked; if it returns `false` or a string, an `RGXInsertionRejectedError` is thrown with details about the reason and exactly where the rejection occurred.
|
|
957
1080
|
|
|
1081
|
+
When `multiline` is `true` (the default), the literal string parts of the template are processed to strip newlines, trim leading whitespace from each line, and remove empty lines, then joined together. This allows you to write regex patterns across multiple lines in the source code for readability without the newlines and indentation becoming part of the pattern. Only the literal string parts between tokens are affected — interpolated values (tokens) are preserved as-is, including string tokens passed via `${"..."}`. When `multiline` is `false`, all literal string parts are preserved exactly as written, including newlines and whitespace.
|
|
1082
|
+
|
|
958
1083
|
The provided `flags` are passed as `currentFlags` to the resolver, enabling inline modifier groups for any `RegExp` literal tokens whose localizable flags (`i`, `m`, `s`) differ from the parent flags. For example, embedding `/foo/i` in a no-flag context produces `(?i:foo)`, while embedding `/bar/` in an `i`-flag context produces `(?-i:bar)`.
|
|
959
1084
|
|
|
960
1085
|
Example usages:
|
|
@@ -971,11 +1096,26 @@ const pattern3 = rgx()`${beginning}value: ${[word, optionalDigit]}${end}`; // /^
|
|
|
971
1096
|
|
|
972
1097
|
const caseInsensitiveWord = /hello/i;
|
|
973
1098
|
const pattern4 = rgx()`${beginning}${caseInsensitiveWord} world${end}`; // /^(?i:hello) world$/ - "hello" matches case-insensitively via an inline modifier group, while " world" remains case-sensitive
|
|
1099
|
+
|
|
1100
|
+
// Multiline template for readability (multiline is true by default):
|
|
1101
|
+
const pattern5 = rgx()`
|
|
1102
|
+
${beginning}
|
|
1103
|
+
testing ${word}
|
|
1104
|
+
${end}
|
|
1105
|
+
`; // /^testing \w+$/ - same as pattern, but written across multiple lines
|
|
1106
|
+
|
|
1107
|
+
// Preserving literal newlines with multiline mode:
|
|
1108
|
+
const pattern6 = rgx()`
|
|
1109
|
+
foo
|
|
1110
|
+
bar${rgxConstant("newline")}
|
|
1111
|
+
baz
|
|
1112
|
+
`; // /foobar\nbaz/ - the constant newline is preserved, but template newlines are stripped
|
|
974
1113
|
```
|
|
975
1114
|
|
|
976
1115
|
#### Parameters
|
|
977
1116
|
**Direct**
|
|
978
1117
|
- `flags` (`string`, optional): The regex flags to apply to the resulting `ExtRegExp` object (e.g., 'g', 'i', 'm', or custom registered flags). If not provided, no flags will be applied. If provided and not valid regex flags (vanilla or registered custom), an `RGXInvalidRegexFlagsError` will be thrown.
|
|
1118
|
+
- `multiline` (`boolean`, optional): Whether to strip newlines and trim leading whitespace from the literal string parts of the template. Defaults to `true`. When `true`, each literal string part is split by newlines, each line has its leading whitespace trimmed, empty lines are removed, and the remaining lines are joined together. Interpolated tokens (including string tokens via `${"..."}`) are not affected. When `false`, literal string parts are preserved exactly as written.
|
|
979
1119
|
|
|
980
1120
|
**Template Tag**
|
|
981
1121
|
- `strings` (`TemplateStringsArray`): The literal parts of the template string.
|
|
@@ -1270,10 +1410,25 @@ Creates a new `ExtRegExp` from an existing one with additional or replaced flags
|
|
|
1270
1410
|
|
|
1271
1411
|
### regexMatchAtPosition
|
|
1272
1412
|
```typescript
|
|
1273
|
-
function regexMatchAtPosition(regex: RegExp, str: string, position: number):
|
|
1413
|
+
function regexMatchAtPosition(regex: RegExp, str: string, position: number): string | null
|
|
1274
1414
|
```
|
|
1275
1415
|
|
|
1276
|
-
|
|
1416
|
+
Attempts to match the given regular expression at a specific position in the string and returns the matched string, or `null` if there is no match. This is done by creating a sticky (`y` flag) copy of the regex and setting its `lastIndex` to the desired position. The position must be within the bounds of the string (>= 0 and < string length), or an `RGXOutOfBoundsError` will be thrown.
|
|
1417
|
+
|
|
1418
|
+
#### Parameters
|
|
1419
|
+
- `regex` (`RegExp`): The regular expression to match.
|
|
1420
|
+
- `str` (`string`): The string to match against.
|
|
1421
|
+
- `position` (`number`): The zero-based index in the string at which to attempt the match. Must be >= 0 and < `str.length`.
|
|
1422
|
+
|
|
1423
|
+
#### Returns
|
|
1424
|
+
- `string | null`: The matched string if the regex matches at the specified position, otherwise `null`.
|
|
1425
|
+
|
|
1426
|
+
### doesRegexMatchAtPosition
|
|
1427
|
+
```typescript
|
|
1428
|
+
function doesRegexMatchAtPosition(regex: RegExp, str: string, position: number): boolean
|
|
1429
|
+
```
|
|
1430
|
+
|
|
1431
|
+
Tests whether the given regular expression matches at a specific position in the string. This is a boolean wrapper around `regexMatchAtPosition`, returning `true` if the match is non-null.
|
|
1277
1432
|
|
|
1278
1433
|
#### Parameters
|
|
1279
1434
|
- `regex` (`RegExp`): The regular expression to test.
|
|
@@ -1283,6 +1438,22 @@ Tests whether the given regular expression matches at a specific position in the
|
|
|
1283
1438
|
#### Returns
|
|
1284
1439
|
- `boolean`: `true` if the regex matches at the specified position, otherwise `false`.
|
|
1285
1440
|
|
|
1441
|
+
### assertRegexMatchesAtPosition
|
|
1442
|
+
```typescript
|
|
1443
|
+
function assertRegexMatchesAtPosition(regex: RegExp, str: string, position: number, contextSize?: number | null): string
|
|
1444
|
+
```
|
|
1445
|
+
|
|
1446
|
+
Asserts that the given regular expression matches at a specific position in the string, throwing an `RGXRegexNotMatchedAtPositionError` if it does not. On success, returns the matched string.
|
|
1447
|
+
|
|
1448
|
+
#### Parameters
|
|
1449
|
+
- `regex` (`RegExp`): The regular expression to match.
|
|
1450
|
+
- `str` (`string`): The string to match against.
|
|
1451
|
+
- `position` (`number`): The zero-based index in the string at which to assert the match. Must be >= 0 and < `str.length`.
|
|
1452
|
+
- `contextSize` (`number | null`, optional): The number of characters on each side of the position to include in the error's context output. Defaults to `10`.
|
|
1453
|
+
|
|
1454
|
+
#### Returns
|
|
1455
|
+
- `string`: The matched string if the regex matches at the specified position. Throws `RGXRegexNotMatchedAtPositionError` if there is no match.
|
|
1456
|
+
|
|
1286
1457
|
### cloneRGXToken
|
|
1287
1458
|
```typescript
|
|
1288
1459
|
function cloneRGXTokeN<T extends RGXToken>(token: T, depth: CloneDepth="max"): T
|
|
@@ -1350,14 +1521,14 @@ Asserts that an RGX constant with the given name does not exist. If the assertio
|
|
|
1350
1521
|
function defineRGXConstant(name: string, value: RGXToken): RGXToken
|
|
1351
1522
|
```
|
|
1352
1523
|
|
|
1353
|
-
Defines a new RGX constant with the given name and value. Throws an `RGXConstantConflictError` if a constant with the same name already exists.
|
|
1524
|
+
Defines a new RGX constant with the given name and value. If the value is a native token (string, number, boolean, or no-op), it is automatically wrapped in an `RGXClassWrapperToken` before being stored. This ensures that native-valued constants are not stripped by multiline template processing in `rgx`, since only the literal string parts of the template are affected by multiline mode. Throws an `RGXConstantConflictError` if a constant with the same name already exists.
|
|
1354
1525
|
|
|
1355
1526
|
#### Parameters
|
|
1356
1527
|
- `name` (`string`): The name for the constant.
|
|
1357
|
-
- `value` (`RGXToken`): The token value to associate with the name.
|
|
1528
|
+
- `value` (`RGXToken`): The token value to associate with the name. Native tokens are automatically wrapped in `RGXClassWrapperToken`.
|
|
1358
1529
|
|
|
1359
1530
|
#### Returns
|
|
1360
|
-
- `RGXToken`: The value
|
|
1531
|
+
- `RGXToken`: The stored value (after wrapping, if applicable).
|
|
1361
1532
|
|
|
1362
1533
|
### rgxConstant
|
|
1363
1534
|
```typescript
|
|
@@ -1389,6 +1560,8 @@ Deletes an existing RGX constant by name. Throws an `RGXInvalidConstantKeyError`
|
|
|
1389
1560
|
The library defines the following built-in constants, which are available immediately after import. Each can be retrieved via `rgxConstant(name)`.
|
|
1390
1561
|
|
|
1391
1562
|
### Control Characters
|
|
1563
|
+
Since these are defined as native tokens (strings), they are automatically wrapped in `RGXClassWrapperToken` by `defineRGXConstant`, ensuring they are preserved in multiline mode.
|
|
1564
|
+
|
|
1392
1565
|
| Name | Resolves To | Description |
|
|
1393
1566
|
| --- | --- | --- |
|
|
1394
1567
|
| `"newline"` | `\n` | Newline character |
|
package/dist/collection.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { RGXToken, ValidRegexString } from "./types";
|
|
1
|
+
import { RGXConvertibleToken, RGXToken, ValidRegexString } from "./types";
|
|
2
2
|
import { CloneDepth } from "@ptolemy2002/immutability-utils";
|
|
3
3
|
import { Collection } from "@ptolemy2002/ts-utils";
|
|
4
4
|
export type RGXTokenCollectionMode = 'union' | 'concat';
|
|
5
5
|
export type RGXTokenCollectionInput = RGXToken | RGXTokenCollection;
|
|
6
|
-
export declare class RGXTokenCollection implements Collection<RGXToken
|
|
6
|
+
export declare class RGXTokenCollection implements Collection<RGXToken>, RGXConvertibleToken {
|
|
7
7
|
mode: RGXTokenCollectionMode;
|
|
8
8
|
tokens: RGXToken[];
|
|
9
9
|
static check: (value: unknown) => value is RGXTokenCollection;
|
package/dist/constants.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ export declare function listRGXConstants(): string[];
|
|
|
3
3
|
export declare function hasRGXConstant(name: string): boolean;
|
|
4
4
|
export declare function assertHasRGXConstant(name: string): void;
|
|
5
5
|
export declare function assertNotHasRGXConstant(name: string): void;
|
|
6
|
-
export declare function defineRGXConstant(name: string, value: RGXToken): RGXToken;
|
|
6
|
+
export declare function defineRGXConstant(name: string, value: RGXToken): RegExp | import("./types").RGXConvertibleToken | RGXToken[];
|
|
7
7
|
export declare function rgxConstant(name: string): RGXToken;
|
|
8
8
|
export declare function deleteRGXConstant(name: string): void;
|
package/dist/constants.js
CHANGED
|
@@ -7,7 +7,9 @@ exports.assertNotHasRGXConstant = assertNotHasRGXConstant;
|
|
|
7
7
|
exports.defineRGXConstant = defineRGXConstant;
|
|
8
8
|
exports.rgxConstant = rgxConstant;
|
|
9
9
|
exports.deleteRGXConstant = deleteRGXConstant;
|
|
10
|
+
const class_1 = require("./class");
|
|
10
11
|
const errors_1 = require("./errors");
|
|
12
|
+
const typeGuards_1 = require("./typeGuards");
|
|
11
13
|
const rgxConstants = {};
|
|
12
14
|
function listRGXConstants() {
|
|
13
15
|
return Object.keys(rgxConstants);
|
|
@@ -27,8 +29,9 @@ function assertNotHasRGXConstant(name) {
|
|
|
27
29
|
}
|
|
28
30
|
function defineRGXConstant(name, value) {
|
|
29
31
|
assertNotHasRGXConstant(name);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// Not strings themselves so that they aren't removed in multiline mode.
|
|
33
|
+
rgxConstants[name] = (0, typeGuards_1.isRGXNativeToken)(value) ? (0, class_1.rgxClassWrapper)(value) : value;
|
|
34
|
+
return rgxConstants[name];
|
|
32
35
|
}
|
|
33
36
|
function rgxConstant(name) {
|
|
34
37
|
assertHasRGXConstant(name);
|
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';
|
|
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';
|
|
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
|
@@ -28,3 +28,4 @@ __exportStar(require("./notSupported"), exports);
|
|
|
28
28
|
__exportStar(require("./insertionRejected"), exports);
|
|
29
29
|
__exportStar(require("./constantConflict"), exports);
|
|
30
30
|
__exportStar(require("./invalidConstantKey"), exports);
|
|
31
|
+
__exportStar(require("./regexNotMatchedAtPosition"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RGXError } from "./base";
|
|
2
|
+
export declare class RGXRegexNotMatchedAtPositionError extends RGXError {
|
|
3
|
+
pattern: RegExp;
|
|
4
|
+
source: string;
|
|
5
|
+
_position: number;
|
|
6
|
+
contextSize: number | null;
|
|
7
|
+
set position(value: number);
|
|
8
|
+
get position(): number;
|
|
9
|
+
constructor(message: string, pattern: RegExp, source: string, position: number, contextSize?: number | null);
|
|
10
|
+
sourceContext(): string;
|
|
11
|
+
hasLeftContext(): boolean;
|
|
12
|
+
hasRightContext(): boolean;
|
|
13
|
+
hasFullContext(): boolean;
|
|
14
|
+
calcMessage(message: string): string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RGXRegexNotMatchedAtPositionError = void 0;
|
|
4
|
+
const base_1 = require("./base");
|
|
5
|
+
const outOfBounds_1 = require("./outOfBounds");
|
|
6
|
+
class RGXRegexNotMatchedAtPositionError extends base_1.RGXError {
|
|
7
|
+
set position(value) {
|
|
8
|
+
(0, outOfBounds_1.assertInRange)(value, { min: 0, max: this.source.length, inclusiveRight: false }, "position is outside the bounds of the source string");
|
|
9
|
+
this._position = value;
|
|
10
|
+
}
|
|
11
|
+
get position() {
|
|
12
|
+
return this._position;
|
|
13
|
+
}
|
|
14
|
+
constructor(message, pattern, source, position, contextSize = null) {
|
|
15
|
+
super(message, 'REGEX_NOT_MATCHED_AT_POSITION');
|
|
16
|
+
this.name = 'RGXRegexNotMatchedAtPositionError';
|
|
17
|
+
this.pattern = pattern;
|
|
18
|
+
this.source = source;
|
|
19
|
+
this.position = position;
|
|
20
|
+
this.contextSize = contextSize;
|
|
21
|
+
}
|
|
22
|
+
sourceContext() {
|
|
23
|
+
if (this.hasFullContext())
|
|
24
|
+
return this.source;
|
|
25
|
+
const start = Math.max(0, this.position - this.contextSize);
|
|
26
|
+
const end = Math.min(this.source.length, this.position + this.contextSize);
|
|
27
|
+
return this.source.slice(start, end);
|
|
28
|
+
}
|
|
29
|
+
hasLeftContext() {
|
|
30
|
+
if (this.contextSize === null)
|
|
31
|
+
return false;
|
|
32
|
+
return this.position - this.contextSize >= 0;
|
|
33
|
+
}
|
|
34
|
+
hasRightContext() {
|
|
35
|
+
if (this.contextSize === null)
|
|
36
|
+
return false;
|
|
37
|
+
return this.position + this.contextSize <= this.source.length;
|
|
38
|
+
}
|
|
39
|
+
hasFullContext() {
|
|
40
|
+
return !this.hasLeftContext() && !this.hasRightContext();
|
|
41
|
+
}
|
|
42
|
+
calcMessage(message) {
|
|
43
|
+
let context = this.sourceContext();
|
|
44
|
+
if (this.contextSize !== null) {
|
|
45
|
+
if (this.hasLeftContext())
|
|
46
|
+
context = `...${context}`;
|
|
47
|
+
if (this.hasRightContext())
|
|
48
|
+
context = `${context}...`;
|
|
49
|
+
}
|
|
50
|
+
return `${message}; Pattern: ${this.pattern.toString()}, Position: ${this.position}, Context: ${context}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.RGXRegexNotMatchedAtPositionError = RGXRegexNotMatchedAtPositionError;
|
package/dist/index.d.ts
CHANGED
|
@@ -12,5 +12,6 @@ export * from "./ExtRegExp";
|
|
|
12
12
|
export * from "./flag-transformer";
|
|
13
13
|
export * from "./clone";
|
|
14
14
|
export * from "./constants";
|
|
15
|
+
export * from "./walker";
|
|
15
16
|
export declare function rgxa(tokens: t.RGXToken[], flags?: string): ExtRegExp;
|
|
16
|
-
export default function rgx(flags?: string): (strings: TemplateStringsArray, ...tokens: t.RGXToken[]) => ExtRegExp;
|
|
17
|
+
export default function rgx(flags?: string, multiline?: boolean): (strings: TemplateStringsArray, ...tokens: t.RGXToken[]) => ExtRegExp;
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __exportStar(require("./ExtRegExp"), exports);
|
|
|
33
33
|
__exportStar(require("./flag-transformer"), exports);
|
|
34
34
|
__exportStar(require("./clone"), exports);
|
|
35
35
|
__exportStar(require("./constants"), exports);
|
|
36
|
+
__exportStar(require("./walker"), exports);
|
|
36
37
|
// Call this for certain class methods to work correctly
|
|
37
38
|
(0, class_1.rgxClassInit)();
|
|
38
39
|
// Call this for our custom flags to work correctly
|
|
@@ -43,9 +44,9 @@ function rgxa(tokens, flags = '') {
|
|
|
43
44
|
const pattern = (0, concat_1.rgxConcat)(tokens, true, flags);
|
|
44
45
|
return (0, ExtRegExp_1.extRegExp)(pattern, flags);
|
|
45
46
|
}
|
|
46
|
-
function rgx(flags = '') {
|
|
47
|
+
function rgx(flags = '', multiline = true) {
|
|
47
48
|
(0, ExtRegExp_1.assertValidRegexFlags)(flags);
|
|
48
49
|
return (strings, ...tokens) => {
|
|
49
|
-
return rgxa((0, internal_1.taggedTemplateToArray)(strings, tokens), flags);
|
|
50
|
+
return rgxa((0, internal_1.taggedTemplateToArray)(strings, tokens, multiline), flags);
|
|
50
51
|
};
|
|
51
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function taggedTemplateToArray<T>(strings: TemplateStringsArray, tokens: T[]): (string | T)[];
|
|
1
|
+
export declare function taggedTemplateToArray<T>(strings: TemplateStringsArray, tokens: T[], multiline: boolean): (string | T)[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.taggedTemplateToArray = taggedTemplateToArray;
|
|
4
|
-
function taggedTemplateToArray(strings, tokens) {
|
|
4
|
+
function taggedTemplateToArray(strings, tokens, multiline) {
|
|
5
5
|
function isNullOrUndefined(value) {
|
|
6
6
|
return value === null || value === undefined;
|
|
7
7
|
}
|
|
@@ -10,8 +10,16 @@ function taggedTemplateToArray(strings, tokens) {
|
|
|
10
10
|
const string = strings[i];
|
|
11
11
|
const token = tokens[i];
|
|
12
12
|
// Strings always come before tokens
|
|
13
|
-
if (!isNullOrUndefined(string))
|
|
14
|
-
|
|
13
|
+
if (!isNullOrUndefined(string)) {
|
|
14
|
+
if (!multiline) {
|
|
15
|
+
array.push(string);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// Remove all empty lines and trim whitespace from the start of each line.
|
|
19
|
+
let lines = string.split("\n").map(line => line.trimStart()).filter(line => line.length > 0).join("");
|
|
20
|
+
array.push(lines);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
15
23
|
if (!isNullOrUndefined(token))
|
|
16
24
|
array.push(token);
|
|
17
25
|
}
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export declare function regexMatchAtPosition(regex: RegExp, str: string, position: number):
|
|
1
|
+
export declare function regexMatchAtPosition(regex: RegExp, str: string, position: number): string | null;
|
|
2
|
+
export declare function doesRegexMatchAtPosition(regex: RegExp, str: string, position: number): boolean;
|
|
3
|
+
export declare function assertRegexMatchesAtPosition(regex: RegExp, str: string, position: number, contextSize?: number | null): string;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.regexMatchAtPosition = regexMatchAtPosition;
|
|
4
|
+
exports.doesRegexMatchAtPosition = doesRegexMatchAtPosition;
|
|
5
|
+
exports.assertRegexMatchesAtPosition = assertRegexMatchesAtPosition;
|
|
4
6
|
const outOfBounds_1 = require("../errors/outOfBounds");
|
|
5
7
|
const regexWithFlags_1 = require("./regexWithFlags");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
6
9
|
function regexMatchAtPosition(regex, str, position) {
|
|
7
10
|
/*
|
|
8
11
|
The y flag means sticky mode, which means the next match must start at
|
|
@@ -12,5 +15,15 @@ function regexMatchAtPosition(regex, str, position) {
|
|
|
12
15
|
(0, outOfBounds_1.assertInRange)(position, { min: 0, max: str.length, inclusiveRight: false }, 'String index out of bounds');
|
|
13
16
|
const stickyRegex = (0, regexWithFlags_1.regexWithFlags)(regex, "y");
|
|
14
17
|
stickyRegex.lastIndex = position;
|
|
15
|
-
|
|
18
|
+
const match = stickyRegex.exec(str);
|
|
19
|
+
return match ? match[0] : null;
|
|
20
|
+
}
|
|
21
|
+
function doesRegexMatchAtPosition(regex, str, position) {
|
|
22
|
+
return regexMatchAtPosition(regex, str, position) !== null;
|
|
23
|
+
}
|
|
24
|
+
function assertRegexMatchesAtPosition(regex, str, position, contextSize = 10) {
|
|
25
|
+
if (!doesRegexMatchAtPosition(regex, str, position)) {
|
|
26
|
+
throw new errors_1.RGXRegexNotMatchedAtPositionError("Regex not matched at index", regex, str, position, contextSize);
|
|
27
|
+
}
|
|
28
|
+
return regexMatchAtPosition(regex, str, position);
|
|
16
29
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { CloneDepth } from "@ptolemy2002/immutability-utils";
|
|
2
|
+
import { RGXTokenCollection, RGXTokenCollectionInput } from "../collection";
|
|
3
|
+
import { RGXConvertibleToken, RGXToken } from "../types";
|
|
4
|
+
import { RGXPart } from "./part";
|
|
5
|
+
export type RGXWalkerOptions<R> = {
|
|
6
|
+
startingSourcePosition?: number;
|
|
7
|
+
reducedCurrent?: R;
|
|
8
|
+
};
|
|
9
|
+
export type RGXWalkerFlags = {
|
|
10
|
+
stopped: boolean;
|
|
11
|
+
skipped: boolean;
|
|
12
|
+
nonCapture: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare class RGXWalker<R> implements RGXConvertibleToken {
|
|
15
|
+
readonly source: string;
|
|
16
|
+
_sourcePosition: number;
|
|
17
|
+
readonly tokens: RGXTokenCollection;
|
|
18
|
+
_tokenPosition: number;
|
|
19
|
+
reducedCurrent: R;
|
|
20
|
+
capturedStrings: string[];
|
|
21
|
+
flags: RGXWalkerFlags;
|
|
22
|
+
static check: (value: unknown) => value is RGXWalker<unknown>;
|
|
23
|
+
static assert: (value: unknown) => asserts value is RGXWalker<unknown>;
|
|
24
|
+
get sourcePosition(): number;
|
|
25
|
+
set sourcePosition(value: number);
|
|
26
|
+
get tokenPosition(): number;
|
|
27
|
+
set tokenPosition(value: number);
|
|
28
|
+
constructor(source: string, tokens: RGXTokenCollectionInput, options?: RGXWalkerOptions<R>);
|
|
29
|
+
resetFlags(): void;
|
|
30
|
+
stop(): void;
|
|
31
|
+
skip(): void;
|
|
32
|
+
preventCapture(): void;
|
|
33
|
+
atTokenEnd(): boolean;
|
|
34
|
+
hasNextToken(predicate?: (token: RGXToken) => boolean): boolean;
|
|
35
|
+
atSourceEnd(): boolean;
|
|
36
|
+
hasNextSource(predicate?: (rest: string) => boolean): boolean;
|
|
37
|
+
hasCapturedStrings(minCount?: number): boolean;
|
|
38
|
+
getLastCapturedString(): string | null | undefined;
|
|
39
|
+
nextToken(): RGXToken;
|
|
40
|
+
remainingSource(): string | null;
|
|
41
|
+
capture(token: RGXToken): string;
|
|
42
|
+
step(flagReset?: boolean): string | null;
|
|
43
|
+
stepToToken(predicate: (token: RGXToken) => boolean): void;
|
|
44
|
+
stepToPart(predicate?: (part: RGXPart<R>) => boolean): void;
|
|
45
|
+
walk(): void;
|
|
46
|
+
toRgx(): RGXTokenCollection;
|
|
47
|
+
clone(depth?: CloneDepth): RGXWalker<R>;
|
|
48
|
+
}
|
|
49
|
+
export declare function rgxWalker<R>(...args: ConstructorParameters<typeof RGXWalker<R>>): RGXWalker<R>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RGXWalker = void 0;
|
|
4
|
+
exports.rgxWalker = rgxWalker;
|
|
5
|
+
const immutability_utils_1 = require("@ptolemy2002/immutability-utils");
|
|
6
|
+
const collection_1 = require("../collection");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
const part_1 = require("./part");
|
|
9
|
+
const index_1 = require("../index");
|
|
10
|
+
const internal_1 = require("../internal");
|
|
11
|
+
class RGXWalker {
|
|
12
|
+
get sourcePosition() {
|
|
13
|
+
return this._sourcePosition;
|
|
14
|
+
}
|
|
15
|
+
set sourcePosition(value) {
|
|
16
|
+
(0, errors_1.assertInRange)(value, { min: 0, max: this.source.length, inclusiveRight: false }, "sourcePosition is outside the bounds of the source string");
|
|
17
|
+
this._sourcePosition = value;
|
|
18
|
+
}
|
|
19
|
+
get tokenPosition() {
|
|
20
|
+
return this._tokenPosition;
|
|
21
|
+
}
|
|
22
|
+
set tokenPosition(value) {
|
|
23
|
+
(0, errors_1.assertInRange)(value, { min: 0, max: this.tokens.length }, "tokenPosition is outside the bounds of the token collection");
|
|
24
|
+
this._tokenPosition = value;
|
|
25
|
+
}
|
|
26
|
+
constructor(source, tokens, options = {}) {
|
|
27
|
+
this.capturedStrings = [];
|
|
28
|
+
this.flags = {
|
|
29
|
+
stopped: false,
|
|
30
|
+
skipped: false,
|
|
31
|
+
nonCapture: false
|
|
32
|
+
};
|
|
33
|
+
this.source = source;
|
|
34
|
+
this.sourcePosition = options.startingSourcePosition ?? 0;
|
|
35
|
+
this.tokens = new collection_1.RGXTokenCollection(tokens, "concat");
|
|
36
|
+
this.tokenPosition = 0;
|
|
37
|
+
this.reducedCurrent = options.reducedCurrent ?? null;
|
|
38
|
+
}
|
|
39
|
+
resetFlags() {
|
|
40
|
+
this.flags.stopped = false;
|
|
41
|
+
this.flags.skipped = false;
|
|
42
|
+
this.flags.nonCapture = false;
|
|
43
|
+
}
|
|
44
|
+
stop() {
|
|
45
|
+
this.flags.stopped = true;
|
|
46
|
+
}
|
|
47
|
+
skip() {
|
|
48
|
+
this.flags.skipped = true;
|
|
49
|
+
}
|
|
50
|
+
preventCapture() {
|
|
51
|
+
this.flags.nonCapture = true;
|
|
52
|
+
}
|
|
53
|
+
atTokenEnd() {
|
|
54
|
+
return this.tokenPosition >= this.tokens.length;
|
|
55
|
+
}
|
|
56
|
+
hasNextToken(predicate = () => true) {
|
|
57
|
+
return !this.atTokenEnd() && predicate(this.nextToken());
|
|
58
|
+
}
|
|
59
|
+
atSourceEnd() {
|
|
60
|
+
return this.sourcePosition >= this.source.length - 1;
|
|
61
|
+
}
|
|
62
|
+
hasNextSource(predicate = () => true) {
|
|
63
|
+
return !this.atSourceEnd() && predicate(this.remainingSource());
|
|
64
|
+
}
|
|
65
|
+
hasCapturedStrings(minCount = 1) {
|
|
66
|
+
return this.capturedStrings.length >= minCount;
|
|
67
|
+
}
|
|
68
|
+
getLastCapturedString() {
|
|
69
|
+
if (!this.hasCapturedStrings())
|
|
70
|
+
return null;
|
|
71
|
+
return this.capturedStrings[this.capturedStrings.length - 1];
|
|
72
|
+
}
|
|
73
|
+
nextToken() {
|
|
74
|
+
if (this.tokenPosition >= this.tokens.length)
|
|
75
|
+
return null;
|
|
76
|
+
return this.tokens.at(this.tokenPosition);
|
|
77
|
+
}
|
|
78
|
+
remainingSource() {
|
|
79
|
+
if (this.sourcePosition >= this.source.length - 1)
|
|
80
|
+
return null;
|
|
81
|
+
return this.source.slice(this.sourcePosition);
|
|
82
|
+
}
|
|
83
|
+
capture(token) {
|
|
84
|
+
const regex = (0, index_1.rgxa)([token]);
|
|
85
|
+
const match = (0, index_1.assertRegexMatchesAtPosition)(regex, this.source, this.sourcePosition);
|
|
86
|
+
if (!this.flags.nonCapture)
|
|
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
|
|
92
|
+
return match;
|
|
93
|
+
}
|
|
94
|
+
step(flagReset = true) {
|
|
95
|
+
if (flagReset)
|
|
96
|
+
this.resetFlags();
|
|
97
|
+
if (!this.hasNextToken())
|
|
98
|
+
return null;
|
|
99
|
+
const token = this.nextToken();
|
|
100
|
+
if (token instanceof part_1.RGXPart)
|
|
101
|
+
token.triggerEvent("pre-capture", this);
|
|
102
|
+
let captured = null;
|
|
103
|
+
if (!this.flags.skipped) {
|
|
104
|
+
captured = this.capture(token);
|
|
105
|
+
if (token instanceof part_1.RGXPart && !this.flags.nonCapture)
|
|
106
|
+
token.triggerEvent("post-capture", this);
|
|
107
|
+
}
|
|
108
|
+
this.tokenPosition++;
|
|
109
|
+
return captured;
|
|
110
|
+
}
|
|
111
|
+
stepToToken(predicate) {
|
|
112
|
+
while (this.hasNextToken()) {
|
|
113
|
+
this.resetFlags();
|
|
114
|
+
if (predicate(this.nextToken()))
|
|
115
|
+
break;
|
|
116
|
+
this.step(false);
|
|
117
|
+
if (this.flags.stopped)
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
stepToPart(predicate = () => true) {
|
|
122
|
+
// If we're currently at a part, step once so that we can get to the next one.
|
|
123
|
+
if (this.nextToken() instanceof part_1.RGXPart)
|
|
124
|
+
this.step();
|
|
125
|
+
if (this.flags.stopped)
|
|
126
|
+
return;
|
|
127
|
+
this.stepToToken(token => token instanceof part_1.RGXPart && predicate(token));
|
|
128
|
+
}
|
|
129
|
+
walk() {
|
|
130
|
+
return this.stepToToken(() => false);
|
|
131
|
+
}
|
|
132
|
+
// When used as a convertible token, just treat the walker as
|
|
133
|
+
// a collection.
|
|
134
|
+
toRgx() {
|
|
135
|
+
return this.tokens;
|
|
136
|
+
}
|
|
137
|
+
// Clone method
|
|
138
|
+
clone(depth = "max") {
|
|
139
|
+
if (depth === 0)
|
|
140
|
+
return this;
|
|
141
|
+
const clone = new RGXWalker(this.source, this.tokens.clone((0, immutability_utils_1.depthDecrement)(1)), {
|
|
142
|
+
startingSourcePosition: this.sourcePosition,
|
|
143
|
+
reducedCurrent: (0, immutability_utils_1.extClone)(this.reducedCurrent, (0, immutability_utils_1.depthDecrement)(1))
|
|
144
|
+
});
|
|
145
|
+
clone._tokenPosition = this.tokenPosition;
|
|
146
|
+
clone.capturedStrings = (0, immutability_utils_1.extClone)(this.capturedStrings, (0, immutability_utils_1.depthDecrement)(1));
|
|
147
|
+
clone.flags = (0, immutability_utils_1.extClone)(this.flags, (0, immutability_utils_1.depthDecrement)(1));
|
|
148
|
+
return clone;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.RGXWalker = RGXWalker;
|
|
152
|
+
RGXWalker.check = (0, internal_1.createClassGuardFunction)(RGXWalker);
|
|
153
|
+
RGXWalker.assert = (0, internal_1.createAssertClassGuardFunction)(RGXWalker);
|
|
154
|
+
function rgxWalker(...args) {
|
|
155
|
+
return new RGXWalker(...args);
|
|
156
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./base"), exports);
|
|
18
|
+
__exportStar(require("./part"), exports);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RGXConvertibleToken, RGXToken } from "../types";
|
|
2
|
+
import type { RGXWalker } from "./base";
|
|
3
|
+
import { CloneDepth } from "@ptolemy2002/immutability-utils";
|
|
4
|
+
export type RGXPartEventType = "pre-capture" | "post-capture";
|
|
5
|
+
export type RGXPartOptions<R, T = string> = {
|
|
6
|
+
transform: (captured: string) => T;
|
|
7
|
+
onEvent: ((part: RGXPart<R, T>, eventType: RGXPartEventType, walker: RGXWalker<R>) => void) | null;
|
|
8
|
+
};
|
|
9
|
+
export declare class RGXPart<R, T = string> implements RGXConvertibleToken {
|
|
10
|
+
token: RGXToken;
|
|
11
|
+
capturedString: string | null;
|
|
12
|
+
capturedValue: T | null;
|
|
13
|
+
readonly transform: RGXPartOptions<R, T>["transform"];
|
|
14
|
+
readonly onEvent: RGXPartOptions<R, T>["onEvent"];
|
|
15
|
+
static check: (value: unknown) => value is RGXPart<unknown, unknown>;
|
|
16
|
+
static assert: (value: unknown) => asserts value is RGXPart<unknown, unknown>;
|
|
17
|
+
constructor(token: RGXToken, options?: Partial<RGXPartOptions<R, T>>);
|
|
18
|
+
triggerEvent(eventType: RGXPartEventType, walker: RGXWalker<R>): void;
|
|
19
|
+
get rgxIsGroup(): boolean;
|
|
20
|
+
get rgxIsRepeatable(): boolean;
|
|
21
|
+
toRgx(): RGXToken;
|
|
22
|
+
clone(depth?: CloneDepth): RGXPart<R, T>;
|
|
23
|
+
}
|
|
24
|
+
export declare function rgxPart<R, T = string>(...args: ConstructorParameters<typeof RGXPart<R, T>>): RGXPart<R, T>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RGXPart = void 0;
|
|
4
|
+
exports.rgxPart = rgxPart;
|
|
5
|
+
const typeGuards_1 = require("../typeGuards");
|
|
6
|
+
const internal_1 = require("../internal");
|
|
7
|
+
const clone_1 = require("../clone");
|
|
8
|
+
const immutability_utils_1 = require("@ptolemy2002/immutability-utils");
|
|
9
|
+
class RGXPart {
|
|
10
|
+
constructor(token, options = {}) {
|
|
11
|
+
this.capturedString = null;
|
|
12
|
+
this.capturedValue = null;
|
|
13
|
+
this.token = token;
|
|
14
|
+
this.transform = options.transform ?? ((captured) => captured);
|
|
15
|
+
this.onEvent = options.onEvent ?? null;
|
|
16
|
+
}
|
|
17
|
+
triggerEvent(eventType, walker) {
|
|
18
|
+
switch (eventType) {
|
|
19
|
+
case "post-capture": {
|
|
20
|
+
this.capturedString = walker.getLastCapturedString();
|
|
21
|
+
this.capturedValue = this.transform(this.capturedString);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
this.onEvent?.(this, eventType, walker);
|
|
26
|
+
}
|
|
27
|
+
// Properties used for conversion to an RGXToken
|
|
28
|
+
get rgxIsGroup() {
|
|
29
|
+
return (0, typeGuards_1.isRGXGroupedToken)(this.token);
|
|
30
|
+
}
|
|
31
|
+
get rgxIsRepeatable() {
|
|
32
|
+
if ((0, typeGuards_1.isRGXToken)(this.token, 'convertible'))
|
|
33
|
+
return this.token.rgxIsRepeatable ?? true;
|
|
34
|
+
// Assume any other token is repeatable, since we don't know its implementation.
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
toRgx() {
|
|
38
|
+
return this.token;
|
|
39
|
+
}
|
|
40
|
+
// Clone method
|
|
41
|
+
clone(depth = "max") {
|
|
42
|
+
if (depth === 0)
|
|
43
|
+
return this;
|
|
44
|
+
return new RGXPart((0, clone_1.cloneRGXToken)(this.token, (0, immutability_utils_1.depthDecrement)(depth, 1)), { transform: this.transform, onEvent: this.onEvent });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.RGXPart = RGXPart;
|
|
48
|
+
RGXPart.check = (0, internal_1.createClassGuardFunction)(RGXPart);
|
|
49
|
+
RGXPart.assert = (0, internal_1.createAssertClassGuardFunction)(RGXPart);
|
|
50
|
+
function rgxPart(...args) {
|
|
51
|
+
return new RGXPart(...args);
|
|
52
|
+
}
|