@ptolemy2002/rgx 12.5.0 → 12.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/dist/walker/base.d.ts +9 -0
- package/dist/walker/base.js +136 -78
- package/dist/walker/part.d.ts +2 -2
- package/package.json +1 -1
package/dist/walker/base.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type RGXWalkerOptions<R, S = unknown> = {
|
|
|
9
9
|
looping?: boolean;
|
|
10
10
|
};
|
|
11
11
|
export type RGXTokenOrPart<R, S = unknown, T = unknown> = RGXToken | RGXPart<R, S, T>;
|
|
12
|
+
export type RGXWalkerStepDirective = "stop" | "skip" | "silent";
|
|
12
13
|
export declare class RGXWalker<R, S = unknown> {
|
|
13
14
|
readonly source: string;
|
|
14
15
|
_sourcePosition: number;
|
|
@@ -39,6 +40,14 @@ export declare class RGXWalker<R, S = unknown> {
|
|
|
39
40
|
remainingSource(): string | null;
|
|
40
41
|
capture(token: RGXTokenOrPart<R, S>, includeMatch: true): RegExpExecArray;
|
|
41
42
|
capture(token: RGXTokenOrPart<R, S>, includeMatch?: false): string;
|
|
43
|
+
private advanceToken;
|
|
44
|
+
private determineBranch;
|
|
45
|
+
private registerCapture;
|
|
46
|
+
private unregisterLastCapture;
|
|
47
|
+
private handleBeforeCapture;
|
|
48
|
+
private attemptCapture;
|
|
49
|
+
private validateCapture;
|
|
50
|
+
private handleAfterCapture;
|
|
42
51
|
step(): RGXCapture | null;
|
|
43
52
|
stepToToken(predicate: (token: RGXTokenOrPart<R>) => boolean): this;
|
|
44
53
|
stepToPart(predicate?: (part: RGXPart<R, S, unknown>) => boolean): this;
|
package/dist/walker/base.js
CHANGED
|
@@ -96,6 +96,106 @@ class RGXWalker {
|
|
|
96
96
|
this.sourcePosition += match[0].length;
|
|
97
97
|
return includeMatch ? match : match[0];
|
|
98
98
|
}
|
|
99
|
+
advanceToken() {
|
|
100
|
+
if (!this.infinite || this.tokenPosition < this.tokens.length - 1) {
|
|
101
|
+
this.tokenPosition++;
|
|
102
|
+
if (this.looping && this.atTokenEnd()) {
|
|
103
|
+
this.tokenPosition = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
determineBranch(capture) {
|
|
108
|
+
for (let i = 0; i < capture.length - 1; i++) {
|
|
109
|
+
if (capture.groups?.[`rgx_branch_${i}`] !== undefined)
|
|
110
|
+
return i;
|
|
111
|
+
}
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
registerCapture(captureResult, token) {
|
|
115
|
+
this.captures.push(captureResult);
|
|
116
|
+
if (token instanceof part_1.RGXPart && token.hasId()) {
|
|
117
|
+
if (!(token.id in this.namedCaptures))
|
|
118
|
+
this.namedCaptures[token.id] = [];
|
|
119
|
+
this.namedCaptures[token.id].push(captureResult);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
unregisterLastCapture(token) {
|
|
123
|
+
this.captures.pop();
|
|
124
|
+
if (token instanceof part_1.RGXPart && token.hasId()) {
|
|
125
|
+
this.namedCaptures[token.id].pop();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
handleBeforeCapture(token) {
|
|
129
|
+
const control = token.beforeCapture?.({ part: token, walker: this });
|
|
130
|
+
// If this happens, beforeCapture itself stopped the walker, so we just need to respect that.
|
|
131
|
+
if (this.stopped)
|
|
132
|
+
return "stop";
|
|
133
|
+
if (control === "stop" || control === "stop-silent")
|
|
134
|
+
return "stop";
|
|
135
|
+
if (control === "skip")
|
|
136
|
+
return "skip";
|
|
137
|
+
if (control === "silent")
|
|
138
|
+
return "silent";
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
// Returns the capture on success, or a directive on handled failure. Unhandled errors are rethrown.
|
|
142
|
+
attemptCapture(branchedToken, part) {
|
|
143
|
+
try {
|
|
144
|
+
return this.capture(branchedToken, true);
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
if (part !== null && e instanceof errors_1.RGXRegexNotMatchedAtPositionError) {
|
|
148
|
+
const control = part.afterFailure?.(e, { part, walker: this });
|
|
149
|
+
// If this happens, afterFailure itself stopped the walker, so we just need to respect that.
|
|
150
|
+
if (this.stopped)
|
|
151
|
+
return "stop";
|
|
152
|
+
if (control === "stop" || control === "stop-silent")
|
|
153
|
+
return "stop";
|
|
154
|
+
if (control === "skip")
|
|
155
|
+
return "skip";
|
|
156
|
+
// Handling silent is pointless here since it won't add a capture in either case, so we don't check for it.
|
|
157
|
+
}
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Returns a directive on handled validation failure, null on success. Unhandled errors are rethrown.
|
|
162
|
+
validateCapture(token, captureResult, start) {
|
|
163
|
+
try {
|
|
164
|
+
token.validate(captureResult, { part: token, walker: this });
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
this.sourcePosition = start; // Reset source position on validation failure
|
|
169
|
+
if (e instanceof errors_1.RGXPartValidationFailedError) {
|
|
170
|
+
const control = token.afterValidationFailure?.(e, { part: token, walker: this });
|
|
171
|
+
// If this happens, afterValidationFailure itself stopped the walker, so we just need to respect that.
|
|
172
|
+
if (this.stopped)
|
|
173
|
+
return "stop";
|
|
174
|
+
if (control === "stop" || control === "stop-silent")
|
|
175
|
+
return "stop";
|
|
176
|
+
if (control === "skip")
|
|
177
|
+
return "skip";
|
|
178
|
+
// Handling silent is pointless here since it won't add a capture in either case, so we don't check for it.
|
|
179
|
+
}
|
|
180
|
+
throw e;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
handleAfterCapture(token, captureResult, silent, start) {
|
|
184
|
+
const control = token.afterCapture?.(captureResult, { part: token, walker: this });
|
|
185
|
+
// If this happens, afterCapture itself stopped the walker, so we just need to respect that.
|
|
186
|
+
if (this.stopped)
|
|
187
|
+
return "stop";
|
|
188
|
+
if (!silent && (control === "skip" || control === "silent" || control === "stop-silent")) {
|
|
189
|
+
this.unregisterLastCapture(token);
|
|
190
|
+
}
|
|
191
|
+
if (control === "skip") {
|
|
192
|
+
this.sourcePosition = start;
|
|
193
|
+
return "skip";
|
|
194
|
+
}
|
|
195
|
+
if (control === "stop" || control === "stop-silent")
|
|
196
|
+
return "stop";
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
99
199
|
step() {
|
|
100
200
|
if (!this.infinite && !this.looping && this.atTokenEnd()) {
|
|
101
201
|
this._stopped = true;
|
|
@@ -109,106 +209,64 @@ class RGXWalker {
|
|
|
109
209
|
const token = this.currentToken();
|
|
110
210
|
const isPart = token instanceof part_1.RGXPart;
|
|
111
211
|
let silent = false;
|
|
112
|
-
// Ask Part what to do — control flow via return values, not flags.
|
|
113
212
|
if (isPart) {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
213
|
+
const dir = this.handleBeforeCapture(token);
|
|
214
|
+
if (dir === "stop") {
|
|
116
215
|
this._stopped = true;
|
|
117
216
|
return null;
|
|
118
217
|
}
|
|
119
|
-
if (
|
|
120
|
-
this.
|
|
218
|
+
if (dir === "skip") {
|
|
219
|
+
this.advanceToken();
|
|
121
220
|
return null;
|
|
122
221
|
}
|
|
123
|
-
|
|
124
|
-
silent = true;
|
|
125
|
-
}
|
|
222
|
+
silent = dir === "silent";
|
|
126
223
|
}
|
|
127
|
-
// Capture the match
|
|
128
224
|
const start = this.sourcePosition;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let capture;
|
|
135
|
-
try {
|
|
136
|
-
capture = this.capture(branchedToken, true);
|
|
225
|
+
const branchedToken = isPart ? createBranchGroups(token.token) : createBranchGroups(token);
|
|
226
|
+
const captureAttempt = this.attemptCapture(branchedToken, isPart ? token : null);
|
|
227
|
+
if (captureAttempt === "stop") {
|
|
228
|
+
this._stopped = true;
|
|
229
|
+
return null;
|
|
137
230
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (control === "stop") {
|
|
142
|
-
this._stopped = true;
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
if (control === "skip") {
|
|
146
|
-
this.tokenPosition++;
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
// Handling silent is pointless here since it won't add a capture in either case, so we don't check for it.
|
|
150
|
-
}
|
|
151
|
-
throw e;
|
|
231
|
+
if (captureAttempt === "skip") {
|
|
232
|
+
this.advanceToken();
|
|
233
|
+
return null;
|
|
152
234
|
}
|
|
153
|
-
const raw = isPart ? token.rawTransform(
|
|
235
|
+
const raw = isPart ? token.rawTransform(captureAttempt[0]) : captureAttempt[0];
|
|
154
236
|
const end = this.sourcePosition;
|
|
155
237
|
const value = isPart ? token.transform(raw) : raw;
|
|
156
|
-
let branch = 0;
|
|
157
|
-
// Determine branch index for captureResult by finding the first index
|
|
158
|
-
// with non-undefined match group.
|
|
159
|
-
for (let i = 0; i < capture.length - 1; i++) {
|
|
160
|
-
const branchKey = `rgx_branch_${i}`;
|
|
161
|
-
if (capture.groups && capture.groups[branchKey] !== undefined) {
|
|
162
|
-
branch = i;
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
238
|
const captureResult = {
|
|
167
|
-
raw, value, start, end,
|
|
239
|
+
raw, value, start, end,
|
|
240
|
+
branch: this.determineBranch(captureAttempt),
|
|
168
241
|
ownerId: isPart && token.hasId() ? token.id : null,
|
|
169
|
-
groups:
|
|
242
|
+
groups: captureAttempt.groups ?? null
|
|
170
243
|
};
|
|
171
|
-
// Validate the part. If validation fails, it will throw an error, so nothing below will run.
|
|
172
244
|
if (isPart) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.sourcePosition = start; // Reset source position on validation failure
|
|
178
|
-
if (e instanceof errors_1.RGXPartValidationFailedError) {
|
|
179
|
-
const control = token.afterValidationFailure?.(e, { part: token, walker: this });
|
|
180
|
-
if (control === "stop") {
|
|
181
|
-
this._stopped = true;
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
if (control === "skip") {
|
|
185
|
-
this.tokenPosition++;
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
// Handling silent is pointless here since it won't add a capture in either case, so we don't check for it.
|
|
189
|
-
}
|
|
190
|
-
throw e;
|
|
245
|
+
const dir = this.validateCapture(token, captureResult, start);
|
|
246
|
+
if (dir === "stop") {
|
|
247
|
+
this._stopped = true;
|
|
248
|
+
return null;
|
|
191
249
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.captures.push(captureResult);
|
|
196
|
-
if (isPart && token.hasId()) {
|
|
197
|
-
if (!(token.id in this.namedCaptures))
|
|
198
|
-
this.namedCaptures[token.id] = [];
|
|
199
|
-
this.namedCaptures[token.id].push(captureResult);
|
|
250
|
+
if (dir === "skip") {
|
|
251
|
+
this.advanceToken();
|
|
252
|
+
return null;
|
|
200
253
|
}
|
|
201
254
|
}
|
|
202
|
-
|
|
255
|
+
if (!silent)
|
|
256
|
+
this.registerCapture(captureResult, token);
|
|
203
257
|
if (isPart) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
258
|
+
const dir = this.handleAfterCapture(token, captureResult, silent, start);
|
|
259
|
+
if (dir === "stop") {
|
|
260
|
+
this.advanceToken();
|
|
261
|
+
this._stopped = true;
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (dir === "skip") {
|
|
265
|
+
this.advanceToken();
|
|
266
|
+
return null;
|
|
210
267
|
}
|
|
211
268
|
}
|
|
269
|
+
this.advanceToken();
|
|
212
270
|
return captureResult;
|
|
213
271
|
}
|
|
214
272
|
stepToToken(predicate) {
|
package/dist/walker/part.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { RGXToken } from "../types";
|
|
|
2
2
|
import type { RGXWalker } from "./base";
|
|
3
3
|
import { CloneDepth } from "@ptolemy2002/immutability-utils";
|
|
4
4
|
import { RGXPartValidationFailedError, RGXRegexNotMatchedAtPositionError } from "../errors";
|
|
5
|
-
export type RGXPartControl = "skip" | "stop" | "silent" | void;
|
|
5
|
+
export type RGXPartControl = "skip" | "stop" | "silent" | "stop-silent" | void;
|
|
6
6
|
export type RGXCapture<T = unknown> = {
|
|
7
7
|
raw: string;
|
|
8
8
|
value: T;
|
|
@@ -22,7 +22,7 @@ export type RGXPartOptions<R, S = unknown, T = string> = {
|
|
|
22
22
|
transform: (captured: string) => T;
|
|
23
23
|
validate: (captured: RGXCapture<T>, context: RGXPartContext<R, S, T>) => boolean | string;
|
|
24
24
|
beforeCapture: ((context: RGXPartContext<R, S, T>) => RGXPartControl) | null;
|
|
25
|
-
afterCapture: ((capture: RGXCapture<T>, context: RGXPartContext<R, S, T>) =>
|
|
25
|
+
afterCapture: ((capture: RGXCapture<T>, context: RGXPartContext<R, S, T>) => RGXPartControl) | null;
|
|
26
26
|
afterFailure: ((e: RGXRegexNotMatchedAtPositionError, context: RGXPartContext<R, S, T>) => RGXPartControl) | null;
|
|
27
27
|
afterValidationFailure: ((e: RGXPartValidationFailedError, context: RGXPartContext<R, S, T>) => RGXPartControl) | null;
|
|
28
28
|
};
|