@jesscss/less-parser 2.0.0-alpha.8 → 2.0.0-alpha.9

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.
Files changed (58) hide show
  1. package/README.md +27 -10
  2. package/lib/ast/grammar.d.ts +2 -0
  3. package/lib/ast/grammar.d.ts.map +1 -0
  4. package/lib/grammar.cjs +39855 -31054
  5. package/lib/grammar.d.ts.map +1 -1
  6. package/lib/grammar.js +39855 -31054
  7. package/lib/index.cjs +49609 -15
  8. package/lib/index.d.ts +4 -9
  9. package/lib/index.d.ts.map +1 -1
  10. package/lib/index.js +49607 -5
  11. package/lib/parse-error.d.ts +15 -0
  12. package/lib/parse-error.d.ts.map +1 -0
  13. package/package.json +9 -24
  14. package/lib/builders.d.ts +0 -381
  15. package/lib/builders.d.ts.map +0 -1
  16. package/lib/functional-parser.cjs +0 -2654
  17. package/lib/functional-parser.d.ts +0 -18
  18. package/lib/functional-parser.d.ts.map +0 -1
  19. package/lib/functional-parser.js +0 -2589
  20. package/lib/jess.cjs +0 -3968
  21. package/lib/jess.d.ts +0 -7
  22. package/lib/jess.d.ts.map +0 -1
  23. package/lib/jess.js +0 -3962
  24. package/lib/lessParser.d.ts +0 -38
  25. package/lib/lessParser.d.ts.map +0 -1
  26. package/lib/lessRecursiveParser.d.ts +0 -99
  27. package/lib/lessRecursiveParser.d.ts.map +0 -1
  28. package/lib/lessTokens.d.ts +0 -23
  29. package/lib/lessTokens.d.ts.map +0 -1
  30. package/lib/productions/guards.d.ts +0 -113
  31. package/lib/productions/guards.d.ts.map +0 -1
  32. package/lib/productions/index.d.ts +0 -5
  33. package/lib/productions/index.d.ts.map +0 -1
  34. package/lib/productions/root.d.ts +0 -38
  35. package/lib/productions/root.d.ts.map +0 -1
  36. package/lib/productions/selectors.d.ts +0 -41
  37. package/lib/productions/selectors.d.ts.map +0 -1
  38. package/lib/productions/values.d.ts +0 -35
  39. package/lib/productions/values.d.ts.map +0 -1
  40. package/lib/utils.d.ts +0 -9
  41. package/lib/utils.d.ts.map +0 -1
  42. package/src/__tests__/debug-log.ts +0 -35
  43. package/src/__tests__/wall5-parse.test.ts +0 -67
  44. package/src/builders.ts +0 -3190
  45. package/src/cst.ts +0 -25
  46. package/src/functional-parser.ts +0 -162
  47. package/src/grammar.ts +0 -870
  48. package/src/index.ts +0 -19
  49. package/src/jess.ts +0 -6
  50. package/src/lessParser.ts +0 -120
  51. package/src/lessRecursiveParser.ts +0 -279
  52. package/src/lessTokens.ts +0 -350
  53. package/src/productions/guards.ts +0 -1066
  54. package/src/productions/index.ts +0 -29
  55. package/src/productions/root.ts +0 -1613
  56. package/src/productions/selectors.ts +0 -1309
  57. package/src/productions/values.ts +0 -1449
  58. package/src/utils.ts +0 -178
