@openrewrite/rewrite 8.72.2 → 8.72.4
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/execution.js +3 -16
- package/dist/execution.js.map +1 -1
- package/dist/index.js +34 -49
- package/dist/index.js.map +1 -1
- package/dist/java/markers.js +12 -29
- package/dist/java/markers.js.map +1 -1
- package/dist/java/print.js +2 -13
- package/dist/java/print.js.map +1 -1
- package/dist/java/rpc.js +1344 -1725
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/type-visitor.js +143 -184
- package/dist/java/type-visitor.js.map +1 -1
- package/dist/java/visitor.js +1072 -1249
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/add-import.js +495 -534
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/autodetect.js +116 -159
- package/dist/javascript/autodetect.js.map +1 -1
- package/dist/javascript/cleanup/add-parse-int-radix.js +41 -57
- package/dist/javascript/cleanup/add-parse-int-radix.js.map +1 -1
- package/dist/javascript/cleanup/prefer-optional-chain.js +89 -105
- package/dist/javascript/cleanup/prefer-optional-chain.js.map +1 -1
- package/dist/javascript/cleanup/use-object-property-shorthand.js +74 -95
- package/dist/javascript/cleanup/use-object-property-shorthand.js.map +1 -1
- package/dist/javascript/comparator.js +815 -1167
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/dependency-workspace.js +206 -219
- package/dist/javascript/dependency-workspace.js.map +1 -1
- package/dist/javascript/format/format.js +682 -908
- package/dist/javascript/format/format.js.map +1 -1
- package/dist/javascript/format/minimum-viable-spacing-visitor.js +152 -231
- package/dist/javascript/format/minimum-viable-spacing-visitor.js.map +1 -1
- package/dist/javascript/format/normalize-whitespace-visitor.js +12 -31
- package/dist/javascript/format/normalize-whitespace-visitor.js.map +1 -1
- package/dist/javascript/format/prettier-config-loader.js +134 -153
- package/dist/javascript/format/prettier-config-loader.js.map +1 -1
- package/dist/javascript/format/prettier-format.js +112 -129
- package/dist/javascript/format/prettier-format.js.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.js +112 -136
- package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
- package/dist/javascript/markers.js +59 -92
- package/dist/javascript/markers.js.map +1 -1
- package/dist/javascript/migrate/es6/modernize-octal-escape-sequences.js +39 -52
- package/dist/javascript/migrate/es6/modernize-octal-escape-sequences.js.map +1 -1
- package/dist/javascript/migrate/es6/modernize-octal-literals.js +25 -38
- package/dist/javascript/migrate/es6/modernize-octal-literals.js.map +1 -1
- package/dist/javascript/migrate/es6/remove-duplicate-object-keys.js +66 -82
- package/dist/javascript/migrate/es6/remove-duplicate-object-keys.js.map +1 -1
- package/dist/javascript/migrate/typescript/export-assignment-to-export-default.js +10 -23
- package/dist/javascript/migrate/typescript/export-assignment-to-export-default.js.map +1 -1
- package/dist/javascript/node-resolution-result.js +137 -166
- package/dist/javascript/node-resolution-result.js.map +1 -1
- package/dist/javascript/package-json-parser.js +312 -343
- package/dist/javascript/package-json-parser.js.map +1 -1
- package/dist/javascript/package-manager.js +145 -170
- package/dist/javascript/package-manager.js.map +1 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +94 -68
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/print.js +1572 -1835
- package/dist/javascript/print.js.map +1 -1
- package/dist/javascript/project-parser.js +151 -172
- package/dist/javascript/project-parser.js.map +1 -1
- package/dist/javascript/recipes/add-dependency.js +140 -175
- package/dist/javascript/recipes/add-dependency.js.map +1 -1
- package/dist/javascript/recipes/async-callback-in-sync-array-method.js +20 -36
- package/dist/javascript/recipes/async-callback-in-sync-array-method.js.map +1 -1
- package/dist/javascript/recipes/auto-format.js +3 -14
- package/dist/javascript/recipes/auto-format.js.map +1 -1
- package/dist/javascript/recipes/change-import.js +447 -495
- package/dist/javascript/recipes/change-import.js.map +1 -1
- package/dist/javascript/recipes/order-imports.js +162 -175
- package/dist/javascript/recipes/order-imports.js.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.js +167 -197
- package/dist/javascript/recipes/upgrade-dependency-version.js.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js +166 -193
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js.map +1 -1
- package/dist/javascript/remove-import.js +689 -724
- package/dist/javascript/remove-import.js.map +1 -1
- package/dist/javascript/rpc.js +1007 -1332
- package/dist/javascript/rpc.js.map +1 -1
- package/dist/javascript/search/find-dependency.js +84 -110
- package/dist/javascript/search/find-dependency.js.map +1 -1
- package/dist/javascript/search/uses-method.js +5 -19
- package/dist/javascript/search/uses-method.js.map +1 -1
- package/dist/javascript/search/uses-type.js +9 -20
- package/dist/javascript/search/uses-type.js.map +1 -1
- package/dist/javascript/templating/comparator.js +737 -822
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.js +211 -245
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/pattern.js +169 -190
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +172 -210
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/rewrite.js +75 -97
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.js +69 -82
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/tree-debug.js +109 -137
- package/dist/javascript/tree-debug.js.map +1 -1
- package/dist/javascript/visitor.js +1090 -1254
- package/dist/javascript/visitor.js.map +1 -1
- package/dist/json/print.js +72 -103
- package/dist/json/print.js.map +1 -1
- package/dist/json/rpc.js +120 -181
- package/dist/json/rpc.js.map +1 -1
- package/dist/json/visitor.js +69 -100
- package/dist/json/visitor.js.map +1 -1
- package/dist/marketplace.js +20 -33
- package/dist/marketplace.js.map +1 -1
- package/dist/parse-error.js +41 -62
- package/dist/parse-error.js.map +1 -1
- package/dist/parser.js +7 -18
- package/dist/parser.js.map +1 -1
- package/dist/path-utils.js +46 -59
- package/dist/path-utils.js.map +1 -1
- package/dist/preconditions.js +30 -47
- package/dist/preconditions.js.map +1 -1
- package/dist/print.js +6 -19
- package/dist/print.js.map +1 -1
- package/dist/recipe.js +42 -73
- package/dist/recipe.js.map +1 -1
- package/dist/rpc/index.js +74 -115
- package/dist/rpc/index.js.map +1 -1
- package/dist/rpc/queue.js +71 -90
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/recipe.js +32 -57
- package/dist/rpc/recipe.js.map +1 -1
- package/dist/rpc/request/generate.js +4 -13
- package/dist/rpc/request/generate.js.map +1 -1
- package/dist/rpc/request/get-languages.js +2 -11
- package/dist/rpc/request/get-languages.js.map +1 -1
- package/dist/rpc/request/get-marketplace.js +9 -20
- package/dist/rpc/request/get-marketplace.js.map +1 -1
- package/dist/rpc/request/get-object.js +4 -13
- package/dist/rpc/request/get-object.js.map +1 -1
- package/dist/rpc/request/install-recipes.js +25 -36
- package/dist/rpc/request/install-recipes.js.map +1 -1
- package/dist/rpc/request/metrics.js +8 -17
- package/dist/rpc/request/metrics.js.map +1 -1
- package/dist/rpc/request/parse-project.js +36 -45
- package/dist/rpc/request/parse-project.js.map +1 -1
- package/dist/rpc/request/parse.js +5 -14
- package/dist/rpc/request/parse.js.map +1 -1
- package/dist/rpc/request/prepare-recipe.js +37 -52
- package/dist/rpc/request/prepare-recipe.js.map +1 -1
- package/dist/rpc/request/print.js +5 -14
- package/dist/rpc/request/print.js.map +1 -1
- package/dist/rpc/request/visit.js +56 -71
- package/dist/rpc/request/visit.js.map +1 -1
- package/dist/rpc/rewrite-rpc.js +70 -97
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/rpc/server.js +76 -89
- package/dist/rpc/server.js.map +1 -1
- package/dist/run.js +47 -66
- package/dist/run.js.map +1 -1
- package/dist/search/is-source-file.js +8 -19
- package/dist/search/is-source-file.js.map +1 -1
- package/dist/test/rewrite-test.js +154 -188
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/text/print.js +23 -38
- package/dist/text/print.js.map +1 -1
- package/dist/text/rpc.js +29 -44
- package/dist/text/rpc.js.map +1 -1
- package/dist/text/visitor.js +16 -33
- package/dist/text/visitor.js.map +1 -1
- package/dist/util.js +13 -24
- package/dist/util.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/visitor.js +84 -115
- package/dist/visitor.js.map +1 -1
- package/dist/yaml/index.d.ts +2 -0
- package/dist/yaml/index.d.ts.map +1 -1
- package/dist/yaml/index.js +2 -0
- package/dist/yaml/index.js.map +1 -1
- package/dist/yaml/markers.d.ts +21 -0
- package/dist/yaml/markers.d.ts.map +1 -0
- package/dist/yaml/markers.js +37 -0
- package/dist/yaml/markers.js.map +1 -0
- package/dist/yaml/parser.d.ts.map +1 -1
- package/dist/yaml/parser.js +4 -1
- package/dist/yaml/parser.js.map +1 -1
- package/dist/yaml/print.d.ts +1 -1
- package/dist/yaml/print.d.ts.map +1 -1
- package/dist/yaml/print.js +175 -208
- package/dist/yaml/print.js.map +1 -1
- package/dist/yaml/rpc.js +154 -219
- package/dist/yaml/rpc.js.map +1 -1
- package/dist/yaml/visitor.js +78 -113
- package/dist/yaml/visitor.js.map +1 -1
- package/package.json +1 -1
- package/src/javascript/parser.ts +56 -14
- package/src/yaml/index.ts +2 -0
- package/src/yaml/markers.ts +70 -0
- package/src/yaml/parser.ts +5 -1
- package/src/yaml/print.ts +5 -2
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.DebugPatternMatchingComparator = exports.PatternMatchingComparator = void 0;
|
|
13
4
|
/*
|
|
@@ -73,51 +64,46 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
73
64
|
captures: new CaptureMapImpl(state.storage)
|
|
74
65
|
};
|
|
75
66
|
}
|
|
76
|
-
visit(j, p, parent) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
|
|
96
|
-
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
97
|
-
const context = this.buildConstraintContext(cursorAtCapturedNode);
|
|
98
|
-
if (!captureMarker.constraint(p, context)) {
|
|
99
|
-
const captureName = captureMarker.captureName || 'unnamed';
|
|
100
|
-
const targetKind = p.kind || 'unknown';
|
|
101
|
-
return this.constraintFailed(captureName, targetKind);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
const success = this.matcher.handleCapture(captureMarker, p, undefined);
|
|
105
|
-
if (!success) {
|
|
67
|
+
async visit(j, p, parent) {
|
|
68
|
+
// Check if the pattern node is a capture - this handles unwrapped captures
|
|
69
|
+
// (Wrapped captures in J.RightPadded are handled by visitRightPadded override)
|
|
70
|
+
// Note: targetCursor will be pushed by parent's visit() method after this check
|
|
71
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(j);
|
|
72
|
+
if (captureMarker) {
|
|
73
|
+
// Push targetCursor to position it at the captured node for constraint evaluation
|
|
74
|
+
// Only create cursor if targetCursor was initialized (meaning user provided one)
|
|
75
|
+
const savedTargetCursor = this.targetCursor;
|
|
76
|
+
const cursorAtCapturedNode = this.targetCursor !== undefined
|
|
77
|
+
? new __1.Cursor(p, this.targetCursor)
|
|
78
|
+
: new __1.Cursor(p);
|
|
79
|
+
this.targetCursor = cursorAtCapturedNode;
|
|
80
|
+
try {
|
|
81
|
+
// Evaluate constraint with context (cursor + previous captures)
|
|
82
|
+
// Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
|
|
83
|
+
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
84
|
+
const context = this.buildConstraintContext(cursorAtCapturedNode);
|
|
85
|
+
if (!captureMarker.constraint(p, context)) {
|
|
106
86
|
const captureName = captureMarker.captureName || 'unnamed';
|
|
107
|
-
|
|
87
|
+
const targetKind = p.kind || 'unknown';
|
|
88
|
+
return this.constraintFailed(captureName, targetKind);
|
|
108
89
|
}
|
|
109
|
-
return j;
|
|
110
90
|
}
|
|
111
|
-
|
|
112
|
-
|
|
91
|
+
const success = this.matcher.handleCapture(captureMarker, p, undefined);
|
|
92
|
+
if (!success) {
|
|
93
|
+
const captureName = captureMarker.captureName || 'unnamed';
|
|
94
|
+
return this.captureConflict(captureName);
|
|
113
95
|
}
|
|
114
|
-
}
|
|
115
|
-
if (!this.match) {
|
|
116
96
|
return j;
|
|
117
97
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
98
|
+
finally {
|
|
99
|
+
this.targetCursor = savedTargetCursor;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!this.match) {
|
|
103
|
+
return j;
|
|
104
|
+
}
|
|
105
|
+
// Continue with parent's visit which will push targetCursor and traverse
|
|
106
|
+
return await super.visit(j, p, parent);
|
|
121
107
|
}
|
|
122
108
|
hasSameKind(j, other) {
|
|
123
109
|
return super.hasSameKind(j, other) ||
|
|
@@ -140,102 +126,92 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
140
126
|
* Override visitRightPadded to check if this wrapper has a CaptureMarker.
|
|
141
127
|
* If so, capture the entire wrapper (to preserve markers like semicolons).
|
|
142
128
|
*/
|
|
143
|
-
visitRightPadded(right, p) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement, this.buildConstraintContext(cursorAtCapturedNode))) {
|
|
169
|
-
const captureName = captureMarker.captureName || 'unnamed';
|
|
170
|
-
const targetKind = targetElement.kind || 'unknown';
|
|
171
|
-
return this.constraintFailed(captureName, targetKind);
|
|
172
|
-
}
|
|
173
|
-
// Handle the capture with the wrapper - use the element for pattern matching
|
|
174
|
-
const success = this.matcher.handleCapture(captureMarker, targetElement, targetWrapper);
|
|
175
|
-
if (!success) {
|
|
176
|
-
const captureName = captureMarker.captureName || 'unnamed';
|
|
177
|
-
return this.captureConflict(captureName);
|
|
178
|
-
}
|
|
179
|
-
return right;
|
|
129
|
+
async visitRightPadded(right, p) {
|
|
130
|
+
if (!this.match) {
|
|
131
|
+
return right;
|
|
132
|
+
}
|
|
133
|
+
// Check if this RightPadded has a CaptureMarker (attached during pattern construction)
|
|
134
|
+
// Note: Markers are now only at the wrapper level, not at the element level
|
|
135
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(right);
|
|
136
|
+
if (captureMarker) {
|
|
137
|
+
// Extract the target wrapper if it's also a RightPadded
|
|
138
|
+
const isRightPadded = p.kind === java_1.J.Kind.RightPadded;
|
|
139
|
+
const targetWrapper = isRightPadded ? p : undefined;
|
|
140
|
+
const targetElement = isRightPadded ? targetWrapper.element : p;
|
|
141
|
+
// Push targetCursor to position it at the captured element for constraint evaluation
|
|
142
|
+
const savedTargetCursor = this.targetCursor;
|
|
143
|
+
const cursorAtCapturedNode = this.targetCursor !== undefined
|
|
144
|
+
? (targetWrapper ? new __1.Cursor(targetWrapper, this.targetCursor) : new __1.Cursor(targetElement, this.targetCursor))
|
|
145
|
+
: (targetWrapper ? new __1.Cursor(targetWrapper) : new __1.Cursor(targetElement));
|
|
146
|
+
this.targetCursor = cursorAtCapturedNode;
|
|
147
|
+
try {
|
|
148
|
+
// Evaluate constraint with cursor at the captured node (always defined)
|
|
149
|
+
// Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
|
|
150
|
+
if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement, this.buildConstraintContext(cursorAtCapturedNode))) {
|
|
151
|
+
const captureName = captureMarker.captureName || 'unnamed';
|
|
152
|
+
const targetKind = targetElement.kind || 'unknown';
|
|
153
|
+
return this.constraintFailed(captureName, targetKind);
|
|
180
154
|
}
|
|
181
|
-
|
|
182
|
-
|
|
155
|
+
// Handle the capture with the wrapper - use the element for pattern matching
|
|
156
|
+
const success = this.matcher.handleCapture(captureMarker, targetElement, targetWrapper);
|
|
157
|
+
if (!success) {
|
|
158
|
+
const captureName = captureMarker.captureName || 'unnamed';
|
|
159
|
+
return this.captureConflict(captureName);
|
|
183
160
|
}
|
|
161
|
+
return right;
|
|
184
162
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
visitContainer(container, p) {
|
|
190
|
-
const _super = Object.create(null, {
|
|
191
|
-
visitContainer: { get: () => super.visitContainer }
|
|
192
|
-
});
|
|
193
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
194
|
-
// Check if any elements are variadic captures
|
|
195
|
-
const hasVariadicCapture = container.elements.some(elem => utils_1.PlaceholderUtils.isVariadicCapture(elem));
|
|
196
|
-
// If no variadic captures, use parent implementation
|
|
197
|
-
if (!hasVariadicCapture) {
|
|
198
|
-
return _super.visitContainer.call(this, container, p);
|
|
199
|
-
}
|
|
200
|
-
// Otherwise, handle variadic captures ourselves
|
|
201
|
-
if (!this.match) {
|
|
202
|
-
return container;
|
|
203
|
-
}
|
|
204
|
-
// Extract the other container
|
|
205
|
-
const isContainer = p.kind === java_1.J.Kind.Container;
|
|
206
|
-
if (!isContainer) {
|
|
207
|
-
// Set up cursors temporarily for kindMismatch to use
|
|
208
|
-
const savedCursor = this.cursor;
|
|
209
|
-
const savedTargetCursor = this.targetCursor;
|
|
210
|
-
this.cursor = new __1.Cursor(container, this.cursor);
|
|
211
|
-
this.targetCursor = new __1.Cursor(p, this.targetCursor);
|
|
212
|
-
try {
|
|
213
|
-
return this.kindMismatch();
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
216
|
-
this.cursor = savedCursor;
|
|
217
|
-
this.targetCursor = savedTargetCursor;
|
|
218
|
-
}
|
|
163
|
+
finally {
|
|
164
|
+
this.targetCursor = savedTargetCursor;
|
|
219
165
|
}
|
|
220
|
-
|
|
221
|
-
|
|
166
|
+
}
|
|
167
|
+
// Not a capture wrapper - use parent implementation
|
|
168
|
+
return super.visitRightPadded(right, p);
|
|
169
|
+
}
|
|
170
|
+
async visitContainer(container, p) {
|
|
171
|
+
// Check if any elements are variadic captures
|
|
172
|
+
const hasVariadicCapture = container.elements.some(elem => utils_1.PlaceholderUtils.isVariadicCapture(elem));
|
|
173
|
+
// If no variadic captures, use parent implementation
|
|
174
|
+
if (!hasVariadicCapture) {
|
|
175
|
+
return super.visitContainer(container, p);
|
|
176
|
+
}
|
|
177
|
+
// Otherwise, handle variadic captures ourselves
|
|
178
|
+
if (!this.match) {
|
|
179
|
+
return container;
|
|
180
|
+
}
|
|
181
|
+
// Extract the other container
|
|
182
|
+
const isContainer = p.kind === java_1.J.Kind.Container;
|
|
183
|
+
if (!isContainer) {
|
|
184
|
+
// Set up cursors temporarily for kindMismatch to use
|
|
222
185
|
const savedCursor = this.cursor;
|
|
223
186
|
const savedTargetCursor = this.targetCursor;
|
|
224
187
|
this.cursor = new __1.Cursor(container, this.cursor);
|
|
225
|
-
this.targetCursor = new __1.Cursor(
|
|
188
|
+
this.targetCursor = new __1.Cursor(p, this.targetCursor);
|
|
226
189
|
try {
|
|
227
|
-
|
|
228
|
-
// filterEmpty=true to skip J.Empty elements (they represent missing elements in destructuring)
|
|
229
|
-
if (!(yield this.matchSequence(container.elements, otherContainer.elements, true))) {
|
|
230
|
-
return this.structuralMismatch('elements');
|
|
231
|
-
}
|
|
190
|
+
return this.kindMismatch();
|
|
232
191
|
}
|
|
233
192
|
finally {
|
|
234
193
|
this.cursor = savedCursor;
|
|
235
194
|
this.targetCursor = savedTargetCursor;
|
|
236
195
|
}
|
|
237
|
-
|
|
238
|
-
|
|
196
|
+
}
|
|
197
|
+
const otherContainer = p;
|
|
198
|
+
// Push wrappers onto both cursors
|
|
199
|
+
const savedCursor = this.cursor;
|
|
200
|
+
const savedTargetCursor = this.targetCursor;
|
|
201
|
+
this.cursor = new __1.Cursor(container, this.cursor);
|
|
202
|
+
this.targetCursor = new __1.Cursor(otherContainer, this.targetCursor);
|
|
203
|
+
try {
|
|
204
|
+
// Use matchSequence for variadic matching
|
|
205
|
+
// filterEmpty=true to skip J.Empty elements (they represent missing elements in destructuring)
|
|
206
|
+
if (!await this.matchSequence(container.elements, otherContainer.elements, true)) {
|
|
207
|
+
return this.structuralMismatch('elements');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
this.cursor = savedCursor;
|
|
212
|
+
this.targetCursor = savedTargetCursor;
|
|
213
|
+
}
|
|
214
|
+
return container;
|
|
239
215
|
}
|
|
240
216
|
/**
|
|
241
217
|
* Visit a single element in a container (for non-variadic matching).
|
|
@@ -246,200 +222,181 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
246
222
|
* @param index The index in the container
|
|
247
223
|
* @returns true if matching should continue, false if it failed
|
|
248
224
|
*/
|
|
249
|
-
visitContainerElement(element, otherElement, index) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return this.match;
|
|
253
|
-
});
|
|
225
|
+
async visitContainerElement(element, otherElement, index) {
|
|
226
|
+
await this.visitRightPadded(element, otherElement);
|
|
227
|
+
return this.match;
|
|
254
228
|
}
|
|
255
|
-
visitMethodInvocation(methodInvocation, other) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return this.abort(methodInvocation);
|
|
269
|
-
}
|
|
270
|
-
if (other.kind !== java_1.J.Kind.MethodInvocation) {
|
|
271
|
-
// Set up cursors for kindMismatch
|
|
272
|
-
const savedCursor = this.cursor;
|
|
273
|
-
const savedTargetCursor = this.targetCursor;
|
|
274
|
-
this.cursor = new __1.Cursor(methodInvocation, this.cursor);
|
|
275
|
-
this.targetCursor = new __1.Cursor(other, this.targetCursor);
|
|
276
|
-
try {
|
|
277
|
-
return this.kindMismatch();
|
|
278
|
-
}
|
|
279
|
-
finally {
|
|
280
|
-
this.cursor = savedCursor;
|
|
281
|
-
this.targetCursor = savedTargetCursor;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
const otherMethodInvocation = other;
|
|
285
|
-
// Set up cursors for the entire method
|
|
229
|
+
async visitMethodInvocation(methodInvocation, other) {
|
|
230
|
+
// Check if any arguments are variadic captures
|
|
231
|
+
const hasVariadicCapture = methodInvocation.arguments.elements.some(arg => utils_1.PlaceholderUtils.isVariadicCapture(arg));
|
|
232
|
+
// If no variadic captures, use parent implementation (which includes semantic/type-aware matching)
|
|
233
|
+
if (!hasVariadicCapture) {
|
|
234
|
+
return super.visitMethodInvocation(methodInvocation, other);
|
|
235
|
+
}
|
|
236
|
+
// Otherwise, handle variadic captures ourselves
|
|
237
|
+
if (!this.match) {
|
|
238
|
+
return this.abort(methodInvocation);
|
|
239
|
+
}
|
|
240
|
+
if (other.kind !== java_1.J.Kind.MethodInvocation) {
|
|
241
|
+
// Set up cursors for kindMismatch
|
|
286
242
|
const savedCursor = this.cursor;
|
|
287
243
|
const savedTargetCursor = this.targetCursor;
|
|
288
244
|
this.cursor = new __1.Cursor(methodInvocation, this.cursor);
|
|
289
|
-
this.targetCursor = new __1.Cursor(
|
|
245
|
+
this.targetCursor = new __1.Cursor(other, this.targetCursor);
|
|
290
246
|
try {
|
|
291
|
-
|
|
292
|
-
if ((methodInvocation.select === undefined) !== (otherMethodInvocation.select === undefined)) {
|
|
293
|
-
return this.structuralMismatch('select');
|
|
294
|
-
}
|
|
295
|
-
// Visit select if present
|
|
296
|
-
if (methodInvocation.select && otherMethodInvocation.select) {
|
|
297
|
-
yield this.visit(methodInvocation.select.element, otherMethodInvocation.select.element);
|
|
298
|
-
if (!this.match)
|
|
299
|
-
return methodInvocation;
|
|
300
|
-
}
|
|
301
|
-
// Compare typeParameters
|
|
302
|
-
if ((methodInvocation.typeParameters === undefined) !== (otherMethodInvocation.typeParameters === undefined)) {
|
|
303
|
-
return this.structuralMismatch('typeParameters');
|
|
304
|
-
}
|
|
305
|
-
// Visit typeParameters if present
|
|
306
|
-
if (methodInvocation.typeParameters && otherMethodInvocation.typeParameters) {
|
|
307
|
-
if (methodInvocation.typeParameters.elements.length !== otherMethodInvocation.typeParameters.elements.length) {
|
|
308
|
-
return this.arrayLengthMismatch('typeParameters.elements');
|
|
309
|
-
}
|
|
310
|
-
// Visit each type parameter in lock step (visit RightPadded to check for markers)
|
|
311
|
-
for (let i = 0; i < methodInvocation.typeParameters.elements.length; i++) {
|
|
312
|
-
yield this.visitRightPadded(methodInvocation.typeParameters.elements[i], otherMethodInvocation.typeParameters.elements[i]);
|
|
313
|
-
if (!this.match)
|
|
314
|
-
return methodInvocation;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
// Visit name
|
|
318
|
-
yield this.visit(methodInvocation.name, otherMethodInvocation.name);
|
|
319
|
-
if (!this.match) {
|
|
320
|
-
return methodInvocation;
|
|
321
|
-
}
|
|
322
|
-
// Special handling for variadic captures in arguments
|
|
323
|
-
if (!(yield this.matchArguments(methodInvocation.arguments.elements, otherMethodInvocation.arguments.elements))) {
|
|
324
|
-
return this.structuralMismatch('arguments');
|
|
325
|
-
}
|
|
326
|
-
return methodInvocation;
|
|
247
|
+
return this.kindMismatch();
|
|
327
248
|
}
|
|
328
249
|
finally {
|
|
329
250
|
this.cursor = savedCursor;
|
|
330
251
|
this.targetCursor = savedTargetCursor;
|
|
331
252
|
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return (
|
|
343
|
-
});
|
|
344
|
-
// If no variadic captures, use parent implementation
|
|
345
|
-
if (!hasVariadicCapture) {
|
|
346
|
-
return _super.visitBlock.call(this, block, other);
|
|
253
|
+
}
|
|
254
|
+
const otherMethodInvocation = other;
|
|
255
|
+
// Set up cursors for the entire method
|
|
256
|
+
const savedCursor = this.cursor;
|
|
257
|
+
const savedTargetCursor = this.targetCursor;
|
|
258
|
+
this.cursor = new __1.Cursor(methodInvocation, this.cursor);
|
|
259
|
+
this.targetCursor = new __1.Cursor(otherMethodInvocation, this.targetCursor);
|
|
260
|
+
try {
|
|
261
|
+
// Compare select
|
|
262
|
+
if ((methodInvocation.select === undefined) !== (otherMethodInvocation.select === undefined)) {
|
|
263
|
+
return this.structuralMismatch('select');
|
|
347
264
|
}
|
|
348
|
-
//
|
|
349
|
-
if (
|
|
350
|
-
|
|
265
|
+
// Visit select if present
|
|
266
|
+
if (methodInvocation.select && otherMethodInvocation.select) {
|
|
267
|
+
await this.visit(methodInvocation.select.element, otherMethodInvocation.select.element);
|
|
268
|
+
if (!this.match)
|
|
269
|
+
return methodInvocation;
|
|
351
270
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return this.
|
|
271
|
+
// Compare typeParameters
|
|
272
|
+
if ((methodInvocation.typeParameters === undefined) !== (otherMethodInvocation.typeParameters === undefined)) {
|
|
273
|
+
return this.structuralMismatch('typeParameters');
|
|
274
|
+
}
|
|
275
|
+
// Visit typeParameters if present
|
|
276
|
+
if (methodInvocation.typeParameters && otherMethodInvocation.typeParameters) {
|
|
277
|
+
if (methodInvocation.typeParameters.elements.length !== otherMethodInvocation.typeParameters.elements.length) {
|
|
278
|
+
return this.arrayLengthMismatch('typeParameters.elements');
|
|
360
279
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
this.
|
|
280
|
+
// Visit each type parameter in lock step (visit RightPadded to check for markers)
|
|
281
|
+
for (let i = 0; i < methodInvocation.typeParameters.elements.length; i++) {
|
|
282
|
+
await this.visitRightPadded(methodInvocation.typeParameters.elements[i], otherMethodInvocation.typeParameters.elements[i]);
|
|
283
|
+
if (!this.match)
|
|
284
|
+
return methodInvocation;
|
|
364
285
|
}
|
|
365
286
|
}
|
|
366
|
-
|
|
367
|
-
|
|
287
|
+
// Visit name
|
|
288
|
+
await this.visit(methodInvocation.name, otherMethodInvocation.name);
|
|
289
|
+
if (!this.match) {
|
|
290
|
+
return methodInvocation;
|
|
291
|
+
}
|
|
292
|
+
// Special handling for variadic captures in arguments
|
|
293
|
+
if (!await this.matchArguments(methodInvocation.arguments.elements, otherMethodInvocation.arguments.elements)) {
|
|
294
|
+
return this.structuralMismatch('arguments');
|
|
295
|
+
}
|
|
296
|
+
return methodInvocation;
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
this.cursor = savedCursor;
|
|
300
|
+
this.targetCursor = savedTargetCursor;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async visitBlock(block, other) {
|
|
304
|
+
// Check if any statements have CaptureMarker indicating they're variadic
|
|
305
|
+
const hasVariadicCapture = block.statements.some(stmt => {
|
|
306
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(stmt);
|
|
307
|
+
return (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
|
|
308
|
+
});
|
|
309
|
+
// If no variadic captures, use parent implementation
|
|
310
|
+
if (!hasVariadicCapture) {
|
|
311
|
+
return super.visitBlock(block, other);
|
|
312
|
+
}
|
|
313
|
+
// Otherwise, handle variadic captures ourselves
|
|
314
|
+
if (!this.match) {
|
|
315
|
+
return this.abort(block);
|
|
316
|
+
}
|
|
317
|
+
if (other.kind !== java_1.J.Kind.Block) {
|
|
318
|
+
// Set up cursors for kindMismatch
|
|
368
319
|
const savedCursor = this.cursor;
|
|
369
320
|
const savedTargetCursor = this.targetCursor;
|
|
370
321
|
this.cursor = new __1.Cursor(block, this.cursor);
|
|
371
|
-
this.targetCursor = new __1.Cursor(
|
|
322
|
+
this.targetCursor = new __1.Cursor(other, this.targetCursor);
|
|
372
323
|
try {
|
|
373
|
-
|
|
374
|
-
if (!(yield this.matchSequence(block.statements, otherBlock.statements, false))) {
|
|
375
|
-
return this.structuralMismatch('statements');
|
|
376
|
-
}
|
|
377
|
-
return block;
|
|
324
|
+
return this.kindMismatch();
|
|
378
325
|
}
|
|
379
326
|
finally {
|
|
380
327
|
this.cursor = savedCursor;
|
|
381
328
|
this.targetCursor = savedTargetCursor;
|
|
382
329
|
}
|
|
383
|
-
}
|
|
330
|
+
}
|
|
331
|
+
const otherBlock = other;
|
|
332
|
+
// Set up cursors for structural comparison
|
|
333
|
+
const savedCursor = this.cursor;
|
|
334
|
+
const savedTargetCursor = this.targetCursor;
|
|
335
|
+
this.cursor = new __1.Cursor(block, this.cursor);
|
|
336
|
+
this.targetCursor = new __1.Cursor(otherBlock, this.targetCursor);
|
|
337
|
+
try {
|
|
338
|
+
// Special handling for variadic captures in statements
|
|
339
|
+
if (!await this.matchSequence(block.statements, otherBlock.statements, false)) {
|
|
340
|
+
return this.structuralMismatch('statements');
|
|
341
|
+
}
|
|
342
|
+
return block;
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
this.cursor = savedCursor;
|
|
346
|
+
this.targetCursor = savedTargetCursor;
|
|
347
|
+
}
|
|
384
348
|
}
|
|
385
|
-
visitJsCompilationUnit(compilationUnit, other) {
|
|
386
|
-
|
|
387
|
-
|
|
349
|
+
async visitJsCompilationUnit(compilationUnit, other) {
|
|
350
|
+
// Check if any statements are variadic captures
|
|
351
|
+
const hasVariadicCapture = compilationUnit.statements.some(stmt => {
|
|
352
|
+
return utils_1.PlaceholderUtils.isVariadicCapture(stmt);
|
|
388
353
|
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
//
|
|
399
|
-
if (!this.match) {
|
|
400
|
-
return this.abort(compilationUnit);
|
|
401
|
-
}
|
|
402
|
-
if (other.kind !== index_1.JS.Kind.CompilationUnit) {
|
|
403
|
-
// Set up cursors for kindMismatch
|
|
404
|
-
const savedCursor = this.cursor;
|
|
405
|
-
const savedTargetCursor = this.targetCursor;
|
|
406
|
-
this.cursor = new __1.Cursor(compilationUnit, this.cursor);
|
|
407
|
-
this.targetCursor = new __1.Cursor(other, this.targetCursor);
|
|
408
|
-
try {
|
|
409
|
-
return this.kindMismatch();
|
|
410
|
-
}
|
|
411
|
-
finally {
|
|
412
|
-
this.cursor = savedCursor;
|
|
413
|
-
this.targetCursor = savedTargetCursor;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
const otherCompilationUnit = other;
|
|
417
|
-
// Set up cursors for structural comparison
|
|
354
|
+
// If no variadic captures, use parent implementation
|
|
355
|
+
if (!hasVariadicCapture) {
|
|
356
|
+
return super.visitJsCompilationUnit(compilationUnit, other);
|
|
357
|
+
}
|
|
358
|
+
// Otherwise, handle variadic captures ourselves
|
|
359
|
+
if (!this.match) {
|
|
360
|
+
return this.abort(compilationUnit);
|
|
361
|
+
}
|
|
362
|
+
if (other.kind !== index_1.JS.Kind.CompilationUnit) {
|
|
363
|
+
// Set up cursors for kindMismatch
|
|
418
364
|
const savedCursor = this.cursor;
|
|
419
365
|
const savedTargetCursor = this.targetCursor;
|
|
420
366
|
this.cursor = new __1.Cursor(compilationUnit, this.cursor);
|
|
421
|
-
this.targetCursor = new __1.Cursor(
|
|
367
|
+
this.targetCursor = new __1.Cursor(other, this.targetCursor);
|
|
422
368
|
try {
|
|
423
|
-
|
|
424
|
-
if (!(yield this.matchSequence(compilationUnit.statements, otherCompilationUnit.statements, false))) {
|
|
425
|
-
return this.structuralMismatch('statements');
|
|
426
|
-
}
|
|
427
|
-
return compilationUnit;
|
|
369
|
+
return this.kindMismatch();
|
|
428
370
|
}
|
|
429
371
|
finally {
|
|
430
372
|
this.cursor = savedCursor;
|
|
431
373
|
this.targetCursor = savedTargetCursor;
|
|
432
374
|
}
|
|
433
|
-
}
|
|
375
|
+
}
|
|
376
|
+
const otherCompilationUnit = other;
|
|
377
|
+
// Set up cursors for structural comparison
|
|
378
|
+
const savedCursor = this.cursor;
|
|
379
|
+
const savedTargetCursor = this.targetCursor;
|
|
380
|
+
this.cursor = new __1.Cursor(compilationUnit, this.cursor);
|
|
381
|
+
this.targetCursor = new __1.Cursor(otherCompilationUnit, this.targetCursor);
|
|
382
|
+
try {
|
|
383
|
+
// Special handling for variadic captures in top-level statements
|
|
384
|
+
if (!await this.matchSequence(compilationUnit.statements, otherCompilationUnit.statements, false)) {
|
|
385
|
+
return this.structuralMismatch('statements');
|
|
386
|
+
}
|
|
387
|
+
return compilationUnit;
|
|
388
|
+
}
|
|
389
|
+
finally {
|
|
390
|
+
this.cursor = savedCursor;
|
|
391
|
+
this.targetCursor = savedTargetCursor;
|
|
392
|
+
}
|
|
434
393
|
}
|
|
435
394
|
/**
|
|
436
395
|
* Matches argument lists, with special handling for variadic captures.
|
|
437
396
|
* A variadic capture can match zero or more consecutive arguments.
|
|
438
397
|
*/
|
|
439
|
-
matchArguments(patternArgs, targetArgs) {
|
|
440
|
-
return
|
|
441
|
-
return yield this.matchSequence(patternArgs, targetArgs, true);
|
|
442
|
-
});
|
|
398
|
+
async matchArguments(patternArgs, targetArgs) {
|
|
399
|
+
return await this.matchSequence(patternArgs, targetArgs, true);
|
|
443
400
|
}
|
|
444
401
|
/**
|
|
445
402
|
* Generic sequence matching with variadic capture support.
|
|
@@ -453,10 +410,8 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
453
410
|
* @param filterEmpty Whether to filter out J.Empty elements when capturing (true for arguments, false for statements)
|
|
454
411
|
* @returns true if the sequence matches, false otherwise
|
|
455
412
|
*/
|
|
456
|
-
matchSequence(patternElements, targetElements, filterEmpty) {
|
|
457
|
-
return
|
|
458
|
-
return yield this.matchSequenceOptimized(patternElements, targetElements, 0, 0, filterEmpty);
|
|
459
|
-
});
|
|
413
|
+
async matchSequence(patternElements, targetElements, filterEmpty) {
|
|
414
|
+
return await this.matchSequenceOptimized(patternElements, targetElements, 0, 0, filterEmpty);
|
|
460
415
|
}
|
|
461
416
|
/**
|
|
462
417
|
* Optimized sequence matcher with pivot detection and backtracking.
|
|
@@ -470,154 +425,152 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
470
425
|
* @param filterEmpty Whether to filter out J.Empty elements when capturing
|
|
471
426
|
* @returns true if the remaining sequence matches, false otherwise
|
|
472
427
|
*/
|
|
473
|
-
matchSequenceOptimized(patternElements, targetElements, patternIdx, targetIdx, filterEmpty) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
nonVariadicRemainingPatterns++;
|
|
497
|
-
}
|
|
498
|
-
// A pattern is deterministic if it's not a capture at all (i.e., a literal/fixed structure)
|
|
499
|
-
// Variadic captures and non-variadic captures are both non-deterministic
|
|
500
|
-
if (nextCaptureMarker) {
|
|
501
|
-
allRemainingPatternsAreDeterministic = false;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
const remainingTargetElements = targetElements.length - targetIdx;
|
|
505
|
-
const maxPossible = Math.min(remainingTargetElements - nonVariadicRemainingPatterns, max);
|
|
506
|
-
// Pivot detection optimization: try to find where next pattern matches
|
|
507
|
-
// This avoids unnecessary backtracking when constraints make the split point obvious
|
|
508
|
-
let pivotDetected = false;
|
|
509
|
-
let pivotAt = -1;
|
|
510
|
-
// Skip pivot detection if we're using deterministic optimization
|
|
511
|
-
// (when all remaining patterns are literals, there's only ONE valid consumption amount)
|
|
512
|
-
const useDeterministicOptimization = allRemainingPatternsAreDeterministic && maxPossible >= min && maxPossible <= max;
|
|
513
|
-
if (!useDeterministicOptimization && patternIdx + 1 < patternElements.length && min <= maxPossible) {
|
|
514
|
-
const nextPattern = patternElements[patternIdx + 1];
|
|
515
|
-
// Scan through possible consumption amounts starting from min
|
|
516
|
-
for (let tryConsume = min; tryConsume <= maxPossible; tryConsume++) {
|
|
517
|
-
// Check if element after our consumption would match next pattern
|
|
518
|
-
if (targetIdx + tryConsume < targetElements.length) {
|
|
519
|
-
const candidateElement = targetElements[targetIdx + tryConsume];
|
|
520
|
-
// Skip J.Empty for arguments
|
|
521
|
-
if (filterEmpty && candidateElement.element.kind === java_1.J.Kind.Empty) {
|
|
522
|
-
continue;
|
|
523
|
-
}
|
|
524
|
-
// Test if next pattern matches this element
|
|
525
|
-
const savedMatch = this.match;
|
|
526
|
-
const savedState = this.matcher.saveState();
|
|
527
|
-
yield this.visitRightPadded(nextPattern, candidateElement);
|
|
528
|
-
const matchesNext = this.match;
|
|
529
|
-
this.match = savedMatch;
|
|
530
|
-
this.matcher.restoreState(savedState);
|
|
531
|
-
if (matchesNext) {
|
|
532
|
-
// Found pivot! Try this consumption amount first
|
|
533
|
-
pivotDetected = true;
|
|
534
|
-
pivotAt = tryConsume;
|
|
535
|
-
break;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
428
|
+
async matchSequenceOptimized(patternElements, targetElements, patternIdx, targetIdx, filterEmpty) {
|
|
429
|
+
var _a, _b;
|
|
430
|
+
// Base case: all patterns matched
|
|
431
|
+
if (patternIdx >= patternElements.length) {
|
|
432
|
+
return targetIdx >= targetElements.length; // Success if all targets consumed
|
|
433
|
+
}
|
|
434
|
+
// Check for markers at wrapper level only (markers are now only at the outermost level)
|
|
435
|
+
const patternWrapper = patternElements[patternIdx];
|
|
436
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternWrapper);
|
|
437
|
+
const isVariadic = (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
|
|
438
|
+
if (isVariadic) {
|
|
439
|
+
// Variadic pattern: try different consumption amounts with backtracking
|
|
440
|
+
const variadicOptions = captureMarker.variadicOptions;
|
|
441
|
+
const min = (_a = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.min) !== null && _a !== void 0 ? _a : 0;
|
|
442
|
+
const max = (_b = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.max) !== null && _b !== void 0 ? _b : Infinity;
|
|
443
|
+
// Calculate maximum possible consumption and check if remaining patterns are deterministic
|
|
444
|
+
let nonVariadicRemainingPatterns = 0;
|
|
445
|
+
let allRemainingPatternsAreDeterministic = true;
|
|
446
|
+
for (let i = patternIdx + 1; i < patternElements.length; i++) {
|
|
447
|
+
const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternElements[i]);
|
|
448
|
+
const nextIsVariadic = (nextCaptureMarker === null || nextCaptureMarker === void 0 ? void 0 : nextCaptureMarker.variadicOptions) !== undefined;
|
|
449
|
+
if (!nextIsVariadic) {
|
|
450
|
+
nonVariadicRemainingPatterns++;
|
|
539
451
|
}
|
|
540
|
-
//
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
// Example: foo(${args}, 999) matching foo(1,2,42) -> args MUST be [1,2], only try consume=2
|
|
545
|
-
if (useDeterministicOptimization) {
|
|
546
|
-
consumptionOrder.push(maxPossible);
|
|
452
|
+
// A pattern is deterministic if it's not a capture at all (i.e., a literal/fixed structure)
|
|
453
|
+
// Variadic captures and non-variadic captures are both non-deterministic
|
|
454
|
+
if (nextCaptureMarker) {
|
|
455
|
+
allRemainingPatternsAreDeterministic = false;
|
|
547
456
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
457
|
+
}
|
|
458
|
+
const remainingTargetElements = targetElements.length - targetIdx;
|
|
459
|
+
const maxPossible = Math.min(remainingTargetElements - nonVariadicRemainingPatterns, max);
|
|
460
|
+
// Pivot detection optimization: try to find where next pattern matches
|
|
461
|
+
// This avoids unnecessary backtracking when constraints make the split point obvious
|
|
462
|
+
let pivotDetected = false;
|
|
463
|
+
let pivotAt = -1;
|
|
464
|
+
// Skip pivot detection if we're using deterministic optimization
|
|
465
|
+
// (when all remaining patterns are literals, there's only ONE valid consumption amount)
|
|
466
|
+
const useDeterministicOptimization = allRemainingPatternsAreDeterministic && maxPossible >= min && maxPossible <= max;
|
|
467
|
+
if (!useDeterministicOptimization && patternIdx + 1 < patternElements.length && min <= maxPossible) {
|
|
468
|
+
const nextPattern = patternElements[patternIdx + 1];
|
|
469
|
+
// Scan through possible consumption amounts starting from min
|
|
470
|
+
for (let tryConsume = min; tryConsume <= maxPossible; tryConsume++) {
|
|
471
|
+
// Check if element after our consumption would match next pattern
|
|
472
|
+
if (targetIdx + tryConsume < targetElements.length) {
|
|
473
|
+
const candidateElement = targetElements[targetIdx + tryConsume];
|
|
474
|
+
// Skip J.Empty for arguments
|
|
475
|
+
if (filterEmpty && candidateElement.element.kind === java_1.J.Kind.Empty) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
// Test if next pattern matches this element
|
|
479
|
+
const savedMatch = this.match;
|
|
480
|
+
const savedState = this.matcher.saveState();
|
|
481
|
+
await this.visitRightPadded(nextPattern, candidateElement);
|
|
482
|
+
const matchesNext = this.match;
|
|
483
|
+
this.match = savedMatch;
|
|
484
|
+
this.matcher.restoreState(savedState);
|
|
485
|
+
if (matchesNext) {
|
|
486
|
+
// Found pivot! Try this consumption amount first
|
|
487
|
+
pivotDetected = true;
|
|
488
|
+
pivotAt = tryConsume;
|
|
489
|
+
break;
|
|
554
490
|
}
|
|
555
491
|
}
|
|
556
492
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
493
|
+
}
|
|
494
|
+
// Determine consumption order
|
|
495
|
+
const consumptionOrder = [];
|
|
496
|
+
// OPTIMIZATION: If all remaining patterns are deterministic (literals, not captures),
|
|
497
|
+
// there's only ONE mathematically valid consumption amount. Skip backtracking entirely.
|
|
498
|
+
// Example: foo(${args}, 999) matching foo(1,2,42) -> args MUST be [1,2], only try consume=2
|
|
499
|
+
if (useDeterministicOptimization) {
|
|
500
|
+
consumptionOrder.push(maxPossible);
|
|
501
|
+
}
|
|
502
|
+
else if (pivotDetected && pivotAt >= 0) {
|
|
503
|
+
// Try pivot first, then others as fallback
|
|
504
|
+
consumptionOrder.push(pivotAt);
|
|
505
|
+
for (let c = maxPossible; c >= min; c--) {
|
|
506
|
+
if (c !== pivotAt) {
|
|
560
507
|
consumptionOrder.push(c);
|
|
561
508
|
}
|
|
562
509
|
}
|
|
563
|
-
for (const consume of consumptionOrder) {
|
|
564
|
-
// Capture elements for this consumption amount
|
|
565
|
-
// For empty argument lists, there will be a single J.Empty element that we need to filter out
|
|
566
|
-
const rawWrappers = targetElements.slice(targetIdx, targetIdx + consume);
|
|
567
|
-
const capturedWrappers = filterEmpty
|
|
568
|
-
? rawWrappers.filter(w => w.element.kind !== java_1.J.Kind.Empty)
|
|
569
|
-
: rawWrappers;
|
|
570
|
-
const capturedElements = capturedWrappers.map(w => w.element);
|
|
571
|
-
// Check min/max constraints against filtered elements
|
|
572
|
-
if (capturedElements.length < min || capturedElements.length > max) {
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
// Evaluate constraint for variadic capture
|
|
576
|
-
// For variadic captures, constraint receives the entire array of captured elements
|
|
577
|
-
// The targetCursor points to the parent container (always defined in container matching)
|
|
578
|
-
if (captureMarker.constraint) {
|
|
579
|
-
const cursor = this.targetCursor || new __1.Cursor(targetElements[0]);
|
|
580
|
-
if (!captureMarker.constraint(capturedElements, this.buildConstraintContext(cursor))) {
|
|
581
|
-
continue; // Try next consumption amount
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
// Save current state for backtracking
|
|
585
|
-
const savedState = this.matcher.saveState();
|
|
586
|
-
// Handle the variadic capture
|
|
587
|
-
const success = this.matcher.handleVariadicCapture(captureMarker, capturedElements, capturedWrappers);
|
|
588
|
-
if (!success) {
|
|
589
|
-
// Restore state and try next amount
|
|
590
|
-
this.matcher.restoreState(savedState);
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
// Try to match the rest of the pattern
|
|
594
|
-
const restMatches = yield this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + consume, filterEmpty);
|
|
595
|
-
if (restMatches) {
|
|
596
|
-
return true; // Found a valid matching
|
|
597
|
-
}
|
|
598
|
-
// Backtrack: restore state and try next amount
|
|
599
|
-
this.matcher.restoreState(savedState);
|
|
600
|
-
}
|
|
601
|
-
return false; // No consumption amount worked
|
|
602
510
|
}
|
|
603
511
|
else {
|
|
604
|
-
//
|
|
605
|
-
|
|
606
|
-
|
|
512
|
+
// Greedy approach: max to min
|
|
513
|
+
for (let c = maxPossible; c >= min; c--) {
|
|
514
|
+
consumptionOrder.push(c);
|
|
607
515
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
//
|
|
611
|
-
|
|
612
|
-
|
|
516
|
+
}
|
|
517
|
+
for (const consume of consumptionOrder) {
|
|
518
|
+
// Capture elements for this consumption amount
|
|
519
|
+
// For empty argument lists, there will be a single J.Empty element that we need to filter out
|
|
520
|
+
const rawWrappers = targetElements.slice(targetIdx, targetIdx + consume);
|
|
521
|
+
const capturedWrappers = filterEmpty
|
|
522
|
+
? rawWrappers.filter(w => w.element.kind !== java_1.J.Kind.Empty)
|
|
523
|
+
: rawWrappers;
|
|
524
|
+
const capturedElements = capturedWrappers.map(w => w.element);
|
|
525
|
+
// Check min/max constraints against filtered elements
|
|
526
|
+
if (capturedElements.length < min || capturedElements.length > max) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
// Evaluate constraint for variadic capture
|
|
530
|
+
// For variadic captures, constraint receives the entire array of captured elements
|
|
531
|
+
// The targetCursor points to the parent container (always defined in container matching)
|
|
532
|
+
if (captureMarker.constraint) {
|
|
533
|
+
const cursor = this.targetCursor || new __1.Cursor(targetElements[0]);
|
|
534
|
+
if (!captureMarker.constraint(capturedElements, this.buildConstraintContext(cursor))) {
|
|
535
|
+
continue; // Try next consumption amount
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
// Save current state for backtracking
|
|
539
|
+
const savedState = this.matcher.saveState();
|
|
540
|
+
// Handle the variadic capture
|
|
541
|
+
const success = this.matcher.handleVariadicCapture(captureMarker, capturedElements, capturedWrappers);
|
|
542
|
+
if (!success) {
|
|
543
|
+
// Restore state and try next amount
|
|
544
|
+
this.matcher.restoreState(savedState);
|
|
545
|
+
continue;
|
|
613
546
|
}
|
|
614
|
-
|
|
615
|
-
|
|
547
|
+
// Try to match the rest of the pattern
|
|
548
|
+
const restMatches = await this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + consume, filterEmpty);
|
|
549
|
+
if (restMatches) {
|
|
550
|
+
return true; // Found a valid matching
|
|
616
551
|
}
|
|
617
|
-
//
|
|
618
|
-
|
|
552
|
+
// Backtrack: restore state and try next amount
|
|
553
|
+
this.matcher.restoreState(savedState);
|
|
619
554
|
}
|
|
620
|
-
|
|
555
|
+
return false; // No consumption amount worked
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
// Regular non-variadic element - must match exactly one target element
|
|
559
|
+
if (targetIdx >= targetElements.length) {
|
|
560
|
+
return false; // Pattern has more elements than target
|
|
561
|
+
}
|
|
562
|
+
const targetWrapper = targetElements[targetIdx];
|
|
563
|
+
const targetElement = targetWrapper.element;
|
|
564
|
+
// For arguments, J.Empty represents no argument, so regular captures should not match it
|
|
565
|
+
if (filterEmpty && targetElement.kind === java_1.J.Kind.Empty) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
if (!await this.visitSequenceElement(patternWrapper, targetWrapper, targetIdx)) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
// Continue matching the rest
|
|
572
|
+
return await this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + 1, filterEmpty);
|
|
573
|
+
}
|
|
621
574
|
}
|
|
622
575
|
/**
|
|
623
576
|
* Visit a single element in a sequence during non-variadic matching.
|
|
@@ -628,20 +581,18 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
628
581
|
* @param targetIdx The index in the target sequence
|
|
629
582
|
* @returns true if matching succeeded, false otherwise
|
|
630
583
|
*/
|
|
631
|
-
visitSequenceElement(patternWrapper, targetWrapper, targetIdx) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
return true;
|
|
644
|
-
});
|
|
584
|
+
async visitSequenceElement(patternWrapper, targetWrapper, targetIdx) {
|
|
585
|
+
// Save current state for backtracking (both match state and capture bindings)
|
|
586
|
+
const savedMatch = this.match;
|
|
587
|
+
const savedState = this.matcher.saveState();
|
|
588
|
+
await this.visitRightPadded(patternWrapper, targetWrapper);
|
|
589
|
+
if (!this.match) {
|
|
590
|
+
// Restore state on match failure
|
|
591
|
+
this.match = savedMatch;
|
|
592
|
+
this.matcher.restoreState(savedState);
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
return true;
|
|
645
596
|
}
|
|
646
597
|
}
|
|
647
598
|
exports.PatternMatchingComparator = PatternMatchingComparator;
|
|
@@ -789,467 +740,431 @@ class DebugPatternMatchingComparator extends PatternMatchingComparator {
|
|
|
789
740
|
}
|
|
790
741
|
}
|
|
791
742
|
}
|
|
792
|
-
visit(j, p, parent) {
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
:
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
this.debug.
|
|
807
|
-
const constraintResult = captureMarker.constraint(p, this.buildConstraintContext(cursorAtCapturedNode));
|
|
808
|
-
if (!constraintResult) {
|
|
809
|
-
this.debug.log('info', 'constraint', `Constraint failed for capture: ${captureMarker.captureName}`);
|
|
810
|
-
this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${p.kind}`, `Constraint evaluation returned false`);
|
|
811
|
-
return this.abort(j);
|
|
812
|
-
}
|
|
813
|
-
this.debug.log('debug', 'constraint', `Constraint passed for capture: ${captureMarker.captureName}`);
|
|
814
|
-
}
|
|
815
|
-
const success = this.matcher.handleCapture(captureMarker, p, undefined);
|
|
816
|
-
if (!success) {
|
|
743
|
+
async visit(j, p, parent) {
|
|
744
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(j);
|
|
745
|
+
if (captureMarker) {
|
|
746
|
+
const savedTargetCursor = this.targetCursor;
|
|
747
|
+
const cursorAtCapturedNode = this.targetCursor !== undefined
|
|
748
|
+
? new __1.Cursor(p, this.targetCursor)
|
|
749
|
+
: new __1.Cursor(p);
|
|
750
|
+
this.targetCursor = cursorAtCapturedNode;
|
|
751
|
+
try {
|
|
752
|
+
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
753
|
+
this.debug.log('debug', 'constraint', `Evaluating constraint for capture: ${captureMarker.captureName}`);
|
|
754
|
+
const constraintResult = captureMarker.constraint(p, this.buildConstraintContext(cursorAtCapturedNode));
|
|
755
|
+
if (!constraintResult) {
|
|
756
|
+
this.debug.log('info', 'constraint', `Constraint failed for capture: ${captureMarker.captureName}`);
|
|
757
|
+
this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${p.kind}`, `Constraint evaluation returned false`);
|
|
817
758
|
return this.abort(j);
|
|
818
759
|
}
|
|
819
|
-
|
|
760
|
+
this.debug.log('debug', 'constraint', `Constraint passed for capture: ${captureMarker.captureName}`);
|
|
820
761
|
}
|
|
821
|
-
|
|
822
|
-
|
|
762
|
+
const success = this.matcher.handleCapture(captureMarker, p, undefined);
|
|
763
|
+
if (!success) {
|
|
764
|
+
return this.abort(j);
|
|
823
765
|
}
|
|
824
|
-
}
|
|
825
|
-
return yield _super.visit.call(this, j, p, parent);
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
visitElement(j, other) {
|
|
829
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
830
|
-
if (!this.match) {
|
|
831
766
|
return j;
|
|
832
767
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
return this.abort(j, 'kind-mismatch', 'kind', kindStr, this.formatKind(other.kind));
|
|
768
|
+
finally {
|
|
769
|
+
this.targetCursor = savedTargetCursor;
|
|
836
770
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
this.debug.popPath();
|
|
861
|
-
this.debug.popPath();
|
|
862
|
-
}
|
|
863
|
-
}
|
|
771
|
+
}
|
|
772
|
+
return await super.visit(j, p, parent);
|
|
773
|
+
}
|
|
774
|
+
async visitElement(j, other) {
|
|
775
|
+
if (!this.match) {
|
|
776
|
+
return j;
|
|
777
|
+
}
|
|
778
|
+
const kindStr = this.formatKind(j.kind);
|
|
779
|
+
if (j.kind !== other.kind) {
|
|
780
|
+
return this.abort(j, 'kind-mismatch', 'kind', kindStr, this.formatKind(other.kind));
|
|
781
|
+
}
|
|
782
|
+
for (const key of Object.keys(j)) {
|
|
783
|
+
if (key.startsWith('_') || key === 'kind' || key === 'id' || key === 'markers' || key === 'prefix') {
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
const jValue = j[key];
|
|
787
|
+
const otherValue = other[key];
|
|
788
|
+
if (Array.isArray(jValue)) {
|
|
789
|
+
if (!Array.isArray(otherValue) || jValue.length !== otherValue.length) {
|
|
790
|
+
this.debug.pushPath(`${kindStr}#${key}`);
|
|
791
|
+
const result = this.abort(j, 'array-length-mismatch', key, jValue.length, Array.isArray(otherValue) ? otherValue.length : otherValue);
|
|
792
|
+
this.debug.popPath();
|
|
793
|
+
return result;
|
|
864
794
|
}
|
|
865
|
-
|
|
795
|
+
for (let i = 0; i < jValue.length; i++) {
|
|
866
796
|
this.debug.pushPath(`${kindStr}#${key}`);
|
|
797
|
+
this.debug.pushPath(i.toString());
|
|
867
798
|
try {
|
|
868
|
-
|
|
799
|
+
await this.visitProperty(jValue[i], otherValue[i]);
|
|
869
800
|
if (!this.match) {
|
|
870
801
|
return j;
|
|
871
802
|
}
|
|
872
803
|
}
|
|
873
804
|
finally {
|
|
874
805
|
this.debug.popPath();
|
|
806
|
+
this.debug.popPath();
|
|
875
807
|
}
|
|
876
808
|
}
|
|
877
809
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
visitRightPadded(right, p) {
|
|
882
|
-
const _super = Object.create(null, {
|
|
883
|
-
visitRightPadded: { get: () => super.visitRightPadded }
|
|
884
|
-
});
|
|
885
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
886
|
-
if (!this.match) {
|
|
887
|
-
return right;
|
|
888
|
-
}
|
|
889
|
-
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(right);
|
|
890
|
-
if (captureMarker) {
|
|
891
|
-
const isRightPadded = p.kind === java_1.J.Kind.RightPadded;
|
|
892
|
-
const targetWrapper = isRightPadded ? p : undefined;
|
|
893
|
-
const targetElement = isRightPadded ? targetWrapper.element : p;
|
|
894
|
-
const savedTargetCursor = this.targetCursor;
|
|
895
|
-
const cursorAtCapturedNode = this.targetCursor !== undefined
|
|
896
|
-
? (targetWrapper ? new __1.Cursor(targetWrapper, this.targetCursor) : new __1.Cursor(targetElement, this.targetCursor))
|
|
897
|
-
: (targetWrapper ? new __1.Cursor(targetWrapper) : new __1.Cursor(targetElement));
|
|
898
|
-
this.targetCursor = cursorAtCapturedNode;
|
|
810
|
+
else {
|
|
811
|
+
this.debug.pushPath(`${kindStr}#${key}`);
|
|
899
812
|
try {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (!constraintResult) {
|
|
904
|
-
this.debug.log('info', 'constraint', `Constraint failed for wrapped capture: ${captureMarker.captureName}`);
|
|
905
|
-
this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${targetElement.kind}`, `Constraint evaluation returned false`);
|
|
906
|
-
return this.abort(right);
|
|
907
|
-
}
|
|
908
|
-
this.debug.log('debug', 'constraint', `Constraint passed for wrapped capture: ${captureMarker.captureName}`);
|
|
813
|
+
await this.visitProperty(jValue, otherValue);
|
|
814
|
+
if (!this.match) {
|
|
815
|
+
return j;
|
|
909
816
|
}
|
|
910
|
-
const success = this.matcher.handleCapture(captureMarker, targetElement, targetWrapper);
|
|
911
|
-
if (!success) {
|
|
912
|
-
return this.abort(right);
|
|
913
|
-
}
|
|
914
|
-
return right;
|
|
915
817
|
}
|
|
916
818
|
finally {
|
|
917
|
-
this.
|
|
819
|
+
this.debug.popPath();
|
|
918
820
|
}
|
|
919
821
|
}
|
|
920
|
-
|
|
921
|
-
|
|
822
|
+
}
|
|
823
|
+
return j;
|
|
922
824
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const otherContainer = p;
|
|
933
|
-
const hasVariadicCapture = container.elements.some(elem => utils_1.PlaceholderUtils.isVariadicCapture(elem));
|
|
934
|
-
const savedCursor = this.cursor;
|
|
825
|
+
async visitRightPadded(right, p) {
|
|
826
|
+
if (!this.match) {
|
|
827
|
+
return right;
|
|
828
|
+
}
|
|
829
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(right);
|
|
830
|
+
if (captureMarker) {
|
|
831
|
+
const isRightPadded = p.kind === java_1.J.Kind.RightPadded;
|
|
832
|
+
const targetWrapper = isRightPadded ? p : undefined;
|
|
833
|
+
const targetElement = isRightPadded ? targetWrapper.element : p;
|
|
935
834
|
const savedTargetCursor = this.targetCursor;
|
|
936
|
-
|
|
937
|
-
|
|
835
|
+
const cursorAtCapturedNode = this.targetCursor !== undefined
|
|
836
|
+
? (targetWrapper ? new __1.Cursor(targetWrapper, this.targetCursor) : new __1.Cursor(targetElement, this.targetCursor))
|
|
837
|
+
: (targetWrapper ? new __1.Cursor(targetWrapper) : new __1.Cursor(targetElement));
|
|
838
|
+
this.targetCursor = cursorAtCapturedNode;
|
|
938
839
|
try {
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
840
|
+
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
841
|
+
this.debug.log('debug', 'constraint', `Evaluating constraint for wrapped capture: ${captureMarker.captureName}`);
|
|
842
|
+
const constraintResult = captureMarker.constraint(targetElement, this.buildConstraintContext(cursorAtCapturedNode));
|
|
843
|
+
if (!constraintResult) {
|
|
844
|
+
this.debug.log('info', 'constraint', `Constraint failed for wrapped capture: ${captureMarker.captureName}`);
|
|
845
|
+
this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${targetElement.kind}`, `Constraint evaluation returned false`);
|
|
846
|
+
return this.abort(right);
|
|
942
847
|
}
|
|
848
|
+
this.debug.log('debug', 'constraint', `Constraint passed for wrapped capture: ${captureMarker.captureName}`);
|
|
943
849
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
return this.arrayLengthMismatch('elements');
|
|
948
|
-
}
|
|
949
|
-
for (let i = 0; i < container.elements.length; i++) {
|
|
950
|
-
this.debug.pushPath(i.toString());
|
|
951
|
-
try {
|
|
952
|
-
if (!(yield this.visitContainerElement(container.elements[i], otherContainer.elements[i], i))) {
|
|
953
|
-
return container;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
finally {
|
|
957
|
-
this.debug.popPath();
|
|
958
|
-
}
|
|
959
|
-
}
|
|
850
|
+
const success = this.matcher.handleCapture(captureMarker, targetElement, targetWrapper);
|
|
851
|
+
if (!success) {
|
|
852
|
+
return this.abort(right);
|
|
960
853
|
}
|
|
854
|
+
return right;
|
|
961
855
|
}
|
|
962
856
|
finally {
|
|
963
|
-
this.cursor = savedCursor;
|
|
964
857
|
this.targetCursor = savedTargetCursor;
|
|
965
858
|
}
|
|
859
|
+
}
|
|
860
|
+
return await super.visitRightPadded(right, p);
|
|
861
|
+
}
|
|
862
|
+
async visitContainer(container, p) {
|
|
863
|
+
if (!this.match) {
|
|
966
864
|
return container;
|
|
967
|
-
}
|
|
865
|
+
}
|
|
866
|
+
const isContainer = p.kind === java_1.J.Kind.Container;
|
|
867
|
+
if (!isContainer) {
|
|
868
|
+
return this.abort(container);
|
|
869
|
+
}
|
|
870
|
+
const otherContainer = p;
|
|
871
|
+
const hasVariadicCapture = container.elements.some(elem => utils_1.PlaceholderUtils.isVariadicCapture(elem));
|
|
872
|
+
const savedCursor = this.cursor;
|
|
873
|
+
const savedTargetCursor = this.targetCursor;
|
|
874
|
+
this.cursor = new __1.Cursor(container, this.cursor);
|
|
875
|
+
this.targetCursor = new __1.Cursor(otherContainer, this.targetCursor);
|
|
876
|
+
try {
|
|
877
|
+
if (hasVariadicCapture) {
|
|
878
|
+
if (!await this.matchSequence(container.elements, otherContainer.elements, true)) {
|
|
879
|
+
return this.arrayLengthMismatch('elements');
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
// Non-variadic path - track indices
|
|
884
|
+
if (container.elements.length !== otherContainer.elements.length) {
|
|
885
|
+
return this.arrayLengthMismatch('elements');
|
|
886
|
+
}
|
|
887
|
+
for (let i = 0; i < container.elements.length; i++) {
|
|
888
|
+
this.debug.pushPath(i.toString());
|
|
889
|
+
try {
|
|
890
|
+
if (!await this.visitContainerElement(container.elements[i], otherContainer.elements[i], i)) {
|
|
891
|
+
return container;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
finally {
|
|
895
|
+
this.debug.popPath();
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
finally {
|
|
901
|
+
this.cursor = savedCursor;
|
|
902
|
+
this.targetCursor = savedTargetCursor;
|
|
903
|
+
}
|
|
904
|
+
return container;
|
|
968
905
|
}
|
|
969
906
|
/**
|
|
970
907
|
* Override visitContainerProperty to add path tracking with property context.
|
|
971
908
|
*/
|
|
972
|
-
visitContainerProperty(propertyName, container, otherContainer) {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
}
|
|
986
|
-
});
|
|
909
|
+
async visitContainerProperty(propertyName, container, otherContainer) {
|
|
910
|
+
// Get parent from cursor
|
|
911
|
+
const parent = this.cursor.value;
|
|
912
|
+
// Push path for the property
|
|
913
|
+
const kindStr = this.formatKind(parent.kind);
|
|
914
|
+
this.debug.pushPath(`${kindStr}#${propertyName}`);
|
|
915
|
+
try {
|
|
916
|
+
await this.visitContainer(container, otherContainer);
|
|
917
|
+
return container;
|
|
918
|
+
}
|
|
919
|
+
finally {
|
|
920
|
+
this.debug.popPath();
|
|
921
|
+
}
|
|
987
922
|
}
|
|
988
923
|
/**
|
|
989
924
|
* Override visitRightPaddedProperty to add path tracking with property context.
|
|
990
925
|
*/
|
|
991
|
-
visitRightPaddedProperty(propertyName, rightPadded, otherRightPadded) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
926
|
+
async visitRightPaddedProperty(propertyName, rightPadded, otherRightPadded) {
|
|
927
|
+
// Get parent from cursor
|
|
928
|
+
const parent = this.cursor.value;
|
|
929
|
+
// Push path for the property
|
|
930
|
+
const kindStr = this.formatKind(parent.kind);
|
|
931
|
+
this.debug.pushPath(`${kindStr}#${propertyName}`);
|
|
932
|
+
try {
|
|
933
|
+
return await this.visitRightPadded(rightPadded, otherRightPadded);
|
|
934
|
+
}
|
|
935
|
+
finally {
|
|
936
|
+
this.debug.popPath();
|
|
937
|
+
}
|
|
1005
938
|
}
|
|
1006
939
|
/**
|
|
1007
940
|
* Override visitLeftPaddedProperty to add path tracking with property context.
|
|
1008
941
|
*/
|
|
1009
|
-
visitLeftPaddedProperty(propertyName, leftPadded, otherLeftPadded) {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1022
|
-
});
|
|
942
|
+
async visitLeftPaddedProperty(propertyName, leftPadded, otherLeftPadded) {
|
|
943
|
+
// Get parent from cursor
|
|
944
|
+
const parent = this.cursor.value;
|
|
945
|
+
// Push path for the property
|
|
946
|
+
const kindStr = this.formatKind(parent.kind);
|
|
947
|
+
this.debug.pushPath(`${kindStr}#${propertyName}`);
|
|
948
|
+
try {
|
|
949
|
+
return await this.visitLeftPadded(leftPadded, otherLeftPadded);
|
|
950
|
+
}
|
|
951
|
+
finally {
|
|
952
|
+
this.debug.popPath();
|
|
953
|
+
}
|
|
1023
954
|
}
|
|
1024
|
-
visitContainerElement(element, otherElement, index) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
});
|
|
1028
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1029
|
-
// Don't push index here - it should be handled by the caller with proper context
|
|
1030
|
-
return yield _super.visitContainerElement.call(this, element, otherElement, index);
|
|
1031
|
-
});
|
|
955
|
+
async visitContainerElement(element, otherElement, index) {
|
|
956
|
+
// Don't push index here - it should be handled by the caller with proper context
|
|
957
|
+
return await super.visitContainerElement(element, otherElement, index);
|
|
1032
958
|
}
|
|
1033
|
-
visitArrayProperty(parent, propertyName, array1, array2, visitor) {
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
|
-
// Visit each element with index tracking
|
|
1045
|
-
for (let i = 0; i < array1.length; i++) {
|
|
1046
|
-
this.debug.pushPath(i.toString());
|
|
1047
|
-
try {
|
|
1048
|
-
yield visitor(array1[i], array2[i], i);
|
|
1049
|
-
if (!this.match) {
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
finally {
|
|
1054
|
-
this.debug.popPath();
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
finally {
|
|
1059
|
-
this.debug.popPath();
|
|
959
|
+
async visitArrayProperty(parent, propertyName, array1, array2, visitor) {
|
|
960
|
+
// Push path for the property
|
|
961
|
+
const kindStr = this.formatKind(parent.kind);
|
|
962
|
+
this.debug.pushPath(`${kindStr}#${propertyName}`);
|
|
963
|
+
try {
|
|
964
|
+
// Check length mismatch (will have path context)
|
|
965
|
+
if (array1.length !== array2.length) {
|
|
966
|
+
this.arrayLengthMismatch(propertyName);
|
|
967
|
+
return;
|
|
1060
968
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
// Push path component for the container
|
|
1070
|
-
// Extract kind from cursors if available
|
|
1071
|
-
const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
|
|
1072
|
-
if (pattern && pattern.kind) {
|
|
1073
|
-
const kindStr = this.formatKind(pattern.kind);
|
|
1074
|
-
// Determine property name based on the kind
|
|
1075
|
-
let propertyName = 'elements';
|
|
1076
|
-
if (pattern.kind.includes('MethodInvocation')) {
|
|
1077
|
-
propertyName = 'arguments';
|
|
1078
|
-
}
|
|
1079
|
-
else if (pattern.kind.includes('Block')) {
|
|
1080
|
-
propertyName = 'statements';
|
|
969
|
+
// Visit each element with index tracking
|
|
970
|
+
for (let i = 0; i < array1.length; i++) {
|
|
971
|
+
this.debug.pushPath(i.toString());
|
|
972
|
+
try {
|
|
973
|
+
await visitor(array1[i], array2[i], i);
|
|
974
|
+
if (!this.match) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
1081
977
|
}
|
|
1082
|
-
|
|
1083
|
-
}
|
|
1084
|
-
try {
|
|
1085
|
-
return yield _super.matchSequence.call(this, patternElements, targetElements, filterEmpty);
|
|
1086
|
-
}
|
|
1087
|
-
finally {
|
|
1088
|
-
if ((_b = this.cursor) === null || _b === void 0 ? void 0 : _b.value) {
|
|
978
|
+
finally {
|
|
1089
979
|
this.debug.popPath();
|
|
1090
980
|
}
|
|
1091
981
|
}
|
|
1092
|
-
}
|
|
982
|
+
}
|
|
983
|
+
finally {
|
|
984
|
+
this.debug.popPath();
|
|
985
|
+
}
|
|
1093
986
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
// Restore state on match failure
|
|
1106
|
-
this.match = savedMatch;
|
|
1107
|
-
this.matcher.restoreState(savedState);
|
|
1108
|
-
// Restore the explanation if one was set during matching
|
|
1109
|
-
if (explanation) {
|
|
1110
|
-
this.debug.restoreExplanation(explanation);
|
|
1111
|
-
}
|
|
1112
|
-
return false;
|
|
1113
|
-
}
|
|
1114
|
-
return true;
|
|
987
|
+
async matchSequence(patternElements, targetElements, filterEmpty) {
|
|
988
|
+
var _a, _b;
|
|
989
|
+
// Push path component for the container
|
|
990
|
+
// Extract kind from cursors if available
|
|
991
|
+
const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
|
|
992
|
+
if (pattern && pattern.kind) {
|
|
993
|
+
const kindStr = this.formatKind(pattern.kind);
|
|
994
|
+
// Determine property name based on the kind
|
|
995
|
+
let propertyName = 'elements';
|
|
996
|
+
if (pattern.kind.includes('MethodInvocation')) {
|
|
997
|
+
propertyName = 'arguments';
|
|
1115
998
|
}
|
|
1116
|
-
|
|
999
|
+
else if (pattern.kind.includes('Block')) {
|
|
1000
|
+
propertyName = 'statements';
|
|
1001
|
+
}
|
|
1002
|
+
this.debug.pushPath(`${kindStr}#${propertyName}`);
|
|
1003
|
+
}
|
|
1004
|
+
try {
|
|
1005
|
+
return await super.matchSequence(patternElements, targetElements, filterEmpty);
|
|
1006
|
+
}
|
|
1007
|
+
finally {
|
|
1008
|
+
if ((_b = this.cursor) === null || _b === void 0 ? void 0 : _b.value) {
|
|
1117
1009
|
this.debug.popPath();
|
|
1118
1010
|
}
|
|
1119
|
-
}
|
|
1011
|
+
}
|
|
1120
1012
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternElements[i]);
|
|
1138
|
-
const nextIsVariadic = (nextCaptureMarker === null || nextCaptureMarker === void 0 ? void 0 : nextCaptureMarker.variadicOptions) !== undefined;
|
|
1139
|
-
if (!nextIsVariadic) {
|
|
1140
|
-
nonVariadicRemainingPatterns++;
|
|
1141
|
-
}
|
|
1142
|
-
if (nextCaptureMarker) {
|
|
1143
|
-
allRemainingPatternsAreDeterministic = false;
|
|
1144
|
-
}
|
|
1013
|
+
async visitSequenceElement(patternWrapper, targetWrapper, targetIdx) {
|
|
1014
|
+
this.debug.pushPath(targetIdx.toString());
|
|
1015
|
+
try {
|
|
1016
|
+
// Save current state for backtracking (both match state and capture bindings)
|
|
1017
|
+
const savedMatch = this.match;
|
|
1018
|
+
const savedState = this.matcher.saveState();
|
|
1019
|
+
await this.visitRightPadded(patternWrapper, targetWrapper);
|
|
1020
|
+
if (!this.match) {
|
|
1021
|
+
// Preserve explanation before restoring state
|
|
1022
|
+
const explanation = this.debug.getExplanation();
|
|
1023
|
+
// Restore state on match failure
|
|
1024
|
+
this.match = savedMatch;
|
|
1025
|
+
this.matcher.restoreState(savedState);
|
|
1026
|
+
// Restore the explanation if one was set during matching
|
|
1027
|
+
if (explanation) {
|
|
1028
|
+
this.debug.restoreExplanation(explanation);
|
|
1145
1029
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
}
|
|
1030
|
+
return false;
|
|
1031
|
+
}
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
finally {
|
|
1035
|
+
this.debug.popPath();
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
async matchSequenceOptimized(patternElements, targetElements, patternIdx, targetIdx, filterEmpty) {
|
|
1039
|
+
var _a, _b;
|
|
1040
|
+
if (patternIdx >= patternElements.length) {
|
|
1041
|
+
return targetIdx >= targetElements.length;
|
|
1042
|
+
}
|
|
1043
|
+
const patternWrapper = patternElements[patternIdx];
|
|
1044
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternWrapper);
|
|
1045
|
+
const isVariadic = (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
|
|
1046
|
+
if (isVariadic) {
|
|
1047
|
+
const variadicOptions = captureMarker.variadicOptions;
|
|
1048
|
+
const min = (_a = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.min) !== null && _a !== void 0 ? _a : 0;
|
|
1049
|
+
const max = (_b = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.max) !== null && _b !== void 0 ? _b : Infinity;
|
|
1050
|
+
let nonVariadicRemainingPatterns = 0;
|
|
1051
|
+
let allRemainingPatternsAreDeterministic = true;
|
|
1052
|
+
for (let i = patternIdx + 1; i < patternElements.length; i++) {
|
|
1053
|
+
const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternElements[i]);
|
|
1054
|
+
const nextIsVariadic = (nextCaptureMarker === null || nextCaptureMarker === void 0 ? void 0 : nextCaptureMarker.variadicOptions) !== undefined;
|
|
1055
|
+
if (!nextIsVariadic) {
|
|
1056
|
+
nonVariadicRemainingPatterns++;
|
|
1174
1057
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
// there's only ONE mathematically valid consumption amount. Skip backtracking entirely.
|
|
1178
|
-
// Example: foo(${args}, 999) matching foo(1,2,42) -> args MUST be [1,2], only try consume=2
|
|
1179
|
-
if (useDeterministicOptimization) {
|
|
1180
|
-
consumptionOrder.push(maxPossible);
|
|
1058
|
+
if (nextCaptureMarker) {
|
|
1059
|
+
allRemainingPatternsAreDeterministic = false;
|
|
1181
1060
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1061
|
+
}
|
|
1062
|
+
const remainingTargetElements = targetElements.length - targetIdx;
|
|
1063
|
+
const maxPossible = Math.min(remainingTargetElements - nonVariadicRemainingPatterns, max);
|
|
1064
|
+
let pivotDetected = false;
|
|
1065
|
+
let pivotAt = -1;
|
|
1066
|
+
// Skip pivot detection if we're using deterministic optimization
|
|
1067
|
+
// (when all remaining patterns are literals, there's only ONE valid consumption amount)
|
|
1068
|
+
const useDeterministicOptimization = allRemainingPatternsAreDeterministic && maxPossible >= min && maxPossible <= max;
|
|
1069
|
+
if (!useDeterministicOptimization && patternIdx + 1 < patternElements.length && min <= maxPossible) {
|
|
1070
|
+
const nextPattern = patternElements[patternIdx + 1];
|
|
1071
|
+
for (let tryConsume = min; tryConsume <= maxPossible; tryConsume++) {
|
|
1072
|
+
if (targetIdx + tryConsume < targetElements.length) {
|
|
1073
|
+
const candidateElement = targetElements[targetIdx + tryConsume];
|
|
1074
|
+
if (filterEmpty && candidateElement.element.kind === java_1.J.Kind.Empty) {
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
const savedMatch = this.match;
|
|
1078
|
+
const savedState = this.matcher.saveState();
|
|
1079
|
+
await this.visitRightPadded(nextPattern, candidateElement);
|
|
1080
|
+
const matchesNext = this.match;
|
|
1081
|
+
this.match = savedMatch;
|
|
1082
|
+
this.matcher.restoreState(savedState);
|
|
1083
|
+
if (matchesNext) {
|
|
1084
|
+
pivotDetected = true;
|
|
1085
|
+
pivotAt = tryConsume;
|
|
1086
|
+
break;
|
|
1187
1087
|
}
|
|
1188
1088
|
}
|
|
1189
1089
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1090
|
+
}
|
|
1091
|
+
const consumptionOrder = [];
|
|
1092
|
+
// OPTIMIZATION: If all remaining patterns are deterministic (literals, not captures),
|
|
1093
|
+
// there's only ONE mathematically valid consumption amount. Skip backtracking entirely.
|
|
1094
|
+
// Example: foo(${args}, 999) matching foo(1,2,42) -> args MUST be [1,2], only try consume=2
|
|
1095
|
+
if (useDeterministicOptimization) {
|
|
1096
|
+
consumptionOrder.push(maxPossible);
|
|
1097
|
+
}
|
|
1098
|
+
else if (pivotDetected && pivotAt >= 0) {
|
|
1099
|
+
consumptionOrder.push(pivotAt);
|
|
1100
|
+
for (let c = maxPossible; c >= min; c--) {
|
|
1101
|
+
if (c !== pivotAt) {
|
|
1192
1102
|
consumptionOrder.push(c);
|
|
1193
1103
|
}
|
|
1194
1104
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
if (!success) {
|
|
1220
|
-
this.matcher.restoreState(savedState);
|
|
1105
|
+
}
|
|
1106
|
+
else {
|
|
1107
|
+
for (let c = maxPossible; c >= min; c--) {
|
|
1108
|
+
consumptionOrder.push(c);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
for (const consume of consumptionOrder) {
|
|
1112
|
+
// Capture elements for this consumption amount
|
|
1113
|
+
// For empty argument lists, there will be a single J.Empty element that we need to filter out
|
|
1114
|
+
const rawWrappers = targetElements.slice(targetIdx, targetIdx + consume);
|
|
1115
|
+
const capturedWrappers = filterEmpty
|
|
1116
|
+
? rawWrappers.filter(w => w.element.kind !== java_1.J.Kind.Empty)
|
|
1117
|
+
: rawWrappers;
|
|
1118
|
+
const capturedElements = capturedWrappers.map(w => w.element);
|
|
1119
|
+
// Check min/max constraints against filtered elements
|
|
1120
|
+
if (capturedElements.length < min || capturedElements.length > max) {
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (captureMarker.constraint) {
|
|
1124
|
+
this.debug.log('debug', 'constraint', `Evaluating variadic constraint for capture: ${captureMarker.captureName} (${capturedElements.length} elements)`);
|
|
1125
|
+
const cursor = this.targetCursor || new __1.Cursor(targetElements[0]);
|
|
1126
|
+
const constraintResult = captureMarker.constraint(capturedElements, this.buildConstraintContext(cursor));
|
|
1127
|
+
if (!constraintResult) {
|
|
1128
|
+
this.debug.log('info', 'constraint', `Variadic constraint failed for capture: ${captureMarker.captureName}`);
|
|
1221
1129
|
continue;
|
|
1222
1130
|
}
|
|
1223
|
-
|
|
1224
|
-
if (restMatches) {
|
|
1225
|
-
return true;
|
|
1226
|
-
}
|
|
1227
|
-
// Preserve explanation from this failed attempt before restoring state
|
|
1228
|
-
// This is especially important when using deterministic optimization (only one attempt)
|
|
1229
|
-
const currentExplanation = this.debug.getExplanation();
|
|
1230
|
-
this.matcher.restoreState(savedState);
|
|
1231
|
-
// Restore the explanation if one was set during this attempt
|
|
1232
|
-
if (currentExplanation) {
|
|
1233
|
-
this.debug.restoreExplanation(currentExplanation);
|
|
1234
|
-
}
|
|
1131
|
+
this.debug.log('debug', 'constraint', `Variadic constraint passed for capture: ${captureMarker.captureName}`);
|
|
1235
1132
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1133
|
+
const savedState = this.matcher.saveState();
|
|
1134
|
+
const success = this.matcher.handleVariadicCapture(captureMarker, capturedElements, capturedWrappers);
|
|
1135
|
+
if (!success) {
|
|
1136
|
+
this.matcher.restoreState(savedState);
|
|
1137
|
+
continue;
|
|
1241
1138
|
}
|
|
1242
|
-
const
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
return false;
|
|
1139
|
+
const restMatches = await this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + consume, filterEmpty);
|
|
1140
|
+
if (restMatches) {
|
|
1141
|
+
return true;
|
|
1246
1142
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1143
|
+
// Preserve explanation from this failed attempt before restoring state
|
|
1144
|
+
// This is especially important when using deterministic optimization (only one attempt)
|
|
1145
|
+
const currentExplanation = this.debug.getExplanation();
|
|
1146
|
+
this.matcher.restoreState(savedState);
|
|
1147
|
+
// Restore the explanation if one was set during this attempt
|
|
1148
|
+
if (currentExplanation) {
|
|
1149
|
+
this.debug.restoreExplanation(currentExplanation);
|
|
1249
1150
|
}
|
|
1250
|
-
return yield this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + 1, filterEmpty);
|
|
1251
1151
|
}
|
|
1252
|
-
|
|
1152
|
+
return false;
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
if (targetIdx >= targetElements.length) {
|
|
1156
|
+
return false;
|
|
1157
|
+
}
|
|
1158
|
+
const targetWrapper = targetElements[targetIdx];
|
|
1159
|
+
const targetElement = targetWrapper.element;
|
|
1160
|
+
if (filterEmpty && targetElement.kind === java_1.J.Kind.Empty) {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
if (!await this.visitSequenceElement(patternWrapper, targetWrapper, targetIdx)) {
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
return await this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + 1, filterEmpty);
|
|
1167
|
+
}
|
|
1253
1168
|
}
|
|
1254
1169
|
}
|
|
1255
1170
|
exports.DebugPatternMatchingComparator = DebugPatternMatchingComparator;
|