ruvector 0.2.27 → 0.2.29
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/LICENSE +21 -21
- package/README.md +2270 -2270
- package/bin/cli.js +9570 -9479
- package/bin/mcp-server.js +3854 -3854
- package/dist/core/intelligence-engine.d.ts +13 -0
- package/dist/core/intelligence-engine.d.ts.map +1 -1
- package/dist/core/intelligence-engine.js +38 -0
- package/dist/core/onnx/bundled-parallel.mjs +164 -164
- package/dist/core/onnx/embed-worker.mjs +67 -67
- package/dist/core/onnx/loader.js +434 -434
- package/dist/core/onnx/package.json +3 -3
- package/dist/core/onnx/pkg/LICENSE +21 -21
- package/dist/core/onnx/pkg/loader.js +348 -348
- package/dist/core/onnx/pkg/package.json +3 -3
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.d.ts +112 -112
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.js +5 -5
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.js +638 -638
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm.d.ts +29 -29
- package/dist/core/onnx-embedder.d.ts.map +1 -1
- package/dist/core/onnx-embedder.js +24 -30
- package/dist/core/parallel-workers.js +439 -439
- package/dist/workers/benchmark.js +15 -15
- package/package.json +122 -122
- package/src/decompiler/api-prober.js +302 -302
- package/src/decompiler/index.js +463 -463
- package/src/decompiler/metrics.js +86 -86
- package/src/decompiler/model-decompiler.js +423 -423
- package/src/decompiler/module-splitter.js +498 -498
- package/src/decompiler/module-tree.js +142 -142
- package/src/decompiler/name-predictor.js +400 -400
- package/src/decompiler/npm-fetch.js +176 -176
- package/src/decompiler/reconstructor.js +499 -499
- package/src/decompiler/reference-tracker.js +285 -285
- package/src/decompiler/statement-parser.js +285 -285
- package/src/decompiler/style-improver.js +438 -438
- package/src/decompiler/subcategories.js +339 -339
- package/src/decompiler/validator.js +379 -379
- package/src/decompiler/witness.js +140 -140
- package/wasm/package.json +26 -26
- package/wasm/ruvector_decompiler_wasm.d.ts +27 -27
- package/wasm/ruvector_decompiler_wasm.js +220 -220
- package/wasm/ruvector_decompiler_wasm_bg.wasm.d.ts +16 -16
- package/dist/core/onnx/pkg/ruvector.db +0 -0
|
@@ -1,285 +1,285 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* statement-parser.js - Parse JavaScript source into top-level statements.
|
|
3
|
-
*
|
|
4
|
-
* Tracks brace/paren/bracket depth and string/template/regex contexts
|
|
5
|
-
* to split at true statement boundaries. Never splits a statement
|
|
6
|
-
* across modules -- a statement is atomic.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
'use strict';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Parse source into top-level statements by tracking brace/paren/bracket depth.
|
|
13
|
-
*
|
|
14
|
-
* A "top-level statement" ends when:
|
|
15
|
-
* - We encounter a semicolon at depth 0, OR
|
|
16
|
-
* - We encounter a closing brace that brings depth to 0 AND the next
|
|
17
|
-
* non-whitespace token does not continue the expression (like `=`, `.`,
|
|
18
|
-
* `,`, `(`, etc.) -- this avoids splitting `var { x } = obj;` or
|
|
19
|
-
* `obj.method()` into two statements.
|
|
20
|
-
*
|
|
21
|
-
* String literals, template literals, regex literals, and comments are
|
|
22
|
-
* tracked so delimiters inside them are not counted.
|
|
23
|
-
*
|
|
24
|
-
* @param {string} source
|
|
25
|
-
* @returns {Array<{code: string, start: number, end: number}>}
|
|
26
|
-
*/
|
|
27
|
-
function parseTopLevelStatements(source) {
|
|
28
|
-
const statements = [];
|
|
29
|
-
let depth = 0;
|
|
30
|
-
let start = 0;
|
|
31
|
-
let i = 0;
|
|
32
|
-
const len = source.length;
|
|
33
|
-
|
|
34
|
-
while (i < len) {
|
|
35
|
-
const ch = source[i];
|
|
36
|
-
const next = i + 1 < len ? source[i + 1] : '';
|
|
37
|
-
|
|
38
|
-
// ── Skip single-line comments ──
|
|
39
|
-
if (ch === '/' && next === '/') {
|
|
40
|
-
const eol = source.indexOf('\n', i + 2);
|
|
41
|
-
i = eol === -1 ? len : eol + 1;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ── Skip multi-line comments ──
|
|
46
|
-
if (ch === '/' && next === '*') {
|
|
47
|
-
const end = source.indexOf('*/', i + 2);
|
|
48
|
-
i = end === -1 ? len : end + 2;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ── Skip string literals ──
|
|
53
|
-
if (ch === '"' || ch === "'") {
|
|
54
|
-
i = skipString(source, i, ch);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ── Skip template literals ──
|
|
59
|
-
if (ch === '`') {
|
|
60
|
-
i = skipTemplateLiteral(source, i);
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ── Skip regex literals ──
|
|
65
|
-
if (ch === '/' && isRegexStart(source, i)) {
|
|
66
|
-
i = skipRegex(source, i);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ── Track depth ──
|
|
71
|
-
if (ch === '{' || ch === '(' || ch === '[') {
|
|
72
|
-
depth++;
|
|
73
|
-
i++;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (ch === '}' || ch === ')' || ch === ']') {
|
|
78
|
-
depth = Math.max(0, depth - 1);
|
|
79
|
-
|
|
80
|
-
// Closing brace at depth 0 MAY be a statement boundary
|
|
81
|
-
if (depth === 0 && ch === '}') {
|
|
82
|
-
if (!isStatementBoundaryAfterBrace(source, i + 1)) {
|
|
83
|
-
i++;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const code = source.substring(start, i + 1).trim();
|
|
88
|
-
if (code.length > 0) {
|
|
89
|
-
statements.push({ code, start, end: i + 1 });
|
|
90
|
-
}
|
|
91
|
-
start = i + 1;
|
|
92
|
-
i++;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
i++;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ── Semicolon at depth 0 is a statement boundary ──
|
|
101
|
-
if (ch === ';' && depth === 0) {
|
|
102
|
-
const code = source.substring(start, i + 1).trim();
|
|
103
|
-
if (code.length > 0) {
|
|
104
|
-
statements.push({ code, start, end: i + 1 });
|
|
105
|
-
}
|
|
106
|
-
start = i + 1;
|
|
107
|
-
i++;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
i++;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Remaining code (unterminated statement)
|
|
115
|
-
const remaining = source.substring(start).trim();
|
|
116
|
-
if (remaining.length > 0) {
|
|
117
|
-
statements.push({ code: remaining, start, end: len });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return statements;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* After a `}` at depth 0, decide whether this is truly a statement boundary.
|
|
125
|
-
* Returns true if it IS a boundary (next token starts a new statement).
|
|
126
|
-
* Returns false if the expression continues (e.g. `}.method()`, `} = obj`, etc.)
|
|
127
|
-
*
|
|
128
|
-
* @param {string} source
|
|
129
|
-
* @param {number} afterPos - position right after the `}`
|
|
130
|
-
* @returns {boolean}
|
|
131
|
-
*/
|
|
132
|
-
function isStatementBoundaryAfterBrace(source, afterPos) {
|
|
133
|
-
const len = source.length;
|
|
134
|
-
let j = afterPos;
|
|
135
|
-
|
|
136
|
-
// Skip whitespace and comments to find the next meaningful token
|
|
137
|
-
while (j < len) {
|
|
138
|
-
const c = source[j];
|
|
139
|
-
|
|
140
|
-
if (c === ' ' || c === '\t' || c === '\r' || c === '\n') {
|
|
141
|
-
j++;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (c === '/' && j + 1 < len && source[j + 1] === '/') {
|
|
146
|
-
const eol = source.indexOf('\n', j + 2);
|
|
147
|
-
j = eol === -1 ? len : eol + 1;
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (c === '/' && j + 1 < len && source[j + 1] === '*') {
|
|
152
|
-
const end = source.indexOf('*/', j + 2);
|
|
153
|
-
j = end === -1 ? len : end + 2;
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (j >= len) return true;
|
|
161
|
-
|
|
162
|
-
const nextChar = source[j];
|
|
163
|
-
|
|
164
|
-
// These tokens CONTINUE the expression -- NOT a statement boundary
|
|
165
|
-
const continuationChars = '.=,([?:&|+\\-*/%<>^~!;)';
|
|
166
|
-
if (continuationChars.includes(nextChar)) {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Check for multi-char continuation tokens
|
|
171
|
-
const ahead = source.substring(j, j + 15);
|
|
172
|
-
if (/^(?:instanceof|in|of|from)\s/.test(ahead)) return false;
|
|
173
|
-
if (/^as\s/.test(ahead)) return false;
|
|
174
|
-
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Skip a string literal starting at position i (where source[i] is the quote).
|
|
180
|
-
* @param {string} source
|
|
181
|
-
* @param {number} i
|
|
182
|
-
* @param {string} quote - the quote character
|
|
183
|
-
* @returns {number}
|
|
184
|
-
*/
|
|
185
|
-
function skipString(source, i, quote) {
|
|
186
|
-
const len = source.length;
|
|
187
|
-
i++;
|
|
188
|
-
while (i < len) {
|
|
189
|
-
if (source[i] === '\\') { i += 2; continue; }
|
|
190
|
-
if (source[i] === quote) return i + 1;
|
|
191
|
-
i++;
|
|
192
|
-
}
|
|
193
|
-
return len;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Skip a template literal starting at position i (where source[i] is backtick).
|
|
198
|
-
* @param {string} source
|
|
199
|
-
* @param {number} i
|
|
200
|
-
* @returns {number}
|
|
201
|
-
*/
|
|
202
|
-
function skipTemplateLiteral(source, i) {
|
|
203
|
-
const len = source.length;
|
|
204
|
-
i++;
|
|
205
|
-
while (i < len) {
|
|
206
|
-
if (source[i] === '\\') { i += 2; continue; }
|
|
207
|
-
if (source[i] === '`') return i + 1;
|
|
208
|
-
if (source[i] === '$' && i + 1 < len && source[i + 1] === '{') {
|
|
209
|
-
i = skipTemplateExpression(source, i + 2);
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
i++;
|
|
213
|
-
}
|
|
214
|
-
return len;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Skip a template expression (inside ${...}) starting after the opening ${.
|
|
219
|
-
* @param {string} source
|
|
220
|
-
* @param {number} i
|
|
221
|
-
* @returns {number}
|
|
222
|
-
*/
|
|
223
|
-
function skipTemplateExpression(source, i) {
|
|
224
|
-
const len = source.length;
|
|
225
|
-
let exprDepth = 1;
|
|
226
|
-
while (i < len && exprDepth > 0) {
|
|
227
|
-
const ch = source[i];
|
|
228
|
-
if (ch === '\\') { i += 2; continue; }
|
|
229
|
-
if (ch === '{') { exprDepth++; i++; continue; }
|
|
230
|
-
if (ch === '}') { exprDepth--; i++; continue; }
|
|
231
|
-
if (ch === '`') { i = skipTemplateLiteral(source, i); continue; }
|
|
232
|
-
if (ch === '"' || ch === "'") { i = skipString(source, i, ch); continue; }
|
|
233
|
-
i++;
|
|
234
|
-
}
|
|
235
|
-
return i;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Heuristic: is source[i] the start of a regex literal?
|
|
240
|
-
* @param {string} source
|
|
241
|
-
* @param {number} i
|
|
242
|
-
* @returns {boolean}
|
|
243
|
-
*/
|
|
244
|
-
function isRegexStart(source, i) {
|
|
245
|
-
let j = i - 1;
|
|
246
|
-
while (j >= 0 && (source[j] === ' ' || source[j] === '\t' || source[j] === '\n' || source[j] === '\r')) {
|
|
247
|
-
j--;
|
|
248
|
-
}
|
|
249
|
-
if (j < 0) return true;
|
|
250
|
-
const prev = source[j];
|
|
251
|
-
if (/[\w$)\].]/.test(prev)) return false;
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Skip a regex literal starting at position i.
|
|
257
|
-
* @param {string} source
|
|
258
|
-
* @param {number} i
|
|
259
|
-
* @returns {number}
|
|
260
|
-
*/
|
|
261
|
-
function skipRegex(source, i) {
|
|
262
|
-
const len = source.length;
|
|
263
|
-
i++;
|
|
264
|
-
while (i < len) {
|
|
265
|
-
if (source[i] === '\\') { i += 2; continue; }
|
|
266
|
-
if (source[i] === '[') {
|
|
267
|
-
i++;
|
|
268
|
-
while (i < len && source[i] !== ']') {
|
|
269
|
-
if (source[i] === '\\') { i += 2; continue; }
|
|
270
|
-
i++;
|
|
271
|
-
}
|
|
272
|
-
i++;
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
if (source[i] === '/') {
|
|
276
|
-
i++;
|
|
277
|
-
while (i < len && /[gimsuy]/.test(source[i])) i++;
|
|
278
|
-
return i;
|
|
279
|
-
}
|
|
280
|
-
i++;
|
|
281
|
-
}
|
|
282
|
-
return len;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
module.exports = { parseTopLevelStatements };
|
|
1
|
+
/**
|
|
2
|
+
* statement-parser.js - Parse JavaScript source into top-level statements.
|
|
3
|
+
*
|
|
4
|
+
* Tracks brace/paren/bracket depth and string/template/regex contexts
|
|
5
|
+
* to split at true statement boundaries. Never splits a statement
|
|
6
|
+
* across modules -- a statement is atomic.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse source into top-level statements by tracking brace/paren/bracket depth.
|
|
13
|
+
*
|
|
14
|
+
* A "top-level statement" ends when:
|
|
15
|
+
* - We encounter a semicolon at depth 0, OR
|
|
16
|
+
* - We encounter a closing brace that brings depth to 0 AND the next
|
|
17
|
+
* non-whitespace token does not continue the expression (like `=`, `.`,
|
|
18
|
+
* `,`, `(`, etc.) -- this avoids splitting `var { x } = obj;` or
|
|
19
|
+
* `obj.method()` into two statements.
|
|
20
|
+
*
|
|
21
|
+
* String literals, template literals, regex literals, and comments are
|
|
22
|
+
* tracked so delimiters inside them are not counted.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} source
|
|
25
|
+
* @returns {Array<{code: string, start: number, end: number}>}
|
|
26
|
+
*/
|
|
27
|
+
function parseTopLevelStatements(source) {
|
|
28
|
+
const statements = [];
|
|
29
|
+
let depth = 0;
|
|
30
|
+
let start = 0;
|
|
31
|
+
let i = 0;
|
|
32
|
+
const len = source.length;
|
|
33
|
+
|
|
34
|
+
while (i < len) {
|
|
35
|
+
const ch = source[i];
|
|
36
|
+
const next = i + 1 < len ? source[i + 1] : '';
|
|
37
|
+
|
|
38
|
+
// ── Skip single-line comments ──
|
|
39
|
+
if (ch === '/' && next === '/') {
|
|
40
|
+
const eol = source.indexOf('\n', i + 2);
|
|
41
|
+
i = eol === -1 ? len : eol + 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Skip multi-line comments ──
|
|
46
|
+
if (ch === '/' && next === '*') {
|
|
47
|
+
const end = source.indexOf('*/', i + 2);
|
|
48
|
+
i = end === -1 ? len : end + 2;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Skip string literals ──
|
|
53
|
+
if (ch === '"' || ch === "'") {
|
|
54
|
+
i = skipString(source, i, ch);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Skip template literals ──
|
|
59
|
+
if (ch === '`') {
|
|
60
|
+
i = skipTemplateLiteral(source, i);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Skip regex literals ──
|
|
65
|
+
if (ch === '/' && isRegexStart(source, i)) {
|
|
66
|
+
i = skipRegex(source, i);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Track depth ──
|
|
71
|
+
if (ch === '{' || ch === '(' || ch === '[') {
|
|
72
|
+
depth++;
|
|
73
|
+
i++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (ch === '}' || ch === ')' || ch === ']') {
|
|
78
|
+
depth = Math.max(0, depth - 1);
|
|
79
|
+
|
|
80
|
+
// Closing brace at depth 0 MAY be a statement boundary
|
|
81
|
+
if (depth === 0 && ch === '}') {
|
|
82
|
+
if (!isStatementBoundaryAfterBrace(source, i + 1)) {
|
|
83
|
+
i++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const code = source.substring(start, i + 1).trim();
|
|
88
|
+
if (code.length > 0) {
|
|
89
|
+
statements.push({ code, start, end: i + 1 });
|
|
90
|
+
}
|
|
91
|
+
start = i + 1;
|
|
92
|
+
i++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
i++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Semicolon at depth 0 is a statement boundary ──
|
|
101
|
+
if (ch === ';' && depth === 0) {
|
|
102
|
+
const code = source.substring(start, i + 1).trim();
|
|
103
|
+
if (code.length > 0) {
|
|
104
|
+
statements.push({ code, start, end: i + 1 });
|
|
105
|
+
}
|
|
106
|
+
start = i + 1;
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
i++;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Remaining code (unterminated statement)
|
|
115
|
+
const remaining = source.substring(start).trim();
|
|
116
|
+
if (remaining.length > 0) {
|
|
117
|
+
statements.push({ code: remaining, start, end: len });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return statements;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* After a `}` at depth 0, decide whether this is truly a statement boundary.
|
|
125
|
+
* Returns true if it IS a boundary (next token starts a new statement).
|
|
126
|
+
* Returns false if the expression continues (e.g. `}.method()`, `} = obj`, etc.)
|
|
127
|
+
*
|
|
128
|
+
* @param {string} source
|
|
129
|
+
* @param {number} afterPos - position right after the `}`
|
|
130
|
+
* @returns {boolean}
|
|
131
|
+
*/
|
|
132
|
+
function isStatementBoundaryAfterBrace(source, afterPos) {
|
|
133
|
+
const len = source.length;
|
|
134
|
+
let j = afterPos;
|
|
135
|
+
|
|
136
|
+
// Skip whitespace and comments to find the next meaningful token
|
|
137
|
+
while (j < len) {
|
|
138
|
+
const c = source[j];
|
|
139
|
+
|
|
140
|
+
if (c === ' ' || c === '\t' || c === '\r' || c === '\n') {
|
|
141
|
+
j++;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (c === '/' && j + 1 < len && source[j + 1] === '/') {
|
|
146
|
+
const eol = source.indexOf('\n', j + 2);
|
|
147
|
+
j = eol === -1 ? len : eol + 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (c === '/' && j + 1 < len && source[j + 1] === '*') {
|
|
152
|
+
const end = source.indexOf('*/', j + 2);
|
|
153
|
+
j = end === -1 ? len : end + 2;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (j >= len) return true;
|
|
161
|
+
|
|
162
|
+
const nextChar = source[j];
|
|
163
|
+
|
|
164
|
+
// These tokens CONTINUE the expression -- NOT a statement boundary
|
|
165
|
+
const continuationChars = '.=,([?:&|+\\-*/%<>^~!;)';
|
|
166
|
+
if (continuationChars.includes(nextChar)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check for multi-char continuation tokens
|
|
171
|
+
const ahead = source.substring(j, j + 15);
|
|
172
|
+
if (/^(?:instanceof|in|of|from)\s/.test(ahead)) return false;
|
|
173
|
+
if (/^as\s/.test(ahead)) return false;
|
|
174
|
+
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Skip a string literal starting at position i (where source[i] is the quote).
|
|
180
|
+
* @param {string} source
|
|
181
|
+
* @param {number} i
|
|
182
|
+
* @param {string} quote - the quote character
|
|
183
|
+
* @returns {number}
|
|
184
|
+
*/
|
|
185
|
+
function skipString(source, i, quote) {
|
|
186
|
+
const len = source.length;
|
|
187
|
+
i++;
|
|
188
|
+
while (i < len) {
|
|
189
|
+
if (source[i] === '\\') { i += 2; continue; }
|
|
190
|
+
if (source[i] === quote) return i + 1;
|
|
191
|
+
i++;
|
|
192
|
+
}
|
|
193
|
+
return len;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Skip a template literal starting at position i (where source[i] is backtick).
|
|
198
|
+
* @param {string} source
|
|
199
|
+
* @param {number} i
|
|
200
|
+
* @returns {number}
|
|
201
|
+
*/
|
|
202
|
+
function skipTemplateLiteral(source, i) {
|
|
203
|
+
const len = source.length;
|
|
204
|
+
i++;
|
|
205
|
+
while (i < len) {
|
|
206
|
+
if (source[i] === '\\') { i += 2; continue; }
|
|
207
|
+
if (source[i] === '`') return i + 1;
|
|
208
|
+
if (source[i] === '$' && i + 1 < len && source[i + 1] === '{') {
|
|
209
|
+
i = skipTemplateExpression(source, i + 2);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
i++;
|
|
213
|
+
}
|
|
214
|
+
return len;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Skip a template expression (inside ${...}) starting after the opening ${.
|
|
219
|
+
* @param {string} source
|
|
220
|
+
* @param {number} i
|
|
221
|
+
* @returns {number}
|
|
222
|
+
*/
|
|
223
|
+
function skipTemplateExpression(source, i) {
|
|
224
|
+
const len = source.length;
|
|
225
|
+
let exprDepth = 1;
|
|
226
|
+
while (i < len && exprDepth > 0) {
|
|
227
|
+
const ch = source[i];
|
|
228
|
+
if (ch === '\\') { i += 2; continue; }
|
|
229
|
+
if (ch === '{') { exprDepth++; i++; continue; }
|
|
230
|
+
if (ch === '}') { exprDepth--; i++; continue; }
|
|
231
|
+
if (ch === '`') { i = skipTemplateLiteral(source, i); continue; }
|
|
232
|
+
if (ch === '"' || ch === "'") { i = skipString(source, i, ch); continue; }
|
|
233
|
+
i++;
|
|
234
|
+
}
|
|
235
|
+
return i;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Heuristic: is source[i] the start of a regex literal?
|
|
240
|
+
* @param {string} source
|
|
241
|
+
* @param {number} i
|
|
242
|
+
* @returns {boolean}
|
|
243
|
+
*/
|
|
244
|
+
function isRegexStart(source, i) {
|
|
245
|
+
let j = i - 1;
|
|
246
|
+
while (j >= 0 && (source[j] === ' ' || source[j] === '\t' || source[j] === '\n' || source[j] === '\r')) {
|
|
247
|
+
j--;
|
|
248
|
+
}
|
|
249
|
+
if (j < 0) return true;
|
|
250
|
+
const prev = source[j];
|
|
251
|
+
if (/[\w$)\].]/.test(prev)) return false;
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Skip a regex literal starting at position i.
|
|
257
|
+
* @param {string} source
|
|
258
|
+
* @param {number} i
|
|
259
|
+
* @returns {number}
|
|
260
|
+
*/
|
|
261
|
+
function skipRegex(source, i) {
|
|
262
|
+
const len = source.length;
|
|
263
|
+
i++;
|
|
264
|
+
while (i < len) {
|
|
265
|
+
if (source[i] === '\\') { i += 2; continue; }
|
|
266
|
+
if (source[i] === '[') {
|
|
267
|
+
i++;
|
|
268
|
+
while (i < len && source[i] !== ']') {
|
|
269
|
+
if (source[i] === '\\') { i += 2; continue; }
|
|
270
|
+
i++;
|
|
271
|
+
}
|
|
272
|
+
i++;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (source[i] === '/') {
|
|
276
|
+
i++;
|
|
277
|
+
while (i < len && /[gimsuy]/.test(source[i])) i++;
|
|
278
|
+
return i;
|
|
279
|
+
}
|
|
280
|
+
i++;
|
|
281
|
+
}
|
|
282
|
+
return len;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
module.exports = { parseTopLevelStatements };
|