@@ -1,1309 +0,0 @@
1
- /* eslint-disable -- Retired Chevrotain parser; not linted (see @ts-nocheck below). */
2
- // @ts-nocheck — Retired Chevrotain parser. Uses the legacy 6-tuple `.location`
3
- // shape removed from Node in the provenance-side-table refactor; the functional
4
- // Parséman grammar (grammar-rules.ts + builders.ts) is the maintained parser.
5
- // Not type-checked.
6
- // Selector-related production rules for LessRecursiveParser
7
- // Converted from Chevrotain-based productions.ts lines 1145-2060
8
-
9
- import type { RuleContext, ExtendTarget, TokenMap } from '../lessRecursiveParser.js';
10
- import type { IToken, IOrAlt } from 'chevrotain';
11
- import {
12
- Node,
13
- Ampersand,
14
- Block,
15
- Any,
16
- type LocationInfo,
17
- type TreeContext,
18
- BasicSelector,
19
- Combinator,
20
- type Combinators,
21
- List,
22
- Sequence,
23
- Call,
24
- Quoted,
25
- AtRule,
26
- AtRuleStatement,
27
- Interpolated,
28
- InterpolatedSelector,
29
- AttributeSelector,
30
- Reference,
31
- Extend,
32
- Mixin,
33
- VarDeclaration,
34
- Declaration,
35
- Expression,
36
- SelectorCapture,
37
- ComplexSelector,
38
- CompoundSelector,
39
- Selector,
40
- SelectorList,
41
- Url,
42
- Nil,
43
- Collection,
44
- type ComplexSelectorValue,
45
- type ComplexSelectorComponent,
46
- type SimpleSelector,
47
- isNode,
48
- N,
49
- StyleImport
50
- } from '@jesscss/core';
51
-
52
- import { createInterpolatedReference, getInterpolatedNode, getInterpolatedOrString } from '../utils.js';
53
- import { all } from 'known-css-properties';
54
-
55
- /** Use `any` for `this` to avoid structural incompatibility between LessRecursiveParser and CssRecursiveParser */
56
- type P = any;
57
- type Alt = IOrAlt<any>[];
58
- type AltContext = (ctx?: RuleContext) => Alt;
59
- type ProductionRule = (...args: any[]) => any;
60
- const COMBINATORS = new Set<string>([' ', '>', '+', '~', '|', '||']);
61
-
62
- function toCombinator(image: string): Combinators {
63
- if (COMBINATORS.has(image)) {
64
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
65
- return image as Combinators;
66
- }
67
- throw new Error(`Unexpected selector combinator "${image}".`);
68
- }
69
-
70
- type ComplexSelectorComponentNode = Exclude<ComplexSelectorComponent, string>;
71
-
72
- function isComplexSelectorComponentNode(node: Node | undefined): node is ComplexSelectorComponentNode {
73
- return node instanceof Call
74
- || (node instanceof Selector
75
- && !(node instanceof SelectorList)
76
- && !(node instanceof ComplexSelector));
77
- }
78
-
79
- export function attributeSelector(this: P, T: TokenMap, valueAlt?: AltContext) {
80
- const $ = this;
81
-
82
- valueAlt ??= () => [
83
- {
84
- GATE: () => !$.isType(T.InterpolatedIdent),
85
- ALT: () => {
86
- const token = $.CONSUME5(T.Ident);
87
- if ($.RECORDING_PHASE) {
88
- return;
89
- }
90
- return new Any(token.image, { role: 'ident' }, $.getLocationInfo(token), $.context);
91
- }
92
- },
93
- {
94
- GATE: () => $.isType(T.InterpolatedIdent),
95
- ALT: () => {
96
- const token = $.CONSUME(T.InterpolatedIdent);
97
- if ($.RECORDING_PHASE) {
98
- return;
99
- }
100
- const match = /([$@])\{([^}]+)\}/.exec(token.image);
101
- if (match && match[0] === token.image) {
102
- return createInterpolatedReference(
103
- match[1]!,
104
- match[2]!,
105
- $.getLocationInfo(token),
106
- $.context
107
- );
108
- }
109
- const result = getInterpolatedOrString(token.image, $.getLocationInfo(token), $.context);
110
- return typeof result === 'string'
111
- ? new Any(result, { role: 'ident' }, $.getLocationInfo(token), $.context)
112
- : result;
113
- }
114
- },
115
- { ALT: () => $.SUBRULE($.string) }
116
- ];
117
-
118
- return (ctx: RuleContext = {}) => {
119
- const RECORDING_PHASE = $.RECORDING_PHASE;
120
- $.startRule();
121
-
122
- $.CONSUME2(T.LSquare);
123
- const key: Any = $.SUBRULE2($.attributeName);
124
- let op: IToken | undefined;
125
- let value: Node | undefined;
126
- let mod: IToken | undefined;
127
- $.OPTION(() => {
128
- op = $.OR([
129
- { ALT: () => $.CONSUME4(T.Eq) },
130
- { ALT: () => $.CONSUME6(T.AttrMatch) }
131
- ]);
132
- value = $.OR2(valueAlt!(ctx));
133
- });
134
- $.OPTION2(() => mod = $.CONSUME7(T.AttrFlag));
135
- $.CONSUME8(T.RSquare);
136
-
137
- if (!RECORDING_PHASE) {
138
- const location = $.endRule();
139
- return new AttributeSelector({
140
- name: key.valueOf(),
141
- op: op?.image,
142
- value,
143
- mod: mod?.image
144
- }, undefined, location, $.context);
145
- }
146
- };
147
- }
148
-
149
- // ── Helper: getAmpersandTemplateValue ────────────────────────────────
150
-
151
- function getAmpersandTemplateValue(image: string): string | undefined {
152
- if (image === '&') {
153
- return undefined;
154
- }
155
- if (image.startsWith('&')) {
156
- return image.slice(1) || undefined;
157
- }
158
- if (image.includes('&')) {
159
- return image;
160
- }
161
- return undefined;
162
- }
163
-
164
- // ── Helper: wrapOuterExpressionIfNeeded ──────────────────────────────
165
- // Imported from root.ts where it's defined.
166
- import { wrapOuterExpressionIfNeeded } from './root.js';
167
-
168
- // ── Helper: groupExtendsByTargetAndFlag ──────────────────────────────
169
-
170
- const { isArray } = Array;
171
- type ExtendSelectorKind = 'simple' | 'basic' | 'pseudo' | 'complex' | 'compound';
172
-
173
- function getAllowedExtendSelectors(context: TreeContext): ExtendSelectorKind[] | undefined {
174
- const val: ExtendSelectorKind[] | undefined = context.opts.allowExtendSelectors;
175
- return val;
176
- }
177
-
178
- function findDisallowedExtendSelector(selector: Selector | string, allowed?: readonly ExtendSelectorKind[]): { kind: ExtendSelectorKind; selector: Selector } | undefined {
179
- // A bare-string selector (strings-not-nodes) is a plain simple selector — not a
180
- // disallowed extend target.
181
- if (typeof selector === 'string') {
182
- return undefined;
183
- }
184
- if (!allowed) {
185
- return undefined;
186
- }
187
- if (isNode(selector, N.SelectorList)) {
188
- for (const item of selector.value) {
189
- const disallowed = findDisallowedExtendSelector(item, allowed);
190
- if (disallowed) {
191
- return disallowed;
192
- }
193
- }
194
- return undefined;
195
- }
196
- const kinds: ExtendSelectorKind[] = isNode(selector, N.BasicSelector)
197
- ? ['simple', 'basic']
198
- : isNode(selector, N.PseudoSelector)
199
- ? ['simple', 'pseudo']
200
- : isNode(selector, N.CompoundSelector)
201
- ? ['compound']
202
- : isNode(selector, N.ComplexSelector)
203
- ? ['complex']
204
- : ['simple'];
205
- if (kinds.some(kind => allowed.includes(kind))) {
206
- return undefined;
207
- }
208
- return {
209
- kind: kinds[0]!,
210
- selector
211
- };
212
- }
213
-
214
- function formatAllowedExtendSelectors(allowed: readonly ExtendSelectorKind[]) {
215
- if (allowed.length === 0) {
216
- return 'no selector kinds';
217
- }
218
- if (allowed.length === 1) {
219
- return `${allowed[0]} value`;
220
- }
221
- const head = allowed.slice(0, -1).join(', ');
222
- return `${head}, or ${allowed[allowed.length - 1]} value`;
223
- }
224
-
225
- function validateExtendTarget($: P, selector: Selector, source: ':extend()' | '&:extend()') {
226
- const allowed = getAllowedExtendSelectors($.context);
227
- const disallowed = findDisallowedExtendSelector(selector, allowed);
228
- if (!disallowed || !allowed) {
229
- return;
230
- }
231
- throw new Error(
232
- `${source} only allows ${formatAllowedExtendSelectors(allowed)}, but found ${disallowed.kind} selector "${disallowed.selector.valueOf()}".`
233
- );
234
- }
235
-
236
- /**
237
- * Groups extends by target (using valueOf()) and flag.
238
- * Returns an array of grouped Extend nodes where extends with the same target and flag
239
- * are combined into a single Extend node with a SelectorList of all matching value.
240
- *
241
- * @todo Group complex value into selector lists
242
- */
243
- function groupExtendsByTargetAndFlag(
244
- extendNodes: Extend[]
245
- ): Array<Extend | Extend[]> {
246
- // Group extends by target and flag
247
- const groups = new Map<string, Extend | Extend[]>();
248
-
249
- for (const ext of extendNodes) {
250
- const { target, flag = 1 } = ext; // ExtendFlag.Exact = 1
251
- // Create a key from target valueOf() and flag
252
- const key = `${target.valueOf()}|${flag}`;
253
-
254
- let group = groups.get(key);
255
- if (!group) {
256
- groups.set(key, ext);
257
- } else if (isArray(group)) {
258
- group.push(ext);
259
- } else {
260
- groups.set(key, [group, ext]);
261
- }
262
- }
263
-
264
- return Array.from(groups.values());
265
- }
266
-
267
- // ── Helper: mergeExtends ─────────────────────────────────────────────
268
-
269
- function mergeExtends(
270
- selector: Selector | undefined,
271
- extendTargets: ExtendTarget[],
272
- location: LocationInfo,
273
- context: TreeContext,
274
- flag: IToken | undefined
275
- ): Extend | Extend[] {
276
- let extendNodes: Extend[] | undefined;
277
- let currentTarget = extendTargets[0]!.target;
278
- let currentFlag = (extendTargets[0]!.flag ?? flag) ? 0 : 1; // ExtendFlag.All = 0, ExtendFlag.Exact = 1
279
- let currentNode = new Extend({
280
- selector,
281
- target: currentTarget,
282
- flag: currentFlag
283
- }, undefined, location, context);
284
- const flushCurrent = (): void => {
285
- (extendNodes ??= []).push(currentNode);
286
- };
287
- for (let i = 1; i < extendTargets.length; i++) {
288
- let ext = extendTargets[i]!;
289
- let thisFlag = (ext.flag ?? flag) ? 0 : 1;
290
- /**
291
- * Merge extends. We do this instead of merging earlier so that
292
- * selector lists with different flags are not merged.
293
- */
294
- if (thisFlag === currentFlag) {
295
- const { target } = currentNode;
296
- let nextTarget: Selector;
297
- if (!(target instanceof SelectorList)) {
298
- nextTarget = new SelectorList([target, ext.target], undefined, location, context);
299
- } else {
300
- nextTarget = new SelectorList([...target.value, ext.target], undefined, location, context);
301
- }
302
- currentNode = new Extend({
303
- selector,
304
- target: nextTarget,
305
- flag: currentFlag
306
- }, undefined, location, context);
307
- } else {
308
- flushCurrent();
309
- currentFlag = thisFlag;
310
- currentTarget = ext.target;
311
- currentNode = new Extend({
312
- selector,
313
- target: currentTarget,
314
- flag: currentFlag
315
- }, undefined, location, context);
316
- }
317
- };
318
- if (!extendNodes) {
319
- return currentNode;
320
- }
321
- flushCurrent();
322
- if (extendNodes.length === 1) {
323
- return extendNodes[0]!;
324
- }
325
- return extendNodes;
326
- }
327
-
328
- function extendWithSelector(node: Extend, selector: Selector | undefined, location: LocationInfo | undefined, context: TreeContext): Extend {
329
- return new Extend({
330
- selector,
331
- target: node.target,
332
- namespace: node.namespace,
333
- flag: node.flag
334
- }, undefined, location, context);
335
- }
336
-
337
- // ── Helper: isSelectorLikeListItem / isLegacySelectorLikeValue ───────
338
-
339
- /** True for a node that could be one item in the old unquoted selector list (e.g. .a or #id). */
340
- function isSelectorLikeListItem(node: Node): boolean {
341
- if (node.type === 'SelectorCapture' || isNode(node, N.Call)) {
342
- return false;
343
- }
344
- if (isNode(node, N.Reference)) {
345
- return node.options.type === 'mixin-ruleset';
346
- }
347
- if (node instanceof List) {
348
- return node.value.length > 0 && node.value.every(isSelectorLikeListItem);
349
- }
350
- if (node instanceof Sequence) {
351
- return node.value.length > 0 && node.value.every(isSelectorLikeListItem);
352
- }
353
- return false;
354
- }
355
-
356
- /** True only for the legacy unquoted selector-list form (e.g. @var: .a, .b, .c), not @var: .a; */
357
- function isLegacySelectorLikeValue(node: Node): boolean {
358
- if (node.type === 'SelectorCapture' || isNode(node, N.Call)) {
359
- return false;
360
- }
361
- if (isNode(node, N.Reference)) {
362
- return false;
363
- }
364
- if (node instanceof List) {
365
- return node.value.length > 1 && node.value.every(isSelectorLikeListItem);
366
- }
367
- if (node instanceof Sequence) {
368
- return node.value.length > 1 && node.value.every(isSelectorLikeListItem);
369
- }
370
- return false;
371
- }
372
-
373
- // ══════════════════════════════════════════════════════════════════════
374
- // PRODUCTION RULES
375
- // ══════════════════════════════════════════════════════════════════════
376
-
377
- /**
378
- * We need to now handle a returned `Extend` node from the complexSelector rule
379
- */
380
- export function relativeSelector(this: P, T: TokenMap) {
381
- const $ = this;
382
- return (ctx: RuleContext = {}) => {
383
- return $.OR([
384
- {
385
- ALT: () => {
386
- let co = $.CONSUME(T.Combinator);
387
- let node: ComplexSelector | Extend = $.SUBRULE2($.complexSelector, { ARGS: [ctx] });
388
- if ($.RECORDING_PHASE) {
389
- return node;
390
- }
391
-
392
- let combinator = new Combinator(toCombinator(co.image), undefined, $.getLocationInfo(co), $.context);
393
- let targetNode =
394
- node instanceof Extend
395
- ? node.selector
396
- : node;
397
- if (targetNode instanceof ComplexSelector) {
398
- const nodes = [combinator, ...targetNode.value] as ComplexSelectorValue;
399
- const complex = new ComplexSelector(nodes, undefined, $.getLocationFromNodes(nodes), $.context);
400
- if (node instanceof Extend) {
401
- const location = node.location.length === 6
402
- ? ([...node.location] as LocationInfo)
403
- : undefined;
404
- if (location) {
405
- location[0] = co.startOffset!;
406
- location[1] = co.startLine!;
407
- location[2] = co.startColumn!;
408
- }
409
- node = extendWithSelector(node, complex, location, $.context);
410
- } else {
411
- node = complex;
412
- }
413
- } else {
414
- if (!isComplexSelectorComponentNode(targetNode)) {
415
- const targetType = typeof targetNode === 'string' ? 'string' : (targetNode as Node | undefined)?.type ?? 'none';
416
- throw new Error(`Expected selector component after relative combinator; got ${targetType}.`);
417
- }
418
- let nodes = [combinator, targetNode];
419
- let complex = new ComplexSelector(nodes, undefined, $.getLocationFromNodes(nodes), $.context);
420
- if (node instanceof Extend) {
421
- const location = node.location.length === 6
422
- ? ([...node.location] as LocationInfo)
423
- : undefined;
424
- if (location) {
425
- location[0] = co.startOffset!;
426
- location[1] = co.startLine!;
427
- location[2] = co.startColumn!;
428
- }
429
- node = extendWithSelector(node, complex, location, $.context);
430
- } else {
431
- node = complex;
432
- }
433
- }
434
- return node;
435
- }
436
- },
437
- {
438
- ALT: () => $.SUBRULE3($.complexSelector, { ARGS: [ctx] })
439
- }
440
- ]);
441
- };
442
- }
443
-
444
- export function forgivingSelectorList(this: P, T: TokenMap) {
445
- const $ = this;
446
- return (ctx: RuleContext = {}) => {
447
- const RECORDING_PHASE = $.RECORDING_PHASE;
448
- $.startRule();
449
-
450
- let sequences: ComplexSelector[] | undefined;
451
- let i = 0;
452
-
453
- if (!RECORDING_PHASE) {
454
- sequences = [];
455
- }
456
-
457
- $.AT_LEAST_ONE_SEP({
458
- SEP: T.Comma,
459
- DEF: () => {
460
- const selector = $.SUBRULE($.relativeSelector, { ARGS: [ctx] });
461
- if (!RECORDING_PHASE) {
462
- i++;
463
- if (i === 1 && ctx.qualifiedRule) {
464
- sequences!.push(selector);
465
- } else {
466
- sequences!.push(selector);
467
- }
468
- }
469
- }
470
- });
471
-
472
- if (RECORDING_PHASE) {
473
- return;
474
- }
475
- const location = $.endRule();
476
- if (sequences!.length === 1) {
477
- return sequences![0];
478
- }
479
- return new SelectorList(sequences!, undefined, location, $.context);
480
- };
481
- }
482
-
483
- export function selectorList(this: P, T: TokenMap) {
484
- const $ = this;
485
- return (ctx: RuleContext = {}) => {
486
- const RECORDING_PHASE = $.RECORDING_PHASE;
487
- $.startRule();
488
-
489
- let sequences: ComplexSelector[] | undefined;
490
- let i = 0;
491
-
492
- if (!RECORDING_PHASE) {
493
- sequences = [];
494
- }
495
-
496
- $.AT_LEAST_ONE_SEP({
497
- SEP: T.Comma,
498
- DEF: () => {
499
- const selector = $.SUBRULE2($.complexSelector, { ARGS: [ctx] });
500
- if (!RECORDING_PHASE) {
501
- i++;
502
- if (i === 1 && ctx.qualifiedRule) {
503
- sequences!.push(selector);
504
- } else {
505
- sequences!.push(selector);
506
- }
507
- }
508
- }
509
- });
510
-
511
- if (RECORDING_PHASE) {
512
- return;
513
- }
514
- const location = $.endRule();
515
- if (sequences!.length === 1) {
516
- return sequences![0];
517
- }
518
- return new SelectorList(sequences!, undefined, location, $.context);
519
- };
520
- }
521
-
522
- export function compoundSelector(this: P, T: TokenMap): ProductionRule {
523
- const $ = this;
524
- return (ctx: RuleContext = {}) => {
525
- /**
526
- A sequence of simple value that are not separated by
527
- a combinator.
528
- .e.g. `a#selected`
529
- */
530
- // compoundSelector
531
- // : simpleSelector+
532
- // ;
533
- let RECORDING_PHASE = $.RECORDING_PHASE;
534
- let value: SimpleSelector[];
535
- if (!RECORDING_PHASE) {
536
- value = [];
537
- }
538
- let sel: SimpleSelector = $.SUBRULE($.simpleSelector, { ARGS: [ctx] });
539
- if (!RECORDING_PHASE) {
540
- value!.push(sel);
541
- }
542
- $.MANY({
543
- /** Make sure we don't ignore space combinators */
544
- GATE: () => !$.hasWS() && !(ctx.inExtend && $.isType(T.All)),
545
- DEF: () => {
546
- let sel: SimpleSelector = $.SUBRULE2($.simpleSelector, { ARGS: [ctx] });
547
- if (!RECORDING_PHASE) {
548
- value!.push(sel);
549
- }
550
- }
551
- });
552
- if (RECORDING_PHASE) {
553
- return;
554
- }
555
- if (value!.length === 1) {
556
- return value![0]!;
557
- }
558
- return new CompoundSelector(value!, undefined, $.getLocationFromNodes(value!), $.context);
559
- };
560
- }
561
-
562
- /**
563
- * Extended with :extend
564
- */
565
- export function complexSelector(this: P, T: TokenMap): ProductionRule {
566
- const $ = this;
567
- return (ctx: RuleContext = {}) => {
568
- const RECORDING_PHASE = $.RECORDING_PHASE;
569
- $.startRule();
570
-
571
- let value: ComplexSelectorValue | undefined;
572
- if (!RECORDING_PHASE) {
573
- const first: ComplexSelectorComponent = $.SUBRULE($.compoundSelector, { ARGS: [ctx] });
574
- value = [first];
575
- } else {
576
- $.SUBRULE($.compoundSelector, { ARGS: [ctx] });
577
- }
578
-
579
- $.MANY({
580
- GATE: () => {
581
- if (ctx.inExtend && $.isType(T.All)) {
582
- return false;
583
- }
584
- return $.hasWS() || $.isType(T.Combinator);
585
- },
586
- DEF: () => {
587
- let co: IToken | undefined;
588
- let combinator: Combinator | undefined;
589
-
590
- $.OPTION(() => {
591
- co = $.CONSUME(T.Combinator);
592
- });
593
-
594
- if (!RECORDING_PHASE) {
595
- if (co) {
596
- const coImg = toCombinator(co.image);
597
- combinator = new Combinator(coImg, undefined, $.getLocationInfo(co), $.context);
598
- } else {
599
- const ws = $.claimSpaceCombinator($.LA(1).startOffset);
600
- combinator = new Combinator(' ', undefined, ws ? $.getLocationInfo(ws) : undefined, $.context);
601
- }
602
- }
603
-
604
- const compound: ComplexSelectorComponent = $.SUBRULE2($.compoundSelector, { ARGS: [ctx] });
605
- if (!RECORDING_PHASE) {
606
- value!.push(combinator!, compound);
607
- }
608
- }
609
- });
610
-
611
- let selector: Selector | undefined;
612
- if (!RECORDING_PHASE) {
613
- const location = $.endRule();
614
- selector = value!.length === 1
615
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
616
- ? value![0] as Selector | undefined
617
- : new ComplexSelector(value!, undefined, location, $.context);
618
- }
619
-
620
- let flag: IToken | undefined;
621
-
622
- /** Inside :extend(...), only consume the optional trailing "all" keyword. */
623
- $.OPTION2({
624
- GATE: () => !!ctx.inExtend && $.isType(T.All),
625
- DEF: () => {
626
- flag = $.CONSUME(T.All);
627
- }
628
- });
629
-
630
- /**
631
- * Outside :extend(...), only enter the extend production when the next token
632
- * is actually :extend(. Do not commit based on context alone.
633
- */
634
- $.OPTION3({
635
- GATE: () => !ctx.inExtend && !!ctx.qualifiedRule && $.isType(T.Extend),
636
- DEF: () => {
637
- const initialSelector = ctx.selector;
638
- if (!RECORDING_PHASE) {
639
- ctx.selector = selector;
640
- }
641
- try {
642
- $.SUBRULE($.extend, { ARGS: [ctx] });
643
- } finally {
644
- ctx.selector = initialSelector;
645
- }
646
- }
647
- });
648
-
649
- if (ctx.inExtend) {
650
- validateExtendTarget($, selector!, ':extend()');
651
- (ctx.extendTargets ??= []).push({ selector: ctx.selector, target: selector!, flag });
652
- }
653
-
654
- return selector;
655
- };
656
- }
657
-
658
- /**
659
- * &:extend(...) statement ending with a semicolon.
660
- * This is the only valid standalone extend statement in Less.
661
- */
662
- export function ampersandExtend(this: P, T: TokenMap) {
663
- const $ = this;
664
- return (ctx: RuleContext = {}) => {
665
- $.startRule();
666
-
667
- $.CONSUME(T.Ampersand);
668
- $.CONSUME(T.Extend);
669
- ctx.inExtend = true;
670
- $.SUBRULE($.selectorList, { ARGS: [ctx] });
671
- ctx.inExtend = false;
672
- let extendTargets = ctx.extendTargets!;
673
- let flag = $.OPTION(() => $.CONSUME(T.AllFlag));
674
- $.CONSUME(T.RParen);
675
- $.CONSUME(T.Semi);
676
-
677
- let location = $.endRule();
678
- if (!$.RECORDING_PHASE) {
679
- let result = mergeExtends(undefined, extendTargets, location, $.context, flag);
680
- /** We've converted these extend targets to nodes, so we can reset extend targets */
681
- ctx.extendTargets = undefined;
682
- if (ctx.extendNodes) {
683
- if (isArray(result)) {
684
- ctx.extendNodes = [...ctx.extendNodes, ...result];
685
- } else {
686
- ctx.extendNodes.push(result);
687
- }
688
- } else {
689
- if (isArray(result)) {
690
- ctx.extendNodes = result;
691
- } else {
692
- ctx.extendNodes = [result];
693
- }
694
- }
695
- // Return Nil instead of the extend node - extends are handled via ctx.extendNodes
696
- // pathway to avoid duplicates (cssMain collects returned nodes into rules.rules).
697
- // Nil is needed because undefined confuses cssMain (treats it as semicolon).
698
- return new Nil(undefined, undefined, location, $.context);
699
- }
700
- };
701
- }
702
-
703
- export function extend(this: P, T: TokenMap) {
704
- const $ = this;
705
- return (ctx: RuleContext = {}) => {
706
- $.startRule();
707
- $.CONSUME(T.Extend);
708
-
709
- ctx.inExtend = true;
710
- $.SUBRULE($.selectorList, { ARGS: [ctx] });
711
- let extendTargets = ctx.extendTargets;
712
- ctx.inExtend = false;
713
-
714
- let selector = ctx.selector;
715
- let flag = $.OPTION(() => $.CONSUME(T.AllFlag));
716
- $.CONSUME(T.RParen);
717
-
718
- let location = $.endRule();
719
- if (!$.RECORDING_PHASE) {
720
- // When .c:extend(...) is parsed, selector is .c
721
- // The extend will be processed in qualifiedRuleBody where selector: undefined is set
722
- // for extends that stay inside the ruleset (not bubbled)
723
- // Bubbled extends keep their selector and get it set correctly in qualifiedRule
724
- let merged = mergeExtends(selector, extendTargets!, location, $.context, flag);
725
- /**
726
- * If we don't have as many extends as we have value, we need a way to signal
727
- * that these should be bumped above the ruleset.
728
- */
729
- /** We've converted these extend targets to nodes, so we can reset extend targets */
730
- ctx.extendTargets = undefined;
731
- if (ctx.extendNodes) {
732
- if (isArray(merged)) {
733
- ctx.extendNodes = [...ctx.extendNodes, ...merged];
734
- } else {
735
- ctx.extendNodes.push(merged);
736
- }
737
- } else {
738
- if (isArray(merged)) {
739
- ctx.extendNodes = merged;
740
- } else {
741
- ctx.extendNodes = [merged];
742
- }
743
- }
744
- }
745
- };
746
- }
747
-
748
- export function simpleSelector(this: P, T: TokenMap): ProductionRule {
749
- const $ = this;
750
- return (ctx: RuleContext = {}) => {
751
- let selectorAlt: Alt = [
752
- {
753
- GATE: () => (
754
- (!ctx.inExtend || $.LA(1).tokenType !== T.All)
755
- && $.LA(1).tokenType !== T.InterpolatedIdent
756
- ),
757
- /**
758
- * In Less/Sass (and now CSS), the first inner selector can be an identifier
759
- */
760
- ALT: () => $.CONSUME(T.Ident)
761
- },
762
- {
763
- /**
764
- * Unlike CSS Nesting, Less allows outer qualified rules
765
- * to have `&`, and it is just silently absorbed if there
766
- * is no parent selector.
767
- */
768
- ALT: () => {
769
- let amp = $.CONSUME(T.Ampersand);
770
- const value = getAmpersandTemplateValue(amp.image);
771
- return new Ampersand({ appendValue: value }, undefined, $.getLocationInfo(amp), $.context);
772
- }
773
- },
774
- {
775
- ALT: () => {
776
- $.startRule();
777
- $.CONSUME(T.AmpersandLParen);
778
- const parts: string[] = [];
779
- let sawQuoted = false;
780
- $.MANY(() => {
781
- $.OR2([
782
- {
783
- GATE: () => $.isType(T.QuoteStart),
784
- ALT: () => {
785
- const quoted: Quoted = $.SUBRULE($.string, { ARGS: [ctx] });
786
- parts.push(quoted.valueOf());
787
- sawQuoted = true;
788
- }
789
- },
790
- {
791
- GATE: () => $.isType(T.WS),
792
- ALT: () => {
793
- parts.push($.CONSUME(T.WS).image);
794
- }
795
- },
796
- {
797
- ALT: () => {
798
- parts.push($.CONSUME(T.AmpersandTemplateContents).image);
799
- }
800
- }
801
- ]);
802
- });
803
- $.CONSUME(T.AmpersandTemplateEnd);
804
- const location = $.endRule();
805
- const value = parts.join('');
806
- const appendValue = sawQuoted && value === ''
807
- ? ''
808
- : value === 'nil'
809
- ? ''
810
- : value;
811
- return new Ampersand({ appendValue }, undefined, location, $.context);
812
- }
813
- },
814
- { ALT: () => $.CONSUME(T.InterpolatedIdent) },
815
- { ALT: () => $.CONSUME(T.InterpolatedSelector) },
816
- { ALT: () => $.SUBRULE($.classSelector, { ARGS: [ctx] }) },
817
- { ALT: () => $.SUBRULE($.idSelector, { ARGS: [ctx] }) },
818
- { ALT: () => $.CONSUME(T.Star) },
819
- { ALT: () => {
820
- let initialIsQualifiedRule = ctx.qualifiedRule;
821
- ctx.qualifiedRule = false;
822
- /** Make sure we prevent things like :extend() inside pseudo-value */
823
- try {
824
- let pseudo = $.SUBRULE($.pseudoSelector, { ARGS: [ctx] });
825
- return pseudo;
826
- } finally {
827
- ctx.qualifiedRule = initialIsQualifiedRule;
828
- }
829
- } },
830
- /** @todo - replicate this fix we made with the Jess parser
831
- *
832
- * { ALT: () => $.attributeSelector(ctx, () => [
833
- {
834
- ALT: () => {
835
- let token = $.CONSUME($.T.InterpolatedIdent);
836
- let location = $.getLocationInfo(token);
837
- let image = token.image;
838
- let match = interpolatedRegex.exec(image);
839
- interpolatedRegex.lastIndex = 0;
840
- if (match && match[0] === image) {
841
- return new Reference(
842
- { key: new Keyword(match[2]!, undefined, location, $.context) },
843
- { type: 'index' },
844
- location,
845
- $.context
846
- );
847
- }
848
- return getInterpolatedNode(image, location, $.context);
849
- }
850
- },
851
- */
852
- { ALT: () => $.SUBRULE($.attributeSelector, { ARGS: [ctx] }) },
853
- /** Supports keyframes value */
854
- { ALT: () => $.CONSUME(T.DimensionInt) },
855
- { ALT: () => $.CONSUME(T.DimensionNum) }
856
- ];
857
-
858
- let selector = $.OR(selectorAlt);
859
-
860
- if ($.isToken(selector)) {
861
- if (selector.tokenType.name === 'Ampersand') {
862
- const value = getAmpersandTemplateValue(selector.image);
863
- return new Ampersand({ appendValue: value }, undefined, $.getLocationInfo(selector), $.context);
864
- }
865
- if (
866
- selector.tokenType.name === 'InterpolatedSelector'
867
- || selector.tokenType.name === 'InterpolatedIdent'
868
- ) {
869
- // Create an InterpolatedSelector wrapper for interpolated value
870
- let nameValue = selector.image;
871
- let interpolatedNode = getInterpolatedNode(nameValue, $.getLocationInfo(selector), $.context);
872
-
873
- return new InterpolatedSelector(interpolatedNode, undefined, $.getLocationInfo(selector), $.context);
874
- }
875
- return new BasicSelector(selector.image, undefined, $.getLocationInfo(selector), $.context);
876
- }
877
- const result: Node = selector;
878
- return result;
879
- };
880
- }
881
-
882
- export function anonymousMixinDefinition(this: P, T: TokenMap) {
883
- const $ = this;
884
- return (ctx: RuleContext = {}) => {
885
- $.startRule();
886
- let params: List | undefined;
887
- let anonToken: IToken | undefined;
888
- $.OPTION(() => {
889
- anonToken = $.CONSUME(T.AnonMixinStart);
890
- $.OPTION2(() => {
891
- params = $.SUBRULE($.mixinArgList, { ARGS: [{ ...ctx, isDefinition: true }] });
892
- });
893
- $.CONSUME(T.RParen);
894
- });
895
- let rules = $.SUBRULE($.wrappedDeclarationList, { ARGS: [ctx] });
896
-
897
- if ($.RECORDING_PHASE) {
898
- return;
899
- }
900
-
901
- // Set rulesVisibility for detached rulesets based on leakyScope
902
- // Less, for whatever reason, has slightly different lookup rules for
903
- // "detached rulesets".
904
-
905
- // Parse as Anonymous mixin
906
- if (!rules.options.rulesVisibility) {
907
- rules.options.rulesVisibility = {};
908
- }
909
- if ($.leakyScope) {
910
- rules.options.rulesVisibility.Mixin = 'public';
911
- rules.options.rulesVisibility.VarDeclaration = 'private';
912
- } else {
913
- rules.options.rulesVisibility.Mixin = 'private';
914
- rules.options.rulesVisibility.VarDeclaration = 'private';
915
- }
916
-
917
- if (!anonToken) {
918
- /** To Less, this is a "detached ruleset" */
919
- // Check if this should be parsed as Collection or Rules
920
- const shouldBeCollection = (() => {
921
- let properties: Declaration[] = [];
922
- for (const node of rules.rules) {
923
- if (node.type === 'Declaration') {
924
- properties.push(node);
925
- } else if (node.type === 'Comment' || node.type === 'VarDeclaration') {
926
- continue;
927
- } else {
928
- /** Not a valid collection, parse as anonymous mixin */
929
- return false;
930
- }
931
- }
932
-
933
- if (properties.length === 0) {
934
- /** If just var declarations and/or comments, parse as collection */
935
- return true;
936
- }
937
-
938
- const validPropertyCount = properties.filter((decl) => {
939
- const { name } = decl;
940
- const propName = typeof name === 'string' ? name : name?.valueOf();
941
- if (!propName) {
942
- return false;
943
- }
944
- // Skip custom properties (--*)
945
- if (propName.startsWith('--')) {
946
- return true; // Custom properties are always valid
947
- }
948
- return all.includes(propName);
949
- }).length;
950
-
951
- // Majority means more than 50%
952
- const majorityValid = validPropertyCount > properties.length / 2;
953
- /** If this looks like mostly CSS properties, parse as mixin instead */
954
- return !majorityValid;
955
- })();
956
-
957
- const usage = (ctx as RuleContext).detachedRulesetUsage ?? 'none';
958
- const forceMixinForDynamicUsage =
959
- usage === 'function-arg'
960
- || usage === 'mixin-arg'
961
- || usage === 'default-param';
962
- const shouldBeCollectionFinal = shouldBeCollection && !forceMixinForDynamicUsage;
963
- if (shouldBeCollectionFinal) {
964
- return new Collection(rules.rules, rules.options, $.endRule(), $.context);
965
- }
966
- }
967
-
968
- // If anonToken exists, it's an anonymous mixin with (optional) parameters, return as Mixin
969
- return new Mixin({ params, rules: rules.rules }, undefined, $.endRule(), $.context);
970
- };
971
- }
972
-
973
- /**
974
- * Mostly copied from css importAtRule, but it maps
975
- * differently to Jess nodes depending on if it's meant
976
- * to be a Jess-style import or just an at-rule
977
- */
978
- export function importAtRule(this: P, T: TokenMap) {
979
- const $ = this;
980
- return (ctx: RuleContext = {}) => {
981
- const isCssUrl = (url: string, options: string[]) => {
982
- if (options.includes('inline')) {
983
- return false;
984
- }
985
- const lower = url.toLowerCase();
986
- const forcedLess = options.includes('less');
987
- if (forcedLess) {
988
- return false;
989
- }
990
- if (options.includes('css')) {
991
- return true;
992
- }
993
- if (/\.css([?#].*)?$/.test(lower)) {
994
- return true;
995
- }
996
- if (/\.less([?#].*)?$/.test(lower)) {
997
- return false;
998
- }
999
- // Remote imports default to CSS.
1000
- if (lower.startsWith('http://') || lower.startsWith('https://') || lower.startsWith('//')) {
1001
- return true;
1002
- }
1003
- return false;
1004
- };
1005
-
1006
- $.startRule();
1007
-
1008
- let name = $.CONSUME(T.AtImport);
1009
-
1010
- let options: string[] = [];
1011
-
1012
- $.OPTION(() => {
1013
- $.CONSUME(T.LParen);
1014
- $.AT_LEAST_ONE_SEP({
1015
- SEP: T.Comma,
1016
- DEF: () => {
1017
- let opt = $.CONSUME(T.PlainIdent);
1018
- options.push(opt.image);
1019
- }
1020
- });
1021
- $.CONSUME(T.RParen);
1022
- });
1023
-
1024
- let urlNode: Quoted | Url = $.OR([
1025
- { ALT: () => $.SUBRULE($.urlFunction, { ARGS: [ctx] }) },
1026
- { ALT: () => $.SUBRULE($.string, { ARGS: [ctx] }) }
1027
- ]);
1028
-
1029
- let extraNodes: Node[] | undefined;
1030
- $.OPTION2(() => {
1031
- extraNodes = $.SUBRULE($.importPostlude, { ARGS: [{}] });
1032
- });
1033
-
1034
- $.CONSUME(T.Semi);
1035
-
1036
- if (!$.RECORDING_PHASE) {
1037
- let isAtRule: boolean | undefined;
1038
- let postludeNode: Node | undefined;
1039
-
1040
- let url = urlNode.valueOf();
1041
- isAtRule = isCssUrl(url, options);
1042
-
1043
- let preludeNodes: Node[] = [urlNode];
1044
-
1045
- if (extraNodes && extraNodes.length) {
1046
- if (isAtRule) {
1047
- isAtRule = true;
1048
- for (const n of extraNodes) {
1049
- preludeNodes.push(n);
1050
- }
1051
- } else {
1052
- const postludeLoc = $.getLocationFromNodes(extraNodes);
1053
- postludeNode = new Sequence(extraNodes, undefined, postludeLoc, $.context);
1054
- }
1055
- }
1056
-
1057
- let location = $.endRule();
1058
- if (isAtRule) {
1059
- const prelude = new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), $.context);
1060
- const atRule = new AtRuleStatement({
1061
- name: name.image,
1062
- prelude: prelude
1063
- }, undefined, location, $.context);
1064
- return atRule;
1065
- }
1066
-
1067
- return new StyleImport({
1068
- path: urlNode
1069
- }, {
1070
- type: 'import',
1071
- importOptions: {
1072
- type: options.includes('less') ? 'less' : undefined,
1073
- reference: options.includes('reference'),
1074
- once: !options.includes('multiple'),
1075
- multiple: options.includes('multiple'),
1076
- optional: options.includes('optional'),
1077
- inline: options.includes('inline'),
1078
- postlude: postludeNode
1079
- }
1080
- }, location, $.context);
1081
- }
1082
- };
1083
- }
1084
-
1085
- /** Less variables */
1086
- export function varDeclarationOrCall(this: P, T: TokenMap) {
1087
- const $ = this;
1088
- return (ctx: RuleContext = {}) => {
1089
- $.startRule();
1090
-
1091
- let name = $.SUBRULE($.varName, { ARGS: [{}] });
1092
- let value: Node | undefined;
1093
- let args: List | undefined;
1094
- let important: IToken | undefined;
1095
-
1096
- $.OR([
1097
- {
1098
- /**
1099
- * This is a variable declaration
1100
- * Disallows `@atrule :foo;` because it resembles a pseudo-selector
1101
- */
1102
- ALT: () => {
1103
- $.CONSUME(T.Colon);
1104
- return $.OR2([
1105
- {
1106
- GATE: () => {
1107
- const type = $.LA(1).tokenType;
1108
- return type === T.AnonMixinStart || type === T.LCurly;
1109
- },
1110
- ALT: () => {
1111
- value = $.SUBRULE($.anonymousMixinDefinition, { ARGS: [ctx] });
1112
- $.OPTION(() => $.CONSUME(T.Semi));
1113
- return value;
1114
- }
1115
- },
1116
- {
1117
- GATE: () => {
1118
- const type = $.LA(1).tokenType;
1119
- return type !== T.AnonMixinStart && type !== T.LCurly;
1120
- },
1121
- ALT: () => {
1122
- value = $.SUBRULE($.valueList, { ARGS: [{ ...ctx, allowMixinCallWithoutAccessor: true }] });
1123
- $.OPTION2(() => {
1124
- important = $.CONSUME(T.Important);
1125
- });
1126
- return value;
1127
- }
1128
- }
1129
- ]);
1130
- }
1131
- },
1132
- /** This is a variable call. Allow optional whitespace between name and (. */
1133
- {
1134
- GATE: () => $.isType(T.LParen),
1135
- /**
1136
- * This is a change from Less 1.x-4.x
1137
- * e.g.
1138
- * ```
1139
- * @dr: #(@var1, @var2) {
1140
- * // ...
1141
- * }
1142
- * @dr(arg1, arg2);
1143
- */
1144
- ALT: () => {
1145
- args = $.SUBRULE($.mixinArgs, { ARGS: [ctx] });
1146
- return args;
1147
- }
1148
- }
1149
- ]);
1150
-
1151
- let location = $.endRule();
1152
- if ($.RECORDING_PHASE) {
1153
- return;
1154
- }
1155
- let nameVal = getInterpolatedOrString(name!.image);
1156
- let nameNode: Any | Interpolated;
1157
- if (!(nameVal instanceof Interpolated)) {
1158
- nameNode = new Any(nameVal, { role: 'ident' }, $.getLocationInfo(name!), $.context);
1159
- } else {
1160
- nameNode = nameVal;
1161
- }
1162
- /** An anonymous mixin call */
1163
- if (!value) {
1164
- // When @variable() is called, look up the variable first
1165
- // The variable's value (which may be a Call node) will be executed
1166
- const nameRef = new Reference({ key: nameNode }, { type: 'variable', role: 'name' });
1167
- // Pass markImportant in options if !important is present
1168
- const callOptions = important ? { markImportant: true } : undefined;
1169
- const callNode = new Call({ name: nameRef, args }, callOptions, location, $.context);
1170
- // Clear important since it's now on the Call
1171
- if (important) {
1172
- important = undefined;
1173
- }
1174
- // Variable calls are expressions at the outermost level (but not parenthesized).
1175
- // e.g. `$media()`, NOT `$(media())`
1176
- return new Expression(callNode, undefined, location, $.context);
1177
- }
1178
-
1179
- // If the value is a Call node and we have !important, set markImportant on the Call
1180
- // instead of on the VarDeclaration (mixin call semantics)
1181
- if (important && value instanceof Call) {
1182
- value.options = value.options || {};
1183
- value.options.markImportant = true;
1184
- important = undefined;
1185
- }
1186
-
1187
- if (value && isLegacySelectorLikeValue(value)) {
1188
- const varName = String(nameNode.valueOf());
1189
- $.warnDeprecation(
1190
- `Unquoted selector capture in '${varName}' is no longer supported. Use '*[ ... ]' (e.g. ${varName}: *[.a, .b]).`,
1191
- $.LA(1),
1192
- 'unquoted-selector-capture'
1193
- );
1194
- }
1195
-
1196
- return new VarDeclaration({
1197
- name: nameNode instanceof Any ? String(nameNode.valueOf()) : nameNode,
1198
- value: value,
1199
- important: important ? new Any(important.image, { role: 'flag' }, $.getLocationInfo(important), $.context) : undefined
1200
- }, undefined, location, $.context);
1201
- };
1202
- }
1203
-
1204
- export function selectorCapture(this: P, T: TokenMap) {
1205
- const $ = this;
1206
- return (ctx: RuleContext = {}) => {
1207
- $.startRule();
1208
- $.CONSUME(T.Star);
1209
- // Use $.OR with a gate for a positive assertion
1210
- const selector = $.OR([
1211
- {
1212
- GATE: $.noSep.bind($),
1213
- ALT: () => {
1214
- $.CONSUME(T.LSquare);
1215
- const selector = $.SUBRULE($.forgivingSelectorList, { ARGS: [{ ...ctx, inner: true }] });
1216
- $.CONSUME(T.RSquare);
1217
- return selector;
1218
- }
1219
- }
1220
- ]);
1221
-
1222
- const location = $.endRule();
1223
- if ($.RECORDING_PHASE) {
1224
- return;
1225
- }
1226
- return new SelectorCapture(
1227
- selector,
1228
- undefined,
1229
- location,
1230
- $.context
1231
- );
1232
- };
1233
- }
1234
-
1235
- export function valueSequence(this: P, _T: TokenMap): ProductionRule {
1236
- const $ = this;
1237
- return (ctx: RuleContext = {}) => {
1238
- const RECORDING_PHASE = $.RECORDING_PHASE;
1239
- $.startRule();
1240
- let nodes: Node[] | undefined;
1241
- if (!RECORDING_PHASE) {
1242
- nodes = [];
1243
- }
1244
-
1245
- {
1246
- const exprCtx: RuleContext = { ...ctx, wrapInExpression: true };
1247
- let value = $.SUBRULE2($.expressionSum, { ARGS: [exprCtx] });
1248
- if (!RECORDING_PHASE) {
1249
- value = wrapOuterExpressionIfNeeded.call($, value, exprCtx);
1250
- nodes!.push(value);
1251
- }
1252
- }
1253
-
1254
- $.MANY(() => {
1255
- const exprCtx: RuleContext = { ...ctx, wrapInExpression: true };
1256
- let value = $.SUBRULE3($.expressionSum, { ARGS: [exprCtx] });
1257
- if (!RECORDING_PHASE) {
1258
- value = wrapOuterExpressionIfNeeded.call($, value, exprCtx);
1259
- nodes!.push(value);
1260
- }
1261
- });
1262
-
1263
- if (RECORDING_PHASE) {
1264
- return;
1265
- }
1266
-
1267
- let location = $.endRule();
1268
- if (nodes!.length === 1) {
1269
- const single = nodes![0]!;
1270
- return single;
1271
- }
1272
- const seq = new Sequence(nodes!, undefined, location, $.context);
1273
- return seq;
1274
- };
1275
- }
1276
-
1277
- export function squareValue(this: P, T: TokenMap) {
1278
- const $ = this;
1279
- return (ctx: RuleContext = {}) => {
1280
- $.startRule();
1281
- $.CONSUME(T.LSquare);
1282
- let node: Node = $.OR([
1283
- {
1284
- GATE: () => !$.looseMode,
1285
- ALT: () => {
1286
- let ident = $.CONSUME(T.Ident);
1287
- return new Any(ident.image, { role: 'ident' }, $.getLocationInfo(ident), $.context);
1288
- }
1289
- },
1290
- {
1291
- GATE: () => !!$.looseMode,
1292
- ALT: () => {
1293
- let nodes: Node[] = [];
1294
- $.MANY(() => {
1295
- let node = $.SUBRULE($.anyInnerValue, { ARGS: [ctx] });
1296
- const wrapped = node;
1297
- nodes.push(wrapped);
1298
- });
1299
- const seq = new Sequence(nodes, undefined, $.getLocationFromNodes(nodes), $.context);
1300
- return seq;
1301
- }
1302
- }
1303
- ]);
1304
- $.CONSUME(T.RSquare);
1305
- let location = $.endRule();
1306
- const blk = new Block(node, { type: 'square' }, location, $.context);
1307
- return blk;
1308
- };
1309
- }