Skip to content

Commit ba298b6

Browse files
authored
Auto merge of #338 - servo:nesting, r=tiaanl
[css-nesting] Remove DeclarationListParser. For nesting, we need to parse declaration and nested rules together. Rather than having DeclarationListParser, organize the parsing code in two: * StyleSheetParser for the top level stylesheet, which has the relevant hacks and so on. * RuleBodyParser which does potentially everything else. For that, introduce a new trait that agglomerates all the nesting needs (so potentially all of at-rule + qualified-rule + declaration parsing). While at it, change the API a little bit so that these take mutable references, so that we can avoid some copying.
2 parents da24523 + bcbc543 commit ba298b6

File tree

4 files changed

+121
-110
lines changed

4 files changed

+121
-110
lines changed

src/css-parsing-tests/declaration_list.json

+15-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,21 @@
3535
],
3636

3737
"@ media screen { div{;}} a:b;; @media print{div{", [
38-
["error", "invalid"],
38+
["qualified rule",
39+
["@", " ", ["ident", "media" ], " ", [ "ident", "screen" ], " " ],
40+
[
41+
" ",
42+
[
43+
"ident",
44+
"div"
45+
],
46+
[
47+
"{}",
48+
";"
49+
]
50+
]
51+
],
52+
["declaration", "a", [["ident", "b"]], false],
3953
["at-rule", "media", [" ", ["ident", "print"]], [["ident", "div"], ["{}"]]]
4054
],
4155

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ pub use crate::nth::parse_nth;
8282
pub use crate::parser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
8383
pub use crate::parser::{Delimiter, Delimiters, Parser, ParserInput, ParserState};
8484
pub use crate::rules_and_declarations::{parse_important, parse_one_declaration};
85-
pub use crate::rules_and_declarations::{parse_one_rule, RuleListParser};
85+
pub use crate::rules_and_declarations::{parse_one_rule, StyleSheetParser};
8686
pub use crate::rules_and_declarations::{AtRuleParser, QualifiedRuleParser};
87-
pub use crate::rules_and_declarations::{DeclarationListParser, DeclarationParser};
87+
pub use crate::rules_and_declarations::{RuleBodyParser, RuleBodyItemParser, DeclarationParser};
8888
pub use crate::serializer::{serialize_identifier, serialize_name, serialize_string};
8989
pub use crate::serializer::{CssStringWriter, ToCss, TokenSerializationType};
9090
pub use crate::tokenizer::{SourceLocation, SourcePosition, Token};

src/rules_and_declarations.rs

+86-98
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
// https://drafts.csswg.org/css-syntax/#parsing
66

7-
use super::{BasicParseError, BasicParseErrorKind, Delimiter};
8-
use super::{ParseError, Parser, Token};
7+
use super::{BasicParseError, BasicParseErrorKind, Delimiter, Delimiters, ParseError, Parser, Token};
98
use crate::cow_rc_str::CowRcStr;
109
use crate::parser::{parse_nested_block, parse_until_after, parse_until_before, ParserState};
1110

@@ -50,14 +49,9 @@ pub trait DeclarationParser<'i> {
5049
&mut self,
5150
name: CowRcStr<'i>,
5251
input: &mut Parser<'i, 't>,
53-
) -> Result<Self::Declaration, ParseError<'i, Self::Error>>;
54-
55-
/// Whether to try to parse qualified rules along with declarations. See
56-
/// <https://github.com/w3c/csswg-drafts/issues/7961> for the current state of the discussion.
57-
/// This is a low effort opt-in to be able to experiment with it, but it's likely to be needed
58-
/// when nesting is less experimental as well (e.g., you probably don't want to allow nesting
59-
/// in a style attribute anyways).
60-
fn enable_nesting(&self) -> bool { false }
52+
) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
53+
Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name))))
54+
}
6155
}
6256

6357
/// A trait to provide various parsing of at-rules.
@@ -99,8 +93,6 @@ pub trait AtRuleParser<'i> {
9993
name: CowRcStr<'i>,
10094
input: &mut Parser<'i, 't>,
10195
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
102-
let _ = name;
103-
let _ = input;
10496
Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
10597
}
10698

@@ -140,7 +132,6 @@ pub trait AtRuleParser<'i> {
140132
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
141133
let _ = prelude;
142134
let _ = start;
143-
let _ = input;
144135
Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))
145136
}
146137
}
@@ -178,7 +169,6 @@ pub trait QualifiedRuleParser<'i> {
178169
&mut self,
179170
input: &mut Parser<'i, 't>,
180171
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
181-
let _ = input;
182172
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
183173
}
184174

