diff --git a/color/lib.rs b/color/lib.rs index 93c8f198..4d5890e0 100644 --- a/color/lib.rs +++ b/color/lib.rs @@ -44,7 +44,7 @@ where /// value on success. pub fn parse_color_with<'i, P>( color_parser: &P, - input: &mut Parser<'i, '_>, + input: &mut Parser<'i>, ) -> Result> where P: ColorParser<'i>, @@ -112,7 +112,7 @@ where /// Parse the alpha component by itself from either number or percentage, /// clipping the result to [0.0..1.0]. #[inline] -fn parse_alpha_component<'i, 't, P>( +fn parse_alpha_component<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result> @@ -125,7 +125,7 @@ where .clamp(0.0, OPAQUE)) } -fn parse_legacy_alpha<'i, 't, P>( +fn parse_legacy_alpha<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result> @@ -140,7 +140,7 @@ where }) } -fn parse_modern_alpha<'i, 't, P>( +fn parse_modern_alpha<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result, ParseError> @@ -156,7 +156,7 @@ where } #[inline] -fn parse_rgb<'i, 't, P>( +fn parse_rgb<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result> @@ -222,7 +222,7 @@ where /// /// #[inline] -fn parse_hsl<'i, 't, P>( +fn parse_hsl<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result> @@ -261,7 +261,7 @@ where /// /// #[inline] -fn parse_hwb<'i, 't, P>( +fn parse_hwb<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result> @@ -340,7 +340,7 @@ type IntoColorFn = fn(l: Option, a: Option, b: Option, alpha: Option) -> Output; #[inline] -fn parse_lab_like<'i, 't, P>( +fn parse_lab_like<'i, P>( color_parser: &P, arguments: &mut Parser, lightness_range: f32, @@ -366,7 +366,7 @@ where } #[inline] -fn parse_lch_like<'i, 't, P>( +fn parse_lch_like<'i, P>( color_parser: &P, arguments: &mut Parser, lightness_range: f32, @@ -393,7 +393,7 @@ where /// Parse the color() function. #[inline] -fn parse_color_with_color_space<'i, 't, P>( +fn parse_color_with_color_space<'i, P>( color_parser: &P, arguments: &mut Parser, ) -> Result> @@ -427,7 +427,7 @@ type ComponentParseResult = Result<(Option, Option, Option, Option), ParseError>; /// Parse the color components and alpha with the modern [color-4] syntax. -pub fn parse_components<'i, 't, P, F1, F2, F3, R1, R2, R3>( +pub fn parse_components<'i, P, F1, F2, F3, R1, R2, R3>( color_parser: &P, input: &mut Parser, f1: F1, diff --git a/color/tests.rs b/color/tests.rs index 7cfac15d..fbbb553d 100644 --- a/color/tests.rs +++ b/color/tests.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; -use cssparser::ParserInput; use serde_json::{Value, json}; fn almost_equals(a: &Value, b: &Value) -> bool { @@ -60,8 +59,7 @@ fn run_raw_json_tests(json_data: &str, run: F) { fn run_json_tests Value>(json_data: &str, parse: F) { run_raw_json_tests(json_data, |input, expected| match input { Value::String(input) => { - let mut parse_input = ParserInput::new(&input); - let result = parse(&mut Parser::new(&mut parse_input)); + let result = parse(&mut Parser::new(&input)); assert_json_eq(result, expected, &input); } _ => panic!("Unexpected JSON"), @@ -149,9 +147,7 @@ fn color4_color_function() { macro_rules! parse_single_color { ($i:expr) => {{ - let input = $i; - let mut input = ParserInput::new(input); - let mut input = Parser::new(&mut input); + let mut input = Parser::new($i); Color::parse(&mut input).map_err(Into::>::into) }}; } @@ -355,8 +351,7 @@ fn generic_parser() { ]; for (input, expected) in TESTS { - let mut input = ParserInput::new(input); - let mut input = Parser::new(&mut input); + let mut input = Parser::new(input); let actual: OutputType = parse_color_with(&TestColorParser, &mut input).unwrap(); assert_eq!(actual, *expected); diff --git a/fuzz/fuzz_targets/cssparser.rs b/fuzz/fuzz_targets/cssparser.rs index c95516d9..6740cbdd 100644 --- a/fuzz/fuzz_targets/cssparser.rs +++ b/fuzz/fuzz_targets/cssparser.rs @@ -5,8 +5,7 @@ use cssparser::*; const DEBUG: bool = false; fn parse_and_serialize(input: &str, preserving_comments: bool) -> String { - let mut input = ParserInput::new(input); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new(input); let mut serialization = String::new(); let result = do_parse_and_serialize( &mut parser, @@ -21,7 +20,7 @@ fn parse_and_serialize(input: &str, preserving_comments: bool) -> String { serialization } -fn do_parse_and_serialize<'i>( +fn do_parse_and_serialize( input: &mut Parser, preserving_comments: bool, mut previous_token_type: TokenSerializationType, diff --git a/src/lib.rs b/src/lib.rs index 61451dea..2730e14a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ pub use crate::from_bytes::{EncodingSupport, stylesheet_encoding}; pub use crate::macros::_cssparser_internal_to_lowercase; pub use crate::nth::parse_nth; pub use crate::parser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind}; -pub use crate::parser::{Delimiter, Delimiters, Parser, ParserInput, ParserState}; +pub use crate::parser::{Delimiter, Delimiters, Parser, ParserState}; pub use crate::rules_and_declarations::{AtRuleParser, QualifiedRuleParser}; pub use crate::rules_and_declarations::{DeclarationParser, RuleBodyItemParser, RuleBodyParser}; pub use crate::rules_and_declarations::{StyleSheetParser, parse_one_rule}; diff --git a/src/nth.rs b/src/nth.rs index 69f06f77..40affc40 100644 --- a/src/nth.rs +++ b/src/nth.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use super::{BasicParseError, Parser, ParserInput, Token}; +use super::{BasicParseError, Parser, Token}; /// Parse the *An+B* notation, as found in the `:nth-child()` selector. /// The input is typically the arguments of a function, @@ -117,8 +117,7 @@ fn parse_n_dash_digits(string: &str) -> Result { } fn parse_number_saturate(string: &str) -> Result { - let mut input = ParserInput::new(string); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new(string); let int = if let Ok(&Token::Number { int_value: Some(int), .. diff --git a/src/parser.rs b/src/parser.rs index 2efff17a..1ee17817 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -220,12 +220,17 @@ impl fmt::Display for ParseError { impl std::error::Error for ParseError {} -/// The owned input for a parser. -pub struct ParserInput<'i> { +/// A CSS parser that borrows its `&str` input, yields `Token`s, and keeps track of nested blocks +/// and functions. +pub struct Parser<'i> { tokenizer: Tokenizer<'i>, cached_token: CachedToken<'i>, current_block_depth: u8, nested_block_limit: u8, + /// If `Some(_)`, .parse_nested_block() can be called. + at_start_of: Option, + /// For parsers from `parse_until` or `parse_nested_block` + stop_before: Delimiters, } struct CachedToken<'i> { @@ -234,43 +239,6 @@ struct CachedToken<'i> { end_state: ParserState, } -impl<'i> ParserInput<'i> { - /// 75 nested blocks seems reasonable enough. - const REASONABLE_NESTED_BLOCK_LIMIT: u8 = 75; - - /// Create a new input for a parser. - pub fn new(input: &'i str) -> ParserInput<'i> { - ParserInput { - tokenizer: Tokenizer::new(input), - nested_block_limit: Self::REASONABLE_NESTED_BLOCK_LIMIT, - current_block_depth: 0, - cached_token: CachedToken { - token: Token::Semicolon, // Anything would do. - start_position: SourcePosition(usize::MAX), // No token would match this cache. - end_state: ParserState::default(), - }, - } - } - - /// Sets a limit for how many nested blocks we're allowed to parse. This is useful to avoid - /// running out of stack space. By default, it's set to `REASONABLE_NESTED_BLOCK_LIMIT`, but it - /// can be overridden or cleared. A limit of 0 will be equivalent to no limit at all. - pub fn set_nested_block_limit(&mut self, limit: u8) { - self.nested_block_limit = limit; - } -} - -/// A CSS parser that borrows its `&str` input, -/// yields `Token`s, -/// and keeps track of nested blocks and functions. -pub struct Parser<'i, 't> { - input: &'t mut ParserInput<'i>, - /// If `Some(_)`, .parse_nested_block() can be called. - at_start_of: Option, - /// For parsers from `parse_until` or `parse_nested_block` - stop_before: Delimiters, -} - #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) enum BlockType { Parenthesis, @@ -389,20 +357,37 @@ macro_rules! expect { /// See https://drafts.csswg.org/css-values-5/#arbitrary-substitution pub type ArbitrarySubstitutionFunctions<'a> = &'a [&'static str]; -impl<'i: 't, 't> Parser<'i, 't> { - /// Create a new parser +impl<'i> Parser<'i> { + /// 75 nested blocks seems reasonable enough. + const REASONABLE_NESTED_BLOCK_LIMIT: u8 = 75; + + /// Create a new parser for the given input. #[inline] - pub fn new(input: &'t mut ParserInput<'i>) -> Parser<'i, 't> { - Parser { - input, + pub fn new(input: &'i str) -> Self { + Self { + tokenizer: Tokenizer::new(input), at_start_of: None, stop_before: Delimiter::None, + nested_block_limit: Self::REASONABLE_NESTED_BLOCK_LIMIT, + current_block_depth: 0, + cached_token: CachedToken { + token: Token::Semicolon, // Anything would do. + start_position: SourcePosition(usize::MAX), // No token would match this cache. + end_state: ParserState::default(), + }, } } + /// Sets a limit for how many nested blocks we're allowed to parse. This is useful to avoid + /// running out of stack space. By default, it's set to `REASONABLE_NESTED_BLOCK_LIMIT`, but it + /// can be overridden or cleared. A limit of 0 will be equivalent to no limit at all. + pub fn set_nested_block_limit(&mut self, limit: u8) { + self.nested_block_limit = limit; + } + /// Return the current line that is being parsed. pub fn current_line(&self) -> &'i str { - self.input.tokenizer.current_source_line() + self.tokenizer.current_source_line() } /// Check whether the input is exhausted. That is, if `.next()` would return a token. @@ -437,13 +422,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// This can be used with the `Parser::slice` and `slice_from` methods. #[inline] pub fn position(&self) -> SourcePosition { - self.input.tokenizer.position() + self.tokenizer.position() } /// The current line number and column number. #[inline] pub fn current_source_location(&self) -> SourceLocation { - self.input.tokenizer.current_source_location() + self.tokenizer.current_source_location() } /// The source map URL, if known. @@ -452,7 +437,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// comment. The last such comment is used, so this value may /// change as parsing proceeds. pub fn current_source_map_url(&self) -> Option<&str> { - self.input.tokenizer.current_source_map_url() + self.tokenizer.current_source_map_url() } /// The source URL, if known. @@ -461,7 +446,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// comment. The last such comment is used, so this value may /// change as parsing proceeds. pub fn current_source_url(&self) -> Option<&str> { - self.input.tokenizer.current_source_url() + self.tokenizer.current_source_url() } /// Create a new unexpected token or EOF ParseError at the current location @@ -480,7 +465,7 @@ impl<'i: 't, 't> Parser<'i, 't> { pub fn state(&self) -> ParserState { ParserState { at_start_of: self.at_start_of, - ..self.input.tokenizer.state() + ..self.tokenizer.state() } } @@ -488,24 +473,24 @@ impl<'i: 't, 't> Parser<'i, 't> { #[inline] pub fn skip_whitespace(&mut self) { if let Some(block_type) = self.at_start_of.take() { - consume_until_end_of_block(block_type, &mut self.input.tokenizer); + consume_until_end_of_block(block_type, &mut self.tokenizer); } - self.input.tokenizer.skip_whitespace() + self.tokenizer.skip_whitespace() } #[inline] pub(crate) fn skip_cdc_and_cdo(&mut self) { if let Some(block_type) = self.at_start_of.take() { - consume_until_end_of_block(block_type, &mut self.input.tokenizer); + consume_until_end_of_block(block_type, &mut self.tokenizer); } - self.input.tokenizer.skip_cdc_and_cdo() + self.tokenizer.skip_cdc_and_cdo() } #[inline] pub(crate) fn next_byte(&self) -> Option { - let byte = self.input.tokenizer.next_byte()?; + let byte = self.tokenizer.next_byte()?; if self.stop_before.contains(Delimiters::from_byte(byte)) { return None; } @@ -518,7 +503,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Should only be used with `SourcePosition` values from the same `Parser` instance. #[inline] pub fn reset(&mut self, state: &ParserState) { - self.input.tokenizer.reset(state); + self.tokenizer.reset(state); self.at_start_of = state.at_start_of; } @@ -529,8 +514,7 @@ impl<'i: 't, 't> Parser<'i, 't> { &mut self, fns: ArbitrarySubstitutionFunctions<'i>, ) { - self.input - .tokenizer + self.tokenizer .look_for_arbitrary_substitution_functions(fns) } @@ -538,14 +522,14 @@ impl<'i: 't, 't> Parser<'i, 't> { /// `look_for_arbitrary_substitution_functions` was called, and stop looking. #[inline] pub fn seen_arbitrary_substitution_functions(&mut self) -> bool { - self.input.tokenizer.seen_arbitrary_substitution_functions() + self.tokenizer.seen_arbitrary_substitution_functions() } /// The old name of `try_parse`, which requires raw identifiers in the Rust 2018 edition. #[inline] pub fn r#try(&mut self, thing: F) -> Result where - F: FnOnce(&mut Parser<'i, 't>) -> Result, + F: FnOnce(&mut Parser<'i>) -> Result, { self.try_parse(thing) } @@ -557,7 +541,7 @@ impl<'i: 't, 't> Parser<'i, 't> { #[inline] pub fn try_parse(&mut self, thing: F) -> Result where - F: FnOnce(&mut Parser<'i, 't>) -> Result, + F: FnOnce(&mut Parser<'i>) -> Result, { let start = self.state(); let result = thing(self); @@ -570,13 +554,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Return a slice of the CSS input #[inline] pub fn slice(&self, range: Range) -> &'i str { - self.input.tokenizer.slice(range) + self.tokenizer.slice(range) } /// Return a slice of the CSS input, from the given position to the current one. #[inline] pub fn slice_from(&self, start_position: SourcePosition) -> &'i str { - self.input.tokenizer.slice_from(start_position) + self.tokenizer.slice_from(start_position) } /// Return the next token in the input that is neither whitespace or a comment, @@ -601,7 +585,7 @@ impl<'i: 't, 't> Parser<'i, 't> { while let Token::Comment(..) = self.next_including_whitespace_and_comments()? { // Keep going } - Ok(&self.input.cached_token.token) + Ok(&self.cached_token.token) } /// Same as `Parser::next`, but does not skip whitespace or comment tokens. @@ -614,10 +598,10 @@ impl<'i: 't, 't> Parser<'i, 't> { &mut self, ) -> Result<&Token<'i>, BasicParseError> { if let Some(block_type) = self.at_start_of.take() { - consume_until_end_of_block(block_type, &mut self.input.tokenizer); + consume_until_end_of_block(block_type, &mut self.tokenizer); } - let Some(byte) = self.input.tokenizer.next_byte() else { + let Some(byte) = self.tokenizer.next_byte() else { return Err(BasicParseError::new(BasicParseErrorKind::EndOfInput)); }; @@ -625,23 +609,23 @@ impl<'i: 't, 't> Parser<'i, 't> { return Err(BasicParseError::new(BasicParseErrorKind::EndOfInput)); } - let token_start_position = self.input.tokenizer.position(); - let using_cached_token = self.input.cached_token.start_position == token_start_position; + let token_start_position = self.tokenizer.position(); + let using_cached_token = self.cached_token.start_position == token_start_position; let token = if using_cached_token { - let cached_token = &self.input.cached_token; - self.input.tokenizer.reset(&cached_token.end_state); + let cached_token = &self.cached_token; + self.tokenizer.reset(&cached_token.end_state); if let Token::Function(ref name) = cached_token.token { - self.input.tokenizer.see_function(name) + self.tokenizer.see_function(name) } &cached_token.token } else { - let new_token = self.input.tokenizer.next_unchecked(); - self.input.cached_token = CachedToken { + let new_token = self.tokenizer.next_unchecked(); + self.cached_token = CachedToken { token: new_token, start_position: token_start_position, - end_state: self.input.tokenizer.state(), + end_state: self.tokenizer.state(), }; - &self.input.cached_token.token + &self.cached_token.token }; if let Some(block_type) = BlockType::opening(token) { @@ -657,7 +641,7 @@ impl<'i: 't, 't> Parser<'i, 't> { #[inline] pub fn parse_entirely(&mut self, parse: F) -> Result> where - F: FnOnce(&mut Parser<'i, 't>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { let result = parse(self)?; self.expect_exhausted()?; @@ -678,7 +662,7 @@ impl<'i: 't, 't> Parser<'i, 't> { #[inline] pub fn parse_comma_separated(&mut self, parse_one: F) -> Result, ParseError> where - F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + F: FnMut(&mut Parser<'i>) -> Result>, { self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ false) } @@ -691,7 +675,7 @@ impl<'i: 't, 't> Parser<'i, 't> { #[inline] pub fn parse_comma_separated_ignoring_errors(&mut self, parse_one: F) -> Vec where - F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + F: FnMut(&mut Parser<'i>) -> Result>, { match self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ true) { Ok(values) => values, @@ -706,7 +690,7 @@ impl<'i: 't, 't> Parser<'i, 't> { ignore_errors: bool, ) -> Result, ParseError> where - F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + F: FnMut(&mut Parser<'i>) -> Result>, { // Vec grows from 0 to 4 by default on first push(). So allocate with // capacity 1, so in the somewhat common case of only one item we don't @@ -742,7 +726,7 @@ impl<'i: 't, 't> Parser<'i, 't> { #[inline] pub fn parse_nested_block(&mut self, parse: F) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { parse_nested_block(self, parse) } @@ -762,7 +746,7 @@ impl<'i: 't, 't> Parser<'i, 't> { parse: F, ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { parse_until_before(self, delimiters, ParseUntilErrorBehavior::Consume, parse) } @@ -779,7 +763,7 @@ impl<'i: 't, 't> Parser<'i, 't> { parse: F, ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { parse_until_after(self, delimiters, ParseUntilErrorBehavior::Consume, parse) } @@ -1000,78 +984,72 @@ impl<'i: 't, 't> Parser<'i, 't> { } } -pub fn parse_until_before<'i: 't, 't, F, T, E>( - parser: &mut Parser<'i, 't>, +pub fn parse_until_before<'i, F, T, E>( + parser: &mut Parser<'i>, delimiters: Delimiters, error_behavior: ParseUntilErrorBehavior, parse: F, ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { + let old_stop_before = parser.stop_before; let delimiters = parser.stop_before | delimiters; - let result; - // Introduce a new scope to limit duration of nested_parser’s borrow - { - let mut delimited_parser = Parser { - input: parser.input, - at_start_of: parser.at_start_of.take(), - stop_before: delimiters, - }; - result = delimited_parser.parse_entirely(parse); - if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { - return result; - } - if let Some(block_type) = delimited_parser.at_start_of { - consume_until_end_of_block(block_type, &mut delimited_parser.input.tokenizer); - } + parser.stop_before = delimiters; + let result = parser.parse_entirely(parse); + parser.stop_before = old_stop_before; + if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { + return result; + } + if let Some(block_type) = parser.at_start_of.take() { + consume_until_end_of_block(block_type, &mut parser.tokenizer); } // FIXME: have a special-purpose tokenizer method for this that does less work. - while let Some(next_byte) = parser.input.tokenizer.next_byte() { + while let Some(next_byte) = parser.tokenizer.next_byte() { if delimiters.contains(Delimiters::from_byte(next_byte)) { break; } - let token = parser.input.tokenizer.next_unchecked(); + let token = parser.tokenizer.next_unchecked(); if let Some(block_type) = BlockType::opening(&token) { - consume_until_end_of_block(block_type, &mut parser.input.tokenizer); + consume_until_end_of_block(block_type, &mut parser.tokenizer); } } result } -pub fn parse_until_after<'i: 't, 't, F, T, E>( - parser: &mut Parser<'i, 't>, +pub fn parse_until_after<'i, F, T, E>( + parser: &mut Parser<'i>, delimiters: Delimiters, error_behavior: ParseUntilErrorBehavior, parse: F, ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { let result = parse_until_before(parser, delimiters, error_behavior, parse); if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { return result; } - if let Some(next_byte) = parser.input.tokenizer.next_byte() { + if let Some(next_byte) = parser.tokenizer.next_byte() { let delimiter = Delimiters::from_byte(next_byte); if !parser.stop_before.contains(delimiter) { debug_assert!(delimiters.contains(delimiter)); // We know this byte is ASCII. - parser.input.tokenizer.advance(1); + parser.tokenizer.advance(1); if next_byte == b'{' { - consume_until_end_of_block(BlockType::CurlyBracket, &mut parser.input.tokenizer); + consume_until_end_of_block(BlockType::CurlyBracket, &mut parser.tokenizer); } } } result } -pub fn parse_nested_block<'i: 't, 't, F, T, E>( - parser: &mut Parser<'i, 't>, +pub fn parse_nested_block<'i, F, T, E>( + parser: &mut Parser<'i>, parse: F, ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: FnOnce(&mut Parser<'i>) -> Result>, { let block_type = parser.at_start_of.take().expect( "\ @@ -1080,37 +1058,27 @@ where token was just consumed.\ ", ); - if parser.input.current_block_depth >= parser.input.nested_block_limit - && parser.input.nested_block_limit != 0 - { + if parser.current_block_depth >= parser.nested_block_limit && parser.nested_block_limit != 0 { return Err(ParseError::from_basic_kind( BasicParseErrorKind::TooManyNestedBlocks, )); } // Fine to use wrapping addition, overflow can only occur without a limit. - parser.input.current_block_depth = parser.input.current_block_depth.wrapping_add(1); + parser.current_block_depth = parser.current_block_depth.wrapping_add(1); - let closing_delimiter = match block_type { + let old_stop_before = parser.stop_before; + parser.stop_before = match block_type { BlockType::CurlyBracket => ClosingDelimiter::CloseCurlyBracket, BlockType::SquareBracket => ClosingDelimiter::CloseSquareBracket, BlockType::Parenthesis => ClosingDelimiter::CloseParenthesis, }; - let result; - // Introduce a new scope to limit duration of nested_parser’s borrow - { - let mut nested_parser = Parser { - input: parser.input, - at_start_of: None, - stop_before: closing_delimiter, - }; - result = nested_parser.parse_entirely(parse); - if let Some(block_type) = nested_parser.at_start_of { - consume_until_end_of_block(block_type, &mut nested_parser.input.tokenizer); - } + let result = parser.parse_entirely(parse); + if let Some(nested_block_type) = parser.at_start_of.take() { + consume_until_end_of_block(nested_block_type, &mut parser.tokenizer); } - consume_until_end_of_block(block_type, &mut parser.input.tokenizer); - // See above. - parser.input.current_block_depth = parser.input.current_block_depth.wrapping_sub(1); + consume_until_end_of_block(block_type, &mut parser.tokenizer); + parser.stop_before = old_stop_before; + parser.current_block_depth = parser.current_block_depth.wrapping_sub(1); result } diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 813224b8..f33eadb8 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -49,7 +49,7 @@ pub trait DeclarationParser<'i> { fn parse_value( &mut self, _name: CowRcStr<'i>, - _input: &mut Parser<'i, '_>, + _input: &mut Parser<'i>, _declaration_start: &ParserState, ) -> Result> { Err(ParseError::unexpected_token()) @@ -93,7 +93,7 @@ pub trait AtRuleParser<'i> { fn parse_prelude( &mut self, _name: CowRcStr<'i>, - _input: &mut Parser<'i, '_>, + _input: &mut Parser<'i>, ) -> Result> { Err(ParseError::from_basic_kind( BasicParseErrorKind::AtRuleInvalid, @@ -133,7 +133,7 @@ pub trait AtRuleParser<'i> { &mut self, prelude: Self::Prelude, start: &ParserState, - _input: &mut Parser<'i, '_>, + _input: &mut Parser<'i>, ) -> Result> { let _ = prelude; let _ = start; @@ -174,7 +174,7 @@ pub trait QualifiedRuleParser<'i> { /// that ends where the prelude should end (before the next `{`). fn parse_prelude( &mut self, - _input: &mut Parser<'i, '_>, + _input: &mut Parser<'i>, ) -> Result> { Err(ParseError::from_basic_kind( BasicParseErrorKind::QualifiedRuleInvalid, @@ -192,7 +192,7 @@ pub trait QualifiedRuleParser<'i> { &mut self, prelude: Self::Prelude, start: &ParserState, - _input: &mut Parser<'i, '_>, + _input: &mut Parser<'i>, ) -> Result> { let _ = prelude; let _ = start; @@ -203,9 +203,9 @@ pub trait QualifiedRuleParser<'i> { } /// Provides an iterator for rule bodies and declaration lists. -pub struct RuleBodyParser<'i, 't, 'a, P, I, E> { +pub struct RuleBodyParser<'i, 'a, P, I, E> { /// The input given to the parser. - pub input: &'a mut Parser<'i, 't>, + pub input: &'a mut Parser<'i>, /// The parser given to `RuleBodyParser::new` pub parser: &'a mut P, @@ -226,7 +226,7 @@ pub trait RuleBodyItemParser<'i, DeclOrRule, Error>: fn parse_qualified(&self) -> bool; } -impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { +impl<'i, 'a, P, I, E> RuleBodyParser<'i, 'a, P, I, E> { /// Create a new `RuleBodyParser` for the given `input` and `parser`. /// /// Note that all CSS declaration lists can on principle contain at-rules. @@ -241,7 +241,7 @@ impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { /// The return type for finished declarations and at-rules also needs to be the same, /// since `::next` can return either. /// It could be a custom enum. - pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self { + pub fn new(input: &'a mut Parser<'i>, parser: &'a mut P) -> Self { Self { input, parser, @@ -251,7 +251,7 @@ impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { } /// https://drafts.csswg.org/css-syntax/#consume-a-blocks-contents -impl<'i, I, P, E> Iterator for RuleBodyParser<'i, '_, '_, P, I, E> +impl<'i, I, P, E> Iterator for RuleBodyParser<'i, '_, P, I, E> where P: RuleBodyItemParser<'i, I, E>, { @@ -339,9 +339,9 @@ where } /// Provides an iterator for rule list parsing at the top-level of a stylesheet. -pub struct StyleSheetParser<'i, 't, 'a, P> { +pub struct StyleSheetParser<'i, 'a, P> { /// The input given. - pub input: &'a mut Parser<'i, 't>, + pub input: &'a mut Parser<'i>, /// The parser given. pub parser: &'a mut P, @@ -349,7 +349,7 @@ pub struct StyleSheetParser<'i, 't, 'a, P> { any_rule_so_far: bool, } -impl<'i, 't, 'a, R, P, E> StyleSheetParser<'i, 't, 'a, P> +impl<'i, 'a, R, P, E> StyleSheetParser<'i, 'a, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> + AtRuleParser<'i, AtRule = R, Error = E>, @@ -360,7 +360,7 @@ where /// /// The return type for finished qualified rules and at-rules also needs to be the same, /// since `::next` can return either. It could be a custom enum. - pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self { + pub fn new(input: &'a mut Parser<'i>, parser: &'a mut P) -> Self { Self { input, parser, @@ -370,7 +370,7 @@ where } /// `StyleSheetParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. -impl<'i, R, P, E> Iterator for StyleSheetParser<'i, '_, '_, P> +impl<'i, R, P, E> Iterator for StyleSheetParser<'i, '_, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> + AtRuleParser<'i, AtRule = R, Error = E>, @@ -429,7 +429,7 @@ where /// Parse a single declaration, such as an `( /* ... */ )` parenthesis in an `@supports` prelude. pub fn parse_one_declaration<'i, P, E>( - input: &mut Parser<'i, '_>, + input: &mut Parser<'i>, parser: &mut P, ) -> Result<

