/src/rust-cssparser/src/parser.rs
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | use crate::cow_rc_str::CowRcStr; |
6 | | use crate::tokenizer::{SeenStatus, SourceLocation, SourcePosition, Token}; |
7 | | use smallvec::SmallVec; |
8 | | use std::fmt; |
9 | | use std::ops::BitOr; |
10 | | |
11 | | /// A capture of the internal state of a `Parser` (including the position within the input), |
12 | | /// obtained from the `Parser::position` method. |
13 | | /// |
14 | | /// Can be used with the `Parser::reset` method to restore that state. |
15 | | /// Should only be used with the `Parser` instance it came from. |
16 | | #[derive(Clone, Default)] |
17 | | pub struct ParserState { |
18 | | pub(crate) position: usize, |
19 | | pub(crate) current_line_start_position: u32, |
20 | | /// Current line number shifted by `Self::BLOCK_TYPE_BITS`, with the low 2 bits holding the |
21 | | /// `BlockType` discriminant of the block the last returned token opened (if any). |
22 | | line_number_and_block_type: u32, |
23 | | } |
24 | | |
25 | | impl fmt::Debug for ParserState { |
26 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
27 | 0 | f.debug_struct("ParserState") |
28 | 0 | .field("position", &self.position) |
29 | 0 | .field( |
30 | 0 | "current_line_start_position", |
31 | 0 | &self.current_line_start_position, |
32 | 0 | ) |
33 | 0 | .field("current_line_number", &self.line_number()) |
34 | 0 | .field("at_start_of", &self.at_start_of()) |
35 | 0 | .finish() |
36 | 0 | } |
37 | | } |
38 | | |
39 | | impl ParserState { |
40 | | const BLOCK_TYPE_BITS: u32 = 2; |
41 | | const BLOCK_TYPE_MASK: u32 = (1 << Self::BLOCK_TYPE_BITS) - 1; |
42 | | |
43 | | /// The position from the start of the input, counted in UTF-8 bytes. |
44 | | #[inline] |
45 | 262k | pub fn position(&self) -> SourcePosition { |
46 | 262k | SourcePosition(self.position) |
47 | 262k | } |
48 | | |
49 | | /// The line number and column number |
50 | | #[inline] |
51 | | pub fn source_location(&self) -> SourceLocation { |
52 | | SourceLocation { |
53 | | line: self.line_number(), |
54 | | column: (self.position as u32) |
55 | | .wrapping_sub(self.current_line_start_position) |
56 | | .wrapping_add(1), |
57 | | } |
58 | | } |
59 | | |
60 | | #[inline] |
61 | 0 | pub(crate) fn line_number(&self) -> u32 { |
62 | 0 | self.line_number_and_block_type >> Self::BLOCK_TYPE_BITS |
63 | 0 | } |
64 | | |
65 | | #[inline] |
66 | 4.70M | pub(crate) fn advance_line_number(&mut self, count: u32) { |
67 | 4.70M | self.line_number_and_block_type += count << Self::BLOCK_TYPE_BITS; |
68 | 4.70M | } |
69 | | |
70 | | #[inline] |
71 | 132M | pub(crate) fn at_start_of(&self) -> Option<BlockType> { |
72 | 132M | let block_type = (self.line_number_and_block_type & Self::BLOCK_TYPE_MASK) as u8; |
73 | 132M | BlockType::from_byte(block_type) |
74 | 132M | } <cssparser::parser::ParserState>::at_start_of Line | Count | Source | 71 | 444k | pub(crate) fn at_start_of(&self) -> Option<BlockType> { | 72 | 444k | let block_type = (self.line_number_and_block_type & Self::BLOCK_TYPE_MASK) as u8; | 73 | 444k | BlockType::from_byte(block_type) | 74 | 444k | } |
<cssparser::parser::ParserState>::at_start_of Line | Count | Source | 71 | 131M | pub(crate) fn at_start_of(&self) -> Option<BlockType> { | 72 | 131M | let block_type = (self.line_number_and_block_type & Self::BLOCK_TYPE_MASK) as u8; | 73 | 131M | BlockType::from_byte(block_type) | 74 | 131M | } |
|
75 | | |
76 | | #[inline] |
77 | 4.36M | pub(crate) fn set_at_start_of(&mut self, block_type: BlockType) { |
78 | 4.36M | debug_assert!(self.at_start_of().is_none()); |
79 | 4.36M | self.line_number_and_block_type |= block_type as u32; |
80 | 4.36M | debug_assert_eq!(self.at_start_of(), Some(block_type)); |
81 | 4.36M | } |
82 | | |
83 | | #[inline] |
84 | 132M | pub(crate) fn take_at_start_of(&mut self) -> Option<BlockType> { |
85 | 132M | let block_type = self.at_start_of(); |
86 | 132M | self.line_number_and_block_type &= !Self::BLOCK_TYPE_MASK; |
87 | 132M | block_type |
88 | 132M | } <cssparser::parser::ParserState>::take_at_start_of Line | Count | Source | 84 | 444k | pub(crate) fn take_at_start_of(&mut self) -> Option<BlockType> { | 85 | 444k | let block_type = self.at_start_of(); | 86 | 444k | self.line_number_and_block_type &= !Self::BLOCK_TYPE_MASK; | 87 | 444k | block_type | 88 | 444k | } |
<cssparser::parser::ParserState>::take_at_start_of Line | Count | Source | 84 | 131M | pub(crate) fn take_at_start_of(&mut self) -> Option<BlockType> { | 85 | 131M | let block_type = self.at_start_of(); | 86 | 131M | self.line_number_and_block_type &= !Self::BLOCK_TYPE_MASK; | 87 | 131M | block_type | 88 | 131M | } |
|
89 | | } |
90 | | |
91 | | /// When parsing until a given token, sometimes the caller knows that parsing is going to restart |
92 | | /// at some earlier point, and consuming until we find a top level delimiter is just wasted work. |
93 | | /// |
94 | | /// In that case, callers can pass ParseUntilErrorBehavior::Stop to avoid doing all that wasted |
95 | | /// work. |
96 | | /// |
97 | | /// This is important for things like CSS nesting, where something like: |
98 | | /// |
99 | | /// foo:is(..) { |
100 | | /// ... |
101 | | /// } |
102 | | /// |
103 | | /// Would need to scan the whole {} block to find a semicolon, only for parsing getting restarted |
104 | | /// as a qualified rule later. |
105 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
106 | | pub enum ParseUntilErrorBehavior { |
107 | | /// Consume until we see the relevant delimiter or the end of the stream. |
108 | | Consume, |
109 | | /// Eagerly error. |
110 | | Stop, |
111 | | } |
112 | | |
113 | | /// Details about a `BasicParseError` |
114 | | #[derive(Clone, Debug, PartialEq)] |
115 | | pub enum BasicParseErrorKind { |
116 | | /// An unexpected token was encountered. |
117 | | /// |
118 | | /// The token itself is deliberately not stored: it made this enum 32 bytes, |
119 | | /// which pushed `Result<&Token, BasicParseError>` (returned from every token |
120 | | /// fetch) to 40 bytes and therefore out of registers and into memory. |
121 | | /// Callers that want to name the token can recover it from the source text |
122 | | /// they already carry for the error message. |
123 | | UnexpectedToken, |
124 | | /// The end of the input was encountered unexpectedly. |
125 | | EndOfInput, |
126 | | /// An `@` rule was encountered that was invalid. See `UnexpectedToken` for |
127 | | /// why the rule name is not stored. |
128 | | AtRuleInvalid, |
129 | | /// The body of an '@' rule was invalid. |
130 | | AtRuleBodyInvalid, |
131 | | /// A qualified rule was encountered that was invalid. |
132 | | QualifiedRuleInvalid, |
133 | | /// We've gone over the nesting limit. |
134 | | TooManyNestedBlocks, |
135 | | } |
136 | | |
137 | | impl fmt::Display for BasicParseErrorKind { |
138 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
139 | 0 | match self { |
140 | | BasicParseErrorKind::TooManyNestedBlocks => { |
141 | 0 | write!(f, "nesting block limit reached") |
142 | | } |
143 | 0 | BasicParseErrorKind::UnexpectedToken => write!(f, "unexpected token"), |
144 | 0 | BasicParseErrorKind::EndOfInput => write!(f, "unexpected end of input"), |
145 | 0 | BasicParseErrorKind::AtRuleInvalid => write!(f, "invalid @ rule encountered"), |
146 | 0 | BasicParseErrorKind::AtRuleBodyInvalid => write!(f, "invalid @ rule body encountered"), |
147 | | BasicParseErrorKind::QualifiedRuleInvalid => { |
148 | 0 | write!(f, "invalid qualified rule encountered") |
149 | | } |
150 | | } |
151 | 0 | } |
152 | | } |
153 | | |
154 | | /// The fundamental parsing errors that can be triggered by built-in parsing routines. |
155 | | #[derive(Clone, Debug, PartialEq)] |
156 | | pub struct BasicParseError { |
157 | | /// Details of this error |
158 | | pub kind: BasicParseErrorKind, |
159 | | } |
160 | | |
161 | | impl BasicParseError { |
162 | | /// Create a new BasicParseError of the given kind. |
163 | | #[inline] |
164 | 454k | pub fn new(kind: BasicParseErrorKind) -> Self { |
165 | 454k | Self { kind } |
166 | 454k | } Unexecuted instantiation: <cssparser::parser::BasicParseError>::new <cssparser::parser::BasicParseError>::new Line | Count | Source | 164 | 454k | pub fn new(kind: BasicParseErrorKind) -> Self { | 165 | 454k | Self { kind } | 166 | 454k | } |
|
167 | | |
168 | | /// Create a new BasicParseError for an unexpected token. |
169 | | #[inline] |
170 | 0 | pub fn unexpected_token() -> Self { |
171 | 0 | Self::new(BasicParseErrorKind::UnexpectedToken) |
172 | 0 | } Unexecuted instantiation: <cssparser::parser::BasicParseError>::unexpected_token Unexecuted instantiation: <cssparser::parser::BasicParseError>::unexpected_token |
173 | | } |
174 | | |
175 | | impl<T> From<BasicParseError> for ParseError<T> { |
176 | | #[inline] |
177 | 0 | fn from(this: BasicParseError) -> ParseError<T> { |
178 | 0 | ParseError { |
179 | 0 | kind: ParseErrorKind::Basic(this.kind), |
180 | 0 | } |
181 | 0 | } |
182 | | } |
183 | | |
184 | | /// Details of a `ParseError` |
185 | | #[derive(Clone, Debug, PartialEq)] |
186 | | pub enum ParseErrorKind<T> { |
187 | | /// A fundamental parse error from a built-in parsing routine. |
188 | | Basic(BasicParseErrorKind), |
189 | | /// A parse error reported by downstream consumer code. |
190 | | Custom(T), |
191 | | } |
192 | | |
193 | | impl<T> ParseErrorKind<T> { |
194 | | /// Like `std::convert::Into::into` |
195 | | pub fn into<U>(self) -> ParseErrorKind<U> |
196 | | where |
197 | | T: Into<U>, |
198 | | { |
199 | | match self { |
200 | | ParseErrorKind::Basic(basic) => ParseErrorKind::Basic(basic), |
201 | | ParseErrorKind::Custom(custom) => ParseErrorKind::Custom(custom.into()), |
202 | | } |
203 | | } |
204 | | } |
205 | | |
206 | | impl<E: fmt::Display> fmt::Display for ParseErrorKind<E> { |
207 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
208 | | match self { |
209 | | ParseErrorKind::Basic(basic) => basic.fmt(f), |
210 | | ParseErrorKind::Custom(custom) => custom.fmt(f), |
211 | | } |
212 | | } |
213 | | } |
214 | | |
215 | | /// Extensible parse errors that can be encountered by client parsing implementations. |
216 | | #[derive(Clone, Debug, PartialEq)] |
217 | | pub struct ParseError<E> { |
218 | | /// Details of this error |
219 | | pub kind: ParseErrorKind<E>, |
220 | | } |
221 | | |
222 | | impl<T> ParseError<T> { |
223 | | /// Create a new ParseError from a basic error kind. |
224 | | #[inline] |
225 | 1.17k | pub fn from_basic_kind(kind: BasicParseErrorKind) -> Self { |
226 | 1.17k | Self { |
227 | 1.17k | kind: ParseErrorKind::Basic(kind), |
228 | 1.17k | } |
229 | 1.17k | } |
230 | | |
231 | | /// Create a new ParseError for an unexpected token. |
232 | | #[inline] |
233 | 1.16k | pub fn unexpected_token() -> Self { |
234 | 1.16k | Self::from_basic_kind(BasicParseErrorKind::UnexpectedToken) |
235 | 1.16k | } |
236 | | |
237 | | /// Create a new ParseError from a consumer-defined error. |
238 | | #[inline] |
239 | | pub fn custom<E: Into<T>>(error: E) -> Self { |
240 | | Self { |
241 | | kind: ParseErrorKind::Custom(error.into()), |
242 | | } |
243 | | } |
244 | | |
245 | | /// Extract the fundamental parse error from an extensible error. |
246 | | pub fn basic(self) -> BasicParseError { |
247 | | match self.kind { |
248 | | ParseErrorKind::Basic(kind) => BasicParseError { kind }, |
249 | | ParseErrorKind::Custom(_) => panic!("Not a basic parse error"), |
250 | | } |
251 | | } |
252 | | |
253 | | /// Like `std::convert::Into::into` |
254 | | pub fn into<U>(self) -> ParseError<U> |
255 | | where |
256 | | T: Into<U>, |
257 | | { |
258 | | ParseError { |
259 | | kind: self.kind.into(), |
260 | | } |
261 | | } |
262 | | } |
263 | | |
264 | | impl<E: fmt::Display> fmt::Display for ParseError<E> { |
265 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
266 | | self.kind.fmt(f) |
267 | | } |
268 | | } |
269 | | |
270 | | impl<E: fmt::Display + fmt::Debug> std::error::Error for ParseError<E> {} |
271 | | |
272 | | /// A CSS parser that borrows its `&str` input, yields `Token`s, and keeps track of nested blocks |
273 | | /// and functions. |
274 | | pub struct Parser<'i> { |
275 | | pub(crate) input: &'i str, |
276 | | pub(crate) state: ParserState, |
277 | | cached_token: CachedToken<'i>, |
278 | | current_block_depth: u8, |
279 | | nested_block_limit: u8, |
280 | | /// For parsers from `parse_until` or `parse_nested_block` |
281 | | stop_before: Delimiters, |
282 | | pub(crate) arbitrary_substitution_functions: SeenStatus<'i>, |
283 | | pub(crate) source_map_url: Option<&'i str>, |
284 | | pub(crate) source_url: Option<&'i str>, |
285 | | } |
286 | | |
287 | | struct CachedToken<'i> { |
288 | | token: Token<'i>, |
289 | | start_position: SourcePosition, |
290 | | end_state: ParserState, |
291 | | } |
292 | | |
293 | | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
294 | | #[repr(u8)] |
295 | | pub(crate) enum BlockType { |
296 | | // NOTE: We rely on the discriminants being 1, 2, 3 for the `ParserState` |
297 | | // bit packing (and from_byte). |
298 | | Parenthesis = 1, |
299 | | SquareBracket, |
300 | | CurlyBracket, |
301 | | } |
302 | | |
303 | | impl BlockType { |
304 | | #[inline] |
305 | 132M | fn from_byte(b: u8) -> Option<BlockType> { |
306 | 132M | let ty = match b { |
307 | 293k | 1 => BlockType::Parenthesis, |
308 | 1.09M | 2 => BlockType::SquareBracket, |
309 | 2.97M | 3 => BlockType::CurlyBracket, |
310 | 128M | _ => return None, |
311 | | }; |
312 | 4.36M | debug_assert_eq!(ty as u8, b); |
313 | 4.36M | Some(ty) |
314 | 132M | } <cssparser::parser::BlockType>::from_byte Line | Count | Source | 305 | 444k | fn from_byte(b: u8) -> Option<BlockType> { | 306 | 444k | let ty = match b { | 307 | 18.8k | 1 => BlockType::Parenthesis, | 308 | 8.58k | 2 => BlockType::SquareBracket, | 309 | 194k | 3 => BlockType::CurlyBracket, | 310 | 222k | _ => return None, | 311 | | }; | 312 | 222k | debug_assert_eq!(ty as u8, b); | 313 | 222k | Some(ty) | 314 | 444k | } |
<cssparser::parser::BlockType>::from_byte Line | Count | Source | 305 | 131M | fn from_byte(b: u8) -> Option<BlockType> { | 306 | 131M | let ty = match b { | 307 | 274k | 1 => BlockType::Parenthesis, | 308 | 1.08M | 2 => BlockType::SquareBracket, | 309 | 2.78M | 3 => BlockType::CurlyBracket, | 310 | 127M | _ => return None, | 311 | | }; | 312 | 4.14M | debug_assert_eq!(ty as u8, b); | 313 | 4.14M | Some(ty) | 314 | 131M | } |
|
315 | | |
316 | 1.47M | fn closing(token: &Token) -> Option<BlockType> { |
317 | 1.47M | Some(match *token { |
318 | 17.1k | Token::CloseParenthesis => BlockType::Parenthesis, |
319 | 4.11k | Token::CloseSquareBracket => BlockType::SquareBracket, |
320 | 260k | Token::CloseCurlyBracket => BlockType::CurlyBracket, |
321 | 1.19M | _ => return None, |
322 | | }) |
323 | 1.47M | } |
324 | | } |
325 | | |
326 | | /// A set of characters, to be used with the `Parser::parse_until*` methods. |
327 | | /// |
328 | | /// The union of two sets can be obtained with the `|` operator. Example: |
329 | | /// |
330 | | /// ```rust,ignore |
331 | | /// input.parse_until_before(Delimiter::CurlyBracketBlock | Delimiter::Semicolon) |
332 | | /// ``` |
333 | | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
334 | | pub struct Delimiters { |
335 | | bits: u8, |
336 | | } |
337 | | |
338 | | /// `Delimiters` constants. |
339 | | #[allow(non_upper_case_globals, non_snake_case)] |
340 | | pub mod Delimiter { |
341 | | use super::Delimiters; |
342 | | |
343 | | /// The empty delimiter set |
344 | | pub const None: Delimiters = Delimiters { bits: 0 }; |
345 | | /// The delimiter set with only the `{` opening curly bracket |
346 | | pub const CurlyBracketBlock: Delimiters = Delimiters { bits: 1 << 1 }; |
347 | | /// The delimiter set with only the `;` semicolon |
348 | | pub const Semicolon: Delimiters = Delimiters { bits: 1 << 2 }; |
349 | | /// The delimiter set with only the `!` exclamation point |
350 | | pub const Bang: Delimiters = Delimiters { bits: 1 << 3 }; |
351 | | /// The delimiter set with only the `,` comma |
352 | | pub const Comma: Delimiters = Delimiters { bits: 1 << 4 }; |
353 | | } |
354 | | |
355 | | #[allow(non_upper_case_globals, non_snake_case)] |
356 | | mod ClosingDelimiter { |
357 | | use super::Delimiters; |
358 | | |
359 | | pub const CloseCurlyBracket: Delimiters = Delimiters { bits: 1 << 5 }; |
360 | | pub const CloseSquareBracket: Delimiters = Delimiters { bits: 1 << 6 }; |
361 | | pub const CloseParenthesis: Delimiters = Delimiters { bits: 1 << 7 }; |
362 | | } |
363 | | |
364 | | impl BitOr<Delimiters> for Delimiters { |
365 | | type Output = Delimiters; |
366 | | |
367 | | #[inline] |
368 | | fn bitor(self, other: Delimiters) -> Delimiters { |
369 | | Delimiters { |
370 | | bits: self.bits | other.bits, |
371 | | } |
372 | | } |
373 | | } |
374 | | |
375 | | impl Delimiters { |
376 | | #[inline] |
377 | 126M | fn contains(self, other: Delimiters) -> bool { |
378 | 126M | (self.bits & other.bits) != 0 |
379 | 126M | } |
380 | | |
381 | | #[inline] |
382 | 126M | pub(crate) fn from_byte(byte: u8) -> Delimiters { |
383 | | const TABLE: [Delimiters; 256] = { |
384 | | let mut table = [Delimiter::None; 256]; |
385 | | table[b';' as usize] = Delimiter::Semicolon; |
386 | | table[b'!' as usize] = Delimiter::Bang; |
387 | | table[b',' as usize] = Delimiter::Comma; |
388 | | table[b'{' as usize] = Delimiter::CurlyBracketBlock; |
389 | | table[b'}' as usize] = ClosingDelimiter::CloseCurlyBracket; |
390 | | table[b']' as usize] = ClosingDelimiter::CloseSquareBracket; |
391 | | table[b')' as usize] = ClosingDelimiter::CloseParenthesis; |
392 | | table |
393 | | }; |
394 | | |
395 | 126M | TABLE[byte as usize] |
396 | 126M | } |
397 | | } |
398 | | |
399 | | /// Used in some `fn expect_*` methods |
400 | | macro_rules! expect { |
401 | | ($parser: ident, $($branches: tt)+) => { |
402 | | { |
403 | | match *$parser.next()? { |
404 | | $($branches)+ |
405 | | _ => { |
406 | | return Err(BasicParseError::unexpected_token()) |
407 | | } |
408 | | } |
409 | | } |
410 | | } |
411 | | } |
412 | | |
413 | | /// A list of arbitrary substitution functions. Should be lowercase ascii. |
414 | | /// See https://drafts.csswg.org/css-values-5/#arbitrary-substitution |
415 | | pub type ArbitrarySubstitutionFunctions<'a> = &'a [&'static str]; |
416 | | |
417 | | impl<'i> Parser<'i> { |
418 | | /// 75 nested blocks seems reasonable enough. |
419 | | const REASONABLE_NESTED_BLOCK_LIMIT: u8 = 75; |
420 | | |
421 | | /// Create a new parser for the given input. |
422 | | #[inline] |
423 | 16.4k | pub fn new(input: &'i str) -> Self { |
424 | 16.4k | Self { |
425 | 16.4k | input, |
426 | 16.4k | state: ParserState::default(), |
427 | 16.4k | stop_before: Delimiter::None, |
428 | 16.4k | nested_block_limit: Self::REASONABLE_NESTED_BLOCK_LIMIT, |
429 | 16.4k | current_block_depth: 0, |
430 | 16.4k | cached_token: CachedToken { |
431 | 16.4k | token: Token::Semicolon, // Anything would do. |
432 | 16.4k | start_position: SourcePosition(usize::MAX), // No token would match this cache. |
433 | 16.4k | end_state: ParserState::default(), |
434 | 16.4k | }, |
435 | 16.4k | arbitrary_substitution_functions: SeenStatus::DontCare, |
436 | 16.4k | source_map_url: None, |
437 | 16.4k | source_url: None, |
438 | 16.4k | } |
439 | 16.4k | } <cssparser::parser::Parser>::new Line | Count | Source | 423 | 16.4k | pub fn new(input: &'i str) -> Self { | 424 | 16.4k | Self { | 425 | 16.4k | input, | 426 | 16.4k | state: ParserState::default(), | 427 | 16.4k | stop_before: Delimiter::None, | 428 | 16.4k | nested_block_limit: Self::REASONABLE_NESTED_BLOCK_LIMIT, | 429 | 16.4k | current_block_depth: 0, | 430 | 16.4k | cached_token: CachedToken { | 431 | 16.4k | token: Token::Semicolon, // Anything would do. | 432 | 16.4k | start_position: SourcePosition(usize::MAX), // No token would match this cache. | 433 | 16.4k | end_state: ParserState::default(), | 434 | 16.4k | }, | 435 | 16.4k | arbitrary_substitution_functions: SeenStatus::DontCare, | 436 | 16.4k | source_map_url: None, | 437 | 16.4k | source_url: None, | 438 | 16.4k | } | 439 | 16.4k | } |
Unexecuted instantiation: <cssparser::parser::Parser>::new |
440 | | |
441 | | /// Sets a limit for how many nested blocks we're allowed to parse. This is useful to avoid |
442 | | /// running out of stack space. By default, it's set to `REASONABLE_NESTED_BLOCK_LIMIT`, but it |
443 | | /// can be overridden or cleared. A limit of 0 will be equivalent to no limit at all. |
444 | 0 | pub fn set_nested_block_limit(&mut self, limit: u8) { |
445 | 0 | self.nested_block_limit = limit; |
446 | 0 | } |
447 | | |
448 | | /// Check whether the input is exhausted. That is, if `.next()` would return a token. |
449 | | /// |
450 | | /// This ignores whitespace and comments. |
451 | | #[inline] |
452 | 0 | pub fn is_exhausted(&mut self) -> bool { |
453 | 0 | self.expect_exhausted().is_ok() |
454 | 0 | } |
455 | | |
456 | | /// Check whether the input is exhausted. That is, if `.next()` would return a token. |
457 | | /// Return a `Result` so that the `?` operator can be used: `input.expect_exhausted()?` |
458 | | /// |
459 | | /// This ignores whitespace and comments. |
460 | | #[inline] |
461 | 219k | pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError> { |
462 | 219k | let start = self.state(); |
463 | 219k | let result = match self.next() { |
464 | | Err(BasicParseError { |
465 | | kind: BasicParseErrorKind::EndOfInput, |
466 | | .. |
467 | 219k | }) => Ok(()), |
468 | 0 | Err(e) => unreachable!("Unexpected error encountered: {:?}", e), |
469 | 0 | Ok(_) => Err(BasicParseError::unexpected_token()), |
470 | | }; |
471 | 219k | self.reset(&start); |
472 | 219k | result |
473 | 219k | } <cssparser::parser::Parser>::expect_exhausted Line | Count | Source | 461 | 219k | pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError> { | 462 | 219k | let start = self.state(); | 463 | 219k | let result = match self.next() { | 464 | | Err(BasicParseError { | 465 | | kind: BasicParseErrorKind::EndOfInput, | 466 | | .. | 467 | 219k | }) => Ok(()), | 468 | 0 | Err(e) => unreachable!("Unexpected error encountered: {:?}", e), | 469 | 0 | Ok(_) => Err(BasicParseError::unexpected_token()), | 470 | | }; | 471 | 219k | self.reset(&start); | 472 | 219k | result | 473 | 219k | } |
Unexecuted instantiation: <cssparser::parser::Parser>::expect_exhausted |
474 | | |
475 | | /// Create a new unexpected token or EOF ParseError at the current location |
476 | | #[inline] |
477 | | pub fn new_error_for_next_token<E>(&mut self) -> ParseError<E> { |
478 | | match self.next() { |
479 | | Ok(_) => ParseError::unexpected_token(), |
480 | | Err(e) => e.into(), |
481 | | } |
482 | | } |
483 | | |
484 | | /// Return the current internal state of the parser (including position within the input). |
485 | | /// |
486 | | /// This state can later be restored with the `Parser::reset` method. |
487 | | #[inline] |
488 | 219k | pub fn state(&self) -> ParserState { |
489 | 219k | self.state.clone() |
490 | 219k | } <cssparser::parser::Parser>::state Line | Count | Source | 488 | 219k | pub fn state(&self) -> ParserState { | 489 | 219k | self.state.clone() | 490 | 219k | } |
Unexecuted instantiation: <cssparser::parser::Parser>::state |
491 | | |
492 | | /// Like `next_byte`, but returns `None` if the next byte is one of the delimiters this |
493 | | /// parser was told to stop before. |
494 | | #[inline] |
495 | 126M | pub(crate) fn next_byte_before_delimiter(&self) -> Option<u8> { |
496 | 126M | let byte = self.next_byte()?; |
497 | 126M | if self.stop_before.contains(Delimiters::from_byte(byte)) { |
498 | 407k | return None; |
499 | 125M | } |
500 | 125M | Some(byte) |
501 | 126M | } |
502 | | |
503 | | /// Restore the internal state of the parser (including position within the input) |
504 | | /// to what was previously saved by the `Parser::position` method. |
505 | | /// |
506 | | /// Should only be used with `SourcePosition` values from the same `Parser` instance. |
507 | | #[inline] |
508 | 219k | pub fn reset(&mut self, state: &ParserState) { |
509 | 219k | self.state = state.clone(); |
510 | 219k | } <cssparser::parser::Parser>::reset Line | Count | Source | 508 | 219k | pub fn reset(&mut self, state: &ParserState) { | 509 | 219k | self.state = state.clone(); | 510 | 219k | } |
Unexecuted instantiation: <cssparser::parser::Parser>::reset |
511 | | |
512 | | /// The old name of `try_parse`, which requires raw identifiers in the Rust 2018 edition. |
513 | | #[inline] |
514 | | pub fn r#try<F, T, E>(&mut self, thing: F) -> Result<T, E> |
515 | | where |
516 | | F: FnOnce(&mut Parser<'i>) -> Result<T, E>, |
517 | | { |
518 | | self.try_parse(thing) |
519 | | } |
520 | | |
521 | | /// Execute the given closure, passing it the parser. |
522 | | /// If the result (returned unchanged) is `Err`, |
523 | | /// the internal state of the parser (including position within the input) |
524 | | /// is restored to what it was before the call. |
525 | | #[inline] |
526 | | pub fn try_parse<F, T, E>(&mut self, thing: F) -> Result<T, E> |
527 | | where |
528 | | F: FnOnce(&mut Parser<'i>) -> Result<T, E>, |
529 | | { |
530 | | let start = self.state(); |
531 | | let result = thing(self); |
532 | | if result.is_err() { |
533 | | self.reset(&start) |
534 | | } |
535 | | result |
536 | | } |
537 | | |
538 | | /// Return the next token in the input that is neither whitespace or a comment, |
539 | | /// and advance the position accordingly. |
540 | | /// |
541 | | /// After returning a `Function`, `ParenthesisBlock`, |
542 | | /// `CurlyBracketBlock`, or `SquareBracketBlock` token, |
543 | | /// the next call will skip until after the matching `CloseParenthesis`, |
544 | | /// `CloseCurlyBracket`, or `CloseSquareBracket` token. |
545 | | /// |
546 | | /// See the `Parser::parse_nested_block` method to parse the content of functions or blocks. |
547 | | /// |
548 | | /// This only returns a closing token when it is unmatched (and therefore an error). |
549 | | #[allow(clippy::should_implement_trait)] |
550 | 219k | pub fn next(&mut self) -> Result<&Token<'i>, BasicParseError> { |
551 | 219k | self.skip_whitespace(); |
552 | 219k | self.next_including_whitespace_and_comments() |
553 | 219k | } |
554 | | |
555 | | /// Same as `Parser::next`, but does not skip whitespace tokens. |
556 | 90.7M | pub fn next_including_whitespace(&mut self) -> Result<&Token<'i>, BasicParseError> { |
557 | 125M | while let Token::Comment(..) = self.next_including_whitespace_and_comments()? { |
558 | 35.1M | // Keep going |
559 | 35.1M | } |
560 | 90.5M | Ok(&self.cached_token.token) |
561 | 90.7M | } |
562 | | |
563 | | /// Same as `Parser::next`, but does not skip whitespace or comment tokens. |
564 | | /// |
565 | | /// **Note**: This should only be used in contexts like a CSS pre-processor |
566 | | /// where comments are preserved. |
567 | | /// When parsing higher-level values, per the CSS Syntax specification, |
568 | | /// comments should always be ignored between tokens. |
569 | 126M | pub fn next_including_whitespace_and_comments( |
570 | 126M | &mut self, |
571 | 126M | ) -> Result<&Token<'i>, BasicParseError> { |
572 | 126M | self.skip_block_at_start(); |
573 | | |
574 | 126M | if self.next_byte_before_delimiter().is_none() { |
575 | 454k | return Err(BasicParseError::new(BasicParseErrorKind::EndOfInput)); |
576 | 125M | } |
577 | | |
578 | 125M | let token_start_position = self.position(); |
579 | 125M | let using_cached_token = self.cached_token.start_position == token_start_position; |
580 | 125M | if using_cached_token { |
581 | 0 | self.state = self.cached_token.end_state.clone(); |
582 | 125M | } else { |
583 | 125M | let new_token = self.next_unchecked(); |
584 | 125M | self.cached_token = CachedToken { |
585 | 125M | token: new_token, |
586 | 125M | start_position: token_start_position, |
587 | 125M | end_state: self.state.clone(), |
588 | 125M | }; |
589 | 125M | } |
590 | | |
591 | 125M | Ok(&self.cached_token.token) |
592 | 126M | } |
593 | | |
594 | | /// Have the given closure parse something, then check the the input is exhausted. |
595 | | /// The result is overridden to an `Err(..)` if some input remains. |
596 | | /// |
597 | | /// This can help tell e.g. `color: green;` from `color: green 4px;` |
598 | | #[inline] |
599 | 222k | pub fn parse_entirely<F, T, E>(&mut self, parse: F) -> Result<T, ParseError<E>> |
600 | 222k | where |
601 | 222k | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
602 | | { |
603 | 222k | let result = parse(self)?; |
604 | 219k | self.expect_exhausted()?; |
605 | 219k | Ok(result) |
606 | 222k | } |
607 | | |
608 | | /// Parse a list of comma-separated values, all with the same syntax. |
609 | | /// |
610 | | /// The given closure is called repeatedly with a "delimited" parser |
611 | | /// (see the `Parser::parse_until_before` method) so that it can over |
612 | | /// consume the input past a comma at this block/function nesting level. |
613 | | /// |
614 | | /// Successful results are accumulated in a vector. |
615 | | /// |
616 | | /// This method returns an`Err(..)` the first time that a closure call does, |
617 | | /// or if a closure call leaves some input before the next comma or the end |
618 | | /// of the input. |
619 | | #[inline] |
620 | | pub fn parse_comma_separated<F, T, E>(&mut self, parse_one: F) -> Result<Vec<T>, ParseError<E>> |
621 | | where |
622 | | F: FnMut(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
623 | | { |
624 | | self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ false) |
625 | | } |
626 | | |
627 | | /// Like `parse_comma_separated`, but ignores errors on unknown components, |
628 | | /// rather than erroring out in the whole list. |
629 | | /// |
630 | | /// Caller must deal with the fact that the resulting list might be empty, |
631 | | /// if there's no valid component on the list. |
632 | | #[inline] |
633 | | pub fn parse_comma_separated_ignoring_errors<F, T, E>(&mut self, parse_one: F) -> Vec<T> |
634 | | where |
635 | | F: FnMut(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
636 | | { |
637 | | match self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ true) { |
638 | | Ok(values) => values, |
639 | | Err(..) => unreachable!(), |
640 | | } |
641 | | } |
642 | | |
643 | | #[inline] |
644 | | fn parse_comma_separated_internal<F, T, E>( |
645 | | &mut self, |
646 | | mut parse_one: F, |
647 | | ignore_errors: bool, |
648 | | ) -> Result<Vec<T>, ParseError<E>> |
649 | | where |
650 | | F: FnMut(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
651 | | { |
652 | | // Vec grows from 0 to 4 by default on first push(). So allocate with |
653 | | // capacity 1, so in the somewhat common case of only one item we don't |
654 | | // way overallocate. Note that we always push at least one item if |
655 | | // parsing succeeds. |
656 | | let mut values = Vec::with_capacity(1); |
657 | | loop { |
658 | | self.skip_whitespace(); // Unnecessary for correctness, but may help try() in parse_one rewind less. |
659 | | match self.parse_until_before(Delimiter::Comma, &mut parse_one) { |
660 | | Ok(v) => values.push(v), |
661 | | Err(e) if !ignore_errors => return Err(e), |
662 | | Err(_) => {} |
663 | | } |
664 | | match self.next() { |
665 | | Err(_) => return Ok(values), |
666 | | Ok(&Token::Comma) => continue, |
667 | | Ok(_) => unreachable!(), |
668 | | } |
669 | | } |
670 | | } |
671 | | |
672 | | /// Parse the content of a block or function. |
673 | | /// |
674 | | /// This method panics if the last token yielded by this parser |
675 | | /// (from one of the `next*` methods) |
676 | | /// is not a on that marks the start of a block or function: |
677 | | /// a `Function`, `ParenthesisBlock`, `CurlyBracketBlock`, or `SquareBracketBlock`. |
678 | | /// |
679 | | /// The given closure is called with a "delimited" parser |
680 | | /// that stops at the end of the block or function (at the matching closing token). |
681 | | /// |
682 | | /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. |
683 | | #[inline] |
684 | 222k | pub fn parse_nested_block<F, T, E>(&mut self, parse: F) -> Result<T, ParseError<E>> |
685 | 222k | where |
686 | 222k | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
687 | | { |
688 | 222k | parse_nested_block(self, parse) |
689 | 222k | } |
690 | | |
691 | | /// Limit parsing to until a given delimiter or the end of the input. (E.g. |
692 | | /// a semicolon for a property value.) |
693 | | /// |
694 | | /// The given closure is called with a "delimited" parser |
695 | | /// that stops before the first character at this block/function nesting level |
696 | | /// that matches the given set of delimiters, or at the end of the input. |
697 | | /// |
698 | | /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. |
699 | | #[inline] |
700 | | pub fn parse_until_before<F, T, E>( |
701 | | &mut self, |
702 | | delimiters: Delimiters, |
703 | | parse: F, |
704 | | ) -> Result<T, ParseError<E>> |
705 | | where |
706 | | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
707 | | { |
708 | | parse_until_before(self, delimiters, ParseUntilErrorBehavior::Consume, parse) |
709 | | } |
710 | | |
711 | | /// Like `parse_until_before`, but also consume the delimiter token. |
712 | | /// |
713 | | /// This can be useful when you don’t need to know which delimiter it was |
714 | | /// (e.g. if these is only one in the given set) |
715 | | /// or if it was there at all (as opposed to reaching the end of the input). |
716 | | #[inline] |
717 | | pub fn parse_until_after<F, T, E>( |
718 | | &mut self, |
719 | | delimiters: Delimiters, |
720 | | parse: F, |
721 | | ) -> Result<T, ParseError<E>> |
722 | | where |
723 | | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
724 | | { |
725 | | parse_until_after(self, delimiters, ParseUntilErrorBehavior::Consume, parse) |
726 | | } |
727 | | |
728 | | /// Parse a <whitespace-token> and return its value. |
729 | | #[inline] |
730 | | pub fn expect_whitespace(&mut self) -> Result<&'i str, BasicParseError> { |
731 | | match *self.next_including_whitespace()? { |
732 | | Token::WhiteSpace(value) => Ok(value), |
733 | | _ => Err(BasicParseError::unexpected_token()), |
734 | | } |
735 | | } |
736 | | |
737 | | /// Parse a <ident-token> and return the unescaped value. |
738 | | #[inline] |
739 | 0 | pub fn expect_ident(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { |
740 | 0 | expect! {self, |
741 | 0 | Token::Ident(ref value) => Ok(value), |
742 | | } |
743 | 0 | } |
744 | | |
745 | | /// expect_ident, but clone the CowRcStr |
746 | | #[inline] |
747 | | pub fn expect_ident_cloned(&mut self) -> Result<CowRcStr<'i>, BasicParseError> { |
748 | | self.expect_ident().cloned() |
749 | | } |
750 | | |
751 | | /// Parse a <ident-token> whose unescaped value is an ASCII-insensitive match for the given value. |
752 | | #[inline] |
753 | 0 | pub fn expect_ident_matching(&mut self, expected_value: &str) -> Result<(), BasicParseError> { |
754 | 0 | expect! {self, |
755 | 0 | Token::Ident(ref value) if value.eq_ignore_ascii_case(expected_value) => Ok(()), |
756 | | } |
757 | 0 | } |
758 | | |
759 | | /// Parse a <string-token> and return the unescaped value. |
760 | | #[inline] |
761 | | pub fn expect_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { |
762 | | expect! {self, |
763 | | Token::QuotedString(ref value) => Ok(value), |
764 | | } |
765 | | } |
766 | | |
767 | | /// expect_string, but clone the CowRcStr |
768 | | #[inline] |
769 | | pub fn expect_string_cloned(&mut self) -> Result<CowRcStr<'i>, BasicParseError> { |
770 | | self.expect_string().cloned() |
771 | | } |
772 | | |
773 | | /// Parse either a <ident-token> or a <string-token>, and return the unescaped value. |
774 | | #[inline] |
775 | | pub fn expect_ident_or_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { |
776 | | expect! {self, |
777 | | Token::Ident(ref value) => Ok(value), |
778 | | Token::QuotedString(ref value) => Ok(value), |
779 | | } |
780 | | } |
781 | | |
782 | | /// Parse a <url-token> and return the unescaped value. |
783 | | #[inline] |
784 | | pub fn expect_url(&mut self) -> Result<CowRcStr<'i>, BasicParseError> { |
785 | | expect! {self, |
786 | | Token::UnquotedUrl(ref value) => Ok(value.clone()), |
787 | | Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { |
788 | | self.parse_nested_block(|input| { |
789 | | input.expect_string().map_err(Into::into).cloned() |
790 | | }) |
791 | | .map_err(ParseError::<()>::basic) |
792 | | } |
793 | | } |
794 | | } |
795 | | |
796 | | /// Parse either a <url-token> or a <string-token>, and return the unescaped value. |
797 | | #[inline] |
798 | | pub fn expect_url_or_string(&mut self) -> Result<CowRcStr<'i>, BasicParseError> { |
799 | | expect! {self, |
800 | | Token::UnquotedUrl(ref value) => Ok(value.clone()), |
801 | | Token::QuotedString(ref value) => Ok(value.clone()), |
802 | | Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { |
803 | | self.parse_nested_block(|input| { |
804 | | input.expect_string().map_err(Into::into).cloned() |
805 | | }) |
806 | | .map_err(ParseError::<()>::basic) |
807 | | } |
808 | | } |
809 | | } |
810 | | |
811 | | /// Parse a <number-token> and return the integer value. |
812 | | #[inline] |
813 | | pub fn expect_number(&mut self) -> Result<f32, BasicParseError> { |
814 | | expect! {self, |
815 | | Token::Number { value, .. } => Ok(value), |
816 | | } |
817 | | } |
818 | | |
819 | | /// Parse a <number-token> that does not have a fractional part, and return the integer value. |
820 | | #[inline] |
821 | | pub fn expect_integer(&mut self) -> Result<i32, BasicParseError> { |
822 | | expect! {self, |
823 | | Token::Number { int_value: Some(int_value), .. } => Ok(int_value), |
824 | | } |
825 | | } |
826 | | |
827 | | /// Parse a <percentage-token> and return the value. |
828 | | /// `0%` and `100%` map to `0.0` and `1.0` (not `100.0`), respectively. |
829 | | #[inline] |
830 | | pub fn expect_percentage(&mut self) -> Result<f32, BasicParseError> { |
831 | | expect! {self, |
832 | | Token::Percentage { unit_value, .. } => Ok(unit_value), |
833 | | } |
834 | | } |
835 | | |
836 | | /// Parse a `:` <colon-token>. |
837 | | #[inline] |
838 | 0 | pub fn expect_colon(&mut self) -> Result<(), BasicParseError> { |
839 | 0 | expect! {self, |
840 | 0 | Token::Colon => Ok(()), |
841 | | } |
842 | 0 | } |
843 | | |
844 | | /// Parse a `;` <semicolon-token>. |
845 | | #[inline] |
846 | | pub fn expect_semicolon(&mut self) -> Result<(), BasicParseError> { |
847 | | expect! {self, |
848 | | Token::Semicolon => Ok(()), |
849 | | } |
850 | | } |
851 | | |
852 | | /// Parse a `,` <comma-token>. |
853 | | #[inline] |
854 | | pub fn expect_comma(&mut self) -> Result<(), BasicParseError> { |
855 | | expect! {self, |
856 | | Token::Comma => Ok(()), |
857 | | } |
858 | | } |
859 | | |
860 | | /// Parse a <delim-token> with the given value. |
861 | | #[inline] |
862 | 0 | pub fn expect_delim(&mut self, expected_value: char) -> Result<(), BasicParseError> { |
863 | 0 | expect! {self, |
864 | 0 | Token::Delim(value) if value == expected_value => Ok(()), |
865 | | } |
866 | 0 | } |
867 | | |
868 | | /// Parse a `{ /* ... */ }` curly brackets block. |
869 | | /// |
870 | | /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. |
871 | | #[inline] |
872 | | pub fn expect_curly_bracket_block(&mut self) -> Result<(), BasicParseError> { |
873 | | expect! {self, |
874 | | Token::CurlyBracketBlock => Ok(()), |
875 | | } |
876 | | } |
877 | | |
878 | | /// Parse a `[ /* ... */ ]` square brackets block. |
879 | | /// |
880 | | /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. |
881 | | #[inline] |
882 | | pub fn expect_square_bracket_block(&mut self) -> Result<(), BasicParseError> { |
883 | | expect! {self, |
884 | | Token::SquareBracketBlock => Ok(()), |
885 | | } |
886 | | } |
887 | | |
888 | | /// Parse a `( /* ... */ )` parenthesis block. |
889 | | /// |
890 | | /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. |
891 | | #[inline] |
892 | | pub fn expect_parenthesis_block(&mut self) -> Result<(), BasicParseError> { |
893 | | expect! {self, |
894 | | Token::ParenthesisBlock => Ok(()), |
895 | | } |
896 | | } |
897 | | |
898 | | /// Parse a <function> token and return its name. |
899 | | /// |
900 | | /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. |
901 | | #[inline] |
902 | | pub fn expect_function(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { |
903 | | expect! {self, |
904 | | Token::Function(ref name) => Ok(name), |
905 | | } |
906 | | } |
907 | | |
908 | | /// Parse a <function> token whose name is an ASCII-insensitive match for the given value. |
909 | | /// |
910 | | /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. |
911 | | #[inline] |
912 | | pub fn expect_function_matching(&mut self, expected_name: &str) -> Result<(), BasicParseError> { |
913 | | expect! {self, |
914 | | Token::Function(ref name) if name.eq_ignore_ascii_case(expected_name) => Ok(()), |
915 | | } |
916 | | } |
917 | | |
918 | | /// Parse the input until exhaustion and check that it contains no “error” token. |
919 | | /// |
920 | | /// See `Token::is_parse_error`. This also checks nested blocks and functions recursively. |
921 | | #[inline] |
922 | | pub fn expect_no_error_token(&mut self) -> Result<(), BasicParseError> { |
923 | | loop { |
924 | | match self.next_including_whitespace_and_comments() { |
925 | | Ok(&Token::Function(_)) |
926 | | | Ok(&Token::ParenthesisBlock) |
927 | | | Ok(&Token::SquareBracketBlock) |
928 | | | Ok(&Token::CurlyBracketBlock) => self |
929 | | .parse_nested_block(|input| input.expect_no_error_token().map_err(Into::into)) |
930 | | .map_err(ParseError::<()>::basic)?, |
931 | | Ok(t) => { |
932 | | // FIXME: maybe these should be separate variants of |
933 | | // BasicParseError instead? |
934 | | if t.is_parse_error() { |
935 | | return Err(BasicParseError::unexpected_token()); |
936 | | } |
937 | | } |
938 | | Err(_) => return Ok(()), |
939 | | } |
940 | | } |
941 | | } |
942 | | } |
943 | | |
944 | | pub fn parse_until_before<'i, F, T, E>( |
945 | | parser: &mut Parser<'i>, |
946 | | delimiters: Delimiters, |
947 | | error_behavior: ParseUntilErrorBehavior, |
948 | | parse: F, |
949 | | ) -> Result<T, ParseError<E>> |
950 | | where |
951 | | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
952 | | { |
953 | | let old_stop_before = parser.stop_before; |
954 | | let delimiters = parser.stop_before | delimiters; |
955 | | parser.stop_before = delimiters; |
956 | | let result = parser.parse_entirely(parse); |
957 | | parser.stop_before = old_stop_before; |
958 | | if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { |
959 | | return result; |
960 | | } |
961 | | parser.skip_block_at_start(); |
962 | | // FIXME: have a special-purpose tokenizer method for this that does less work. |
963 | | while let Some(next_byte) = parser.next_byte() { |
964 | | if delimiters.contains(Delimiters::from_byte(next_byte)) { |
965 | | break; |
966 | | } |
967 | | parser.next_unchecked(); |
968 | | parser.skip_block_at_start(); |
969 | | } |
970 | | result |
971 | | } |
972 | | |
973 | | pub fn parse_until_after<'i, F, T, E>( |
974 | | parser: &mut Parser<'i>, |
975 | | delimiters: Delimiters, |
976 | | error_behavior: ParseUntilErrorBehavior, |
977 | | parse: F, |
978 | | ) -> Result<T, ParseError<E>> |
979 | | where |
980 | | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
981 | | { |
982 | | let result = parse_until_before(parser, delimiters, error_behavior, parse); |
983 | | if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { |
984 | | return result; |
985 | | } |
986 | | if let Some(next_byte) = parser.next_byte() { |
987 | | let delimiter = Delimiters::from_byte(next_byte); |
988 | | if !parser.stop_before.contains(delimiter) { |
989 | | debug_assert!(delimiters.contains(delimiter)); |
990 | | // We know this byte is ASCII. |
991 | | parser.advance(1); |
992 | | if next_byte == b'{' { |
993 | | parser.consume_until_end_of_block(BlockType::CurlyBracket); |
994 | | } |
995 | | } |
996 | | } |
997 | | result |
998 | | } |
999 | | |
1000 | 222k | pub fn parse_nested_block<'i, F, T, E>( |
1001 | 222k | parser: &mut Parser<'i>, |
1002 | 222k | parse: F, |
1003 | 222k | ) -> Result<T, ParseError<E>> |
1004 | 222k | where |
1005 | 222k | F: FnOnce(&mut Parser<'i>) -> Result<T, ParseError<E>>, |
1006 | | { |
1007 | 222k | let block_type = parser.state.take_at_start_of().expect( |
1008 | 222k | "\ |
1009 | 222k | A nested parser can only be created when a Function, \ |
1010 | 222k | ParenthesisBlock, SquareBracketBlock, or CurlyBracketBlock \ |
1011 | 222k | token was just consumed.\ |
1012 | 222k | ", |
1013 | | ); |
1014 | 222k | if parser.current_block_depth >= parser.nested_block_limit && parser.nested_block_limit != 0 { |
1015 | 12 | return Err(ParseError::from_basic_kind( |
1016 | 12 | BasicParseErrorKind::TooManyNestedBlocks, |
1017 | 12 | )); |
1018 | 222k | } |
1019 | | // Fine to use wrapping addition, overflow can only occur without a limit. |
1020 | 222k | parser.current_block_depth = parser.current_block_depth.wrapping_add(1); |
1021 | | |
1022 | 222k | let old_stop_before = parser.stop_before; |
1023 | 222k | parser.stop_before = match block_type { |
1024 | 194k | BlockType::CurlyBracket => ClosingDelimiter::CloseCurlyBracket, |
1025 | 8.58k | BlockType::SquareBracket => ClosingDelimiter::CloseSquareBracket, |
1026 | 18.8k | BlockType::Parenthesis => ClosingDelimiter::CloseParenthesis, |
1027 | | }; |
1028 | 222k | let result = parser.parse_entirely(parse); |
1029 | 222k | parser.skip_block_at_start(); |
1030 | 222k | parser.consume_until_end_of_block(block_type); |
1031 | 222k | parser.stop_before = old_stop_before; |
1032 | 222k | parser.current_block_depth = parser.current_block_depth.wrapping_sub(1); |
1033 | 222k | result |
1034 | 222k | } |
1035 | | |
1036 | | impl Parser<'_> { |
1037 | | /// Consume tokens until the end of a block of the given type that we're at the start of, |
1038 | | /// ignoring any `stop_before` delimiters. |
1039 | | #[inline(never)] |
1040 | | #[cold] |
1041 | 222k | pub(crate) fn consume_until_end_of_block(&mut self, block_type: BlockType) { |
1042 | 222k | let mut stack = SmallVec::<[BlockType; 16]>::new(); |
1043 | 222k | stack.push(block_type); |
1044 | | |
1045 | | // FIXME: have a special-purpose tokenizer method for this that does less work. |
1046 | 5.63M | while !self.is_eof() { |
1047 | 5.62M | let token = self.next_unchecked(); |
1048 | 5.62M | if let Some(nested_block_type) = self.state.take_at_start_of() { |
1049 | 4.14M | stack.push(nested_block_type); |
1050 | 4.14M | continue; |
1051 | 1.47M | } |
1052 | 1.47M | if let Some(b) = BlockType::closing(&token) { |
1053 | 282k | if *stack.last().unwrap() == b { |
1054 | 264k | stack.pop(); |
1055 | 264k | if stack.is_empty() { |
1056 | 204k | return; |
1057 | 60.3k | } |
1058 | 17.4k | } |
1059 | 1.19M | } |
1060 | | } |
1061 | 222k | } |
1062 | | } |