@jesscss/scss-parser 2.0.0-alpha.7
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 -0
- package/README.md +132 -0
- package/lib/builders.d.ts +154 -0
- package/lib/cst.cjs +14 -0
- package/lib/cst.d.ts +5 -0
- package/lib/cst.js +12 -0
- package/lib/functional-parser.cjs +2872 -0
- package/lib/functional-parser.d.ts +46 -0
- package/lib/functional-parser.js +2855 -0
- package/lib/grammar.cjs +55569 -0
- package/lib/grammar.d.ts +1 -0
- package/lib/grammar.js +55568 -0
- package/lib/index.cjs +14 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/interp.d.ts +26 -0
- package/lib/jess.cjs +6 -0
- package/lib/jess.d.ts +2 -0
- package/lib/jess.js +2 -0
- package/lib/scss-atroot-helpers.d.ts +4 -0
- package/lib/scss-atrule-helpers.d.ts +36 -0
- package/lib/scss-value-helpers.d.ts +11 -0
- package/package.json +79 -0
- package/src/builders.ts +1408 -0
- package/src/cst.ts +25 -0
- package/src/functional-parser.ts +135 -0
- package/src/grammar.ts +621 -0
- package/src/index.ts +14 -0
- package/src/interp.ts +158 -0
- package/src/jess.ts +11 -0
- package/src/scss-atroot-helpers.ts +105 -0
- package/src/scss-atrule-helpers.ts +191 -0
- package/src/scss-value-helpers.ts +105 -0
|
@@ -0,0 +1,2855 @@
|
|
|
1
|
+
import { scssGrammar } from "./grammar.js";
|
|
2
|
+
import { LessGrammar } from "@jesscss/less-parser/jess";
|
|
3
|
+
import { runFunctionalParse, spannedComponents } from "@jesscss/css-parser/jess";
|
|
4
|
+
import { Ampersand, Any, AtRule, AtRuleStatement, Call, Collection, ComplexSelector, Condition, Declaration, Expression, Extend, ExtendFlag, F_VISIBLE, For, Func, INTERPOLATION_PLACEHOLDER, If, Interpolated, InterpolatedSelector, JsImport, List, Log, Mixin, N, Nil, Node, Operation, Paren, Quoted, Reference, Rest, Rules, Ruleset, SelectorCapture, SelectorList, Sequence, StyleImport, Url, VarDeclaration, While, isNode, sourceSpanOf } from "@jesscss/core";
|
|
5
|
+
//#region ../../node_modules/.pnpm/parseman@0.25.0/node_modules/parseman/dist/index.js
|
|
6
|
+
function union(a, b) {
|
|
7
|
+
if (a.kind === "any" || b.kind === "any") return { kind: "any" };
|
|
8
|
+
if (a.kind === "empty") return b;
|
|
9
|
+
if (b.kind === "empty") return a;
|
|
10
|
+
return {
|
|
11
|
+
kind: "ranges",
|
|
12
|
+
ranges: mergeRanges([...a.ranges, ...b.ranges])
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function intersects(a, b) {
|
|
16
|
+
if (a.kind === "any" || b.kind === "any") return true;
|
|
17
|
+
if (a.kind === "empty" || b.kind === "empty") return false;
|
|
18
|
+
for (const ra of a.ranges) for (const rb of b.ranges) if (ra.lo <= rb.hi && rb.lo <= ra.hi) return true;
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function fromChar(code) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "ranges",
|
|
24
|
+
ranges: [{
|
|
25
|
+
lo: code,
|
|
26
|
+
hi: code
|
|
27
|
+
}]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function fromRange(lo, hi) {
|
|
31
|
+
return {
|
|
32
|
+
kind: "ranges",
|
|
33
|
+
ranges: [{
|
|
34
|
+
lo,
|
|
35
|
+
hi
|
|
36
|
+
}]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function any() {
|
|
40
|
+
return { kind: "any" };
|
|
41
|
+
}
|
|
42
|
+
function empty() {
|
|
43
|
+
return { kind: "empty" };
|
|
44
|
+
}
|
|
45
|
+
function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
|
|
46
|
+
if (seen.has(p)) return true;
|
|
47
|
+
seen.add(p);
|
|
48
|
+
const me = (c) => matchesEmpty(c, seen);
|
|
49
|
+
const d = p._def;
|
|
50
|
+
switch (d.tag) {
|
|
51
|
+
case "literal": return d.value.length === 0;
|
|
52
|
+
case "keywords": return false;
|
|
53
|
+
case "regex": try {
|
|
54
|
+
const m = new RegExp(d.source).exec("");
|
|
55
|
+
return m != null && m[0] === "";
|
|
56
|
+
} catch {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
case "many":
|
|
60
|
+
case "optional":
|
|
61
|
+
case "not": return true;
|
|
62
|
+
case "oneOrMore": return me(d.parser);
|
|
63
|
+
case "sequence": return d.parsers.every(me);
|
|
64
|
+
case "choice": return d.parsers.some(me);
|
|
65
|
+
case "transform":
|
|
66
|
+
case "label":
|
|
67
|
+
case "trivia":
|
|
68
|
+
case "token":
|
|
69
|
+
case "expect":
|
|
70
|
+
case "withCtx":
|
|
71
|
+
case "node":
|
|
72
|
+
case "grammar":
|
|
73
|
+
case "recover": return me(d.parser);
|
|
74
|
+
case "skip": return me(d.main);
|
|
75
|
+
case "lazy": try {
|
|
76
|
+
return me(d.thunk());
|
|
77
|
+
} catch {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
default: return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function sequenceFirstSet(parsers) {
|
|
84
|
+
let fs = empty();
|
|
85
|
+
for (const p of parsers) {
|
|
86
|
+
fs = union(fs, p._meta.firstSet);
|
|
87
|
+
if (!matchesEmpty(p)) return fs;
|
|
88
|
+
}
|
|
89
|
+
return fs;
|
|
90
|
+
}
|
|
91
|
+
function mergeRanges(ranges) {
|
|
92
|
+
if (ranges.length === 0) return [];
|
|
93
|
+
const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
|
|
94
|
+
const out = [{
|
|
95
|
+
lo: sorted[0].lo,
|
|
96
|
+
hi: sorted[0].hi
|
|
97
|
+
}];
|
|
98
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
99
|
+
const top = out[out.length - 1];
|
|
100
|
+
const cur = sorted[i];
|
|
101
|
+
if (cur.lo <= top.hi + 1) {
|
|
102
|
+
if (cur.hi > top.hi) top.hi = cur.hi;
|
|
103
|
+
} else out.push({
|
|
104
|
+
lo: cur.lo,
|
|
105
|
+
hi: cur.hi
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
function failAt(ctx, expected, pos) {
|
|
111
|
+
const r = {
|
|
112
|
+
ok: false,
|
|
113
|
+
expected,
|
|
114
|
+
span: {
|
|
115
|
+
start: pos,
|
|
116
|
+
end: pos
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const probe = ctx._probe;
|
|
120
|
+
if (probe !== void 0 && pos <= probe.offset) {
|
|
121
|
+
const best = probe.best;
|
|
122
|
+
if (best === null || pos > best.span.start) probe.best = r;
|
|
123
|
+
else if (pos === best.span.start) probe.best = {
|
|
124
|
+
...best,
|
|
125
|
+
expected: [...best.expected, ...expected]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return r;
|
|
129
|
+
}
|
|
130
|
+
function cstCaptureActive(ctx) {
|
|
131
|
+
return ctx._cstBuf !== void 0 || ctx._cstLeaves !== void 0;
|
|
132
|
+
}
|
|
133
|
+
function pushCstLeaf(ctx, leaf) {
|
|
134
|
+
pushCstChild(ctx, leaf, leaf);
|
|
135
|
+
}
|
|
136
|
+
function pushCstChild(ctx, built, rawEntry) {
|
|
137
|
+
const b = ctx._cstBuf;
|
|
138
|
+
if (b) {
|
|
139
|
+
if (b.ch) b.ch.push(built);
|
|
140
|
+
else if (b.single !== void 0) {
|
|
141
|
+
b.ch = [b.single, built];
|
|
142
|
+
b.single = void 0;
|
|
143
|
+
} else b.single = built;
|
|
144
|
+
if (b.raw) b.raw.push(rawEntry);
|
|
145
|
+
else if (b.rawSingle !== void 0) {
|
|
146
|
+
b.raw = [b.rawSingle, rawEntry];
|
|
147
|
+
b.rawSingle = void 0;
|
|
148
|
+
} else b.rawSingle = rawEntry;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (ctx._cstChildren) ctx._cstChildren.push(built);
|
|
152
|
+
else if (ctx._cstLeaves) ctx._cstLeaves.push(built);
|
|
153
|
+
if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry);
|
|
154
|
+
}
|
|
155
|
+
function cstRawLen(ctx) {
|
|
156
|
+
const b = ctx._cstBuf;
|
|
157
|
+
if (b) {
|
|
158
|
+
if (b.raw) return b.raw.length;
|
|
159
|
+
return b.rawSingle !== void 0 ? 1 : 0;
|
|
160
|
+
}
|
|
161
|
+
return ctx._cstRawChildren?.length ?? 0;
|
|
162
|
+
}
|
|
163
|
+
function cstLeavesLen(ctx) {
|
|
164
|
+
const b = ctx._cstBuf;
|
|
165
|
+
if (b) {
|
|
166
|
+
if (b.ch) return b.ch.length;
|
|
167
|
+
return b.single !== void 0 ? 1 : 0;
|
|
168
|
+
}
|
|
169
|
+
return ctx._cstLeaves?.length ?? 0;
|
|
170
|
+
}
|
|
171
|
+
function cstTlLen(ctx) {
|
|
172
|
+
const b = ctx._cstBuf;
|
|
173
|
+
if (b) return b.tl?.length ?? 0;
|
|
174
|
+
return ctx._cstTriviaLog?.length ?? 0;
|
|
175
|
+
}
|
|
176
|
+
function saveCstMark(ctx) {
|
|
177
|
+
return {
|
|
178
|
+
raw: cstRawLen(ctx),
|
|
179
|
+
tlog: cstTlLen(ctx),
|
|
180
|
+
leaves: cstLeavesLen(ctx),
|
|
181
|
+
fields: ctx._fields?.length ?? 0
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function rollbackBufList(b, keyMulti, keySingle, len) {
|
|
185
|
+
const arr = b[keyMulti];
|
|
186
|
+
if (arr) {
|
|
187
|
+
if (len === 0) b[keyMulti] = void 0;
|
|
188
|
+
else if (len === 1) {
|
|
189
|
+
b[keySingle] = arr[0];
|
|
190
|
+
b[keyMulti] = void 0;
|
|
191
|
+
} else arr.length = len;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (len === 0) b[keySingle] = void 0;
|
|
195
|
+
}
|
|
196
|
+
function rollbackCstCapture(ctx, mark) {
|
|
197
|
+
const b = ctx._cstBuf;
|
|
198
|
+
if (b) {
|
|
199
|
+
rollbackBufList(b, "raw", "rawSingle", mark.raw);
|
|
200
|
+
rollbackBufList(b, "ch", "single", mark.leaves);
|
|
201
|
+
if (b.tl) if (mark.tlog === 0) b.tl = void 0;
|
|
202
|
+
else b.tl.length = mark.tlog;
|
|
203
|
+
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (ctx._cstRawChildren) ctx._cstRawChildren.length = mark.raw;
|
|
207
|
+
if (ctx._cstTriviaLog) ctx._cstTriviaLog.length = mark.tlog;
|
|
208
|
+
if (ctx._cstLeaves) ctx._cstLeaves.length = mark.leaves;
|
|
209
|
+
if (ctx._fields) ctx._fields.length = mark.fields;
|
|
210
|
+
}
|
|
211
|
+
function pushCstTriviaEntry(ctx, start, end, kindIndex) {
|
|
212
|
+
const insertIdx = cstRawLen(ctx);
|
|
213
|
+
const b = ctx._cstBuf;
|
|
214
|
+
const withKind = ctx.triviaKindLabels !== void 0 && kindIndex !== void 0;
|
|
215
|
+
if (b) {
|
|
216
|
+
if (!b.tl) b.tl = withKind ? [
|
|
217
|
+
start,
|
|
218
|
+
end,
|
|
219
|
+
insertIdx,
|
|
220
|
+
kindIndex
|
|
221
|
+
] : [
|
|
222
|
+
start,
|
|
223
|
+
end,
|
|
224
|
+
insertIdx
|
|
225
|
+
];
|
|
226
|
+
else if (withKind) b.tl.push(start, end, insertIdx, kindIndex);
|
|
227
|
+
else b.tl.push(start, end, insertIdx);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (ctx._cstTriviaLog) if (withKind) ctx._cstTriviaLog.push(start, end, insertIdx, kindIndex);
|
|
231
|
+
else ctx._cstTriviaLog.push(start, end, insertIdx);
|
|
232
|
+
}
|
|
233
|
+
function pushTriviaLogEntry(ctx, start, end, kindIndex) {
|
|
234
|
+
if (!ctx._triviaLog) return;
|
|
235
|
+
if (ctx.triviaKindLabels !== void 0 && kindIndex !== void 0) ctx._triviaLog.push(start, end, kindIndex);
|
|
236
|
+
else ctx._triviaLog.push(start, end);
|
|
237
|
+
}
|
|
238
|
+
function asciiFoldEq(a, b) {
|
|
239
|
+
if (a.length !== b.length) return false;
|
|
240
|
+
for (let i = 0; i < a.length; i++) {
|
|
241
|
+
let ca = a.charCodeAt(i);
|
|
242
|
+
let cb = b.charCodeAt(i);
|
|
243
|
+
if (ca >= 65 && ca <= 90) ca += 32;
|
|
244
|
+
if (cb >= 65 && cb <= 90) cb += 32;
|
|
245
|
+
if (ca !== cb) return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
function literal(value, opts = {}) {
|
|
250
|
+
const caseInsensitive = opts.caseInsensitive ?? false;
|
|
251
|
+
const firstSet2 = value.length > 0 ? fromChar(value.codePointAt(0)) : empty();
|
|
252
|
+
const meta = {
|
|
253
|
+
firstSet: firstSet2,
|
|
254
|
+
canMatchNewline: value.includes("\n"),
|
|
255
|
+
isTrivia: false
|
|
256
|
+
};
|
|
257
|
+
if (caseInsensitive) {
|
|
258
|
+
const upper = value.toUpperCase();
|
|
259
|
+
const lower = value.toLowerCase();
|
|
260
|
+
const firstUpper = upper.codePointAt(0);
|
|
261
|
+
const firstLower = lower.codePointAt(0);
|
|
262
|
+
meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? {
|
|
263
|
+
kind: "ranges",
|
|
264
|
+
ranges: [{
|
|
265
|
+
lo: firstLower,
|
|
266
|
+
hi: firstLower
|
|
267
|
+
}]
|
|
268
|
+
} : {
|
|
269
|
+
kind: "ranges",
|
|
270
|
+
ranges: [{
|
|
271
|
+
lo: firstLower,
|
|
272
|
+
hi: firstLower
|
|
273
|
+
}, {
|
|
274
|
+
lo: firstUpper,
|
|
275
|
+
hi: firstUpper
|
|
276
|
+
}]
|
|
277
|
+
} : firstSet2;
|
|
278
|
+
}
|
|
279
|
+
const expected = [JSON.stringify(value)];
|
|
280
|
+
const parse2 = !caseInsensitive && value.length === 1 ? (() => {
|
|
281
|
+
const code = value.charCodeAt(0);
|
|
282
|
+
return function parse3(input, pos, ctx) {
|
|
283
|
+
if (input.charCodeAt(pos) === code) {
|
|
284
|
+
const span = {
|
|
285
|
+
start: pos,
|
|
286
|
+
end: pos + 1
|
|
287
|
+
};
|
|
288
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, {
|
|
289
|
+
_tag: "leaf",
|
|
290
|
+
value,
|
|
291
|
+
span
|
|
292
|
+
});
|
|
293
|
+
return {
|
|
294
|
+
ok: true,
|
|
295
|
+
value,
|
|
296
|
+
span
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return failAt(ctx, expected, pos);
|
|
300
|
+
};
|
|
301
|
+
})() : function parse3(input, pos, ctx) {
|
|
302
|
+
const end = pos + value.length;
|
|
303
|
+
if (end > input.length) return failAt(ctx, expected, pos);
|
|
304
|
+
const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
|
|
305
|
+
if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
|
|
306
|
+
const span = {
|
|
307
|
+
start: pos,
|
|
308
|
+
end
|
|
309
|
+
};
|
|
310
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, {
|
|
311
|
+
_tag: "leaf",
|
|
312
|
+
value: matchedValue,
|
|
313
|
+
span
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
ok: true,
|
|
317
|
+
value: matchedValue,
|
|
318
|
+
span
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return failAt(ctx, expected, pos);
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
_tag: "literal",
|
|
325
|
+
_meta: meta,
|
|
326
|
+
_def: {
|
|
327
|
+
tag: "literal",
|
|
328
|
+
value,
|
|
329
|
+
caseInsensitive
|
|
330
|
+
},
|
|
331
|
+
parse: parse2
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
var CLASS_ESCAPES = {
|
|
335
|
+
t: 9,
|
|
336
|
+
n: 10,
|
|
337
|
+
r: 13,
|
|
338
|
+
f: 12,
|
|
339
|
+
v: 11,
|
|
340
|
+
"0": 0
|
|
341
|
+
};
|
|
342
|
+
var SPACE_RANGES = [
|
|
343
|
+
[9, 13],
|
|
344
|
+
[32, 32],
|
|
345
|
+
[160, 160],
|
|
346
|
+
[5760, 5760],
|
|
347
|
+
[8192, 8202],
|
|
348
|
+
[8232, 8232],
|
|
349
|
+
[8233, 8233],
|
|
350
|
+
[8239, 8239],
|
|
351
|
+
[8287, 8287],
|
|
352
|
+
[12288, 12288],
|
|
353
|
+
[65279, 65279]
|
|
354
|
+
];
|
|
355
|
+
function shorthandRanges(ch) {
|
|
356
|
+
if (ch === "d") return [[48, 57]];
|
|
357
|
+
if (ch === "s") return SPACE_RANGES;
|
|
358
|
+
return [
|
|
359
|
+
[48, 57],
|
|
360
|
+
[65, 90],
|
|
361
|
+
[97, 122],
|
|
362
|
+
[95, 95]
|
|
363
|
+
];
|
|
364
|
+
}
|
|
365
|
+
function readUnicodeEscape(body, i) {
|
|
366
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
367
|
+
const hex = body.slice(i + 2, i + 6);
|
|
368
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
369
|
+
return {
|
|
370
|
+
cp: Number.parseInt(hex, 16),
|
|
371
|
+
next: i + 6
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function parseClassRanges(body) {
|
|
375
|
+
const ranges = [];
|
|
376
|
+
let i = 0;
|
|
377
|
+
const readAtom = () => {
|
|
378
|
+
const ch = body[i];
|
|
379
|
+
if (ch === void 0) return null;
|
|
380
|
+
if (ch === "\\") {
|
|
381
|
+
const uni = readUnicodeEscape(body, i);
|
|
382
|
+
if (uni) {
|
|
383
|
+
i = uni.next;
|
|
384
|
+
return { cp: uni.cp };
|
|
385
|
+
}
|
|
386
|
+
const e = body[i + 1];
|
|
387
|
+
if (e === void 0) return null;
|
|
388
|
+
i += 2;
|
|
389
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
390
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
391
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
392
|
+
return { cp: e.codePointAt(0) };
|
|
393
|
+
}
|
|
394
|
+
i += ch.length;
|
|
395
|
+
return { cp: ch.codePointAt(0) };
|
|
396
|
+
};
|
|
397
|
+
while (i < body.length) {
|
|
398
|
+
const lo = readAtom();
|
|
399
|
+
if (lo === null) return null;
|
|
400
|
+
if ("set" in lo) {
|
|
401
|
+
ranges.push(...lo.set);
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
405
|
+
i += 1;
|
|
406
|
+
const hi = readAtom();
|
|
407
|
+
if (hi === null || "set" in hi) return null;
|
|
408
|
+
ranges.push([lo.cp, hi.cp]);
|
|
409
|
+
} else ranges.push([lo.cp, lo.cp]);
|
|
410
|
+
}
|
|
411
|
+
return ranges.length ? ranges : null;
|
|
412
|
+
}
|
|
413
|
+
var firstSetAnalyzer = null;
|
|
414
|
+
function registerRegexAnalyzer(analyzer) {
|
|
415
|
+
firstSetAnalyzer = analyzer;
|
|
416
|
+
}
|
|
417
|
+
var permissiveFirstSet = () => ({
|
|
418
|
+
firstSet: any(),
|
|
419
|
+
canMatchNewline: true
|
|
420
|
+
});
|
|
421
|
+
var SCAN_BAIL_AT = 64;
|
|
422
|
+
function inRanges(cp, ranges) {
|
|
423
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
424
|
+
const [lo, hi] = ranges[i];
|
|
425
|
+
if (cp >= lo && cp <= hi) return true;
|
|
426
|
+
}
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
function readClassRanges(body) {
|
|
430
|
+
if (body.startsWith("^")) return null;
|
|
431
|
+
return parseClassRanges(body);
|
|
432
|
+
}
|
|
433
|
+
function shortScanner(source, flags) {
|
|
434
|
+
if (/[imsuvy]/.test(flags)) return null;
|
|
435
|
+
let ranges = null;
|
|
436
|
+
let quant = "";
|
|
437
|
+
if (source[0] === "[") {
|
|
438
|
+
let end = 1;
|
|
439
|
+
while (end < source.length && source[end] !== "]") if (source[end] === "\\") end += 2;
|
|
440
|
+
else end++;
|
|
441
|
+
if (source[end] !== "]") return null;
|
|
442
|
+
ranges = readClassRanges(source.slice(1, end));
|
|
443
|
+
quant = source.slice(end + 1);
|
|
444
|
+
} else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
|
|
445
|
+
ranges = shorthandRanges(source[1]);
|
|
446
|
+
quant = source.slice(2);
|
|
447
|
+
}
|
|
448
|
+
if (!ranges || quant !== "+" && quant !== "*") return null;
|
|
449
|
+
const minOne = quant === "+";
|
|
450
|
+
return (input, pos) => {
|
|
451
|
+
let end = pos;
|
|
452
|
+
while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
|
|
453
|
+
end++;
|
|
454
|
+
if (end - pos >= SCAN_BAIL_AT) return void 0;
|
|
455
|
+
}
|
|
456
|
+
return minOne && end === pos ? null : end;
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
function regex(pattern, flags = "") {
|
|
460
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
461
|
+
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
462
|
+
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
463
|
+
const scan = shortScanner(source, resolvedFlags);
|
|
464
|
+
const { firstSet: firstSet2, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
465
|
+
return {
|
|
466
|
+
_tag: "regex",
|
|
467
|
+
_meta: {
|
|
468
|
+
firstSet: firstSet2,
|
|
469
|
+
canMatchNewline,
|
|
470
|
+
isTrivia: false
|
|
471
|
+
},
|
|
472
|
+
_def: {
|
|
473
|
+
tag: "regex",
|
|
474
|
+
source,
|
|
475
|
+
flags: resolvedFlags
|
|
476
|
+
},
|
|
477
|
+
parse(input, pos, ctx) {
|
|
478
|
+
const scanEnd = scan?.(input, pos);
|
|
479
|
+
if (scanEnd !== void 0) {
|
|
480
|
+
if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
|
|
481
|
+
const value = input.slice(pos, scanEnd);
|
|
482
|
+
const span2 = {
|
|
483
|
+
start: pos,
|
|
484
|
+
end: scanEnd
|
|
485
|
+
};
|
|
486
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, {
|
|
487
|
+
_tag: "leaf",
|
|
488
|
+
value,
|
|
489
|
+
span: span2
|
|
490
|
+
});
|
|
491
|
+
return {
|
|
492
|
+
ok: true,
|
|
493
|
+
value,
|
|
494
|
+
span: span2
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
anchored.lastIndex = pos;
|
|
498
|
+
const m = anchored.exec(input);
|
|
499
|
+
if (m === null) return failAt(ctx, [`/${source}/`], pos);
|
|
500
|
+
const span = {
|
|
501
|
+
start: pos,
|
|
502
|
+
end: pos + m[0].length
|
|
503
|
+
};
|
|
504
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, {
|
|
505
|
+
_tag: "leaf",
|
|
506
|
+
value: m[0],
|
|
507
|
+
span
|
|
508
|
+
});
|
|
509
|
+
return {
|
|
510
|
+
ok: true,
|
|
511
|
+
value: m[0],
|
|
512
|
+
span
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
var ANY = { t: "any" };
|
|
518
|
+
var EMPTY2 = { t: "empty" };
|
|
519
|
+
function charNode(set, nl) {
|
|
520
|
+
return {
|
|
521
|
+
t: "char",
|
|
522
|
+
set,
|
|
523
|
+
nl
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
function rangesToSet(ranges) {
|
|
527
|
+
let fs = empty();
|
|
528
|
+
for (const [lo, hi] of ranges) fs = union(fs, fromRange(lo, hi));
|
|
529
|
+
return fs;
|
|
530
|
+
}
|
|
531
|
+
function rangesHaveNewline(ranges) {
|
|
532
|
+
return ranges.some(([lo, hi]) => lo <= 10 && 10 <= hi);
|
|
533
|
+
}
|
|
534
|
+
var BAIL = /* @__PURE__ */ Symbol("regex-bail");
|
|
535
|
+
function parseRegex(src) {
|
|
536
|
+
let i = 0;
|
|
537
|
+
const parseAlt = () => {
|
|
538
|
+
const arms = [parseSeq()];
|
|
539
|
+
while (src[i] === "|") {
|
|
540
|
+
i++;
|
|
541
|
+
arms.push(parseSeq());
|
|
542
|
+
}
|
|
543
|
+
return arms.length === 1 ? arms[0] : {
|
|
544
|
+
t: "alt",
|
|
545
|
+
arms
|
|
546
|
+
};
|
|
547
|
+
};
|
|
548
|
+
const parseSeq = () => {
|
|
549
|
+
const parts = [];
|
|
550
|
+
while (i < src.length) {
|
|
551
|
+
const ch = src[i];
|
|
552
|
+
if (ch === "|" || ch === ")") break;
|
|
553
|
+
parts.push(parseQuantified());
|
|
554
|
+
}
|
|
555
|
+
if (parts.length === 0) return EMPTY2;
|
|
556
|
+
return parts.length === 1 ? parts[0] : {
|
|
557
|
+
t: "seq",
|
|
558
|
+
parts
|
|
559
|
+
};
|
|
560
|
+
};
|
|
561
|
+
const parseQuantified = () => {
|
|
562
|
+
const atom = parseAtom();
|
|
563
|
+
const ch = src[i];
|
|
564
|
+
let min = null;
|
|
565
|
+
if (ch === "*") {
|
|
566
|
+
min = 0;
|
|
567
|
+
i++;
|
|
568
|
+
} else if (ch === "+") {
|
|
569
|
+
min = 1;
|
|
570
|
+
i++;
|
|
571
|
+
} else if (ch === "?") {
|
|
572
|
+
min = 0;
|
|
573
|
+
i++;
|
|
574
|
+
} else if (ch === "{") min = parseBraceQuantifier();
|
|
575
|
+
if (min === null) return atom;
|
|
576
|
+
if (src[i] === "?") i++;
|
|
577
|
+
return {
|
|
578
|
+
t: "rep",
|
|
579
|
+
node: atom,
|
|
580
|
+
min
|
|
581
|
+
};
|
|
582
|
+
};
|
|
583
|
+
const parseBraceQuantifier = () => {
|
|
584
|
+
const close = src.indexOf("}", i);
|
|
585
|
+
if (close === -1) return null;
|
|
586
|
+
const m = /^(\d+)(?:,(\d*))?$/.exec(src.slice(i + 1, close));
|
|
587
|
+
if (!m) return null;
|
|
588
|
+
i = close + 1;
|
|
589
|
+
return Number.parseInt(m[1], 10);
|
|
590
|
+
};
|
|
591
|
+
const parseAtom = () => {
|
|
592
|
+
const ch = src[i];
|
|
593
|
+
switch (ch) {
|
|
594
|
+
case "(": return parseGroup();
|
|
595
|
+
case "[": return parseClass();
|
|
596
|
+
case ".":
|
|
597
|
+
i++;
|
|
598
|
+
return charNode(any(), false);
|
|
599
|
+
case "\\": return parseEscape();
|
|
600
|
+
case "^":
|
|
601
|
+
case "$":
|
|
602
|
+
i++;
|
|
603
|
+
return EMPTY2;
|
|
604
|
+
case "{":
|
|
605
|
+
i++;
|
|
606
|
+
return charNode(fromRange(123, 123), false);
|
|
607
|
+
default:
|
|
608
|
+
i += ch.length;
|
|
609
|
+
return charNode(fromRange(ch.codePointAt(0), ch.codePointAt(0)), ch === "\n");
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
const parseGroup = () => {
|
|
613
|
+
i++;
|
|
614
|
+
let lookaround = false;
|
|
615
|
+
if (src[i] === "?") {
|
|
616
|
+
const c1 = src[i + 1];
|
|
617
|
+
if (c1 === ":") i += 2;
|
|
618
|
+
else if (c1 === "=" || c1 === "!") {
|
|
619
|
+
i += 2;
|
|
620
|
+
lookaround = true;
|
|
621
|
+
} else if (c1 === "<") {
|
|
622
|
+
const c2 = src[i + 2];
|
|
623
|
+
if (c2 === "=" || c2 === "!") {
|
|
624
|
+
i += 3;
|
|
625
|
+
lookaround = true;
|
|
626
|
+
} else {
|
|
627
|
+
const gt = src.indexOf(">", i);
|
|
628
|
+
if (gt === -1) throw BAIL;
|
|
629
|
+
i = gt + 1;
|
|
630
|
+
}
|
|
631
|
+
} else throw BAIL;
|
|
632
|
+
}
|
|
633
|
+
const inner = parseAlt();
|
|
634
|
+
if (src[i] !== ")") throw BAIL;
|
|
635
|
+
i++;
|
|
636
|
+
return lookaround ? EMPTY2 : inner;
|
|
637
|
+
};
|
|
638
|
+
const parseClass = () => {
|
|
639
|
+
let k = i + 1;
|
|
640
|
+
const negated = src[k] === "^";
|
|
641
|
+
if (negated) k++;
|
|
642
|
+
let body = "";
|
|
643
|
+
while (k < src.length && src[k] !== "]") if (src[k] === "\\") {
|
|
644
|
+
body += src[k] + (src[k + 1] ?? "");
|
|
645
|
+
k += 2;
|
|
646
|
+
} else {
|
|
647
|
+
body += src[k];
|
|
648
|
+
k++;
|
|
649
|
+
}
|
|
650
|
+
if (src[k] !== "]") throw BAIL;
|
|
651
|
+
i = k + 1;
|
|
652
|
+
if (negated) return charNode(any(), true);
|
|
653
|
+
const ranges = parseClassRanges(body);
|
|
654
|
+
if (!ranges) return charNode(any(), true);
|
|
655
|
+
return charNode(rangesToSet(ranges), rangesHaveNewline(ranges));
|
|
656
|
+
};
|
|
657
|
+
const parseEscape = () => {
|
|
658
|
+
const e = src[i + 1];
|
|
659
|
+
if (e === void 0) throw BAIL;
|
|
660
|
+
if (e === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(i + 2, i + 6))) {
|
|
661
|
+
const cp = Number.parseInt(src.slice(i + 2, i + 6), 16);
|
|
662
|
+
i += 6;
|
|
663
|
+
return charNode(fromRange(cp, cp), cp === 10);
|
|
664
|
+
}
|
|
665
|
+
if (e === "x" && /^[0-9a-fA-F]{2}$/.test(src.slice(i + 2, i + 4))) {
|
|
666
|
+
const cp = Number.parseInt(src.slice(i + 2, i + 4), 16);
|
|
667
|
+
i += 4;
|
|
668
|
+
return charNode(fromRange(cp, cp), cp === 10);
|
|
669
|
+
}
|
|
670
|
+
i += 2;
|
|
671
|
+
switch (e) {
|
|
672
|
+
case "d": return charNode(rangesToSet(shorthandRanges("d")), false);
|
|
673
|
+
case "w": return charNode(rangesToSet(shorthandRanges("w")), false);
|
|
674
|
+
case "s": return charNode(rangesToSet(shorthandRanges("s")), true);
|
|
675
|
+
case "D":
|
|
676
|
+
case "W":
|
|
677
|
+
case "S": return charNode(any(), true);
|
|
678
|
+
case "b":
|
|
679
|
+
case "B": return EMPTY2;
|
|
680
|
+
case "n": return charNode(fromRange(10, 10), true);
|
|
681
|
+
case "r": return charNode(fromRange(13, 13), false);
|
|
682
|
+
case "t": return charNode(fromRange(9, 9), false);
|
|
683
|
+
case "f": return charNode(fromRange(12, 12), false);
|
|
684
|
+
case "v": return charNode(fromRange(11, 11), false);
|
|
685
|
+
case "0": return charNode(fromRange(0, 0), false);
|
|
686
|
+
default:
|
|
687
|
+
if (e >= "1" && e <= "9") return ANY;
|
|
688
|
+
return charNode(fromRange(e.codePointAt(0), e.codePointAt(0)), e === "\n");
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
const node2 = parseAlt();
|
|
692
|
+
if (i < src.length) throw BAIL;
|
|
693
|
+
return node2;
|
|
694
|
+
}
|
|
695
|
+
function nullable(n) {
|
|
696
|
+
switch (n.t) {
|
|
697
|
+
case "char": return false;
|
|
698
|
+
case "any": return true;
|
|
699
|
+
case "empty": return true;
|
|
700
|
+
case "seq": return n.parts.every(nullable);
|
|
701
|
+
case "alt": return n.arms.some(nullable);
|
|
702
|
+
case "rep": return n.min === 0 || nullable(n.node);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function firstSet(n) {
|
|
706
|
+
switch (n.t) {
|
|
707
|
+
case "char": return n.set;
|
|
708
|
+
case "any": return any();
|
|
709
|
+
case "empty": return empty();
|
|
710
|
+
case "rep": return firstSet(n.node);
|
|
711
|
+
case "alt": {
|
|
712
|
+
let fs = empty();
|
|
713
|
+
for (const arm of n.arms) fs = union(fs, firstSet(arm));
|
|
714
|
+
return fs;
|
|
715
|
+
}
|
|
716
|
+
case "seq": {
|
|
717
|
+
let fs = empty();
|
|
718
|
+
for (const part of n.parts) {
|
|
719
|
+
fs = union(fs, firstSet(part));
|
|
720
|
+
if (!nullable(part)) break;
|
|
721
|
+
}
|
|
722
|
+
return fs;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function firstCanBeNewline(n) {
|
|
727
|
+
switch (n.t) {
|
|
728
|
+
case "char": return n.nl;
|
|
729
|
+
case "any": return true;
|
|
730
|
+
case "empty": return false;
|
|
731
|
+
case "rep": return firstCanBeNewline(n.node);
|
|
732
|
+
case "alt": return n.arms.some(firstCanBeNewline);
|
|
733
|
+
case "seq":
|
|
734
|
+
for (const part of n.parts) {
|
|
735
|
+
if (firstCanBeNewline(part)) return true;
|
|
736
|
+
if (!nullable(part)) break;
|
|
737
|
+
}
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function firstSetFromRegex(source) {
|
|
742
|
+
let ast;
|
|
743
|
+
try {
|
|
744
|
+
ast = parseRegex(source);
|
|
745
|
+
} catch {
|
|
746
|
+
return {
|
|
747
|
+
firstSet: any(),
|
|
748
|
+
canMatchNewline: true
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
if (nullable(ast)) return {
|
|
752
|
+
firstSet: any(),
|
|
753
|
+
canMatchNewline: true
|
|
754
|
+
};
|
|
755
|
+
return {
|
|
756
|
+
firstSet: firstSet(ast),
|
|
757
|
+
canMatchNewline: firstCanBeNewline(ast)
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
function choice(...args) {
|
|
761
|
+
const parsers = args.map((a) => "gate" in a ? a.combinator : a);
|
|
762
|
+
const gates = args.map((a) => "gate" in a ? a.gate : null);
|
|
763
|
+
const hasGates = gates.some((g) => g !== null);
|
|
764
|
+
const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
|
|
765
|
+
let combined = { kind: "empty" };
|
|
766
|
+
for (const p of parsers) combined = union(combined, p._meta.firstSet);
|
|
767
|
+
const meta = {
|
|
768
|
+
firstSet: combined,
|
|
769
|
+
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
770
|
+
isTrivia: false,
|
|
771
|
+
disjoint
|
|
772
|
+
};
|
|
773
|
+
const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
|
|
774
|
+
const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
|
|
775
|
+
let greedyLitMap = null;
|
|
776
|
+
let sortedParsers = null;
|
|
777
|
+
const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
|
|
778
|
+
if (strategy?.tag === "greedyClassify") {
|
|
779
|
+
greedyLitMap = /* @__PURE__ */ new Map();
|
|
780
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
781
|
+
if (i === strategy.superIndex) continue;
|
|
782
|
+
const litVal = getCoreLiteralValue(parsers[i]);
|
|
783
|
+
if (litVal !== null) greedyLitMap.set(litVal, i);
|
|
784
|
+
}
|
|
785
|
+
} else if (strategy?.tag === "literalsLongestFirst") sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
|
|
786
|
+
return {
|
|
787
|
+
_tag: "choice",
|
|
788
|
+
_meta: meta,
|
|
789
|
+
_def: {
|
|
790
|
+
tag: "choice",
|
|
791
|
+
parsers,
|
|
792
|
+
gates,
|
|
793
|
+
disjoint,
|
|
794
|
+
strategy: strategy ?? { tag: "firstMatch" },
|
|
795
|
+
autoNot
|
|
796
|
+
},
|
|
797
|
+
parse(input, pos, ctx) {
|
|
798
|
+
const expected = [];
|
|
799
|
+
if (disjoint && pos < input.length) {
|
|
800
|
+
const code = input.codePointAt(pos);
|
|
801
|
+
let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
|
|
802
|
+
if (!parser2) {
|
|
803
|
+
for (const p of parsers) if (inFirstSet(code, p._meta.firstSet)) {
|
|
804
|
+
parser2 = p;
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (parser2) {
|
|
809
|
+
const result = parser2.parse(input, pos, ctx);
|
|
810
|
+
if (result.ok) return result;
|
|
811
|
+
expected.push(...result.expected);
|
|
812
|
+
return {
|
|
813
|
+
ok: false,
|
|
814
|
+
expected,
|
|
815
|
+
span: {
|
|
816
|
+
start: pos,
|
|
817
|
+
end: pos
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
return {
|
|
822
|
+
ok: false,
|
|
823
|
+
expected: parsers.flatMap((p) => {
|
|
824
|
+
const r = p.parse(input, pos, ctx);
|
|
825
|
+
return r.ok ? [] : r.expected;
|
|
826
|
+
}),
|
|
827
|
+
span: {
|
|
828
|
+
start: pos,
|
|
829
|
+
end: pos
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
if (strategy?.tag === "greedyClassify") {
|
|
834
|
+
const superResult = parsers[strategy.superIndex].parse(input, pos, ctx);
|
|
835
|
+
if (!superResult.ok) return superResult;
|
|
836
|
+
const end = superResult.span.end;
|
|
837
|
+
const litIdx = greedyLitMap.get(input.slice(pos, end));
|
|
838
|
+
if (litIdx !== void 0) {
|
|
839
|
+
const litVal = getCoreLiteralValue(parsers[litIdx]);
|
|
840
|
+
return {
|
|
841
|
+
ok: true,
|
|
842
|
+
value: applyTransforms(parsers[litIdx], litVal, {
|
|
843
|
+
start: pos,
|
|
844
|
+
end
|
|
845
|
+
}),
|
|
846
|
+
span: {
|
|
847
|
+
start: pos,
|
|
848
|
+
end
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
return superResult;
|
|
853
|
+
}
|
|
854
|
+
if (strategy?.tag === "literalsLongestFirst") {
|
|
855
|
+
for (const p of sortedParsers) {
|
|
856
|
+
const r = p.parse(input, pos, ctx);
|
|
857
|
+
if (r.ok) return r;
|
|
858
|
+
expected.push(...r.expected);
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
ok: false,
|
|
862
|
+
expected,
|
|
863
|
+
span: {
|
|
864
|
+
start: pos,
|
|
865
|
+
end: pos
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
870
|
+
if (gates[i] && !gates[i](ctx.state)) continue;
|
|
871
|
+
const mark = saveCstMark(ctx);
|
|
872
|
+
const logLen = ctx._triviaLog?.length;
|
|
873
|
+
const result = parsers[i].parse(input, pos, ctx);
|
|
874
|
+
if (!result.ok) {
|
|
875
|
+
rollbackCstCapture(ctx, mark);
|
|
876
|
+
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
877
|
+
expected.push(...result.expected);
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
const checks = autoNot[i];
|
|
881
|
+
if (checks && autoNotFires(input, result.span.end, checks)) {
|
|
882
|
+
rollbackCstCapture(ctx, mark);
|
|
883
|
+
if (logLen !== void 0 && ctx._triviaLog) ctx._triviaLog.length = logLen;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
return result;
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
ok: false,
|
|
890
|
+
expected,
|
|
891
|
+
span: {
|
|
892
|
+
start: pos,
|
|
893
|
+
end: pos
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function detectStrategy(parsers) {
|
|
900
|
+
const regexIndices = [];
|
|
901
|
+
const literalIndices = [];
|
|
902
|
+
for (let i = 0; i < parsers.length; i++) if (getCoreRegexDef(parsers[i]) !== null) regexIndices.push(i);
|
|
903
|
+
else if (getCoreLiteralValue(parsers[i]) !== null) literalIndices.push(i);
|
|
904
|
+
if (regexIndices.length === 1 && literalIndices.length === parsers.length - 1 && literalIndices.length > 0) {
|
|
905
|
+
const superIndex = regexIndices[0];
|
|
906
|
+
const regexDef = getCoreRegexDef(parsers[superIndex]);
|
|
907
|
+
const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
908
|
+
const re = new RegExp(regexDef.source, flags);
|
|
909
|
+
if (literalIndices.every((i) => {
|
|
910
|
+
const litVal = getCoreLiteralValue(parsers[i]);
|
|
911
|
+
re.lastIndex = 0;
|
|
912
|
+
const m = re.exec(litVal);
|
|
913
|
+
return m !== null && m[0] === litVal;
|
|
914
|
+
})) return {
|
|
915
|
+
tag: "greedyClassify",
|
|
916
|
+
superIndex
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
if (parsers.length === literalIndices.length) return {
|
|
920
|
+
tag: "literalsLongestFirst",
|
|
921
|
+
sortedIndices: [...literalIndices].sort((a, b) => getCoreLiteralValue(parsers[b]).length - getCoreLiteralValue(parsers[a]).length)
|
|
922
|
+
};
|
|
923
|
+
return { tag: "firstMatch" };
|
|
924
|
+
}
|
|
925
|
+
function computeAutoNot(parsers) {
|
|
926
|
+
return parsers.map((p, i) => {
|
|
927
|
+
const litVal = getCoreLiteralValue(p);
|
|
928
|
+
if (litVal === null) return null;
|
|
929
|
+
const checks = [];
|
|
930
|
+
for (let j = i + 1; j < parsers.length; j++) {
|
|
931
|
+
const other = parsers[j];
|
|
932
|
+
const otherLit = getCoreLiteralValue(other);
|
|
933
|
+
if (otherLit !== null && otherLit.startsWith(litVal) && otherLit.length > litVal.length) {
|
|
934
|
+
checks.push({
|
|
935
|
+
kind: "startsWith",
|
|
936
|
+
value: otherLit.slice(litVal.length)
|
|
937
|
+
});
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
const regexDef = getCoreRegexDef(other);
|
|
941
|
+
if (regexDef !== null) {
|
|
942
|
+
const contSet = continuationFirstSet(litVal, regexDef.source, regexDef.flags);
|
|
943
|
+
if (contSet !== null) checks.push({
|
|
944
|
+
kind: "firstSet",
|
|
945
|
+
set: contSet
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return checks.length > 0 ? checks : null;
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
function autoNotFires(input, end, checks) {
|
|
953
|
+
for (const check of checks) if (check.kind === "firstSet") {
|
|
954
|
+
if (inFirstSet(end < input.length ? input.codePointAt(end) ?? -1 : -1, check.set)) return true;
|
|
955
|
+
} else if (input.startsWith(check.value, end)) return true;
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
function getCoreLiteralValue(p) {
|
|
959
|
+
const def = p._def;
|
|
960
|
+
if (def.tag === "literal" && !def.caseInsensitive) return def.value;
|
|
961
|
+
if (def.tag === "transform") return getCoreLiteralValue(def.parser);
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
function getCoreRegexDef(p) {
|
|
965
|
+
const def = p._def;
|
|
966
|
+
if (def.tag === "regex") return {
|
|
967
|
+
source: def.source,
|
|
968
|
+
flags: def.flags
|
|
969
|
+
};
|
|
970
|
+
if (def.tag === "transform") return getCoreRegexDef(def.parser);
|
|
971
|
+
if (def.tag === "label") return getCoreRegexDef(def.parser);
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
function applyTransforms(p, value, span) {
|
|
975
|
+
const def = p._def;
|
|
976
|
+
if (def.tag === "transform") {
|
|
977
|
+
const inner = applyTransforms(def.parser, value, span);
|
|
978
|
+
return def.fn(inner, span);
|
|
979
|
+
}
|
|
980
|
+
return value;
|
|
981
|
+
}
|
|
982
|
+
function continuationFirstSet(lit, source, flags) {
|
|
983
|
+
const re = new RegExp(source, "y" + flags.replace(/[gy]/g, ""));
|
|
984
|
+
re.lastIndex = 0;
|
|
985
|
+
const base = re.exec(lit);
|
|
986
|
+
if (!base || base[0] !== lit) return null;
|
|
987
|
+
const contCodes = [];
|
|
988
|
+
for (let code = 1; code < 128; code++) {
|
|
989
|
+
re.lastIndex = 0;
|
|
990
|
+
const m = re.exec(lit + String.fromCharCode(code));
|
|
991
|
+
if (m && m[0].length > lit.length) contCodes.push(code);
|
|
992
|
+
}
|
|
993
|
+
if (contCodes.length === 0) return null;
|
|
994
|
+
return codesToFirstSet(contCodes);
|
|
995
|
+
}
|
|
996
|
+
function codesToFirstSet(codes) {
|
|
997
|
+
codes.sort((a, b) => a - b);
|
|
998
|
+
const ranges = [];
|
|
999
|
+
let lo = codes[0], hi = codes[0];
|
|
1000
|
+
for (let i = 1; i < codes.length; i++) if (codes[i] === hi + 1) hi = codes[i];
|
|
1001
|
+
else {
|
|
1002
|
+
ranges.push({
|
|
1003
|
+
lo,
|
|
1004
|
+
hi
|
|
1005
|
+
});
|
|
1006
|
+
lo = hi = codes[i];
|
|
1007
|
+
}
|
|
1008
|
+
ranges.push({
|
|
1009
|
+
lo,
|
|
1010
|
+
hi
|
|
1011
|
+
});
|
|
1012
|
+
return {
|
|
1013
|
+
kind: "ranges",
|
|
1014
|
+
ranges
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function inFirstSet(code, fs) {
|
|
1018
|
+
if (fs.kind === "any") return true;
|
|
1019
|
+
if (fs.kind === "empty") return false;
|
|
1020
|
+
for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
function areDisjoint(sets) {
|
|
1024
|
+
if (sets.some((s) => s.kind === "any")) return false;
|
|
1025
|
+
for (let i = 0; i < sets.length; i++) for (let j = i + 1; j < sets.length; j++) if (intersects(sets[i], sets[j])) return false;
|
|
1026
|
+
return true;
|
|
1027
|
+
}
|
|
1028
|
+
function buildAsciiDispatch(parsers) {
|
|
1029
|
+
const table = Array(128).fill(null);
|
|
1030
|
+
for (const parser2 of parsers) {
|
|
1031
|
+
const fs = parser2._meta.firstSet;
|
|
1032
|
+
if (fs.kind !== "ranges") continue;
|
|
1033
|
+
for (const { lo, hi } of fs.ranges) for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) table[code] = parser2;
|
|
1034
|
+
}
|
|
1035
|
+
return table;
|
|
1036
|
+
}
|
|
1037
|
+
function unwrapTrivia(p) {
|
|
1038
|
+
let cur = p;
|
|
1039
|
+
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
1040
|
+
return cur;
|
|
1041
|
+
}
|
|
1042
|
+
function peelLabel(p) {
|
|
1043
|
+
if (p._def.tag === "label") return {
|
|
1044
|
+
label: p._def.label,
|
|
1045
|
+
parser: p._def.parser
|
|
1046
|
+
};
|
|
1047
|
+
return null;
|
|
1048
|
+
}
|
|
1049
|
+
function analyzeLabeledTrivia(trivia2) {
|
|
1050
|
+
let core = unwrapTrivia(trivia2);
|
|
1051
|
+
let minRepeats = 1;
|
|
1052
|
+
if (core._def.tag === "oneOrMore") {
|
|
1053
|
+
core = core._def.parser;
|
|
1054
|
+
minRepeats = 1;
|
|
1055
|
+
} else if (core._def.tag === "many") {
|
|
1056
|
+
core = core._def.parser;
|
|
1057
|
+
minRepeats = 0;
|
|
1058
|
+
}
|
|
1059
|
+
const arms = [];
|
|
1060
|
+
if (core._def.tag === "choice") for (let i = 0; i < core._def.parsers.length; i++) {
|
|
1061
|
+
const peeled = peelLabel(core._def.parsers[i]);
|
|
1062
|
+
if (!peeled) return null;
|
|
1063
|
+
arms.push({
|
|
1064
|
+
label: peeled.label,
|
|
1065
|
+
kindIndex: i,
|
|
1066
|
+
parser: peeled.parser
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
else {
|
|
1070
|
+
const peeled = peelLabel(core);
|
|
1071
|
+
if (!peeled) return null;
|
|
1072
|
+
arms.push({
|
|
1073
|
+
label: peeled.label,
|
|
1074
|
+
kindIndex: 0,
|
|
1075
|
+
parser: peeled.parser
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
return {
|
|
1079
|
+
labels: arms.map((a) => a.label),
|
|
1080
|
+
arms,
|
|
1081
|
+
minRepeats
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
function matchArmAt(input, pos, arm) {
|
|
1085
|
+
const r = arm.parse(input, pos, { trackLines: false });
|
|
1086
|
+
if (!r.ok || r.span.end <= pos) return null;
|
|
1087
|
+
return { end: r.span.end };
|
|
1088
|
+
}
|
|
1089
|
+
function scanLabeledTriviaChunks(input, cur, spec) {
|
|
1090
|
+
const chunks = [];
|
|
1091
|
+
let pos = cur;
|
|
1092
|
+
while (pos < input.length) {
|
|
1093
|
+
let matched = null;
|
|
1094
|
+
for (const arm of spec.arms) {
|
|
1095
|
+
const m = matchArmAt(input, pos, arm.parser);
|
|
1096
|
+
if (m) {
|
|
1097
|
+
matched = {
|
|
1098
|
+
end: m.end,
|
|
1099
|
+
kindIndex: arm.kindIndex
|
|
1100
|
+
};
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
if (!matched) break;
|
|
1105
|
+
chunks.push({
|
|
1106
|
+
start: pos,
|
|
1107
|
+
end: matched.end,
|
|
1108
|
+
kindIndex: matched.kindIndex
|
|
1109
|
+
});
|
|
1110
|
+
pos = matched.end;
|
|
1111
|
+
}
|
|
1112
|
+
if (chunks.length < spec.minRepeats) return {
|
|
1113
|
+
end: cur,
|
|
1114
|
+
chunks: []
|
|
1115
|
+
};
|
|
1116
|
+
return {
|
|
1117
|
+
end: pos,
|
|
1118
|
+
chunks
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
|
|
1122
|
+
const chunks = [];
|
|
1123
|
+
let pos = cur;
|
|
1124
|
+
while (pos < input.length) {
|
|
1125
|
+
const c = input.charCodeAt(pos);
|
|
1126
|
+
if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {
|
|
1127
|
+
const start = pos;
|
|
1128
|
+
pos++;
|
|
1129
|
+
while (pos < input.length) {
|
|
1130
|
+
const c2 = input.charCodeAt(pos);
|
|
1131
|
+
if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) pos++;
|
|
1132
|
+
else break;
|
|
1133
|
+
}
|
|
1134
|
+
chunks.push({
|
|
1135
|
+
start,
|
|
1136
|
+
end: pos,
|
|
1137
|
+
kindIndex: wsKind
|
|
1138
|
+
});
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (c === 47 && input.charCodeAt(pos + 1) === 42) {
|
|
1142
|
+
let j = pos + 2;
|
|
1143
|
+
while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
|
|
1144
|
+
if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
|
|
1145
|
+
const start = pos;
|
|
1146
|
+
pos = j + 2;
|
|
1147
|
+
chunks.push({
|
|
1148
|
+
start,
|
|
1149
|
+
end: pos,
|
|
1150
|
+
kindIndex: commentKind
|
|
1151
|
+
});
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
return {
|
|
1159
|
+
end: pos,
|
|
1160
|
+
chunks
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
function tryFastLabeledScan(input, cur, trivia2) {
|
|
1164
|
+
const spec = analyzeLabeledTrivia(trivia2);
|
|
1165
|
+
if (!spec || spec.arms.length !== 2) return null;
|
|
1166
|
+
const wsArm = spec.arms.find((a) => {
|
|
1167
|
+
const src = getCoreRegexDef(a.parser)?.source;
|
|
1168
|
+
return src != null && !src.includes("\\*");
|
|
1169
|
+
});
|
|
1170
|
+
const commentArm = spec.arms.find((a) => {
|
|
1171
|
+
const src = getCoreRegexDef(a.parser)?.source;
|
|
1172
|
+
return src != null && src.includes("\\*");
|
|
1173
|
+
});
|
|
1174
|
+
if (!wsArm || !commentArm) return null;
|
|
1175
|
+
const { chunks } = scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
|
|
1176
|
+
if (chunks.length < spec.minRepeats) return {
|
|
1177
|
+
end: cur,
|
|
1178
|
+
chunks: []
|
|
1179
|
+
};
|
|
1180
|
+
return scanFastWsCommentsChunks(input, cur, wsArm.kindIndex, commentArm.kindIndex);
|
|
1181
|
+
}
|
|
1182
|
+
function recordTriviaChunks(ctx, chunks) {
|
|
1183
|
+
const kinds = ctx.triviaKindLabels;
|
|
1184
|
+
const mask = ctx._triviaCaptureMask;
|
|
1185
|
+
for (const ch of chunks) {
|
|
1186
|
+
pushTriviaLogEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
|
|
1187
|
+
if (ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0)) {
|
|
1188
|
+
if (mask === void 0 || kinds === void 0 || (mask & 1 << ch.kindIndex) !== 0) pushCstTriviaEntry(ctx, ch.start, ch.end, kinds ? ch.kindIndex : void 0);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
var NOOP_COMMIT = () => {};
|
|
1193
|
+
var fastTriviaCache = /* @__PURE__ */ new WeakMap();
|
|
1194
|
+
function needsDeferredTriviaCommit(ctx) {
|
|
1195
|
+
return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
|
|
1196
|
+
}
|
|
1197
|
+
function saveTriviaMark(ctx) {
|
|
1198
|
+
const m = saveCstMark(ctx);
|
|
1199
|
+
return {
|
|
1200
|
+
raw: m.raw,
|
|
1201
|
+
tlog: m.tlog,
|
|
1202
|
+
leaves: m.leaves,
|
|
1203
|
+
fields: m.fields,
|
|
1204
|
+
log: ctx._triviaLog ? ctx._triviaLog.length : 0
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
function rollbackTrivia(ctx, mark) {
|
|
1208
|
+
rollbackCstCapture(ctx, {
|
|
1209
|
+
raw: mark.raw,
|
|
1210
|
+
tlog: mark.tlog,
|
|
1211
|
+
leaves: mark.leaves,
|
|
1212
|
+
fields: mark.fields
|
|
1213
|
+
});
|
|
1214
|
+
if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
|
|
1215
|
+
}
|
|
1216
|
+
function scanWithLabels(input, cur, ctx) {
|
|
1217
|
+
const triviaP = ctx.trivia;
|
|
1218
|
+
const spec = analyzeLabeledTrivia(triviaP);
|
|
1219
|
+
if (!spec) return {
|
|
1220
|
+
end: cur,
|
|
1221
|
+
commit: NOOP_COMMIT
|
|
1222
|
+
};
|
|
1223
|
+
const { end, chunks } = tryFastLabeledScan(input, cur, triviaP) ?? scanLabeledTriviaChunks(input, cur, spec);
|
|
1224
|
+
if (end === cur) return {
|
|
1225
|
+
end: cur,
|
|
1226
|
+
commit: NOOP_COMMIT
|
|
1227
|
+
};
|
|
1228
|
+
return {
|
|
1229
|
+
end,
|
|
1230
|
+
commit: () => recordTriviaChunks(ctx, chunks)
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
function advanceTrivia(input, cur, ctx) {
|
|
1234
|
+
const triviaP = ctx.trivia;
|
|
1235
|
+
if (!triviaP) return cur;
|
|
1236
|
+
const fast = fastTriviaScanner(triviaP);
|
|
1237
|
+
if (fast) return fast(input, cur);
|
|
1238
|
+
if (ctx.triviaKindLabels) return scanWithLabels(input, cur, ctx).end;
|
|
1239
|
+
const tr = triviaP.parse(input, cur, {
|
|
1240
|
+
trackLines: ctx.trackLines,
|
|
1241
|
+
state: ctx.state
|
|
1242
|
+
});
|
|
1243
|
+
return tr.ok && tr.span.end > cur ? tr.span.end : cur;
|
|
1244
|
+
}
|
|
1245
|
+
function scanTrivia(input, cur, ctx) {
|
|
1246
|
+
const triviaP = ctx.trivia;
|
|
1247
|
+
if (!triviaP) return {
|
|
1248
|
+
end: cur,
|
|
1249
|
+
commit: NOOP_COMMIT
|
|
1250
|
+
};
|
|
1251
|
+
const log = ctx._triviaLog;
|
|
1252
|
+
const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
|
|
1253
|
+
const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
|
|
1254
|
+
if (fast && log === void 0 && !captureTl) return {
|
|
1255
|
+
end: fast(input, cur),
|
|
1256
|
+
commit: NOOP_COMMIT
|
|
1257
|
+
};
|
|
1258
|
+
if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) return scanWithLabels(input, cur, ctx);
|
|
1259
|
+
if (log !== void 0 || captureTl) {
|
|
1260
|
+
const tr2 = triviaP.parse(input, cur, {
|
|
1261
|
+
trackLines: log !== void 0 ? false : ctx.trackLines,
|
|
1262
|
+
state: ctx.state
|
|
1263
|
+
});
|
|
1264
|
+
if (!tr2.ok || tr2.span.end === cur) return {
|
|
1265
|
+
end: cur,
|
|
1266
|
+
commit: NOOP_COMMIT
|
|
1267
|
+
};
|
|
1268
|
+
const end = tr2.span.end;
|
|
1269
|
+
return {
|
|
1270
|
+
end,
|
|
1271
|
+
commit: () => {
|
|
1272
|
+
pushTriviaLogEntry(ctx, cur, end);
|
|
1273
|
+
if (captureTl) pushCstTriviaEntry(ctx, cur, end);
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
const tr = triviaP.parse(input, cur, {
|
|
1278
|
+
trackLines: ctx.trackLines,
|
|
1279
|
+
state: ctx.state
|
|
1280
|
+
});
|
|
1281
|
+
return {
|
|
1282
|
+
end: tr.ok ? tr.span.end : cur,
|
|
1283
|
+
commit: NOOP_COMMIT
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
function fastTriviaScanner(trivia2) {
|
|
1287
|
+
const cached = fastTriviaCache.get(trivia2);
|
|
1288
|
+
if (cached !== void 0) return cached;
|
|
1289
|
+
const scanner = buildFastTriviaScanner(trivia2);
|
|
1290
|
+
fastTriviaCache.set(trivia2, scanner);
|
|
1291
|
+
return scanner;
|
|
1292
|
+
}
|
|
1293
|
+
function buildFastTriviaScanner(trivia2) {
|
|
1294
|
+
const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
|
|
1295
|
+
const direct = regexTriviaScanner(core);
|
|
1296
|
+
if (direct) return direct;
|
|
1297
|
+
const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
|
|
1298
|
+
if (!repeat) return null;
|
|
1299
|
+
const one = regexTriviaScanner(repeat);
|
|
1300
|
+
if (one) return loopScanner([one]);
|
|
1301
|
+
if (repeat._def.tag !== "choice") return null;
|
|
1302
|
+
const arms = repeat._def.parsers.map(regexTriviaScanner);
|
|
1303
|
+
if (arms.some((s) => s === null)) return null;
|
|
1304
|
+
return loopScanner(arms);
|
|
1305
|
+
}
|
|
1306
|
+
function loopScanner(arms) {
|
|
1307
|
+
return (input, cur) => {
|
|
1308
|
+
let pos = cur;
|
|
1309
|
+
scan: while (pos < input.length) {
|
|
1310
|
+
for (const arm of arms) {
|
|
1311
|
+
const end = arm(input, pos);
|
|
1312
|
+
if (end > pos) {
|
|
1313
|
+
pos = end;
|
|
1314
|
+
continue scan;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
break;
|
|
1318
|
+
}
|
|
1319
|
+
return pos;
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
function regexTriviaScanner(parser2) {
|
|
1323
|
+
if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
|
|
1324
|
+
const source = parser2._def.source;
|
|
1325
|
+
return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
|
|
1326
|
+
}
|
|
1327
|
+
function inRanges2(cp, ranges) {
|
|
1328
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
1329
|
+
const r = ranges[i];
|
|
1330
|
+
if (cp >= r[0] && cp <= r[1]) return true;
|
|
1331
|
+
}
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
function classScanner(classBody) {
|
|
1335
|
+
const ranges = parseClassRanges(classBody);
|
|
1336
|
+
if (!ranges) return null;
|
|
1337
|
+
return (input, cur) => {
|
|
1338
|
+
let pos = cur;
|
|
1339
|
+
while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
|
|
1340
|
+
return pos;
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
function lineCommentScanner(commentCode) {
|
|
1344
|
+
return (input, cur) => {
|
|
1345
|
+
if (input.charCodeAt(cur) !== commentCode) return cur;
|
|
1346
|
+
let pos = cur + 1;
|
|
1347
|
+
while (pos < input.length) {
|
|
1348
|
+
const cc = input.charCodeAt(pos);
|
|
1349
|
+
if (cc === 10 || cc === 13) break;
|
|
1350
|
+
pos++;
|
|
1351
|
+
}
|
|
1352
|
+
return pos;
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
function classRunSource(source) {
|
|
1356
|
+
const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
|
|
1357
|
+
return m ? classScanner(m[1]) : null;
|
|
1358
|
+
}
|
|
1359
|
+
function classifyTriviaArm(arm) {
|
|
1360
|
+
const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
|
|
1361
|
+
if (cls) {
|
|
1362
|
+
const ranges = parseClassRanges(cls[1]);
|
|
1363
|
+
return ranges ? {
|
|
1364
|
+
kind: "class",
|
|
1365
|
+
ranges
|
|
1366
|
+
} : null;
|
|
1367
|
+
}
|
|
1368
|
+
const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
|
|
1369
|
+
if (lc) {
|
|
1370
|
+
const marker = lc[1];
|
|
1371
|
+
return {
|
|
1372
|
+
kind: "comment",
|
|
1373
|
+
code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0)
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
function armScanner(arm) {
|
|
1379
|
+
if (arm.kind === "comment") return lineCommentScanner(arm.code);
|
|
1380
|
+
const ranges = arm.ranges;
|
|
1381
|
+
return (input, cur) => {
|
|
1382
|
+
let pos = cur;
|
|
1383
|
+
while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
|
|
1384
|
+
return pos;
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
function fusedTriviaScanner(ranges, commentCodes) {
|
|
1388
|
+
const c0 = commentCodes[0];
|
|
1389
|
+
const single = commentCodes.length === 1;
|
|
1390
|
+
return (input, cur) => {
|
|
1391
|
+
let pos = cur;
|
|
1392
|
+
const len = input.length;
|
|
1393
|
+
for (;;) {
|
|
1394
|
+
const c = input.charCodeAt(pos);
|
|
1395
|
+
if (pos < len && inRanges2(c, ranges)) {
|
|
1396
|
+
pos++;
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
if (single ? c === c0 : commentCodes.includes(c)) {
|
|
1400
|
+
pos++;
|
|
1401
|
+
while (pos < len) {
|
|
1402
|
+
const cc = input.charCodeAt(pos);
|
|
1403
|
+
if (cc === 10 || cc === 13) break;
|
|
1404
|
+
pos++;
|
|
1405
|
+
}
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
break;
|
|
1409
|
+
}
|
|
1410
|
+
return pos;
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
function splitTopLevelAlts(body) {
|
|
1414
|
+
const arms = [];
|
|
1415
|
+
let inClass = false;
|
|
1416
|
+
let start = 0;
|
|
1417
|
+
for (let i = 0; i < body.length; i++) {
|
|
1418
|
+
const c = body[i];
|
|
1419
|
+
if (c === "\\") {
|
|
1420
|
+
i++;
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
if (inClass) {
|
|
1424
|
+
if (c === "]") inClass = false;
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
if (c === "[") inClass = true;
|
|
1428
|
+
else if (c === "(") return null;
|
|
1429
|
+
else if (c === "|") {
|
|
1430
|
+
arms.push(body.slice(start, i));
|
|
1431
|
+
start = i + 1;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
arms.push(body.slice(start));
|
|
1435
|
+
return arms;
|
|
1436
|
+
}
|
|
1437
|
+
function altStarSource(source) {
|
|
1438
|
+
const m = /^\(\?:(.*)\)[*+]$/.exec(source);
|
|
1439
|
+
if (!m) return null;
|
|
1440
|
+
const armSrcs = splitTopLevelAlts(m[1]);
|
|
1441
|
+
if (!armSrcs || armSrcs.length < 2) return null;
|
|
1442
|
+
const arms = [];
|
|
1443
|
+
for (const src of armSrcs) {
|
|
1444
|
+
const arm = classifyTriviaArm(src);
|
|
1445
|
+
if (!arm) return null;
|
|
1446
|
+
arms.push(arm);
|
|
1447
|
+
}
|
|
1448
|
+
const ranges = [];
|
|
1449
|
+
const commentCodes = [];
|
|
1450
|
+
for (const arm of arms) if (arm.kind === "class") ranges.push(...arm.ranges);
|
|
1451
|
+
else commentCodes.push(arm.code);
|
|
1452
|
+
if (commentCodes.some((code) => inRanges2(code, ranges))) return loopScanner(arms.map(armScanner));
|
|
1453
|
+
if (commentCodes.length === 0) return armScanner({
|
|
1454
|
+
kind: "class",
|
|
1455
|
+
ranges
|
|
1456
|
+
});
|
|
1457
|
+
return fusedTriviaScanner(ranges, commentCodes);
|
|
1458
|
+
}
|
|
1459
|
+
function blockCommentSource(source) {
|
|
1460
|
+
return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
|
|
1461
|
+
}
|
|
1462
|
+
function scanBlockComment(input, cur) {
|
|
1463
|
+
if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
|
|
1464
|
+
const close = input.indexOf("*/", cur + 2);
|
|
1465
|
+
return close === -1 ? cur : close + 2;
|
|
1466
|
+
}
|
|
1467
|
+
function sequence(...parsers) {
|
|
1468
|
+
const meta = {
|
|
1469
|
+
firstSet: sequenceFirstSet(parsers),
|
|
1470
|
+
canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
|
|
1471
|
+
isTrivia: false
|
|
1472
|
+
};
|
|
1473
|
+
const def = {
|
|
1474
|
+
tag: "sequence",
|
|
1475
|
+
parsers
|
|
1476
|
+
};
|
|
1477
|
+
return {
|
|
1478
|
+
_tag: "sequence",
|
|
1479
|
+
_meta: meta,
|
|
1480
|
+
_def: def,
|
|
1481
|
+
parse(input, pos, ctx) {
|
|
1482
|
+
const values = def.valueUnused ? void 0 : [];
|
|
1483
|
+
let cur = pos;
|
|
1484
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
1485
|
+
if (ctx.trivia && i > 0) {
|
|
1486
|
+
let scanEnd;
|
|
1487
|
+
let mark = saveTriviaMark(ctx);
|
|
1488
|
+
if (needsDeferredTriviaCommit(ctx)) {
|
|
1489
|
+
const scan = scanTrivia(input, cur, ctx);
|
|
1490
|
+
scan.commit();
|
|
1491
|
+
scanEnd = scan.end;
|
|
1492
|
+
} else scanEnd = advanceTrivia(input, cur, ctx);
|
|
1493
|
+
const result2 = parsers[i].parse(input, scanEnd, ctx);
|
|
1494
|
+
if (!result2.ok) return result2;
|
|
1495
|
+
if (result2.span.end > scanEnd) cur = result2.span.end;
|
|
1496
|
+
else rollbackTrivia(ctx, mark);
|
|
1497
|
+
if (values !== void 0) values.push(result2.value);
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
const result = parsers[i].parse(input, cur, ctx);
|
|
1501
|
+
if (!result.ok) return result;
|
|
1502
|
+
if (values !== void 0) values.push(result.value);
|
|
1503
|
+
cur = result.span.end;
|
|
1504
|
+
}
|
|
1505
|
+
return {
|
|
1506
|
+
ok: true,
|
|
1507
|
+
value: values ?? void 0,
|
|
1508
|
+
span: {
|
|
1509
|
+
start: pos,
|
|
1510
|
+
end: cur
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
function optional(combinator) {
|
|
1517
|
+
const meta = {
|
|
1518
|
+
firstSet: combinator._meta.firstSet,
|
|
1519
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1520
|
+
isTrivia: false
|
|
1521
|
+
};
|
|
1522
|
+
const firstSetSkippable = !matchesEmpty(combinator);
|
|
1523
|
+
return {
|
|
1524
|
+
_tag: "optional",
|
|
1525
|
+
_meta: meta,
|
|
1526
|
+
_def: {
|
|
1527
|
+
tag: "optional",
|
|
1528
|
+
parser: combinator
|
|
1529
|
+
},
|
|
1530
|
+
parse(input, pos, ctx) {
|
|
1531
|
+
if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) return {
|
|
1532
|
+
ok: true,
|
|
1533
|
+
value: null,
|
|
1534
|
+
span: {
|
|
1535
|
+
start: pos,
|
|
1536
|
+
end: pos
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
const mark = saveTriviaMark(ctx);
|
|
1540
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1541
|
+
if (result.ok) return result;
|
|
1542
|
+
rollbackTrivia(ctx, mark);
|
|
1543
|
+
return {
|
|
1544
|
+
ok: true,
|
|
1545
|
+
value: null,
|
|
1546
|
+
span: {
|
|
1547
|
+
start: pos,
|
|
1548
|
+
end: pos
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function startsFirstSet(combinator, input, pos) {
|
|
1555
|
+
const fs = combinator._meta.firstSet;
|
|
1556
|
+
if (fs.kind === "any") return true;
|
|
1557
|
+
if (fs.kind === "empty") return false;
|
|
1558
|
+
const code = input.codePointAt(pos);
|
|
1559
|
+
if (code === void 0) return false;
|
|
1560
|
+
for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
1561
|
+
return false;
|
|
1562
|
+
}
|
|
1563
|
+
new Set("()[]{}*+?|^$.".split(""));
|
|
1564
|
+
[
|
|
1565
|
+
` if (_cap && _e > _pos) {`,
|
|
1566
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
|
|
1567
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
|
|
1568
|
+
` }`
|
|
1569
|
+
].join("\n");
|
|
1570
|
+
var { isFinite } = Number;
|
|
1571
|
+
registerRegexAnalyzer(firstSetFromRegex);
|
|
1572
|
+
//#endregion
|
|
1573
|
+
//#region src/interp.ts
|
|
1574
|
+
/**
|
|
1575
|
+
* SCSS `#{…}` interpolation helpers for the functional grammar builders.
|
|
1576
|
+
* Mirrors productions/helpers.ts (Chevrotain) without the nested parser bootstrap.
|
|
1577
|
+
*/
|
|
1578
|
+
let parseScssFnLazy;
|
|
1579
|
+
/** Wired from grammar.ts after `parseScssFn` is defined (breaks circular import). */
|
|
1580
|
+
function setParseScssFnForInterp(fn) {
|
|
1581
|
+
parseScssFnLazy = fn;
|
|
1582
|
+
}
|
|
1583
|
+
function findScssInterpolationSpans(value) {
|
|
1584
|
+
const matches = [];
|
|
1585
|
+
let i = 0;
|
|
1586
|
+
while (i < value.length) if (value[i] === "#" && value[i + 1] === "{") {
|
|
1587
|
+
const start = i;
|
|
1588
|
+
i += 2;
|
|
1589
|
+
let depth = 1;
|
|
1590
|
+
const contentStart = i;
|
|
1591
|
+
while (i < value.length && depth > 0) {
|
|
1592
|
+
const ch = value[i];
|
|
1593
|
+
if (ch === "{") depth++;
|
|
1594
|
+
else if (ch === "}") depth--;
|
|
1595
|
+
i++;
|
|
1596
|
+
}
|
|
1597
|
+
if (depth === 0) matches.push({
|
|
1598
|
+
start,
|
|
1599
|
+
end: i,
|
|
1600
|
+
content: value.slice(contentStart, i - 1)
|
|
1601
|
+
});
|
|
1602
|
+
} else i++;
|
|
1603
|
+
return matches;
|
|
1604
|
+
}
|
|
1605
|
+
function unwrapSingleReference(n) {
|
|
1606
|
+
if (isNode(n, N.Reference) && n.options?.type === "variable" && !n.target && typeof n.key === "string") return n;
|
|
1607
|
+
}
|
|
1608
|
+
function valueFromParseResult(r, fallback, loc) {
|
|
1609
|
+
const root = r.tree;
|
|
1610
|
+
if (isNode(root, N.Rules) && root.rules.length > 0) return root.rules[0];
|
|
1611
|
+
if (root instanceof Node) return root;
|
|
1612
|
+
return new Any(fallback, { role: "any" }, loc);
|
|
1613
|
+
}
|
|
1614
|
+
/** Parse a `#{…}` inner expression via the functional value grammar. */
|
|
1615
|
+
function parseScssInterpExpr(expr, loc) {
|
|
1616
|
+
const trimmed = expr.trim();
|
|
1617
|
+
if (!trimmed) return new Any("", { role: "any" }, loc);
|
|
1618
|
+
if (!parseScssFnLazy) throw new Error("parseScssFn not wired for interpolation (setParseScssFnForInterp)");
|
|
1619
|
+
const r = parseScssFnLazy(trimmed, "valueList");
|
|
1620
|
+
if (r.errors.length) return new Any(trimmed, { role: "any" }, loc);
|
|
1621
|
+
const tree = valueFromParseResult(r, trimmed, loc);
|
|
1622
|
+
const ref = unwrapSingleReference(tree);
|
|
1623
|
+
if (ref && typeof ref.key === "string") return new Reference({ key: ref.key }, {
|
|
1624
|
+
type: "variable",
|
|
1625
|
+
role: "ident"
|
|
1626
|
+
}, loc);
|
|
1627
|
+
if (isNode(tree, N.Reference)) return new Expression(tree, void 0, loc);
|
|
1628
|
+
return tree;
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Validate a `selector.parse("…")` argument through the functional selector
|
|
1632
|
+
* grammar. Returns `true` when the text is a well-formed selector list. Used to
|
|
1633
|
+
* gate lifting a `selector.*` call into a `SelectorCapture`; the capture keeps the
|
|
1634
|
+
* lean string payload (`SelectorCapture` supports a bare-string `SelectorLike`).
|
|
1635
|
+
*/
|
|
1636
|
+
function isValidScssSelectorList(selectorText) {
|
|
1637
|
+
const trimmed = selectorText.trim();
|
|
1638
|
+
if (!trimmed) return false;
|
|
1639
|
+
if (!parseScssFnLazy) throw new Error("parseScssFn not wired for interpolation (setParseScssFnForInterp)");
|
|
1640
|
+
return parseScssFnLazy(trimmed, "SelectorList").errors.length === 0;
|
|
1641
|
+
}
|
|
1642
|
+
/** Turn a parsed expression into an interpolation replacement (name/ident slots). */
|
|
1643
|
+
function toInterpReplacement(expr, loc) {
|
|
1644
|
+
const ref = unwrapSingleReference(expr);
|
|
1645
|
+
if (ref && typeof ref.key === "string") return new Reference({ key: ref.key }, {
|
|
1646
|
+
type: "variable",
|
|
1647
|
+
role: "ident"
|
|
1648
|
+
}, loc);
|
|
1649
|
+
if (isNode(expr, N.Reference)) return new Expression(expr, void 0, loc);
|
|
1650
|
+
return expr;
|
|
1651
|
+
}
|
|
1652
|
+
/** Build an `Interpolated` node from a string containing `#{…}` runs. */
|
|
1653
|
+
function buildScssInterpolatedFromString(value, loc, role) {
|
|
1654
|
+
const matches = findScssInterpolationSpans(value);
|
|
1655
|
+
if (matches.length === 0) return new Any(value, { role }, loc);
|
|
1656
|
+
const replacements = [];
|
|
1657
|
+
let source = value;
|
|
1658
|
+
let offset = 0;
|
|
1659
|
+
for (const match of matches) {
|
|
1660
|
+
const adjustedStart = match.start - offset;
|
|
1661
|
+
const adjustedEnd = match.end - offset;
|
|
1662
|
+
source = source.slice(0, adjustedStart) + INTERPOLATION_PLACEHOLDER + source.slice(adjustedEnd);
|
|
1663
|
+
offset += match.end - match.start - INTERPOLATION_PLACEHOLDER.length;
|
|
1664
|
+
replacements.push(toInterpReplacement(parseScssInterpExpr(match.content, loc), loc));
|
|
1665
|
+
}
|
|
1666
|
+
return new Interpolated({
|
|
1667
|
+
source,
|
|
1668
|
+
replacements
|
|
1669
|
+
}, { role }, loc);
|
|
1670
|
+
}
|
|
1671
|
+
//#endregion
|
|
1672
|
+
//#region src/scss-atrule-helpers.ts
|
|
1673
|
+
/**
|
|
1674
|
+
* Shared helpers for SCSS module-system at-rules in the functional parser.
|
|
1675
|
+
*/
|
|
1676
|
+
function isScriptUsePath(path) {
|
|
1677
|
+
return path.endsWith(".js") || path.endsWith(".ts") || path.endsWith(".json");
|
|
1678
|
+
}
|
|
1679
|
+
function defaultNamespaceFromPath(path) {
|
|
1680
|
+
if (path.startsWith("sass:")) return path.slice(5).split("/").filter(Boolean).pop();
|
|
1681
|
+
const base = path.split("/").filter(Boolean).pop();
|
|
1682
|
+
if (!base) return;
|
|
1683
|
+
return base.replace(/\.(scss|sass|css|jess|js|ts|json)$/i, "") || void 0;
|
|
1684
|
+
}
|
|
1685
|
+
function quotedLike(original, nextValue, loc) {
|
|
1686
|
+
const quote = original.options?.quote ?? "\"";
|
|
1687
|
+
const escaped = original.options?.escaped;
|
|
1688
|
+
const nodeLoc = loc ?? sourceSpanOf(original);
|
|
1689
|
+
return new Quoted(new Any(nextValue, { role: "any" }), {
|
|
1690
|
+
quote,
|
|
1691
|
+
escaped
|
|
1692
|
+
}, nodeLoc);
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Detect the CSS `@import` ordering violations Sass parse-rejects (`error/wrong_order/*`).
|
|
1696
|
+
* The full media-query-list / `supports()` grammar is out of scope for the
|
|
1697
|
+
* scanned prelude, so this catches the clearly-invalid, low-false-positive
|
|
1698
|
+
* shapes on the raw prelude text (everything after `@import`, minus the path):
|
|
1699
|
+
*
|
|
1700
|
+
* 1. A bare media feature `(x: y)` (NOT a `fn(...)` call — hence the
|
|
1701
|
+
* no-ident-before-`(` guard) followed by anything other than `and` / `or` /
|
|
1702
|
+
* `,` / `;` / end. Catches `"a" (b: c) supports(d: e)`, `"a" (b: c) d`,
|
|
1703
|
+
* `"a" (b: c) d(e)`.
|
|
1704
|
+
* 2. A comma directly followed by a function call `ident(` — a new import item
|
|
1705
|
+
* can never be `supports(...)` or an unknown function. Catches
|
|
1706
|
+
* `"a" b, supports(c: d)`, `"a" b, c(d)`, and `"a", url(b)`.
|
|
1707
|
+
*
|
|
1708
|
+
* Not caught (documented as remaining): a string after a comma in media context
|
|
1709
|
+
* (`"a" b, "c"` — indistinguishable from a valid plain-import continuation without
|
|
1710
|
+
* modelling media-vs-plain state) and `supports()` value-syntax errors
|
|
1711
|
+
* (`supports(--a:)`).
|
|
1712
|
+
*/
|
|
1713
|
+
function checkImportPreludeOrder(preludeText, recordError) {
|
|
1714
|
+
const text = preludeText;
|
|
1715
|
+
if (/(?<![-\w])\([^()]*:[^()]*\)\s*(?!and(?![-\w])|or(?![-\w])|[,;{})])\S/i.test(text) || /,\s*[a-zA-Z][-\w]*\s*\(/.test(text)) recordError("Invalid @import: a media-query list must not follow a media feature without `and`/`or`, and a comma-separated @import item must be a URL or string (not `supports(…)` or another function).");
|
|
1716
|
+
}
|
|
1717
|
+
function isPlainCssImportPath(rawPath) {
|
|
1718
|
+
return /\.css(?:$|[?#])/i.test(rawPath) || /^[a-z]+:\/\//i.test(rawPath) || rawPath.startsWith("//");
|
|
1719
|
+
}
|
|
1720
|
+
function isPlainCssImportPrelude(prelude, extraText) {
|
|
1721
|
+
if (prelude instanceof Url) return true;
|
|
1722
|
+
if (extraText && extraText.trim()) return true;
|
|
1723
|
+
if (isNode(prelude, N.Quoted)) return isPlainCssImportPath(prelude.valueOf());
|
|
1724
|
+
return true;
|
|
1725
|
+
}
|
|
1726
|
+
function findDisallowedExtendSelector(selector, allowed) {
|
|
1727
|
+
if (isNode(selector, N.SelectorList)) {
|
|
1728
|
+
for (const item of selector.value) {
|
|
1729
|
+
const disallowed = findDisallowedExtendSelector(item, allowed);
|
|
1730
|
+
if (disallowed) return disallowed;
|
|
1731
|
+
}
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
const kinds = isNode(selector, N.BasicSelector) ? ["simple", "basic"] : isNode(selector, N.PseudoSelector) ? ["simple", "pseudo"] : isNode(selector, N.CompoundSelector) ? ["compound"] : isNode(selector, N.ComplexSelector) ? ["complex"] : ["simple"];
|
|
1735
|
+
if (isNode(selector, N.CompoundSelector) && selector.value.length === 1) return findDisallowedExtendSelector(selector.value[0], allowed);
|
|
1736
|
+
if (isNode(selector, N.ComplexSelector) && selector.value.length === 1) return findDisallowedExtendSelector(selector.value[0], allowed);
|
|
1737
|
+
if (kinds.some((k) => allowed.includes(k))) return;
|
|
1738
|
+
return {
|
|
1739
|
+
kind: kinds[0],
|
|
1740
|
+
selector
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
function validateExtendTarget(target, allowed, recordError) {
|
|
1744
|
+
if (!allowed) return;
|
|
1745
|
+
const disallowed = findDisallowedExtendSelector(target, allowed);
|
|
1746
|
+
if (!disallowed) return;
|
|
1747
|
+
recordError(`@extend only allows ${allowed.length === 1 ? `${allowed[0]} value` : allowed.join(", ")}, but found ${disallowed.kind} selector "${disallowed.selector.valueOf()}".`);
|
|
1748
|
+
}
|
|
1749
|
+
function checkForwardPreludeErrors(preludeExtra, recordError) {
|
|
1750
|
+
if (!preludeExtra?.trim()) return;
|
|
1751
|
+
const text = preludeExtra.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n\r]*/g, " ").replace(/\s+/g, " ").trim();
|
|
1752
|
+
if (/\bas\s+\S+-\*/.test(text)) recordError("@forward with \"as <prefix>-*\" prefixing is not supported in Jess and will never be. Use explicit namespacing instead.");
|
|
1753
|
+
if (/\b(show|hide)\b/.test(text)) recordError("@forward with \"show\"/\"hide\" lists is not supported in Jess and will never be. Visibility control belongs to the module itself.");
|
|
1754
|
+
}
|
|
1755
|
+
function isPlaceholderExtendTarget(target) {
|
|
1756
|
+
if (typeof target === "string") return target.startsWith("\\");
|
|
1757
|
+
if (isNode(target, N.BasicSelector)) return target.value.startsWith("\\");
|
|
1758
|
+
if (isNode(target, N.SelectorList) && target.value.length === 1) return isPlaceholderExtendTarget(target.value[0]);
|
|
1759
|
+
if (isNode(target, N.CompoundSelector) && target.value.length === 1) return isPlaceholderExtendTarget(target.value[0]);
|
|
1760
|
+
if (isNode(target, N.ComplexSelector) && target.value.length === 1) return isPlaceholderExtendTarget(target.value[0]);
|
|
1761
|
+
return false;
|
|
1762
|
+
}
|
|
1763
|
+
//#endregion
|
|
1764
|
+
//#region src/scss-atroot-helpers.ts
|
|
1765
|
+
function createNullParentAmpersand(context, selector) {
|
|
1766
|
+
const location = selector ? sourceSpanOf(selector) : void 0;
|
|
1767
|
+
const nil = new Nil(void 0, void 0, location, context);
|
|
1768
|
+
const amp = new Ampersand({ selectorContainer: { selector: nil } }, void 0, location, context);
|
|
1769
|
+
amp.adopt(nil);
|
|
1770
|
+
return amp;
|
|
1771
|
+
}
|
|
1772
|
+
function getNodeLocation(node) {
|
|
1773
|
+
return sourceSpanOf(node);
|
|
1774
|
+
}
|
|
1775
|
+
function prefixAtRootSelector(selector, context) {
|
|
1776
|
+
if (isNode(selector, N.SelectorList)) return new SelectorList(selector.value.map((item) => prefixAtRootSelector(item, context)), void 0, getNodeLocation(selector), context);
|
|
1777
|
+
const amp = createNullParentAmpersand(context, selector);
|
|
1778
|
+
if (isNode(selector, N.ComplexSelector)) return new ComplexSelector([amp, ...selector.value], void 0, getNodeLocation(selector), context);
|
|
1779
|
+
return new ComplexSelector([amp, selector], void 0, getNodeLocation(selector), context);
|
|
1780
|
+
}
|
|
1781
|
+
function lowerPlainAtRootRules(rules, context) {
|
|
1782
|
+
const transformRule = (node) => {
|
|
1783
|
+
if (isNode(node, N.Ruleset)) {
|
|
1784
|
+
const rs = node;
|
|
1785
|
+
if (!isNode(rs.selector, N.Nil)) return new Ruleset({
|
|
1786
|
+
selector: prefixAtRootSelector(rs.selector, context),
|
|
1787
|
+
rules: rs.rules,
|
|
1788
|
+
...rs.guard !== void 0 && { guard: rs.guard },
|
|
1789
|
+
...rs.selectorBeforeExtend !== void 0 && { selectorBeforeExtend: rs.selectorBeforeExtend }
|
|
1790
|
+
}, rs.options, sourceSpanOf(rs), context);
|
|
1791
|
+
return node;
|
|
1792
|
+
}
|
|
1793
|
+
if (isNode(node, N.AtRule) && node.rules) {
|
|
1794
|
+
lowerPlainAtRootRules(node.rules, context);
|
|
1795
|
+
return node;
|
|
1796
|
+
}
|
|
1797
|
+
if (isNode(node, N.If)) {
|
|
1798
|
+
lowerPlainAtRootRules(node, context);
|
|
1799
|
+
if (node.else) lowerPlainAtRootRules(node.else, context);
|
|
1800
|
+
return node;
|
|
1801
|
+
}
|
|
1802
|
+
if (isNode(node, N.For)) {
|
|
1803
|
+
lowerPlainAtRootRules(node, context);
|
|
1804
|
+
return node;
|
|
1805
|
+
}
|
|
1806
|
+
if (isNode(node, N.While)) {
|
|
1807
|
+
lowerPlainAtRootRules(node, context);
|
|
1808
|
+
return node;
|
|
1809
|
+
}
|
|
1810
|
+
return node;
|
|
1811
|
+
};
|
|
1812
|
+
for (let i = 0; i < rules.rules.length; i++) rules.rules[i] = transformRule(rules.rules[i]);
|
|
1813
|
+
}
|
|
1814
|
+
//#endregion
|
|
1815
|
+
//#region src/scss-value-helpers.ts
|
|
1816
|
+
/**
|
|
1817
|
+
* SCSS value desugaring helpers for the functional grammar builders.
|
|
1818
|
+
* Ports productions/helpers.ts without the Chevrotain parser bootstrap.
|
|
1819
|
+
*/
|
|
1820
|
+
function unwrapSingleSequence(n) {
|
|
1821
|
+
if (isNode(n, N.Sequence) && n.value.length === 1) return n.value[0];
|
|
1822
|
+
return n;
|
|
1823
|
+
}
|
|
1824
|
+
function toDeclKey(node) {
|
|
1825
|
+
return String(node.valueOf());
|
|
1826
|
+
}
|
|
1827
|
+
function isValidIdentifierKey(key) {
|
|
1828
|
+
return /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(key);
|
|
1829
|
+
}
|
|
1830
|
+
function makeNamespacedReference(parts, finalType, loc) {
|
|
1831
|
+
let current = new Reference(parts[0], { type: "variable" }, loc);
|
|
1832
|
+
for (let i = 1; i < parts.length; i++) {
|
|
1833
|
+
const isFinal = i === parts.length - 1;
|
|
1834
|
+
current = new Reference({
|
|
1835
|
+
target: current,
|
|
1836
|
+
key: parts[i]
|
|
1837
|
+
}, { type: isFinal ? finalType : "index" }, loc);
|
|
1838
|
+
}
|
|
1839
|
+
return current;
|
|
1840
|
+
}
|
|
1841
|
+
function desugarNamespacedCall(call, loc) {
|
|
1842
|
+
const { name, args } = call;
|
|
1843
|
+
if (typeof name !== "string") return call;
|
|
1844
|
+
if (!name.includes(".")) return call;
|
|
1845
|
+
if (name === "map.get") return call;
|
|
1846
|
+
const parts = name.split(".").filter(Boolean);
|
|
1847
|
+
if (parts.length < 2) return call;
|
|
1848
|
+
return new Call({
|
|
1849
|
+
name: makeNamespacedReference(parts, "function", loc),
|
|
1850
|
+
args
|
|
1851
|
+
}, call.options, loc);
|
|
1852
|
+
}
|
|
1853
|
+
function desugarMapLookup(call, loc) {
|
|
1854
|
+
const { name, args: argsList } = call;
|
|
1855
|
+
if (typeof name !== "string") return call;
|
|
1856
|
+
if (name !== "map-get" && name !== "map.get") return call;
|
|
1857
|
+
const args = isNode(argsList, N.List) ? argsList.value : [];
|
|
1858
|
+
if (args.length < 2) return call;
|
|
1859
|
+
const mapExpr = unwrapSingleSequence(args[0]);
|
|
1860
|
+
const keyArgs = args.slice(1).map((a) => unwrapSingleSequence(a));
|
|
1861
|
+
const initialTarget = isNode(mapExpr, N.Reference) ? mapExpr : isNode(mapExpr, N.Call) ? mapExpr : void 0;
|
|
1862
|
+
if (!initialTarget) return call;
|
|
1863
|
+
let currentTarget = initialTarget;
|
|
1864
|
+
for (const keyNode of keyArgs) {
|
|
1865
|
+
const keyStr = toDeclKey(keyNode);
|
|
1866
|
+
const useDeclaration = isValidIdentifierKey(keyStr);
|
|
1867
|
+
currentTarget = new Reference({
|
|
1868
|
+
target: currentTarget,
|
|
1869
|
+
key: useDeclaration ? keyStr : keyNode
|
|
1870
|
+
}, { type: useDeclaration ? "declaration" : "index" }, loc);
|
|
1871
|
+
}
|
|
1872
|
+
return currentTarget;
|
|
1873
|
+
}
|
|
1874
|
+
//#endregion
|
|
1875
|
+
//#region src/builders.ts
|
|
1876
|
+
/**
|
|
1877
|
+
* ScssGrammar — Parséman-based SCSS parser, extending LessGrammar.
|
|
1878
|
+
*
|
|
1879
|
+
* Adds SCSS-specific grammar on top of Less (which in turn extends CSS):
|
|
1880
|
+
* - Variable declarations: $var: value [!default|!global]; → VarDeclaration
|
|
1881
|
+
* - Variable references: $var → Reference
|
|
1882
|
+
* - Line comments: // ... (added to rw trivia)
|
|
1883
|
+
*
|
|
1884
|
+
* Inherits from LessGrammar:
|
|
1885
|
+
* - Nested rulesets, & ampersand, relative selectors
|
|
1886
|
+
* - anyDeclaration entry point
|
|
1887
|
+
* - atRuleBody, declarationList, Stylesheet overrides
|
|
1888
|
+
* - Less merge operators on Declaration (harmless for SCSS)
|
|
1889
|
+
*
|
|
1890
|
+
* Chevrotain note: in the Chevrotain architecture, ScssRecursiveParser
|
|
1891
|
+
* extends CssRecursiveParser independently of LessRecursiveParser.
|
|
1892
|
+
* Here we take the Parséman inheritance chain
|
|
1893
|
+
* CssParser → LessGrammar → ScssGrammar to maximise code reuse.
|
|
1894
|
+
*/
|
|
1895
|
+
function spanToLocation(span) {
|
|
1896
|
+
return {
|
|
1897
|
+
start: span.start,
|
|
1898
|
+
end: span.end
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
function nodeChildren(children) {
|
|
1902
|
+
return children.filter((c) => c != null && c._tag === "node");
|
|
1903
|
+
}
|
|
1904
|
+
var ScssGrammar = class extends LessGrammar {
|
|
1905
|
+
rw = regex(/(?:[ \t\n\r\f]+|\/\/[^\n\r]*|\/\*(?:[^*]|\*(?!\/))*\*\/)+/);
|
|
1906
|
+
_trivia = this.rw;
|
|
1907
|
+
_parseContext;
|
|
1908
|
+
setContext(context) {
|
|
1909
|
+
this._parseContext = context;
|
|
1910
|
+
}
|
|
1911
|
+
scssVar = regex(/\$-?[_a-zA-Z-][-_a-zA-Z0-9-]*/);
|
|
1912
|
+
VarDeclaration = (g) => sequence(g.scssVar, literal(":"), g.valueList, optional(choice(literal("!default"), literal("!global"))), optional(literal(";")));
|
|
1913
|
+
Reference = (g) => g.scssVar;
|
|
1914
|
+
buildNode(type, span, children, _state, _rawChildren, fields, triviaLog = []) {
|
|
1915
|
+
const loc = spanToLocation(span);
|
|
1916
|
+
switch (type) {
|
|
1917
|
+
case "VarDeclaration": return this._buildScssVarDeclaration(_rawChildren, loc);
|
|
1918
|
+
case "NsVarDeclaration": return this._buildScssNsVarDeclaration(_rawChildren, loc);
|
|
1919
|
+
case "Reference": return this._buildScssReference(children, loc);
|
|
1920
|
+
case "ScssComparison": return this._buildScssComparison(children, loc);
|
|
1921
|
+
case "ScssCondInParens": return this._buildScssCondInParens(children, loc);
|
|
1922
|
+
case "ScssCondTerm": return this._buildScssCondTerm(children, loc);
|
|
1923
|
+
case "ScssCondAnd": return this._buildScssCondJoin(children, loc, "and");
|
|
1924
|
+
case "ScssCondOr": return this._buildScssCondJoin(children, loc, "or");
|
|
1925
|
+
case "ScssRules": return this._buildScssRules(children, loc);
|
|
1926
|
+
case "ScssIf": return this._buildScssIf(children, loc);
|
|
1927
|
+
case "ScssEach": return this._buildScssEach(children, loc);
|
|
1928
|
+
case "ScssFor": return this._buildScssFor(children, loc);
|
|
1929
|
+
case "ScssWhile": return this._buildScssWhile(children, loc);
|
|
1930
|
+
case "ScssCallArg": return this._buildScssCallArg(children, loc);
|
|
1931
|
+
case "ScssCallArgsInner": return this._buildScssCallArgsInner(children, loc);
|
|
1932
|
+
case "ScssMixinParam": return this._buildScssMixinParam(children, loc);
|
|
1933
|
+
case "ScssMixinParams": return this._buildScssMixinParams(children, loc);
|
|
1934
|
+
case "ScssMixinName": return this._buildScssMixinName(children, loc);
|
|
1935
|
+
case "ScssDeclBody": return this._buildScssRules(children, loc);
|
|
1936
|
+
case "ScssMixin": return this._buildScssMixin(children, loc);
|
|
1937
|
+
case "ScssIncludeUsing": return this._buildScssIncludeUsing(children, loc);
|
|
1938
|
+
case "ScssInclude": return this._buildScssInclude(children, loc);
|
|
1939
|
+
case "ScssContent": return this._buildScssContent(children, loc);
|
|
1940
|
+
case "ScssFunction": return this._buildScssFunction(children, loc);
|
|
1941
|
+
case "ScssReturn": return this._buildScssReturn(children, _rawChildren, loc);
|
|
1942
|
+
case "ScssInterpBare": return this._buildScssInterpBare(children, loc);
|
|
1943
|
+
case "ScssInterpolatedName": return this._buildScssInterpolatedName(children, loc);
|
|
1944
|
+
case "InterpValue": return this._buildScssInterpValue(_rawChildren, loc);
|
|
1945
|
+
case "InterpolatedSelector": return this._buildScssInterpolatedSelector(children, loc);
|
|
1946
|
+
case "Declaration": return this._buildScssDeclaration(children, loc, () => super.buildNode(type, span, children, _state, _rawChildren, fields, triviaLog));
|
|
1947
|
+
case "CustomDeclaration": return this._buildScssCustomDeclaration(children, loc, () => super.buildNode(type, span, children, _state, _rawChildren, fields, triviaLog));
|
|
1948
|
+
case "Quoted": return this._buildQuoted(children, loc);
|
|
1949
|
+
case "ScssMapPair": return this._buildScssMapPair(children, loc);
|
|
1950
|
+
case "ScssMapLiteral": return this._buildScssMapLiteral(children, loc);
|
|
1951
|
+
case "ScssIdentValue": return this._buildScssIdentValue(children, _rawChildren, loc);
|
|
1952
|
+
case "ScssWithConfigEntry": return this._buildScssWithConfigEntry(_rawChildren, loc);
|
|
1953
|
+
case "ScssWithConfig": return this._buildScssWithConfig(children, loc);
|
|
1954
|
+
case "ScssUseAs": return this._buildScssUseAs(children, loc);
|
|
1955
|
+
case "ScssUse": return this._buildScssUse(children, loc);
|
|
1956
|
+
case "ScssForward": return this._buildScssForward(children, _rawChildren, loc);
|
|
1957
|
+
case "ScssPlaceholderSelector": return this._buildScssPlaceholderSelector(children, loc);
|
|
1958
|
+
case "ScssPlaceholderRuleset": return this._buildRuleset(children, _rawChildren, loc);
|
|
1959
|
+
case "ScssExtendTarget": return this._buildScssExtendTarget(children, _rawChildren, loc);
|
|
1960
|
+
case "ScssExtend": return this._buildScssExtend(children, _rawChildren, loc);
|
|
1961
|
+
case "ScssImportItem": return this._buildScssImportItem(children, _rawChildren, loc);
|
|
1962
|
+
case "ScssImportAtRule": return this._buildScssImportAtRule(children, loc);
|
|
1963
|
+
case "ScssNestedProps": return this._buildScssNestedProps(children, loc);
|
|
1964
|
+
case "ScssDiagnostic": return this._buildScssDiagnostic(children, loc);
|
|
1965
|
+
case "ScssAtRootFilter": return this._buildScssAtRootFilter(children, loc);
|
|
1966
|
+
case "ScssAtRootSelector": return this._buildScssAtRootSelector(children, loc);
|
|
1967
|
+
case "ScssAtRootPlain": return this._buildScssAtRootPlain(children, loc);
|
|
1968
|
+
case "ScssScopeBlock": return this._buildScssPermissiveAtRule(children, loc);
|
|
1969
|
+
case "ScssLayerBlock": return this._buildScssLayerBlock(children, loc);
|
|
1970
|
+
case "Call": return this._buildCall(_rawChildren, loc);
|
|
1971
|
+
case "SquareParen": return this._buildSquareParen(_rawChildren, loc);
|
|
1972
|
+
case "Paren": return this._buildScssParen(_rawChildren, loc);
|
|
1973
|
+
default: return super.buildNode(type, span, children, _state, _rawChildren, fields, triviaLog);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
_buildScssVarDeclaration(rawChildren, loc) {
|
|
1977
|
+
const items = spannedComponents(rawChildren);
|
|
1978
|
+
const rawName = typeof items[0]?.comp === "string" ? items[0].comp : "";
|
|
1979
|
+
const name = rawName.startsWith("$") ? rawName.slice(1) : rawName;
|
|
1980
|
+
const colonIdx = items.findIndex((i) => i.comp === ":");
|
|
1981
|
+
let end = items.length;
|
|
1982
|
+
for (let i = colonIdx + 1; i < items.length; i++) {
|
|
1983
|
+
const c = items[i].comp;
|
|
1984
|
+
if (c === "!" || c === "!default" || c === "!global" || c === ";") {
|
|
1985
|
+
end = i;
|
|
1986
|
+
break;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
const { value } = this._assembleValue(items.slice(colonIdx + 1, end), loc);
|
|
1990
|
+
return new VarDeclaration({
|
|
1991
|
+
name,
|
|
1992
|
+
value,
|
|
1993
|
+
important: items.some((i) => i.comp === "!" || i.comp === "!default" || i.comp === "!global") || void 0
|
|
1994
|
+
}, {}, loc);
|
|
1995
|
+
}
|
|
1996
|
+
/**
|
|
1997
|
+
* `ns.$member: value [!default|!global];` — a namespaced variable ASSIGNMENT.
|
|
1998
|
+
* Built as a `VarDeclaration` whose name carries the namespace (`ns.member`);
|
|
1999
|
+
* `!default` → conditional-assign, `!global` → `setDefined`. Mirrors the
|
|
2000
|
+
* member-read shape (`Reference{ target, key }`) on the write side while
|
|
2001
|
+
* staying within the `string | Interpolated` declaration-name contract.
|
|
2002
|
+
*/
|
|
2003
|
+
_buildScssNsVarDeclaration(rawChildren, loc) {
|
|
2004
|
+
const items = spannedComponents(rawChildren);
|
|
2005
|
+
const ns = typeof items[0]?.comp === "string" ? items[0].comp : "";
|
|
2006
|
+
const memberItem = items.find((i) => typeof i.comp === "string" && i.comp.startsWith("$"));
|
|
2007
|
+
const memberRaw = typeof memberItem?.comp === "string" ? memberItem.comp : "";
|
|
2008
|
+
const member = memberRaw.startsWith("$") ? memberRaw.slice(1) : memberRaw;
|
|
2009
|
+
const colonIdx = items.findIndex((i) => i.comp === ":");
|
|
2010
|
+
let end = items.length;
|
|
2011
|
+
for (let i = colonIdx + 1; i < items.length; i++) {
|
|
2012
|
+
const c = items[i].comp;
|
|
2013
|
+
if (c === "!" || c === "!default" || c === "!global" || c === ";") {
|
|
2014
|
+
end = i;
|
|
2015
|
+
break;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
const { value } = this._assembleValue(items.slice(colonIdx + 1, end), loc);
|
|
2019
|
+
const sawDefault = items.slice(end).some((i) => i.comp === "!default");
|
|
2020
|
+
const sawGlobal = items.slice(end).some((i) => i.comp === "!global");
|
|
2021
|
+
return new VarDeclaration({
|
|
2022
|
+
name: `${ns}.${member}`,
|
|
2023
|
+
value
|
|
2024
|
+
}, {
|
|
2025
|
+
assign: sawDefault ? "?:" : ":",
|
|
2026
|
+
setDefined: sawGlobal
|
|
2027
|
+
}, loc);
|
|
2028
|
+
}
|
|
2029
|
+
_buildScssReference(children, loc) {
|
|
2030
|
+
const varName = children.filter((c) => c._tag === "leaf")[0]?.value ?? "";
|
|
2031
|
+
return new Reference(varName.startsWith("$") ? varName.slice(1) : varName, { type: "variable" }, loc);
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* `left [op right]` → Condition, or a bare operand when there is no operator.
|
|
2035
|
+
* `!=` desugars to `=` + negate (matches the Chevrotain scssComparison).
|
|
2036
|
+
*/
|
|
2037
|
+
_buildScssComparison(children, loc) {
|
|
2038
|
+
const nodes = nodeChildren(children);
|
|
2039
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2040
|
+
const left = nodes[0] ?? new Any("", {}, loc);
|
|
2041
|
+
const opLeaf = ls.find((l) => /^(?:==|!=|>=|<=|=|>|<)$/.test(l.value));
|
|
2042
|
+
if (!opLeaf || !nodes[1]) return left;
|
|
2043
|
+
let op = opLeaf.value;
|
|
2044
|
+
let negate = false;
|
|
2045
|
+
if (op === "!=") {
|
|
2046
|
+
op = "=";
|
|
2047
|
+
negate = true;
|
|
2048
|
+
} else if (op === "==") op = "=";
|
|
2049
|
+
return new Condition([
|
|
2050
|
+
left,
|
|
2051
|
+
op,
|
|
2052
|
+
nodes[1]
|
|
2053
|
+
], negate ? { negate: true } : {}, loc);
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* Every condition term is wrapped in a Paren, matching the Chevrotain
|
|
2057
|
+
* `scssConditionInParens` production (both the `( … )` group and the bare
|
|
2058
|
+
* comparison / value branch wrap their result in a single Paren).
|
|
2059
|
+
*/
|
|
2060
|
+
_buildScssCondInParens(children, loc) {
|
|
2061
|
+
return new Paren(nodeChildren(children)[0] ?? new Any("", {}, loc), {}, loc);
|
|
2062
|
+
}
|
|
2063
|
+
/** Optional leading `not` negates the term. */
|
|
2064
|
+
_buildScssCondTerm(children, loc) {
|
|
2065
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2066
|
+
const inner = nodeChildren(children)[0] ?? new Any("", {}, loc);
|
|
2067
|
+
if (ls.some((l) => /^not$/i.test(l.value))) return new Condition([inner], { negate: true }, loc);
|
|
2068
|
+
return inner;
|
|
2069
|
+
}
|
|
2070
|
+
/** Fold a left-associative `and` / `or` chain of terms into Conditions. */
|
|
2071
|
+
_buildScssCondJoin(children, loc, op) {
|
|
2072
|
+
const nodes = nodeChildren(children);
|
|
2073
|
+
if (nodes.length === 0) return new Any("", {}, loc);
|
|
2074
|
+
let left = nodes[0];
|
|
2075
|
+
for (let i = 1; i < nodes.length; i++) left = new Condition([
|
|
2076
|
+
left,
|
|
2077
|
+
op,
|
|
2078
|
+
nodes[i]
|
|
2079
|
+
], {}, loc);
|
|
2080
|
+
return left;
|
|
2081
|
+
}
|
|
2082
|
+
/** A `{ … }` control-block body → Rules. */
|
|
2083
|
+
_buildScssRules(children, loc) {
|
|
2084
|
+
return new Rules(this._flattenScssImportLists(nodeChildren(children)), void 0, loc);
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* `@if cond { … } (@else if cond { … })* (@else { … })?` → nested `If` chain.
|
|
2088
|
+
* Children arrive as alternating condition / Rules nodes, with an optional
|
|
2089
|
+
* trailing bare Rules (the final `@else`). Fold from the last branch inward.
|
|
2090
|
+
*/
|
|
2091
|
+
_buildScssIf(children, loc) {
|
|
2092
|
+
const nodes = nodeChildren(children);
|
|
2093
|
+
const conditions = [];
|
|
2094
|
+
const bodies = [];
|
|
2095
|
+
let elseBranch;
|
|
2096
|
+
let pendingCond;
|
|
2097
|
+
for (const n of nodes) if (n instanceof Rules) if (pendingCond !== void 0) {
|
|
2098
|
+
conditions.push(pendingCond);
|
|
2099
|
+
bodies.push(n);
|
|
2100
|
+
pendingCond = void 0;
|
|
2101
|
+
} else elseBranch = n;
|
|
2102
|
+
else pendingCond = n;
|
|
2103
|
+
let elseNode = elseBranch;
|
|
2104
|
+
for (let i = conditions.length - 1; i >= 0; i--) elseNode = new If({
|
|
2105
|
+
condition: conditions[i],
|
|
2106
|
+
rules: bodies[i].rules,
|
|
2107
|
+
else: elseNode
|
|
2108
|
+
}, void 0, loc);
|
|
2109
|
+
return elseNode ?? new Any("", {}, loc);
|
|
2110
|
+
}
|
|
2111
|
+
/** A `$name` loop-binding with no value (`paramVar` — prints as `$name`). */
|
|
2112
|
+
_scssParamVar(varName, loc) {
|
|
2113
|
+
return new VarDeclaration({
|
|
2114
|
+
name: varName,
|
|
2115
|
+
value: new Nil()
|
|
2116
|
+
}, { paramVar: true }, loc);
|
|
2117
|
+
}
|
|
2118
|
+
/**
|
|
2119
|
+
* `@each $a[, $b …] in <expr> { … }` → `For` with a node iterable.
|
|
2120
|
+
* Normalizes to Jess `$for ($a of …)` / `$for ([$a, $b] of …)`.
|
|
2121
|
+
*/
|
|
2122
|
+
_buildScssEach(children, loc) {
|
|
2123
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2124
|
+
const nodes = nodeChildren(children);
|
|
2125
|
+
const body = nodes.find((n) => n instanceof Rules);
|
|
2126
|
+
const vars = [];
|
|
2127
|
+
let pastEach = false;
|
|
2128
|
+
for (const l of ls) {
|
|
2129
|
+
if (/^@each/i.test(l.value)) {
|
|
2130
|
+
pastEach = true;
|
|
2131
|
+
continue;
|
|
2132
|
+
}
|
|
2133
|
+
if (pastEach && l.value === "in") break;
|
|
2134
|
+
if (pastEach && l.value.startsWith("$")) vars.push(l.value.slice(1));
|
|
2135
|
+
}
|
|
2136
|
+
const iterableNodes = nodes.filter((n) => n !== body);
|
|
2137
|
+
let iterable = iterableNodes.length === 1 ? iterableNodes[0] : new Sequence(iterableNodes, void 0, loc);
|
|
2138
|
+
if (iterable.type === "Expression") iterable = iterable.value;
|
|
2139
|
+
const decls = vars.map((v) => this._scssParamVar(v, loc));
|
|
2140
|
+
return new For({
|
|
2141
|
+
pattern: decls.length === 1 ? {
|
|
2142
|
+
kind: "single",
|
|
2143
|
+
value: decls[0]
|
|
2144
|
+
} : {
|
|
2145
|
+
kind: "tuple",
|
|
2146
|
+
values: decls
|
|
2147
|
+
},
|
|
2148
|
+
iterable: {
|
|
2149
|
+
kind: "node",
|
|
2150
|
+
value: iterable
|
|
2151
|
+
},
|
|
2152
|
+
rules: body.rules
|
|
2153
|
+
}, void 0, loc);
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* `@for $i from <start> (to|through) <end> { … }` → `For` with a range iterable.
|
|
2157
|
+
* `through` is inclusive end; `to` is exclusive (`includeEnd: false`).
|
|
2158
|
+
*/
|
|
2159
|
+
_buildScssFor(children, loc) {
|
|
2160
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2161
|
+
const nodes = nodeChildren(children);
|
|
2162
|
+
const includeEnd = ls.some((l) => l.value === "through");
|
|
2163
|
+
const varLeaf = ls.find((l) => l.value.startsWith("$"));
|
|
2164
|
+
const varDecl = this._scssParamVar(varLeaf?.value.slice(1) ?? "", loc);
|
|
2165
|
+
const body = nodes.find((n) => n instanceof Rules);
|
|
2166
|
+
const exprNodes = nodes.filter((n) => n !== body);
|
|
2167
|
+
const startExpr = exprNodes[0] ?? new Any("", {}, loc);
|
|
2168
|
+
const endExpr = exprNodes[1] ?? new Any("", {}, loc);
|
|
2169
|
+
return new For({
|
|
2170
|
+
pattern: {
|
|
2171
|
+
kind: "single",
|
|
2172
|
+
value: varDecl
|
|
2173
|
+
},
|
|
2174
|
+
iterable: {
|
|
2175
|
+
kind: "range",
|
|
2176
|
+
start: startExpr,
|
|
2177
|
+
end: endExpr,
|
|
2178
|
+
includeStart: true,
|
|
2179
|
+
includeEnd
|
|
2180
|
+
},
|
|
2181
|
+
rules: body.rules
|
|
2182
|
+
}, void 0, loc);
|
|
2183
|
+
}
|
|
2184
|
+
/** `@while <cond> { … }` → `While`. */
|
|
2185
|
+
_buildScssWhile(children, loc) {
|
|
2186
|
+
const nodes = nodeChildren(children);
|
|
2187
|
+
const body = nodes.find((n) => n instanceof Rules);
|
|
2188
|
+
return new While({
|
|
2189
|
+
condition: nodes.find((n) => n !== body) ?? new Any("", {}, loc),
|
|
2190
|
+
rules: body.rules
|
|
2191
|
+
}, void 0, loc);
|
|
2192
|
+
}
|
|
2193
|
+
/** Build a module-qualified or plain mixin `Reference`. */
|
|
2194
|
+
_buildScssMixinName(children, loc) {
|
|
2195
|
+
const interp = nodeChildren(children).find((n) => isNode(n, N.Interpolated));
|
|
2196
|
+
if (interp) return new Reference({ key: interp }, {
|
|
2197
|
+
type: "mixin",
|
|
2198
|
+
role: "name"
|
|
2199
|
+
}, loc);
|
|
2200
|
+
const parts = children.filter((c) => c._tag === "leaf").map((l) => l.value).filter((v) => v !== ".");
|
|
2201
|
+
if (parts.length >= 2) {
|
|
2202
|
+
let ref = new Reference(parts[0], { type: "variable" }, loc);
|
|
2203
|
+
for (let i = 1; i < parts.length; i++) {
|
|
2204
|
+
const isFinal = i === parts.length - 1;
|
|
2205
|
+
ref = new Reference({
|
|
2206
|
+
target: ref,
|
|
2207
|
+
key: parts[i]
|
|
2208
|
+
}, {
|
|
2209
|
+
type: isFinal ? "mixin" : "index",
|
|
2210
|
+
...isFinal ? { role: "name" } : {}
|
|
2211
|
+
}, loc);
|
|
2212
|
+
}
|
|
2213
|
+
return ref;
|
|
2214
|
+
}
|
|
2215
|
+
return new Reference({ key: parts[0] ?? "" }, {
|
|
2216
|
+
type: "mixin",
|
|
2217
|
+
role: "name"
|
|
2218
|
+
}, loc);
|
|
2219
|
+
}
|
|
2220
|
+
/** `$x: val` keyword arg, `val...` spread, or plain value. */
|
|
2221
|
+
_buildScssCallArg(children, loc) {
|
|
2222
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2223
|
+
const nodes = nodeChildren(children);
|
|
2224
|
+
const varLeaf = ls.find((l) => l.value.startsWith("$") && l.value !== "$");
|
|
2225
|
+
const hasColon = ls.some((l) => l.value === ":");
|
|
2226
|
+
const hasSpread = ls.some((l) => l.value === "...");
|
|
2227
|
+
if (varLeaf && hasColon) return new VarDeclaration({
|
|
2228
|
+
name: varLeaf.value.slice(1),
|
|
2229
|
+
value: nodes.find((n) => n !== void 0 && !ls.includes(n)) ?? nodes[0] ?? new Nil()
|
|
2230
|
+
}, {}, loc);
|
|
2231
|
+
const value = nodes[0] ?? new Any("", {}, loc);
|
|
2232
|
+
if (hasSpread) return new Rest(value, void 0, loc);
|
|
2233
|
+
return value;
|
|
2234
|
+
}
|
|
2235
|
+
_buildScssCallArgsInner(children, loc) {
|
|
2236
|
+
const nodes = nodeChildren(children);
|
|
2237
|
+
if (nodes.length === 0) return;
|
|
2238
|
+
return new List(nodes, void 0, loc);
|
|
2239
|
+
}
|
|
2240
|
+
/** Mixin param: `...$rest`, `$rest...`, `$a: default`, or bare `$a`. */
|
|
2241
|
+
_buildScssMixinParam(children, loc) {
|
|
2242
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2243
|
+
const nodes = nodeChildren(children);
|
|
2244
|
+
const varName = ls.find((l) => l.value.startsWith("$"))?.value.slice(1) ?? "";
|
|
2245
|
+
const hasPrefixEllipsis = ls[0]?.value === "...";
|
|
2246
|
+
const hasSuffixEllipsis = ls.some((l) => l.value === "..." && ls.indexOf(l) > 0);
|
|
2247
|
+
if (hasPrefixEllipsis || hasSuffixEllipsis) return new Rest(varName, void 0, loc);
|
|
2248
|
+
if (ls.some((l) => l.value === ":") && nodes[0]) return new VarDeclaration({
|
|
2249
|
+
name: varName,
|
|
2250
|
+
value: nodes[0]
|
|
2251
|
+
}, { paramVar: true }, loc);
|
|
2252
|
+
return new Any(varName, { role: "property" }, loc);
|
|
2253
|
+
}
|
|
2254
|
+
_buildScssMixinParams(children, loc) {
|
|
2255
|
+
return new List(nodeChildren(children), void 0, loc);
|
|
2256
|
+
}
|
|
2257
|
+
/** `@mixin name($params) { … }` → `Mixin` (inner vars default to private). */
|
|
2258
|
+
_buildScssMixin(children, loc) {
|
|
2259
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2260
|
+
const nodes = nodeChildren(children);
|
|
2261
|
+
const interpName = nodes.find((n) => isNode(n, N.Interpolated));
|
|
2262
|
+
const nameLeaf = ls.find((l) => !l.value.startsWith("@") && l.value !== "(" && l.value !== ")" && l.value !== "{" && l.value !== "}" && l.value !== ",");
|
|
2263
|
+
return new Mixin({
|
|
2264
|
+
name: interpName ?? nameLeaf?.value ?? "",
|
|
2265
|
+
params: nodes.find((n) => n.type === "List"),
|
|
2266
|
+
rules: nodes.find((n) => n instanceof Rules).rules
|
|
2267
|
+
}, void 0, loc);
|
|
2268
|
+
}
|
|
2269
|
+
/** `using ($c, $n)` param list for `@include … using (…)`. */
|
|
2270
|
+
_buildScssIncludeUsing(children, loc) {
|
|
2271
|
+
return new List(children.filter((c) => c._tag === "leaf").filter((l) => l.value.startsWith("$")).map((l) => this._scssParamVar(l.value.slice(1), loc)), void 0, loc);
|
|
2272
|
+
}
|
|
2273
|
+
/**
|
|
2274
|
+
* `@include name(args) [using (…)] [ { … } ];` → `Call(Reference(type=mixin))`.
|
|
2275
|
+
* An optional content block becomes an anonymous visible `Mixin` on the call.
|
|
2276
|
+
*/
|
|
2277
|
+
_buildScssInclude(children, loc) {
|
|
2278
|
+
const ls = children.filter((c) => c?._tag === "leaf");
|
|
2279
|
+
const nodes = nodeChildren(children);
|
|
2280
|
+
const nameRef = nodes.find((n) => n.type === "Reference");
|
|
2281
|
+
const lists = nodes.filter((n) => n.type === "List");
|
|
2282
|
+
const hasUsing = ls.some((l) => l.value === "using");
|
|
2283
|
+
let args;
|
|
2284
|
+
let usingParams;
|
|
2285
|
+
if (lists.length === 2) {
|
|
2286
|
+
args = lists[0];
|
|
2287
|
+
usingParams = lists[1];
|
|
2288
|
+
} else if (lists.length === 1) if (hasUsing) usingParams = lists[0];
|
|
2289
|
+
else args = lists[0];
|
|
2290
|
+
const contentRules = nodes.find((n) => n instanceof Rules);
|
|
2291
|
+
let contentNode;
|
|
2292
|
+
if (contentRules) {
|
|
2293
|
+
contentNode = new Mixin({
|
|
2294
|
+
rules: contentRules.rules,
|
|
2295
|
+
params: usingParams
|
|
2296
|
+
}, void 0, loc);
|
|
2297
|
+
contentNode.addFlags(F_VISIBLE);
|
|
2298
|
+
}
|
|
2299
|
+
return new Call({
|
|
2300
|
+
name: nameRef ?? new Reference({ key: "" }, {
|
|
2301
|
+
type: "mixin",
|
|
2302
|
+
role: "name"
|
|
2303
|
+
}, loc),
|
|
2304
|
+
args,
|
|
2305
|
+
contentNode
|
|
2306
|
+
}, void 0, loc);
|
|
2307
|
+
}
|
|
2308
|
+
/** `@content[(args)];` → `Call(Reference('content', type=mixin))`. */
|
|
2309
|
+
_buildScssContent(children, loc) {
|
|
2310
|
+
const args = nodeChildren(children).find((n) => n.type === "List");
|
|
2311
|
+
return new Call({
|
|
2312
|
+
name: new Reference({ key: "content" }, {
|
|
2313
|
+
type: "mixin",
|
|
2314
|
+
role: "name"
|
|
2315
|
+
}, loc),
|
|
2316
|
+
args
|
|
2317
|
+
}, void 0, loc);
|
|
2318
|
+
}
|
|
2319
|
+
/** `@function name($params) { … }` → `Func` with `returnName: 'result'`. */
|
|
2320
|
+
_buildScssFunction(children, loc) {
|
|
2321
|
+
const ls = children.filter((c) => c._tag === "leaf");
|
|
2322
|
+
const nodes = nodeChildren(children);
|
|
2323
|
+
const interpName = nodes.find((n) => isNode(n, N.Interpolated));
|
|
2324
|
+
const nameLeaf = ls.find((l) => !l.value.startsWith("@") && l.value !== "(" && l.value !== ")" && l.value !== "{" && l.value !== "}" && l.value !== ",");
|
|
2325
|
+
return new Func({
|
|
2326
|
+
name: interpName ?? nameLeaf?.value ?? "",
|
|
2327
|
+
params: nodes.find((n) => n.type === "List"),
|
|
2328
|
+
body: nodes.find((n) => n instanceof Rules)
|
|
2329
|
+
}, { returnName: "result" }, loc);
|
|
2330
|
+
}
|
|
2331
|
+
/** `@return <value>;` → `$result: <value>;` */
|
|
2332
|
+
_buildScssReturn(children, rawChildren, loc) {
|
|
2333
|
+
const items = spannedComponents(rawChildren);
|
|
2334
|
+
const semiIdx = items.findIndex((i) => i.comp === ";");
|
|
2335
|
+
const valueItems = items.filter((i, idx) => idx > 0 && i.comp !== "@return" && (semiIdx < 0 || idx < semiIdx));
|
|
2336
|
+
const { value } = this._assembleValue(valueItems, loc);
|
|
2337
|
+
return new VarDeclaration({
|
|
2338
|
+
name: "result",
|
|
2339
|
+
value
|
|
2340
|
+
}, void 0, loc);
|
|
2341
|
+
}
|
|
2342
|
+
_buildScssInterpBare(children, loc) {
|
|
2343
|
+
return new Interpolated({
|
|
2344
|
+
source: INTERPOLATION_PLACEHOLDER,
|
|
2345
|
+
replacements: [toInterpReplacement(nodeChildren(children)[0] ?? new Any("", {}, loc), loc)]
|
|
2346
|
+
}, { role: "any" }, loc);
|
|
2347
|
+
}
|
|
2348
|
+
/** `foo-#{$bar}` name segments → Interpolated(role=name) or plain Any. */
|
|
2349
|
+
_buildScssInterpolatedName(children, loc) {
|
|
2350
|
+
let source = "";
|
|
2351
|
+
const replacements = [];
|
|
2352
|
+
for (const c of children) if (c._tag === "leaf") {
|
|
2353
|
+
const v = c.value;
|
|
2354
|
+
if (v === "#{" || v === "}" || v === ".") continue;
|
|
2355
|
+
source += v;
|
|
2356
|
+
} else if (c._tag === "node" && isNode(c, N.Interpolated)) {
|
|
2357
|
+
source += INTERPOLATION_PLACEHOLDER;
|
|
2358
|
+
replacements.push(...c.replacements);
|
|
2359
|
+
}
|
|
2360
|
+
if (replacements.length === 0) return new Any(source, { role: "name" }, loc);
|
|
2361
|
+
return new Interpolated({
|
|
2362
|
+
source,
|
|
2363
|
+
replacements
|
|
2364
|
+
}, { role: "name" }, loc);
|
|
2365
|
+
}
|
|
2366
|
+
_buildScssInterpValue(raw, loc) {
|
|
2367
|
+
return buildScssInterpolatedFromString(spannedComponents(raw).map((i) => typeof i.comp === "string" ? i.comp : "").join(""), loc, "ident");
|
|
2368
|
+
}
|
|
2369
|
+
_buildScssInterpolatedSelector(children, loc) {
|
|
2370
|
+
let source = "";
|
|
2371
|
+
const replacements = [];
|
|
2372
|
+
for (const c of children) if (c._tag === "leaf") {
|
|
2373
|
+
const v = c.value;
|
|
2374
|
+
if (v === "#{" || v === "}") continue;
|
|
2375
|
+
source += v;
|
|
2376
|
+
} else if (c._tag === "node" && isNode(c, N.Interpolated)) {
|
|
2377
|
+
source += INTERPOLATION_PLACEHOLDER;
|
|
2378
|
+
replacements.push(...c.replacements);
|
|
2379
|
+
}
|
|
2380
|
+
return new InterpolatedSelector(new Interpolated({
|
|
2381
|
+
source,
|
|
2382
|
+
replacements
|
|
2383
|
+
}, { role: "ident" }, loc), {}, loc);
|
|
2384
|
+
}
|
|
2385
|
+
_scssInterpDeclName(name, loc) {
|
|
2386
|
+
if (typeof name !== "string") return name;
|
|
2387
|
+
if (name.includes("#{")) return buildScssInterpolatedFromString(name, loc, "property");
|
|
2388
|
+
return name;
|
|
2389
|
+
}
|
|
2390
|
+
_buildScssDeclaration(children, loc, buildLess) {
|
|
2391
|
+
const decl = buildLess();
|
|
2392
|
+
const d = decl;
|
|
2393
|
+
if (d.name !== void 0) d.name = this._scssInterpDeclName(d.name, loc);
|
|
2394
|
+
const valueNodes = nodeChildren(children).filter((n) => isNode(n, N.Collection) || isNode(n, N.Sequence) || isNode(n, N.Keyword) || isNode(n, N.Reference) || isNode(n, N.Num) || isNode(n, N.Paren) || isNode(n, N.List));
|
|
2395
|
+
const collection = valueNodes.find((n) => isNode(n, N.Collection));
|
|
2396
|
+
if (collection && valueNodes.length > 1) {
|
|
2397
|
+
const base = valueNodes.find((n) => n !== collection);
|
|
2398
|
+
if (base) d.value = new Sequence([base, collection], void 0, loc);
|
|
2399
|
+
} else if (collection) d.value = collection;
|
|
2400
|
+
return decl;
|
|
2401
|
+
}
|
|
2402
|
+
_buildScssCustomDeclaration(children, loc, buildLess) {
|
|
2403
|
+
const decl = buildLess();
|
|
2404
|
+
const d = decl;
|
|
2405
|
+
if (d.name !== void 0) d.name = this._scssInterpDeclName(d.name, loc);
|
|
2406
|
+
return decl;
|
|
2407
|
+
}
|
|
2408
|
+
_buildQuoted(children, loc) {
|
|
2409
|
+
const text = children.filter((c) => c._tag === "leaf").map((l) => l.value).join("");
|
|
2410
|
+
const inner = text.slice(1, -1);
|
|
2411
|
+
const quote = text[0];
|
|
2412
|
+
if (inner.includes("#{")) return new Quoted(buildScssInterpolatedFromString(inner, loc, "any"), { quote }, loc);
|
|
2413
|
+
return super._buildQuoted(children, loc);
|
|
2414
|
+
}
|
|
2415
|
+
/** `("k": v, …)` pair inside a map literal. */
|
|
2416
|
+
_buildScssMapPair(children, loc) {
|
|
2417
|
+
const nodes = nodeChildren(children);
|
|
2418
|
+
const keyNode = nodes[0] ?? new Any("", { role: "property" }, loc);
|
|
2419
|
+
const valueNode = nodes[1] ?? new Any("", {}, loc);
|
|
2420
|
+
return new Declaration({
|
|
2421
|
+
name: toDeclKey(keyNode),
|
|
2422
|
+
value: valueNode
|
|
2423
|
+
}, void 0, loc);
|
|
2424
|
+
}
|
|
2425
|
+
_buildScssMapLiteral(children, loc) {
|
|
2426
|
+
return new Collection(nodeChildren(children), void 0, loc);
|
|
2427
|
+
}
|
|
2428
|
+
/** `ns.$var`, `ns.fn(…)`, `ns.\#foo(…)`, or a plain ident. */
|
|
2429
|
+
_buildScssIdentValue(children, raw, loc) {
|
|
2430
|
+
const ls = children.filter((c) => c?._tag === "leaf");
|
|
2431
|
+
const ident = ls.find((l) => !l.value.startsWith(".") && l.value !== "(" && l.value !== ")" && l.value !== "\\")?.value ?? "";
|
|
2432
|
+
const varLeaf = ls.find((l) => l.value.startsWith("$"));
|
|
2433
|
+
const dotLeaf = ls.find((l) => l.value.startsWith(".") && !l.value.startsWith("$"));
|
|
2434
|
+
const hashLeaf = ls.find((l) => l.value.startsWith("#"));
|
|
2435
|
+
const hasCall = ls.some((l) => l.value === "(");
|
|
2436
|
+
const hasEscape = ls.some((l) => l.value === "\\");
|
|
2437
|
+
if (varLeaf && dotLeaf) return new Reference({
|
|
2438
|
+
target: new Reference(ident, { type: "variable" }, loc),
|
|
2439
|
+
key: varLeaf.value.slice(1)
|
|
2440
|
+
}, { type: "variable" }, loc);
|
|
2441
|
+
if (hasEscape && hashLeaf && hasCall) {
|
|
2442
|
+
const key = hashLeaf.value.slice(1);
|
|
2443
|
+
const args = nodeChildren(children).find((n) => isNode(n, N.List));
|
|
2444
|
+
return new Expression(new Call({
|
|
2445
|
+
name: makeNamespacedReference([ident, key], "mixin-ruleset", loc),
|
|
2446
|
+
args
|
|
2447
|
+
}, void 0, loc), void 0, loc);
|
|
2448
|
+
}
|
|
2449
|
+
if (dotLeaf && hasCall) {
|
|
2450
|
+
const fnName = dotLeaf.value.slice(1);
|
|
2451
|
+
if (ident === "selector" && fnName === "parse") {
|
|
2452
|
+
const items = spannedComponents(raw);
|
|
2453
|
+
const open = items.findIndex((i) => i.comp === "(");
|
|
2454
|
+
let close = items.length;
|
|
2455
|
+
for (let i = items.length - 1; i >= 0; i--) if (items[i].comp === ")") {
|
|
2456
|
+
close = i;
|
|
2457
|
+
break;
|
|
2458
|
+
}
|
|
2459
|
+
const { value: argValue } = this._assembleValue(items.slice(open + 1, close), loc);
|
|
2460
|
+
const firstArg = isNode(argValue, N.List) ? argValue.value[0] : argValue;
|
|
2461
|
+
const selectorText = firstArg && isNode(firstArg, N.Quoted) ? typeof firstArg.value === "string" ? firstArg.value : isNode(firstArg.value, N.Any) ? String(firstArg.value.valueOf()) : void 0 : void 0;
|
|
2462
|
+
if (selectorText !== void 0 && isValidScssSelectorList(selectorText)) return new SelectorCapture(selectorText, void 0, loc);
|
|
2463
|
+
}
|
|
2464
|
+
const items = spannedComponents(raw);
|
|
2465
|
+
const open = items.findIndex((i) => i.comp === "(");
|
|
2466
|
+
let close = items.length;
|
|
2467
|
+
for (let i = items.length - 1; i >= 0; i--) if (items[i].comp === ")") {
|
|
2468
|
+
close = i;
|
|
2469
|
+
break;
|
|
2470
|
+
}
|
|
2471
|
+
const { value: argValue } = this._assembleValue(items.slice(open + 1, close), loc);
|
|
2472
|
+
let args;
|
|
2473
|
+
if (argValue !== void 0) args = isNode(argValue, N.List) ? argValue : new List([argValue], void 0, loc);
|
|
2474
|
+
const mapped = desugarMapLookup(new Call({
|
|
2475
|
+
name: `${ident}.${fnName}`,
|
|
2476
|
+
args
|
|
2477
|
+
}, void 0, loc), loc);
|
|
2478
|
+
if (isNode(mapped, N.Reference)) return mapped;
|
|
2479
|
+
const memberType = fnName.startsWith("#") ? "mixin-ruleset" : "function";
|
|
2480
|
+
const call = new Call({
|
|
2481
|
+
name: makeNamespacedReference([ident, fnName.startsWith("#") ? fnName.slice(1) : fnName], memberType, loc),
|
|
2482
|
+
args
|
|
2483
|
+
}, void 0, loc);
|
|
2484
|
+
if (memberType === "mixin-ruleset") return new Expression(call, void 0, loc);
|
|
2485
|
+
return new Expression(desugarNamespacedCall(call, loc), void 0, loc);
|
|
2486
|
+
}
|
|
2487
|
+
return new Any(ident, { role: "ident" }, loc);
|
|
2488
|
+
}
|
|
2489
|
+
_buildStylesheet(children, loc) {
|
|
2490
|
+
const nodes = this._flattenScssImportLists(nodeChildren(children));
|
|
2491
|
+
return new Rules(this._liftStandaloneComments(nodes, loc.start, loc.end, loc), void 0, loc);
|
|
2492
|
+
}
|
|
2493
|
+
_flattenScssImportLists(nodes) {
|
|
2494
|
+
const flat = [];
|
|
2495
|
+
for (const n of nodes) if (isNode(n, N.List) && (n.options?.role === "scss-imports" || n.options?.role === "scss-at-root")) flat.push(...n.value);
|
|
2496
|
+
else flat.push(n);
|
|
2497
|
+
return flat;
|
|
2498
|
+
}
|
|
2499
|
+
_buildScssNestedProps(children, loc) {
|
|
2500
|
+
return new Collection(nodeChildren(children).filter((n) => isNode(n, N.Declaration) || isNode(n, N.VarDeclaration) || n instanceof If || n instanceof For || n instanceof While), void 0, loc);
|
|
2501
|
+
}
|
|
2502
|
+
_buildScssDiagnostic(children, loc) {
|
|
2503
|
+
return new Log({
|
|
2504
|
+
level: children.filter((c) => c?._tag === "leaf").find((l) => l.value.startsWith("@"))?.value.slice(1) ?? "debug",
|
|
2505
|
+
message: nodeChildren(children).find((n) => !isNode(n, N.Any) || n.options?.role !== "atkeyword") ?? nodeChildren(children)[0] ?? new Any("", {}, loc)
|
|
2506
|
+
}, void 0, loc);
|
|
2507
|
+
}
|
|
2508
|
+
_buildScssAtRootFilter(children, loc) {
|
|
2509
|
+
const nodes = nodeChildren(children);
|
|
2510
|
+
const prelude = nodes.find((n) => !(n instanceof Rules)) ?? nodes[0];
|
|
2511
|
+
const body = nodes.find((n) => n instanceof Rules);
|
|
2512
|
+
const name = new Any("@at-root", { role: "atkeyword" }, loc);
|
|
2513
|
+
this._error("@at-root prelude/filter forms are not yet supported in Jess. Write the hoisted rules directly instead.", loc.start, loc.end);
|
|
2514
|
+
return new AtRule({
|
|
2515
|
+
name,
|
|
2516
|
+
prelude,
|
|
2517
|
+
rules: body.rules
|
|
2518
|
+
}, void 0, loc);
|
|
2519
|
+
}
|
|
2520
|
+
_buildScssAtRootSelector(children, loc) {
|
|
2521
|
+
const nodes = nodeChildren(children);
|
|
2522
|
+
const selector = nodes.find((n) => !(n instanceof Rules));
|
|
2523
|
+
const body = nodes.find((n) => n instanceof Rules);
|
|
2524
|
+
const context = this._parseContext;
|
|
2525
|
+
return new Ruleset({
|
|
2526
|
+
selector: prefixAtRootSelector(selector, context),
|
|
2527
|
+
rules: body.rules
|
|
2528
|
+
}, void 0, loc);
|
|
2529
|
+
}
|
|
2530
|
+
_buildScssAtRootPlain(children, loc) {
|
|
2531
|
+
const body = nodeChildren(children).find((n) => n instanceof Rules);
|
|
2532
|
+
const context = this._parseContext;
|
|
2533
|
+
const lowered = new Rules([...body.rules], void 0, loc);
|
|
2534
|
+
lowerPlainAtRootRules(lowered, context);
|
|
2535
|
+
if (lowered.rules.length === 0) return new Nil(void 0, void 0, loc);
|
|
2536
|
+
if (lowered.rules.length === 1) return lowered.rules[0];
|
|
2537
|
+
return new List(lowered.rules, { role: "scss-at-root" }, loc);
|
|
2538
|
+
}
|
|
2539
|
+
_buildScssWithConfigEntry(rawChildren, loc) {
|
|
2540
|
+
const items = spannedComponents(rawChildren);
|
|
2541
|
+
const rawName = typeof items[0]?.comp === "string" ? items[0].comp : "";
|
|
2542
|
+
const name = rawName.startsWith("$") ? rawName.slice(1) : rawName;
|
|
2543
|
+
const colonIdx = items.findIndex((i) => i.comp === ":");
|
|
2544
|
+
let end = items.length;
|
|
2545
|
+
for (let i = colonIdx + 1; i < items.length; i++) {
|
|
2546
|
+
const c = items[i].comp;
|
|
2547
|
+
if (c === "!" || c === "!default" || c === "!global" || c === "," || c === ")") {
|
|
2548
|
+
end = i;
|
|
2549
|
+
break;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
const { value } = this._assembleValue(items.slice(colonIdx + 1, end), loc);
|
|
2553
|
+
const sawDefault = items.slice(end).some((i) => i.comp === "!default");
|
|
2554
|
+
const sawGlobal = items.slice(end).some((i) => i.comp === "!global");
|
|
2555
|
+
return new VarDeclaration({
|
|
2556
|
+
name,
|
|
2557
|
+
value
|
|
2558
|
+
}, {
|
|
2559
|
+
assign: sawDefault ? "?:" : ":",
|
|
2560
|
+
setDefined: sawGlobal
|
|
2561
|
+
}, loc);
|
|
2562
|
+
}
|
|
2563
|
+
_buildScssWithConfig(children, loc) {
|
|
2564
|
+
return new Collection(nodeChildren(children).filter((n) => isNode(n, N.VarDeclaration)), void 0, loc);
|
|
2565
|
+
}
|
|
2566
|
+
_buildScssUseAs(children, loc) {
|
|
2567
|
+
return new Any(children.filter((c) => c?._tag === "leaf").find((l) => l.value !== "as")?.value ?? "", { role: "ident" }, loc);
|
|
2568
|
+
}
|
|
2569
|
+
_buildScssUse(children, loc) {
|
|
2570
|
+
const pathNode = nodeChildren(children).find((n) => isNode(n, N.Quoted));
|
|
2571
|
+
const withConfig = nodeChildren(children).find((n) => isNode(n, N.Collection));
|
|
2572
|
+
const useAs = nodeChildren(children).find((n) => isNode(n, N.Any) && n.options?.role === "ident");
|
|
2573
|
+
const namespace = useAs ? String(useAs.valueOf()) : void 0;
|
|
2574
|
+
const rawPath = pathNode?.valueOf() ?? "";
|
|
2575
|
+
if (rawPath.startsWith("sass:")) return new JsImport({ path: quotedLike(pathNode, `#sass/${rawPath.slice(5)}`, loc) }, { namespace: namespace ?? defaultNamespaceFromPath(rawPath) }, loc);
|
|
2576
|
+
if (isScriptUsePath(rawPath)) return new JsImport({ path: pathNode }, { namespace: namespace ?? defaultNamespaceFromPath(rawPath) }, loc);
|
|
2577
|
+
return new StyleImport({
|
|
2578
|
+
path: pathNode,
|
|
2579
|
+
with: withConfig ? {
|
|
2580
|
+
node: withConfig,
|
|
2581
|
+
type: "set"
|
|
2582
|
+
} : void 0
|
|
2583
|
+
}, {
|
|
2584
|
+
type: "compose",
|
|
2585
|
+
namespace,
|
|
2586
|
+
importOptions: {}
|
|
2587
|
+
}, loc);
|
|
2588
|
+
}
|
|
2589
|
+
_buildScssForward(children, _raw, loc) {
|
|
2590
|
+
const pathNode = nodeChildren(children).find((n) => isNode(n, N.Quoted));
|
|
2591
|
+
const withConfig = nodeChildren(children).find((n) => isNode(n, N.Collection));
|
|
2592
|
+
const preludeText = this._source.slice(loc.start, loc.end);
|
|
2593
|
+
const pathMatch = /(['"])([^'"]+)\1/.exec(preludeText);
|
|
2594
|
+
checkForwardPreludeErrors((pathMatch ? preludeText.slice(preludeText.indexOf(pathMatch[0]) + pathMatch[0].length) : "").replace(/\bwith\s*\([^)]*\)\s*;?\s*$/, "").replace(/;\s*$/, "").trim(), (msg) => this._error(msg, loc.start, loc.end));
|
|
2595
|
+
return new StyleImport({
|
|
2596
|
+
path: pathNode,
|
|
2597
|
+
with: withConfig ? {
|
|
2598
|
+
node: withConfig,
|
|
2599
|
+
type: "set"
|
|
2600
|
+
} : void 0
|
|
2601
|
+
}, {
|
|
2602
|
+
type: "compose",
|
|
2603
|
+
importOptions: { forward: true }
|
|
2604
|
+
}, loc);
|
|
2605
|
+
}
|
|
2606
|
+
_buildScssPlaceholderSelector(children, loc) {
|
|
2607
|
+
const name = `\\${(children.filter((c) => c?._tag === "leaf")[0]?.value ?? "").slice(1)}`;
|
|
2608
|
+
return this._makeBasicSelector(name, loc);
|
|
2609
|
+
}
|
|
2610
|
+
_buildScssPermissiveAtRule(children, loc) {
|
|
2611
|
+
const name = children.filter((c) => c?._tag === "leaf")[0]?.value ?? "";
|
|
2612
|
+
const braceIdx = children.findIndex((c) => c._tag === "leaf" && c.value === "{");
|
|
2613
|
+
const preludeChildren = braceIdx >= 0 ? children.slice(1, braceIdx) : children.slice(1);
|
|
2614
|
+
const bodyChildren = braceIdx >= 0 ? children.slice(braceIdx + 1) : [];
|
|
2615
|
+
return new AtRule({
|
|
2616
|
+
name,
|
|
2617
|
+
prelude: new Sequence(nodeChildren(preludeChildren), void 0, loc),
|
|
2618
|
+
rules: nodeChildren(bodyChildren)
|
|
2619
|
+
}, void 0, loc);
|
|
2620
|
+
}
|
|
2621
|
+
_buildScssLayerBlock(children, loc) {
|
|
2622
|
+
const name = children.filter((c) => c?._tag === "leaf")[0]?.value ?? "";
|
|
2623
|
+
const braceIdx = children.findIndex((c) => c._tag === "leaf" && c.value === "{");
|
|
2624
|
+
const preludeChildren = braceIdx >= 0 ? children.slice(1, braceIdx) : children.slice(1);
|
|
2625
|
+
const bodyChildren = braceIdx >= 0 ? children.slice(braceIdx + 1) : [];
|
|
2626
|
+
const preludeNodes = nodeChildren(preludeChildren);
|
|
2627
|
+
return new AtRule({
|
|
2628
|
+
name,
|
|
2629
|
+
prelude: preludeNodes.length === 1 ? preludeNodes[0] : preludeNodes.length > 0 ? new Sequence(preludeNodes, void 0, loc) : void 0,
|
|
2630
|
+
rules: nodeChildren(bodyChildren)
|
|
2631
|
+
}, void 0, loc);
|
|
2632
|
+
}
|
|
2633
|
+
_buildQueryAtRuleBlock(children, loc) {
|
|
2634
|
+
const name = children.filter((c) => c?._tag === "leaf")[0]?.value ?? "";
|
|
2635
|
+
const braceIdx = children.findIndex((c) => c._tag === "leaf" && c.value === "{");
|
|
2636
|
+
const preludeChildren = braceIdx >= 0 ? children.slice(1, braceIdx) : children.slice(1);
|
|
2637
|
+
const bodyChildren = braceIdx >= 0 ? children.slice(braceIdx + 1) : [];
|
|
2638
|
+
return new AtRule({
|
|
2639
|
+
name,
|
|
2640
|
+
prelude: new Sequence(nodeChildren(preludeChildren), void 0, loc),
|
|
2641
|
+
rules: nodeChildren(bodyChildren)
|
|
2642
|
+
}, void 0, loc);
|
|
2643
|
+
}
|
|
2644
|
+
_buildScssParen(rawChildren, loc) {
|
|
2645
|
+
const inner = this._betweenParens(spannedComponents(rawChildren));
|
|
2646
|
+
const { value } = this._assembleValue(inner, loc);
|
|
2647
|
+
if (value && isNode(value, N.Operation)) return new Expression(value, void 0, loc);
|
|
2648
|
+
if (value && isNode(value, N.List) && value.options?.sep === "/" && value.value.length === 2) {
|
|
2649
|
+
const [left, right] = value.value;
|
|
2650
|
+
return new Expression(new Operation([
|
|
2651
|
+
left,
|
|
2652
|
+
"/",
|
|
2653
|
+
right
|
|
2654
|
+
], void 0, loc), void 0, loc);
|
|
2655
|
+
}
|
|
2656
|
+
return new Paren(value, void 0, loc);
|
|
2657
|
+
}
|
|
2658
|
+
_buildScssExtendTarget(children, raw, loc) {
|
|
2659
|
+
for (const c of children) if (typeof c === "string") return c;
|
|
2660
|
+
const placeholderLeaf = children.find((c) => c?._tag === "leaf" && typeof c.value === "string" && c.value.startsWith("%"));
|
|
2661
|
+
if (placeholderLeaf) return `\\${placeholderLeaf.value.slice(1)}`;
|
|
2662
|
+
const items = nodeChildren(children);
|
|
2663
|
+
if (items.length === 1) return items[0];
|
|
2664
|
+
if (items.length > 1) return this._makeSelectorList(items, loc);
|
|
2665
|
+
const spanItems = spannedComponents(raw).filter((i) => i.comp !== ",");
|
|
2666
|
+
if (spanItems.length === 1 && typeof spanItems[0].comp === "string") {
|
|
2667
|
+
const sel = spanItems[0].comp;
|
|
2668
|
+
return sel.startsWith("%") ? `\\${sel.slice(1)}` : sel;
|
|
2669
|
+
}
|
|
2670
|
+
return items[0];
|
|
2671
|
+
}
|
|
2672
|
+
_scssExtendTargetFrom(children, raw, _loc) {
|
|
2673
|
+
for (const c of children) {
|
|
2674
|
+
if (typeof c === "string") return c;
|
|
2675
|
+
if (c != null && typeof c === "object" && "_tag" in c && c._tag === "node") {
|
|
2676
|
+
const n = c;
|
|
2677
|
+
if ([
|
|
2678
|
+
"SelectorList",
|
|
2679
|
+
"BasicSelector",
|
|
2680
|
+
"CompoundSelector",
|
|
2681
|
+
"ComplexSelector"
|
|
2682
|
+
].includes(n.type)) return n;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
const items = spannedComponents(raw).filter((i) => i.comp !== "@extend" && i.comp !== ";" && i.comp !== "!optional");
|
|
2686
|
+
if (items.length === 1 && typeof items[0].comp === "string") {
|
|
2687
|
+
const sel = items[0].comp;
|
|
2688
|
+
if (sel.startsWith("%")) return `\\${sel.slice(1)}`;
|
|
2689
|
+
return sel;
|
|
2690
|
+
}
|
|
2691
|
+
return nodeChildren(children)[0];
|
|
2692
|
+
}
|
|
2693
|
+
_buildScssExtend(children, raw, loc) {
|
|
2694
|
+
const target = this._scssExtendTargetFrom(children, raw, loc);
|
|
2695
|
+
validateExtendTarget(target, this._parseContext?.opts?.allowExtendSelectors, (msg) => this._error(msg, loc.start, loc.end));
|
|
2696
|
+
const prelude = this._source.slice(loc.start, loc.end);
|
|
2697
|
+
const namespace = /@extend\s+%/.test(prelude) || isPlaceholderExtendTarget(target) ? "*" : void 0;
|
|
2698
|
+
return new Extend({
|
|
2699
|
+
target,
|
|
2700
|
+
flag: ExtendFlag.All,
|
|
2701
|
+
namespace
|
|
2702
|
+
}, void 0, loc);
|
|
2703
|
+
}
|
|
2704
|
+
_buildScssImportItem(children, raw, loc) {
|
|
2705
|
+
const prelude = nodeChildren(children).find((n) => isNode(n, N.Quoted) || isNode(n, N.Url));
|
|
2706
|
+
const pathSpan = spannedComponents(raw).find((i) => isNode(i.comp, N.Quoted) || isNode(i.comp, N.Url) || typeof i.comp === "string" && (i.comp.startsWith("\"") || i.comp.startsWith("'") || i.comp.startsWith("url")));
|
|
2707
|
+
let extraText;
|
|
2708
|
+
if (pathSpan) {
|
|
2709
|
+
const tail = raw.filter((c) => c._tag === "leaf" && c.value !== "@import").map((c) => c.value).join("");
|
|
2710
|
+
const pathText = typeof pathSpan.comp === "string" ? pathSpan.comp : "";
|
|
2711
|
+
const idx = tail.indexOf(pathText);
|
|
2712
|
+
if (idx >= 0) extraText = tail.slice(idx + pathText.length).replace(/^[\s,]+/, "").replace(/[,;]\s*$/, "").trim() || void 0;
|
|
2713
|
+
}
|
|
2714
|
+
const seqItems = [];
|
|
2715
|
+
if (prelude) seqItems.push(prelude);
|
|
2716
|
+
if (extraText) seqItems.push(new Any(extraText, { role: "ident" }, loc));
|
|
2717
|
+
return new Sequence(seqItems, void 0, loc);
|
|
2718
|
+
}
|
|
2719
|
+
_buildScssImportAtRule(children, loc) {
|
|
2720
|
+
checkImportPreludeOrder(this._source.slice(loc.start, loc.end).replace(/^@import\b/i, "").replace(/;\s*$/, ""), (msg) => this._error(msg, loc.start, loc.end));
|
|
2721
|
+
const items = nodeChildren(children).filter((n) => isNode(n, N.Sequence));
|
|
2722
|
+
const importName = new Any("@import", { role: "atkeyword" }, loc);
|
|
2723
|
+
const built = [];
|
|
2724
|
+
for (const item of items) {
|
|
2725
|
+
const seq = item;
|
|
2726
|
+
const prelude = seq.value[0];
|
|
2727
|
+
const extra = seq.value[1];
|
|
2728
|
+
const extraText = extra && isNode(extra, N.Any) ? String(extra.valueOf()).trim() : void 0;
|
|
2729
|
+
const itemLoc = sourceSpanOf(seq) ?? loc;
|
|
2730
|
+
if (!prelude) continue;
|
|
2731
|
+
if (!isPlainCssImportPrelude(prelude, extraText) && isNode(prelude, N.Quoted)) {
|
|
2732
|
+
built.push(new StyleImport({ path: prelude }, {
|
|
2733
|
+
type: "import",
|
|
2734
|
+
importOptions: { multiple: true }
|
|
2735
|
+
}, itemLoc));
|
|
2736
|
+
continue;
|
|
2737
|
+
}
|
|
2738
|
+
const preludeNodes = [prelude];
|
|
2739
|
+
if (extraText) preludeNodes.push(new Any(extraText, { role: "ident" }, itemLoc));
|
|
2740
|
+
built.push(new AtRuleStatement({
|
|
2741
|
+
name: importName,
|
|
2742
|
+
prelude: new Sequence(preludeNodes, void 0, itemLoc)
|
|
2743
|
+
}, void 0, itemLoc));
|
|
2744
|
+
}
|
|
2745
|
+
if (built.length === 1) return built[0];
|
|
2746
|
+
return new List(built, { role: "scss-imports" }, loc);
|
|
2747
|
+
}
|
|
2748
|
+
_buildCall(rawChildren, loc) {
|
|
2749
|
+
const call = super._buildCall(rawChildren, loc);
|
|
2750
|
+
const nameNode = call.name;
|
|
2751
|
+
const stringName = typeof nameNode === "string" ? nameNode : isNode(nameNode, N.Reference) && typeof nameNode.key === "string" ? nameNode.key : "";
|
|
2752
|
+
const mapped = desugarMapLookup(new Call({
|
|
2753
|
+
name: stringName,
|
|
2754
|
+
args: call.args
|
|
2755
|
+
}, call.options, loc), loc);
|
|
2756
|
+
if (isNode(mapped, N.Reference)) return mapped;
|
|
2757
|
+
const desugared = desugarNamespacedCall(new Call({
|
|
2758
|
+
name: stringName,
|
|
2759
|
+
args: call.args
|
|
2760
|
+
}, call.options, loc), loc);
|
|
2761
|
+
const name = desugared.name;
|
|
2762
|
+
if (stringName === "selector.parse") {
|
|
2763
|
+
const firstArg = (isNode(desugared.args, N.List) ? desugared.args.value : [])[0];
|
|
2764
|
+
const selectorText = firstArg && isNode(firstArg, N.Quoted) ? typeof firstArg.value === "string" ? firstArg.value : isNode(firstArg.value, N.Any) ? String(firstArg.value.valueOf()) : void 0 : void 0;
|
|
2765
|
+
if (selectorText !== void 0 && isValidScssSelectorList(selectorText)) return new SelectorCapture(selectorText, void 0, loc);
|
|
2766
|
+
return desugared;
|
|
2767
|
+
}
|
|
2768
|
+
if (typeof name === "string" && name.includes(".")) return new Expression(desugared, void 0, loc);
|
|
2769
|
+
if (isNode(name, N.Reference) && name.options?.type === "function") return new Call({
|
|
2770
|
+
name,
|
|
2771
|
+
args: desugared.args
|
|
2772
|
+
}, void 0, loc);
|
|
2773
|
+
if (typeof name === "string") return new Call({
|
|
2774
|
+
name: new Reference({ key: name }, {
|
|
2775
|
+
type: "function",
|
|
2776
|
+
fallbackValue: true
|
|
2777
|
+
}, loc),
|
|
2778
|
+
args: desugared.args
|
|
2779
|
+
}, void 0, loc);
|
|
2780
|
+
return desugared;
|
|
2781
|
+
}
|
|
2782
|
+
_buildSquareParen(rawChildren, loc) {
|
|
2783
|
+
const inner = super._buildSquareParen(rawChildren, loc).value;
|
|
2784
|
+
return new Paren(inner, { delimiter: isNode(inner, N.Any) && inner.options?.role === "ident" ? "square" : "paren" }, loc);
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
//#endregion
|
|
2788
|
+
//#region src/functional-parser.ts
|
|
2789
|
+
var BuilderHost = class extends ScssGrammar {
|
|
2790
|
+
setSource(src) {
|
|
2791
|
+
this._source = src;
|
|
2792
|
+
}
|
|
2793
|
+
resetWarnings() {
|
|
2794
|
+
this._warnings = [];
|
|
2795
|
+
this._errors = [];
|
|
2796
|
+
this._liftedCommentRanges = [];
|
|
2797
|
+
}
|
|
2798
|
+
getWarnings() {
|
|
2799
|
+
return this._warnings.slice();
|
|
2800
|
+
}
|
|
2801
|
+
getErrors() {
|
|
2802
|
+
return this._errors.slice();
|
|
2803
|
+
}
|
|
2804
|
+
setContext(context) {
|
|
2805
|
+
this._parseContext = context;
|
|
2806
|
+
}
|
|
2807
|
+
/** `ctx.build` host: every structural `node(type, …)` builds through this,
|
|
2808
|
+
* reusing ScssGrammar's (SCSS + inherited Less/CSS) `buildNode` verbatim. */
|
|
2809
|
+
captureTriviaForNode(type) {
|
|
2810
|
+
return type === "CompoundSelector";
|
|
2811
|
+
}
|
|
2812
|
+
build(type, children, fields, span, rawChildren, triviaLog) {
|
|
2813
|
+
return this.buildNode(type, {
|
|
2814
|
+
start: span.start,
|
|
2815
|
+
end: span.end
|
|
2816
|
+
}, children, void 0, rawChildren, fields, triviaLog);
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
const host = new BuilderHost();
|
|
2820
|
+
function parseScssFn(input, rule = "Stylesheet", options = {}) {
|
|
2821
|
+
const g = scssGrammar;
|
|
2822
|
+
host.setContext(options.context);
|
|
2823
|
+
return runFunctionalParse(input, g[rule], host, { trivia: g.rw });
|
|
2824
|
+
}
|
|
2825
|
+
setParseScssFnForInterp(parseScssFn);
|
|
2826
|
+
const EMPTY_LEXER_RESULT = {
|
|
2827
|
+
tokens: [],
|
|
2828
|
+
errors: [],
|
|
2829
|
+
groups: {}
|
|
2830
|
+
};
|
|
2831
|
+
function toParseResult(result) {
|
|
2832
|
+
return {
|
|
2833
|
+
tree: result.tree,
|
|
2834
|
+
errors: result.errors,
|
|
2835
|
+
warnings: result.warnings,
|
|
2836
|
+
trivia: result.trivia,
|
|
2837
|
+
lexerResult: EMPTY_LEXER_RESULT
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* Functional SCSS parser — the default `Parser` export. Wraps `parseScssFn` and
|
|
2842
|
+
* returns the same `IParseResult` shape as the legacy Chevrotain parser (with an
|
|
2843
|
+
* empty `lexerResult`; the functional grammar does not tokenize separately).
|
|
2844
|
+
*/
|
|
2845
|
+
var ScssParser = class {
|
|
2846
|
+
constructor(_config = {}) {}
|
|
2847
|
+
parse(text, rule = "Stylesheet", options) {
|
|
2848
|
+
return toParseResult(parseScssFn(text, rule, { context: options?.context }));
|
|
2849
|
+
}
|
|
2850
|
+
suggest(_text, _init) {
|
|
2851
|
+
return [];
|
|
2852
|
+
}
|
|
2853
|
+
};
|
|
2854
|
+
//#endregion
|
|
2855
|
+
export { parseScssFn as n, ScssGrammar as r, ScssParser as t };
|