Skip to content

Commit d94f191

Browse files
committed
[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.
1 parent da24523 commit d94f191

File tree

2 files changed

+58
-85
lines changed

2 files changed

+58
-85
lines changed

src/lib.rs

Lines changed: 2 additions & 2 deletions
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

Lines changed: 56 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,9 @@ pub trait DeclarationParser<'i> {
5050
&mut self,
5151
name: CowRcStr<'i>,
5252
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 }
53+
) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
54+
Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name))))
55+
}
6156
}
6257

6358
/// A trait to provide various parsing of at-rules.
@@ -99,8 +94,6 @@ pub trait AtRuleParser<'i> {
9994
name: CowRcStr<'i>,
10095
input: &mut Parser<'i, 't>,
10196
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
102-
let _ = name;
103-
let _ = input;
10497
Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
10598
}
10699

@@ -140,7 +133,6 @@ pub trait AtRuleParser<'i> {
140133
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
141134
let _ = prelude;
142135
let _ = start;
143-
let _ = input;
144136
Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))
145137
}
146138
}
@@ -178,7 +170,6 @@ pub trait QualifiedRuleParser<'i> {
178170
&mut self,
179171
input: &mut Parser<'i, 't>,
180172
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
181-
let _ = input;
182173
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
183174
}
184175

@@ -197,24 +188,35 @@ pub trait QualifiedRuleParser<'i> {
197188
) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
198189
let _ = prelude;
199190
let _ = start;
200-
let _ = input;
201191
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
202192
}
203193
}
204194

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

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>,
205+
/// A parser for a rule body item.
206+
pub trait RuleBodyItemParser<'i, DeclOrRule, Error: 'i>
207+
: DeclarationParser<'i, Declaration = DeclOrRule, Error = Error>
208+
+ QualifiedRuleParser<'i, QualifiedRule = DeclOrRule, Error = Error>
209+
+ AtRuleParser<'i, AtRule = DeclOrRule, Error = Error>
217210
{
211+
/// Whether we should attempt to parse declarations. If you know you won't, returning false
212+
/// here is slightly faster.
213+
fn parse_declarations(&self) -> bool;
214+
/// Whether we should attempt to parse qualified rules. If you know you won't, returning false
215+
/// would be slightly faster.
216+
fn parse_qualified(&self) -> bool;
217+
}
218+
219+
impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> {
218220
/// Create a new `DeclarationListParser` for the given `input` and `parser`.
219221
///
220222
/// Note that all CSS declaration lists can on principle contain at-rules.
@@ -229,29 +231,27 @@ where
229231
/// The return type for finished declarations and at-rules also needs to be the same,
230232
/// since `<DeclarationListParser as Iterator>::next` can return either.
231233
/// It could be a custom enum.
232-
pub fn new(input: &'a mut Parser<'i, 't>, parser: P) -> Self {
233-
DeclarationListParser { input, parser }
234+
pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
235+
Self { input, parser, _phantom: std::marker::PhantomData }
234236
}
235237
}
236238

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