@@ -197,24 +187,35 @@ pub trait QualifiedRuleParser<'i> {
197187
) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
198188
let _ = prelude;
199189
let _ = start;
200-
let _ = input;
201190
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
202191
}
203192
}
204193

205-
/// Provides an iterator for declaration list parsing.
206-
pub struct DeclarationListParser<'i, 't, 'a, P> {
207-
/// The input given to `DeclarationListParser::new`
194+
/// Provides an iterator for rule bodies and declaration lists.
195+
pub struct RuleBodyParser<'i, 't, 'a, P, I, E> {
196+
/// The input given to the parser.
208197
pub input: &'a mut Parser<'i, 't>,
209-
210198
/// The parser given to `DeclarationListParser::new`
211-
pub parser: P,
199+
pub parser: &'a mut P,
200+
201+
_phantom: std::marker::PhantomData<(I, E)>,
212202
}
213203

214-
impl<'i, 't, 'a, I, P, E: 'i> DeclarationListParser<'i, 't, 'a, P>
215-
where
216-
P: DeclarationParser<'i, Declaration = I, Error = E> + AtRuleParser<'i, AtRule = I, Error = E>,
204+
/// A parser for a rule body item.
205+
pub trait RuleBodyItemParser<'i, DeclOrRule, Error: 'i>:
206+
DeclarationParser<'i, Declaration = DeclOrRule, Error = Error>
207+
+ QualifiedRuleParser<'i, QualifiedRule = DeclOrRule, Error = Error>
208+
+ AtRuleParser<'i, AtRule = DeclOrRule, Error = Error>
217209
{
210+
/// Whether we should attempt to parse declarations. If you know you won't, returning false
211+
/// here is slightly faster.
212+
fn parse_declarations(&self) -> bool;
213+
/// Whether we should attempt to parse qualified rules. If you know you won't, returning false
214+
/// would be slightly faster.
215+
fn parse_qualified(&self) -> bool;
216+
}
217+
218+
impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> {
218219
/// Create a new `DeclarationListParser` for the given `input` and `parser`.
219220
///
220221
/// Note that all CSS declaration lists can on principle contain at-rules.
@@ -229,55 +230,69 @@ where
229230
/// The return type for finished declarations and at-rules also needs to be the same,
230231
/// since `<DeclarationListParser as Iterator>::next` can return either.
231232
/// It could be a custom enum.
232-
pub fn new(input: &'a mut Parser<'i, 't>, parser: P) -> Self {
233-
DeclarationListParser { input, parser }
233+
pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
234+
Self {
235+
input,
236+
parser,
237+
_phantom: std::marker::PhantomData,
238+
}
234239
}
235240
}
236241

