Skip to content

Commit f84c141

Browse files
committed
feat: improve the grammar
1 parent 9068aec commit f84c141

File tree

8 files changed

+23692
-14481
lines changed

8 files changed

+23692
-14481
lines changed

grammar.js

Lines changed: 177 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,48 @@ module.exports = grammar(CSS, {
7575
optional(alias($.pseudo_class_arguments, $.arguments)),
7676
),
7777

78+
pseudo_element_selector: $ => seq(
79+
optional($._selector),
80+
'::',
81+
alias(choice($.identifier, $._concatenated_identifier), $.tag_name),
82+
optional(alias($.pseudo_element_arguments, $.arguments)),
83+
),
84+
85+
id_selector: $ => seq(
86+
optional($._selector),
87+
'#',
88+
alias(choice($.identifier, $._concatenated_identifier), $.id_name),
89+
),
90+
91+
attribute_selector: $ => seq(
92+
optional($._selector),
93+
'[',
94+
alias(choice($.identifier, $._concatenated_identifier, $.namespace_selector), $.attribute_name),
95+
optional(seq(
96+
choice('=', '~=', '^=', '|=', '*=', '$='),
97+
$._value,
98+
)),
99+
']',
100+
),
101+
102+
child_selector: $ => prec.left(seq(
103+
optional($._selector),
104+
'>',
105+
$._selector,
106+
)),
107+
108+
sibling_selector: $ => prec.left(seq(
109+
optional($._selector),
110+
'~',
111+
$._selector,
112+
)),
113+
114+
adjacent_sibling_selector: $ => prec.left(seq(
115+
optional($._selector),
116+
'+',
117+
$._selector,
118+
)),
119+
78120
// Declarations
79121

80122
declaration: $ => seq(
@@ -89,6 +131,8 @@ module.exports = grammar(CSS, {
89131
';',
90132
),
91133

134+
default: _ => '!default',
135+
92136
// Media queries
93137

94138
_query: ($, original) => choice(
@@ -104,13 +148,71 @@ module.exports = grammar(CSS, {
104148
$.nesting_selector,
105149
$._concatenated_identifier,
106150
$.list_value,
151+
$.map_value,
152+
$.unary_expression,
153+
$.default,
107154
)),
108155
$.variable,
109156
),
110157

111-
use_statement: $ => seq('@use', $._value, ';'),
158+
string_value: $ => choice(
159+
seq(
160+
'\'',
161+
repeat(choice(
162+
alias(/([^#'\n]|\\(.|\n)|\#[^{])+/, $.string_content),
163+
$.interpolation,
164+
)),
165+
'\'',
166+
),
167+
seq(
168+
'"',
169+
repeat(choice(
170+
alias(/([^#"\n]|\\(.|\n)|\#[^{])+/, $.string_content),
171+
$.interpolation,
172+
)),
173+
'"',
174+
),
175+
),
176+
177+
use_statement: $ => seq(
178+
'@use',
179+
$._value,
180+
optional($.as_clause),
181+
optional($.with_clause),
182+
';',
183+
),
184+
185+
as_clause: $ => seq('as', choice('*', $.identifier, $.plain_value)),
186+
187+
with_clause: $ => seq('with', $.with_parameters),
188+
189+
with_parameters: $ => seq(
190+
'(',
191+
sep1(
192+
',',
193+
seq(
194+
$.variable,
195+
':',
196+
$._value,
197+
optional($.default),
198+
),
199+
),
200+
optional(','),
201+
')',
202+
),
203+
204+
visibility_clause: $ => seq(choice('hide', 'show'), $.visibility_parameters),
112205

113-
forward_statement: $ => seq('@forward', $._value, ';'),
206+
visibility_parameters: $ => sep1(',', $.identifier),
207+
208+
forward_statement: $ => seq(
209+
'@forward',
210+
$._value,
211+
optional($.as_clause),
212+
optional($.visibility_clause),
213+
optional($.with_clause),
214+
';',
215+
),
114216

115217
mixin_statement: $ => seq(
116218
'@mixin',
@@ -146,12 +248,16 @@ module.exports = grammar(CSS, {
146248

147249
parameters: $ => seq('(', sep1(',', $.parameter), ')'),
148250

149-
parameter: $ => seq(
150-
$.variable,
151-
optional(seq(
152-
':',
153-
field('default', $._value),
154-
)),
251+
parameter: $ => choice(
252+
seq(
253+
$.variable,
254+
optional('...'),
255+
optional(seq(
256+
':',
257+
field('default', $._value),
258+
)),
259+
),
260+
'...',
155261
),
156262

157263
return_statement: $ => seq('@return', $._value, ';'),
@@ -209,22 +315,46 @@ module.exports = grammar(CSS, {
209315
$.arguments,
210316
),
211317

212-
binary_expression: $ => prec.left(seq(
318+
binary_expression: $ => prec.left(1, seq(
213319
$._value,
214-
choice('+', '-', '*', '/', '==', '<', '>', '!=', '<=', '>='),
320+
choice('+', '-', '*', '/', '==', '<', '>', '!=', '<=', '>=', 'and', 'or'),
215321
$._value,
216322
)),
217323

324+
unary_expression: $ => seq(
325+
choice('-', '+', '/', 'not'),
326+
$._value,
327+
),
328+
218329
list_value: $ => seq(
219330
'(',
220331
sep2(',', $._value),
221332
')',
222333
),
223334

335+
map_value: $ => seq(
336+
'(',
337+
sep1(',', seq(
338+
field('key', $._value),
339+
':',
340+
field('value', $._value),
341+
)),
342+
')',
343+
),
344+
224345
interpolation: $ => seq('#{', $._value, '}'),
225346

226347
placeholder: $ => seq('%', $.identifier),
227348

349+
arguments: $ => seq(
350+
token.immediate('('),
351+
sep(
352+
choice(',', ';'),
353+
seq(repeat1($._value), optional('...')),
354+
),
355+
')',
356+
),
357+
228358
_concatenated_identifier: $ => choice(
229359
seq(
230360
$.identifier,
@@ -243,9 +373,46 @@ module.exports = grammar(CSS, {
243373
),
244374

245375
variable: _ => /([a-zA-Z_]+\.)?\$[a-zA-Z-_][a-zA-Z0-9-_]*/,
376+
377+
plain_value: _ => token(seq(
378+
repeat(choice(
379+
/[-_]/,
380+
/\/[^\*\s,;!{}()\[\]]/, // Slash not followed by a '*' (which would be a comment)
381+
)),
382+
/[a-zA-Z]/,
383+
choice(
384+
/[^/\s,:;!{}()\[\]]/, // Not a slash, not a delimiter character (no colon)
385+
/\/[^\*\s,:;!{}()\[\]]/, // Slash not followed by a '*' (which would be a comment) (no colon)
386+
seq(
387+
repeat1(choice(
388+
/[^/\s,;!{}()\[\]]/, // Not a slash, not a delimiter character
389+
/\/[^\*\s,;!{}()\[\]]/, // Slash not followed by a '*' (which would be a comment)
390+
)),
391+
choice(
392+
/[^/\s,:;!{}()\[\]]/, // Not a slash, not a delimiter character (no colon)
393+
/\/[^\*\s,:;!{}()\[\]]/, // Slash not followed by a '*' (which would be a comment) (no colon)
394+
),
395+
),
396+
),
397+
)),
398+
246399
},
247400
});
248401

402+
/**
403+
* Creates a rule to optionally match one or more of the rules separated by `separator`
404+
*
405+
* @param {RuleOrLiteral} separator
406+
*
407+
* @param {RuleOrLiteral} rule
408+
*
409+
* @return {ChoiceRule}
410+
*
411+
*/
412+
function sep(separator, rule) {
413+
return optional(sep1(separator, rule));
414+
}
415+
249416
/**
250417
* Creates a rule to match one or more of the rules separated by `separator`
251418
*

0 commit comments

Comments
 (0)