247247
fn next(&mut self) -> Option<Self::Item> {
248248
loop {
249249
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
250+
match self.input.next_including_whitespace_and_comments().ok()? {
251+
Token::WhiteSpace(_) | Token::Comment(_) | Token::Semicolon => {
252+
continue;
253253
}
254-
Ok(&Token::Ident(ref name)) => {
254+
Token::Ident(ref name) if self.parser.parse_declarations() => {
255255
let name = name.clone();
256256
let mut result = {
257257
let parser = &mut self.parser;
@@ -261,23 +261,23 @@ where
261261
})
262262
};
263263

264-
if result.is_err() && self.parser.enable_nesting() {
264+
if result.is_err() && self.parser.parse_qualified() {
265265
self.input.reset(&start);
266-
result = parse_qualified_rule(&start, self.input, &mut self.parser);
266+
result = parse_qualified_rule(&start, self.input, &mut *self.parser);
267267
}
268268

269269
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
270270
}
271-
Ok(&Token::AtKeyword(ref name)) => {
271+
Token::AtKeyword(ref name) => {
272272
let name = name.clone();
273-
return Some(parse_at_rule(&start, name, self.input, &mut self.parser));
273+
return Some(parse_at_rule(&start, name, self.input, &mut *self.parser));
274274
}
275-
Ok(token) => {
276-
let result = if self.parser.enable_nesting() {
275+
token => {
276+
let result = if self.parser.parse_qualified() {
277277
self.input.reset(&start);
278278
// XXX do we need to, if we fail, consume only until the next semicolon,
279279
// rather than until the next `{`?
280-
parse_qualified_rule(&start, self.input, &mut self.parser)
280+
parse_qualified_rule(&start, self.input, &mut *self.parser)
281281
} else {
282282
let token = token.clone();
283283
self.input.parse_until_after(Delimiter::Semicolon, |_| {
@@ -286,66 +286,44 @@ where
286286
};
287287
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
288288
}
289-
Err(..) => return None,
290289
}
291290
}
292291
}
293292
}
294293

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

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

303-
is_stylesheet: bool,
304302
any_rule_so_far: bool,
305303
}
306304

307-
impl<'i, 't, 'a, R, P, E: 'i> RuleListParser<'i, 't, 'a, P>
305+
impl<'i, 't, 'a, R, P, E: 'i> StyleSheetParser<'i, 't, 'a, P>
308306
where
309307
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
310308
+ AtRuleParser<'i, AtRule = R, Error = E>,
311309
{
312-
/// Create a new `RuleListParser` for the given `input` at the top-level of a stylesheet
313-
/// and the given `parser`.
314-
///
315310
/// 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.
311+
/// However, either of them can be an empty `impl` since the traits provide default
312+
/// implementations of their methods.
318313
///
319314
/// 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 {
324-
input,
325-
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 {
315+
/// since `<RuleListParser as Iterator>::next` can return either. It could be a custom enum.
316+
pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
317+
Self {
339318
input,
340319
parser,
341-
is_stylesheet: false,
342320
any_rule_so_far: false,
343321
}
344322
}
345323
}
346324

347325
/// `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>
326+
impl<'i, 't, 'a, R, P, E: 'i> Iterator for StyleSheetParser<'i, 't, 'a, P>
349327
where
350328
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
351329
+ AtRuleParser<'i, AtRule = R, Error = E>,
@@ -354,13 +332,8 @@ where
354332

355333
fn next(&mut self) -> Option<Self::Item> {
356334
loop {
357-
if self.is_stylesheet {
358-
self.input.skip_cdc_and_cdo()
359-
} else {
360-
self.input.skip_whitespace()
361-
}
335+
self.input.skip_cdc_and_cdo();
362336
let start = self.input.state();
363-
364337
let at_keyword = match self.input.next_byte()? {
365338
b'@' => match self.input.next_including_whitespace_and_comments() {
366339
Ok(&Token::AtKeyword(ref name)) => Some(name.clone()),
@@ -373,7 +346,7 @@ where
373346
};
374347

375348
if let Some(name) = at_keyword {
376-
let first_stylesheet_rule = self.is_stylesheet && !self.any_rule_so_far;
349+
let first_stylesheet_rule = !self.any_rule_so_far;
377350
self.any_rule_so_far = true;
378351
if first_stylesheet_rule && name.eq_ignore_ascii_case("charset") {
379352
let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
@@ -384,13 +357,13 @@ where
384357
&start,
385358
name.clone(),
386359
self.input,
387-
&mut self.parser,
388-
));
360+
&mut *self.parser,
361+
))
389362
}
390363
} else {
391364
self.any_rule_so_far = true;
392-
let result = parse_qualified_rule(&start, self.input, &mut self.parser);
393-
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
365+
let result = parse_qualified_rule(&start, self.input, &mut *self.parser);
366+
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))))
394367
}
395368
}
396369
}

0 commit comments

Comments
 (0)