237242
/// `DeclarationListParser` is an iterator that yields `Ok(_)` for a valid declaration or at-rule
238243
/// or `Err(())` for an invalid one.
239-
impl<'i, 't, 'a, I, P, E: 'i> Iterator for DeclarationListParser<'i, 't, 'a, P>
244+
impl<'i, 't, 'a, I, P, E: 'i> Iterator for RuleBodyParser<'i, 't, 'a, P, I, E>
240245
where
241-
P: DeclarationParser<'i, Declaration = I, Error = E>
242-
+ AtRuleParser<'i, AtRule = I, Error = E>
243-
+ QualifiedRuleParser<'i, QualifiedRule = I, Error = E>,
246+
P: RuleBodyItemParser<'i, I, E>,
244247
{
245248
type Item = Result<I, (ParseError<'i, E>, &'i str)>;
246249

247250
fn next(&mut self) -> Option<Self::Item> {
248251
loop {
249252
let start = self.input.state();
250-
match self.input.next_including_whitespace_and_comments() {
251-
Ok(&Token::WhiteSpace(_)) | Ok(&Token::Comment(_)) | Ok(&Token::Semicolon) => {
252-
continue
253+
match self.input.next_including_whitespace_and_comments().ok()? {
254+
Token::WhiteSpace(_) | Token::Comment(_) | Token::Semicolon => {
255+
continue;
253256
}
254-
Ok(&Token::Ident(ref name)) => {
257+
Token::Ident(ref name) if self.parser.parse_declarations() => {
255258
let name = name.clone();
259+
let parse_qualified = self.parser.parse_qualified();
260+
let delimiters = if parse_qualified {
261+
Delimiter::Semicolon | Delimiter::CurlyBracketBlock
262+
} else {
263+
Delimiter::Semicolon
264+
};
256265
let mut result = {
257266
let parser = &mut self.parser;
258-
parse_until_after(self.input, Delimiter::Semicolon, |input| {
267+
parse_until_after(self.input, delimiters, |input| {
259268
input.expect_colon()?;
260269
parser.parse_value(name, input)
261270
})
262271
};
263272

264-
if result.is_err() && self.parser.enable_nesting() {
273+
if result.is_err() && parse_qualified {
265274
self.input.reset(&start);
266-
result = parse_qualified_rule(&start, self.input, &mut self.parser);
275+
result =
276+
parse_qualified_rule(&start, self.input, &mut *self.parser, delimiters);
267277
}
268278

269279
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
270280
}
271-
Ok(&Token::AtKeyword(ref name)) => {
281+
Token::AtKeyword(ref name) => {
272282
let name = name.clone();
273-
return Some(parse_at_rule(&start, name, self.input, &mut self.parser));
283+
return Some(parse_at_rule(&start, name, self.input, &mut *self.parser));
274284
}
275-
Ok(token) => {
276-
let result = if self.parser.enable_nesting() {
285+
token => {
286+
let result = if self.parser.parse_qualified() {
277287
self.input.reset(&start);
278-
// XXX do we need to, if we fail, consume only until the next semicolon,
279-
// rather than until the next `{`?
280-
parse_qualified_rule(&start, self.input, &mut self.parser)
288+
// TODO(emilio, nesting): do we need to, if we fail, consume only until the
289+
// next semicolon, rather than until the next `{`?
290+
parse_qualified_rule(
291+
&start,
292+
self.input,
293+
&mut *self.parser,
294+
Delimiter::CurlyBracketBlock,
295+
)
281296
} else {
282297
let token = token.clone();
283298
self.input.parse_until_after(Delimiter::Semicolon, |_| {
@@ -286,66 +301,44 @@ where
286301
};
287302
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
288303
}
289-
Err(..) => return None,
290304
}
291305
}
292306
}
293307
}
294308

295-
/// Provides an iterator for rule list parsing.
296-
pub struct RuleListParser<'i, 't, 'a, P> {
297-
/// The input given to `RuleListParser::new`
309+
/// Provides an iterator for rule list parsing at the top-level of a stylesheet.
310+
pub struct StyleSheetParser<'i, 't, 'a, P> {
311+
/// The input given.
298312
pub input: &'a mut Parser<'i, 't>,
299313

300-
/// The parser given to `RuleListParser::new`
301-
pub parser: P,
314+
/// The parser given.
315+
pub parser: &'a mut P,
302316

303-
is_stylesheet: bool,
304317
any_rule_so_far: bool,
305318
}
306319

307-
impl<'i, 't, 'a, R, P, E: 'i> RuleListParser<'i, 't, 'a, P>
320+
impl<'i, 't, 'a, R, P, E: 'i> StyleSheetParser<'i, 't, 'a, P>
308321
where
309322
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
310323
+ AtRuleParser<'i, AtRule = R, Error = E>,
311324
{
312-
/// Create a new `RuleListParser` for the given `input` at the top-level of a stylesheet
313-
/// and the given `parser`.
314-
///
315325
/// The given `parser` needs to implement both `QualifiedRuleParser` and `AtRuleParser` traits.
316-
/// However, either of them can be an empty `impl`
317-
/// since the traits provide default implementations of their methods.
326+
/// However, either of them can be an empty `impl` since the traits provide default
327+
/// implementations of their methods.
318328
///
319329
/// The return type for finished qualified rules and at-rules also needs to be the same,
320-
/// since `<RuleListParser as Iterator>::next` can return either.
321-
/// It could be a custom enum.
322-
pub fn new_for_stylesheet(input: &'a mut Parser<'i, 't>, parser: P) -> Self {
323-
RuleListParser {
330+
/// since `<RuleListParser as Iterator>::next` can return either. It could be a custom enum.
331+
pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
332+
Self {
324333
input,
325334
parser,
326-
is_stylesheet: true,
327-
any_rule_so_far: false,
328-
}
329-
}
330-
331-
/// Same is `new_for_stylesheet`, but should be used for rule lists inside a block
332-
/// such as the body of an `@media` rule.
333-
///
334-
/// This differs in that `<!--` and `-->` tokens
335-
/// should only be ignored at the stylesheet top-level.
336-
/// (This is to deal with legacy workarounds for `<style>` HTML element parsing.)
337-
pub fn new_for_nested_rule(input: &'a mut Parser<'i, 't>, parser: P) -> Self {
338-
RuleListParser {
339-
input,
340-
parser,
341-
is_stylesheet: false,
342335
any_rule_so_far: false,
343336
}
344337
}
345338
}
346339

347340
/// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or `Err(())` for an invalid one.
348-
impl<'i, 't, 'a, R, P, E: 'i> Iterator for RuleListParser<'i, 't, 'a, P>
341+
impl<'i, 't, 'a, R, P, E: 'i> Iterator for StyleSheetParser<'i, 't, 'a, P>
349342
where
350343
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
351344
+ AtRuleParser<'i, AtRule = R, Error = E>,
@@ -354,13 +347,8 @@ where
354347

355348
fn next(&mut self) -> Option<Self::Item> {
356349
loop {
357-
if self.is_stylesheet {
358-
self.input.skip_cdc_and_cdo()
359-
} else {
360-
self.input.skip_whitespace()
361-
}
350+
self.input.skip_cdc_and_cdo();
362351
let start = self.input.state();
363-
364352
let at_keyword = match self.input.next_byte()? {
365353
b'@' => match self.input.next_including_whitespace_and_comments() {
366354
Ok(&Token::AtKeyword(ref name)) => Some(name.clone()),
@@ -373,7 +361,7 @@ where
373361
};
374362

375363
if let Some(name) = at_keyword {
376-
let first_stylesheet_rule = self.is_stylesheet && !self.any_rule_so_far;
364+
let first_stylesheet_rule = !self.any_rule_so_far;
377365
self.any_rule_so_far = true;
378366
if first_stylesheet_rule && name.eq_ignore_ascii_case("charset") {
379367
let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
@@ -384,12 +372,17 @@ where
384372
&start,
385373
name.clone(),
386374
self.input,
387-
&mut self.parser,
375+
&mut *self.parser,
388376
));
389377
}
390378
} else {
391379
self.any_rule_so_far = true;
392-
let result = parse_qualified_rule(&start, self.input, &mut self.parser);
380+
let result = parse_qualified_rule(
381+
&start,
382+
self.input,
383+
&mut *self.parser,
384+
Delimiter::CurlyBracketBlock,
385+
);
393386
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
394387
}
395388
}
@@ -441,7 +434,7 @@ where
441434
if let Some(name) = at_keyword {
442435
parse_at_rule(&start, name, input, parser).map_err(|e| e.0)
443436
} else {
444-
parse_qualified_rule(&start, input, parser)
437+
parse_qualified_rule(&start, input, parser, Delimiter::CurlyBracketBlock)
445438
}
446439
})
447440
}
@@ -485,19 +478,14 @@ fn parse_qualified_rule<'i, 't, P, E>(
485478
start: &ParserState,
486479
input: &mut Parser<'i, 't>,
487480
parser: &mut P,
481+
delimiters: Delimiters,
488482
) -> Result<<P as QualifiedRuleParser<'i>>::QualifiedRule, ParseError<'i, E>>
489483
where
490484
P: QualifiedRuleParser<'i, Error = E>,
491485
{
492-
let prelude = parse_until_before(input, Delimiter::CurlyBracketBlock, |input| {
493-
parser.parse_prelude(input)
494-
});
495-
match *input.next()? {
496-
Token::CurlyBracketBlock => {
497-
// Do this here so that we consume the `{` even if the prelude is `Err`.
498-
let prelude = prelude?;
499-
parse_nested_block(input, |input| parser.parse_block(prelude, &start, input))
500-
}
501-
_ => unreachable!(),
502-
}
486+
let prelude = parse_until_before(input, delimiters, |input| parser.parse_prelude(input));
487+
input.expect_curly_bracket_block()?;
488+
// Do this here so that we consume the `{` even if the prelude is `Err`.
489+
let prelude = prelude?;
490+
parse_nested_block(input, |input| parser.parse_block(prelude, &start, input))
503491
}

0 commit comments

Comments
 (0)