>::Declaration, (ParseError, &'i str, SourceLocation)> where @@ -448,7 +448,7 @@ where /// Parse a single rule, such as for CSSOM’s `CSSStyleSheet.insertRule`. pub fn parse_one_rule<'i, R, P, E>( - input: &mut Parser<'i, '_>, + input: &mut Parser<'i>, parser: &mut P, ) -> Result> where @@ -481,7 +481,7 @@ where fn parse_at_rule<'i, P, E>( start: &ParserState, name: CowRcStr<'i>, - input: &mut Parser<'i, '_>, + input: &mut Parser<'i>, parser: &mut P, ) -> Result<

>::AtRule, (ParseError, &'i str, SourceLocation)> where @@ -536,7 +536,7 @@ fn looks_like_a_custom_property(input: &mut Parser) -> bool { // https://drafts.csswg.org/css-syntax/#consume-a-qualified-rule fn parse_qualified_rule<'i, P, E>( start: &ParserState, - input: &mut Parser<'i, '_>, + input: &mut Parser<'i>, parser: &mut P, nested: bool, ) -> Result<

>::QualifiedRule, ParseError> diff --git a/src/size_of_tests.rs b/src/size_of_tests.rs index 1a53cc0f..e407578b 100644 --- a/src/size_of_tests.rs +++ b/src/size_of_tests.rs @@ -43,8 +43,7 @@ size_of_test!(std_cow_str, std::borrow::Cow<'static, str>, 24, 32); size_of_test!(cow_rc_str, CowRcStr, 16); size_of_test!(tokenizer, crate::tokenizer::Tokenizer, 96); -size_of_test!(parser_input, crate::parser::ParserInput, 168); -size_of_test!(parser, crate::parser::Parser, 16); +size_of_test!(parser, crate::parser::Parser, 168); size_of_test!(source_position, crate::SourcePosition, 8); size_of_test!(parser_state, crate::ParserState, 24); diff --git a/src/tests.rs b/src/tests.rs index aa913c81..0c538618 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -15,10 +15,10 @@ use self::test::Bencher; use super::{ AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Delimiter, - EncodingSupport, ParseError, ParseErrorKind, Parser, ParserInput, ParserState, - QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, StyleSheetParser, - ToCss, Token, TokenSerializationType, UnicodeRange, parse_important, parse_nth, - parse_one_declaration, parse_one_rule, stylesheet_encoding, + EncodingSupport, ParseError, ParseErrorKind, Parser, ParserState, QualifiedRuleParser, + RuleBodyItemParser, RuleBodyParser, SourceLocation, StyleSheetParser, ToCss, Token, + TokenSerializationType, UnicodeRange, parse_important, parse_nth, parse_one_declaration, + parse_one_rule, stylesheet_encoding, }; macro_rules! JArray { @@ -97,8 +97,7 @@ fn run_raw_json_tests(json_data: &str, run: F) { fn run_json_tests Value>(json_data: &str, parse: F) { run_raw_json_tests(json_data, |input, expected| match input { Value::String(input) => { - let mut parse_input = ParserInput::new(&input); - let result = parse(&mut Parser::new(&mut parse_input)); + let result = parse(&mut Parser::new(&input)); assert_json_eq(result, expected, &input); } _ => panic!("Unexpected JSON"), @@ -228,9 +227,8 @@ fn stylesheet_from_bytes() { environment_encoding, ); let (css_unicode, used_encoding, _) = encoding.decode(&css); - let mut input = ParserInput::new(&css_unicode); - let input = &mut Parser::new(&mut input); - let rules = StyleSheetParser::new(input, &mut JsonParser) + let mut parser = Parser::new(&css_unicode); + let rules = StyleSheetParser::new(&mut parser, &mut JsonParser) .map(|result| result.unwrap_or(JArray!["error", "invalid"])) .collect::>(); JArray![rules, used_encoding.name().to_lowercase()] @@ -251,29 +249,24 @@ fn stylesheet_from_bytes() { #[test] fn expect_no_error_token() { - let mut input = ParserInput::new("foo 4px ( / { !bar }"); - assert!(Parser::new(&mut input).expect_no_error_token().is_ok()); - let mut input = ParserInput::new(")"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); - let mut input = ParserInput::new("}"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); - let mut input = ParserInput::new("(a){]"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); - let mut input = ParserInput::new("'\n'"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); - let mut input = ParserInput::new("url('\n'"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); - let mut input = ParserInput::new("url(a b)"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); - let mut input = ParserInput::new("url(\u{7F}))"); - assert!(Parser::new(&mut input).expect_no_error_token().is_err()); + assert!( + Parser::new("foo 4px ( / { !bar }") + .expect_no_error_token() + .is_ok() + ); + assert!(Parser::new(")").expect_no_error_token().is_err()); + assert!(Parser::new("}").expect_no_error_token().is_err()); + assert!(Parser::new("(a){]").expect_no_error_token().is_err()); + assert!(Parser::new("'\n'").expect_no_error_token().is_err()); + assert!(Parser::new("url('\n'").expect_no_error_token().is_err()); + assert!(Parser::new("url(a b)").expect_no_error_token().is_err()); + assert!(Parser::new("url(\u{7F}))").expect_no_error_token().is_err()); } /// https://github.com/servo/rust-cssparser/issues/71 #[test] fn outer_block_end_consumed() { - let mut input = ParserInput::new("(calc(true))"); - let mut input = Parser::new(&mut input); + let mut input = Parser::new("(calc(true))"); assert!(input.expect_parenthesis_block().is_ok()); assert!( input @@ -289,8 +282,7 @@ fn outer_block_end_consumed() { /// https://github.com/servo/rust-cssparser/issues/174 #[test] fn bad_url_slice_out_of_bounds() { - let mut input = ParserInput::new("url(\u{1}\\"); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new("url(\u{1}\\"); let result = parser.next_including_whitespace_and_comments(); // This used to panic assert_eq!(result, Ok(&Token::BadUrl("\u{1}\\".into()))); } @@ -298,8 +290,7 @@ fn bad_url_slice_out_of_bounds() { /// https://bugzilla.mozilla.org/show_bug.cgi?id=1383975 #[test] fn bad_url_slice_not_at_char_boundary() { - let mut input = ParserInput::new("url(9\n۰"); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new("url(9\n۰"); let result = parser.next_including_whitespace_and_comments(); // This used to panic assert_eq!(result, Ok(&Token::BadUrl("9\n۰".into()))); } @@ -327,31 +318,23 @@ fn unquoted_url_escaping() { )\ " ); - let mut input = ParserInput::new(&serialized); - assert_eq!(Parser::new(&mut input).next(), Ok(&token)); + assert_eq!(Parser::new(&serialized).next(), Ok(&token)); } #[test] fn test_expect_url() { - fn parse<'a>(s: &mut ParserInput<'a>) -> Result, BasicParseError> { + fn parse<'a>(s: &'a str) -> Result, BasicParseError> { Parser::new(s).expect_url() } - let mut input = ParserInput::new("url()"); - assert_eq!(parse(&mut input).unwrap(), ""); - let mut input = ParserInput::new("url( "); - assert_eq!(parse(&mut input).unwrap(), ""); - let mut input = ParserInput::new("url( abc"); - assert_eq!(parse(&mut input).unwrap(), "abc"); - let mut input = ParserInput::new("url( abc \t)"); - assert_eq!(parse(&mut input).unwrap(), "abc"); - let mut input = ParserInput::new("url( 'abc' \t)"); - assert_eq!(parse(&mut input).unwrap(), "abc"); - let mut input = ParserInput::new("url(abc more stuff)"); - assert!(parse(&mut input).is_err()); + assert_eq!(parse("url()").unwrap(), ""); + assert_eq!(parse("url( ").unwrap(), ""); + assert_eq!(parse("url( abc").unwrap(), "abc"); + assert_eq!(parse("url( abc \t)").unwrap(), "abc"); + assert_eq!(parse("url( 'abc' \t)").unwrap(), "abc"); + assert!(parse("url(abc more stuff)").is_err()); // The grammar at https://drafts.csswg.org/css-values/#urls plans for `*` // at the position of "more stuff", but no such modifier is defined yet. - let mut input = ParserInput::new("url('abc' more stuff)"); - assert!(parse(&mut input).is_err()); + assert!(parse("url('abc' more stuff)").is_err()); } #[test] @@ -371,8 +354,7 @@ fn nth() { #[test] fn parse_comma_separated_ignoring_errors() { let input = "red, green something, yellow, whatever, blue"; - let mut input = ParserInput::new(input); - let mut input = Parser::new(&mut input); + let mut input = Parser::new(input); let result = input.parse_comma_separated_ignoring_errors(|input| { let ident = input.expect_ident()?; crate::color::parse_named_color(ident).map_err(|()| ParseError::<()>::unexpected_token()) @@ -467,8 +449,7 @@ fn serializer(preserve_comments: bool) { &mut serialized, preserve_comments, ); - let mut input = ParserInput::new(&serialized); - let parser = &mut Parser::new(&mut input); + let parser = &mut Parser::new(&serialized); Value::Array(component_values_to_json(parser)) }, ); @@ -476,8 +457,7 @@ fn serializer(preserve_comments: bool) { #[test] fn serialize_bad_tokens() { - let mut input = ParserInput::new("url(foo\\) b\\)ar)'ba\\'\"z\n4"); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new("url(foo\\) b\\)ar)'ba\\'\"z\n4"); let token = parser.next().unwrap().clone(); assert!(matches!(token, Token::BadUrl(_))); @@ -496,7 +476,7 @@ fn serialize_bad_tokens() { #[test] fn line_numbers() { - let mut input = ParserInput::new(concat!( + let mut input = Parser::new(concat!( "fo\\30\r\n", "0o bar/*\n", "*/baz\r\n", @@ -506,7 +486,6 @@ fn line_numbers() { ")\"a\\\r\n", "b\"" )); - let mut input = Parser::new(&mut input); assert_eq!( input.current_source_location(), SourceLocation { line: 0, column: 1 } @@ -614,8 +593,7 @@ fn overflow() { " .replace("{309 zeros}", &"0".repeat(309)); - let mut input = ParserInput::new(&css); - let mut input = Parser::new(&mut input); + let mut input = Parser::new(&css); assert_eq!(input.expect_integer(), Ok(2147483646)); assert_eq!(input.expect_integer(), Ok(2147483647)); @@ -642,8 +620,7 @@ fn overflow() { #[test] fn line_delimited() { - let mut input = ParserInput::new(" { foo ; bar } baz;,"); - let mut input = Parser::new(&mut input); + let mut input = Parser::new(" { foo ; bar } baz;,"); assert_eq!(input.next(), Ok(&Token::CurlyBracketBlock)); assert!( { @@ -806,8 +783,7 @@ const ARBITRARY_SUBSTITUTION_FUNCTIONS: ArbitrarySubstitutionFunctions = &["var" #[bench] fn unquoted_url(b: &mut Bencher) { b.iter(|| { - let mut input = ParserInput::new(BACKGROUND_IMAGE); - let mut input = Parser::new(&mut input); + let mut input = Parser::new(BACKGROUND_IMAGE); input.look_for_arbitrary_substitution_functions(ARBITRARY_SUBSTITUTION_FUNCTIONS); let result = input.try_parse(|input| input.expect_url()); @@ -827,8 +803,7 @@ fn unquoted_url(b: &mut Bencher) { fn numeric(b: &mut Bencher) { b.iter(|| { for _ in 0..1000000 { - let mut input = ParserInput::new("10px"); - let mut input = Parser::new(&mut input); + let mut input = Parser::new("10px"); let _ = test::black_box(input.next()); } }) @@ -844,8 +819,7 @@ fn no_stack_overflow_multiple_nested_blocks() { let dup = input.clone(); input.push_str(&dup); } - let mut input = ParserInput::new(&input); - let mut input = Parser::new(&mut input); + let mut input = Parser::new(&input); while input.next().is_ok() {} } @@ -865,19 +839,17 @@ fn nested_block_limit() { // Returns `Err(())` if (and only if) parsing bailed out due to the nesting limit. fn parse(depth: usize, limit: Option) -> Result<(), ()> { let css = format!("{}1{}", "calc(".repeat(depth), ")".repeat(depth)); - let mut input = ParserInput::new(&css); + let mut parser = Parser::new(&css); if let Some(limit) = limit { - input.set_nested_block_limit(limit); + parser.set_nested_block_limit(limit); } - Parser::new(&mut input) - .parse_entirely(parse_calc) - .map_err(|e| match e.kind { - ParseErrorKind::Basic(BasicParseErrorKind::TooManyNestedBlocks) => (), - other => panic!( - "Unexpected error parsing {} nested blocks: {:?}", - depth, other - ), - }) + parser.parse_entirely(parse_calc).map_err(|e| match e.kind { + ParseErrorKind::Basic(BasicParseErrorKind::TooManyNestedBlocks) => (), + other => panic!( + "Unexpected error parsing {} nested blocks: {:?}", + depth, other + ), + }) } // The default limit is 75 nested blocks. @@ -1176,11 +1148,9 @@ fn parse_until_before_stops_at_delimiter_or_end_of_input() { for equivalent in inputs { for (j, x) in equivalent.1.iter().enumerate() { for y in equivalent.1[j + 1..].iter() { - let mut ix = ParserInput::new(x); - let mut ix = Parser::new(&mut ix); + let mut ix = Parser::new(x); - let mut iy = ParserInput::new(y); - let mut iy = Parser::new(&mut iy); + let mut iy = Parser::new(y); let _ = ix.parse_until_before::<_, _, ()>(equivalent.0, |ix| { iy.parse_until_before::<_, _, ()>(equivalent.0, |iy| { @@ -1202,8 +1172,7 @@ fn parse_until_before_stops_at_delimiter_or_end_of_input() { #[test] fn parser_maintains_current_line() { - let mut input = ParserInput::new("ident ident;\nident ident ident;\nident"); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new("ident ident;\nident ident ident;\nident"); assert_eq!(parser.current_line(), "ident ident;"); assert_eq!(parser.next(), Ok(&Token::Ident("ident".into()))); assert_eq!(parser.next(), Ok(&Token::Ident("ident".into()))); @@ -1221,8 +1190,7 @@ fn parser_maintains_current_line() { #[test] fn cdc_regression_test() { - let mut input = ParserInput::new("-->x"); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new("-->x"); parser.skip_cdc_and_cdo(); assert_eq!(parser.next(), Ok(&Token::Ident("x".into()))); assert_eq!( @@ -1239,8 +1207,7 @@ fn parse_entirely_reports_first_error() { enum E { Foo, } - let mut input = ParserInput::new("ident"); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new("ident"); let result: Result<(), _> = parser.parse_entirely(|_| Err(ParseError::custom(E::Foo))); assert_eq!( result, @@ -1270,8 +1237,7 @@ fn parse_sourcemapping_comments() { ]; for test in tests { - let mut input = ParserInput::new(test.0); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new(test.0); while parser.next_including_whitespace().is_ok() {} assert_eq!(parser.current_source_map_url(), test.1); } @@ -1294,8 +1260,7 @@ fn parse_sourceurl_comments() { ]; for test in tests { - let mut input = ParserInput::new(test.0); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new(test.0); while parser.next_including_whitespace().is_ok() {} assert_eq!(parser.current_source_url(), test.1); } @@ -1305,8 +1270,7 @@ fn parse_sourceurl_comments() { #[test] fn roundtrip_percentage_token() { fn test_roundtrip(value: &str) { - let mut input = ParserInput::new(value); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new(value); let token = parser.next().unwrap(); assert_eq!(token.to_css_string(), value); } @@ -1357,8 +1321,7 @@ fn utf16_columns() { ]; for test in tests { - let mut input = ParserInput::new(test.0); - let mut parser = Parser::new(&mut input); + let mut parser = Parser::new(test.0); // Read all tokens. loop {