/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
usecrate::cow_rc_str::CowRcStr; usecrate::tokenizer::{SourceLocation, SourcePosition, Token, Tokenizer}; use smallvec::SmallVec; use std::fmt; use std::ops::BitOr; use std::ops::Range;
/// A capture of the internal state of a `Parser` (including the position within the input), /// obtained from the `Parser::position` method. /// /// Can be used with the `Parser::reset` method to restore that state. /// Should only be used with the `Parser` instance it came from. #[derive(Debug, Clone)] pubstruct ParserState { pub(crate) position: usize, pub(crate) current_line_start_position: usize, pub(crate) current_line_number: u32, pub(crate) at_start_of: Option<BlockType>,
}
impl ParserState { /// The position from the start of the input, counted in UTF-8 bytes. #[inline] pubfn position(&self) -> SourcePosition {
SourcePosition(self.position)
}
/// The line number and column number #[inline] pubfn source_location(&self) -> SourceLocation {
SourceLocation {
line: self.current_line_number,
column: (self.position - self.current_line_start_position + 1) as u32,
}
}
}
/// When parsing until a given token, sometimes the caller knows that parsing is going to restart /// at some earlier point, and consuming until we find a top level delimiter is just wasted work. /// /// In that case, callers can pass ParseUntilErrorBehavior::Stop to avoid doing all that wasted /// work. /// /// This is important for things like CSS nesting, where something like: /// /// foo:is(..) { /// ... /// } /// /// Would need to scan the whole {} block to find a semicolon, only for parsing getting restarted /// as a qualified rule later. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubenum ParseUntilErrorBehavior { /// Consume until we see the relevant delimiter or the end of the stream.
Consume, /// Eagerly error.
Stop,
}
/// Details about a `BasicParseError` #[derive(Clone, Debug, PartialEq)] pubenum BasicParseErrorKind<'i> { /// An unexpected token was encountered.
UnexpectedToken(Token<'i>), /// The end of the input was encountered unexpectedly.
EndOfInput, /// An `@` rule was encountered that was invalid.
AtRuleInvalid(CowRcStr<'i>), /// The body of an '@' rule was invalid.
AtRuleBodyInvalid, /// A qualified rule was encountered that was invalid.
QualifiedRuleInvalid,
}
/// The fundamental parsing errors that can be triggered by built-in parsing routines. #[derive(Clone, Debug, PartialEq)] pubstruct BasicParseError<'i> { /// Details of this error pub kind: BasicParseErrorKind<'i>, /// Location where this error occurred pub location: SourceLocation,
}
impl SourceLocation { /// Create a new BasicParseError at this location for an unexpected token #[inline] pubfn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> { self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token))
}
/// Create a new BasicParseError at this location #[inline] pubfn new_basic_error(self, kind: BasicParseErrorKind<'_>) -> BasicParseError<'_> {
BasicParseError {
kind,
location: self,
}
}
/// Create a new ParseError at this location for an unexpected token #[inline] pubfn new_unexpected_token_error<E>(self, token: Token<'_>) -> ParseError<'_, E> { self.new_error(BasicParseErrorKind::UnexpectedToken(token))
}
/// Create a new basic ParseError at the current location #[inline] pubfn new_error<E>(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> {
ParseError {
kind: ParseErrorKind::Basic(kind),
location: self,
}
}
/// Create a new custom ParseError at this location #[inline] pubfn new_custom_error<'i, E1: Into<E2>, E2>(self, error: E1) -> ParseError<'i, E2> {
ParseError {
kind: ParseErrorKind::Custom(error.into()),
location: self,
}
}
}
/// Details of a `ParseError` #[derive(Clone, Debug, PartialEq)] pubenum ParseErrorKind<'i, T: 'i> { /// A fundamental parse error from a built-in parsing routine.
Basic(BasicParseErrorKind<'i>), /// A parse error reported by downstream consumer code.
Custom(T),
}
/// Extensible parse errors that can be encountered by client parsing implementations. #[derive(Clone, Debug, PartialEq)] pubstruct ParseError<'i, E> { /// Details of this error pub kind: ParseErrorKind<'i, E>, /// Location where this error occurred pub location: SourceLocation,
}
impl<'i, T> ParseError<'i, T> { /// Extract the fundamental parse error from an extensible error. pubfn basic(self) -> BasicParseError<'i> { matchself.kind {
ParseErrorKind::Basic(kind) => BasicParseError {
kind,
location: self.location,
},
ParseErrorKind::Custom(_) => panic!("Not a basic parse error"),
}
}
/// A CSS parser that borrows its `&str` input, /// yields `Token`s, /// and keeps track of nested blocks and functions. pubstruct Parser<'i, 't> {
input: &'t mut ParserInput<'i>, /// If `Some(_)`, .parse_nested_block() can be called.
at_start_of: Option<BlockType>, /// For parsers from `parse_until` or `parse_nested_block`
stop_before: Delimiters,
}
/// A set of characters, to be used with the `Parser::parse_until*` methods. /// /// The union of two sets can be obtained with the `|` operator. Example: /// /// ```{rust,ignore} /// input.parse_until_before(Delimiter::CurlyBracketBlock | Delimiter::Semicolon) /// ``` #[derive(Copy, Clone, PartialEq, Eq, Debug)] pubstruct Delimiters {
bits: u8,
}
/// The empty delimiter set pubconst None: Delimiters = Delimiters { bits: 0 }; /// The delimiter set with only the `{` opening curly bracket pubconst CurlyBracketBlock: Delimiters = Delimiters { bits: 1 << 1 }; /// The delimiter set with only the `;` semicolon pubconst Semicolon: Delimiters = Delimiters { bits: 1 << 2 }; /// The delimiter set with only the `!` exclamation point pubconst Bang: Delimiters = Delimiters { bits: 1 << 3 }; /// The delimiter set with only the `,` comma pubconst Comma: Delimiters = Delimiters { bits: 1 << 4 };
}
#[allow(non_upper_case_globals, non_snake_case)] mod ClosingDelimiter { usesuper::Delimiters;
/// Return the current line that is being parsed. pubfn current_line(&self) -> &'i str { self.input.tokenizer.current_source_line()
}
/// Check whether the input is exhausted. That is, if `.next()` would return a token. /// /// This ignores whitespace and comments. #[inline] pubfn is_exhausted(&mutself) -> bool { self.expect_exhausted().is_ok()
}
/// Check whether the input is exhausted. That is, if `.next()` would return a token. /// Return a `Result` so that the `?` operator can be used: `input.expect_exhausted()?` /// /// This ignores whitespace and comments. #[inline] pubfn expect_exhausted(&mutself) -> Result<(), BasicParseError<'i>> { let start = self.state(); let result = matchself.next() {
Err(BasicParseError {
kind: BasicParseErrorKind::EndOfInput,
..
}) => Ok(()),
Err(e) => unreachable!("Unexpected error encountered: {:?}", e),
Ok(t) => Err(start
.source_location()
.new_basic_unexpected_token_error(t.clone())),
}; self.reset(&start);
result
}
/// Return the current position within the input. /// /// This can be used with the `Parser::slice` and `slice_from` methods. #[inline] pubfn position(&self) -> SourcePosition { self.input.tokenizer.position()
}
/// The current line number and column number. #[inline] pubfn current_source_location(&self) -> SourceLocation { self.input.tokenizer.current_source_location()
}
/// The source map URL, if known. /// /// The source map URL is extracted from a specially formatted /// comment. The last such comment is used, so this value may /// change as parsing proceeds. pubfn current_source_map_url(&self) -> Option<&str> { self.input.tokenizer.current_source_map_url()
}
/// The source URL, if known. /// /// The source URL is extracted from a specially formatted /// comment. The last such comment is used, so this value may /// change as parsing proceeds. pubfn current_source_url(&self) -> Option<&str> { self.input.tokenizer.current_source_url()
}
/// Create a new BasicParseError at the current location #[inline] pubfn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> { self.current_source_location().new_basic_error(kind)
}
/// Create a new basic ParseError at the current location #[inline] pubfn new_error<E>(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> { self.current_source_location().new_error(kind)
}
/// Create a new custom BasicParseError at the current location #[inline] pubfn new_custom_error<E1: Into<E2>, E2>(&self, error: E1) -> ParseError<'i, E2> { self.current_source_location().new_custom_error(error)
}
/// Create a new unexpected token BasicParseError at the current location #[inline] pubfn new_basic_unexpected_token_error(&self, token: Token<'i>) -> BasicParseError<'i> { self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token))
}
/// Create a new unexpected token ParseError at the current location #[inline] pubfn new_unexpected_token_error<E>(&self, token: Token<'i>) -> ParseError<'i, E> { self.new_error(BasicParseErrorKind::UnexpectedToken(token))
}
/// Create a new unexpected token or EOF ParseError at the current location #[inline] pubfn new_error_for_next_token<E>(&mutself) -> ParseError<'i, E> { let token = matchself.next() {
Ok(token) => token.clone(),
Err(e) => return e.into(),
}; self.new_error(BasicParseErrorKind::UnexpectedToken(token))
}
/// Return the current internal state of the parser (including position within the input). /// /// This state can later be restored with the `Parser::reset` method. #[inline] pubfn state(&self) -> ParserState {
ParserState {
at_start_of: self.at_start_of,
..self.input.tokenizer.state()
}
}
/// Advance the input until the next token that’s not whitespace or a comment. #[inline] pubfn skip_whitespace(&mutself) { iflet Some(block_type) = self.at_start_of.take() {
consume_until_end_of_block(block_type, &mutself.input.tokenizer);
}
/// Restore the internal state of the parser (including position within the input) /// to what was previously saved by the `Parser::position` method. /// /// Should only be used with `SourcePosition` values from the same `Parser` instance. #[inline] pubfn reset(&mutself, state: &ParserState) { self.input.tokenizer.reset(state); self.at_start_of = state.at_start_of;
}
/// Start looking for `var()` / `env()` functions. (See the /// `.seen_var_or_env_functions()` method.) #[inline] pubfn look_for_var_or_env_functions(&mutself) { self.input.tokenizer.look_for_var_or_env_functions()
}
/// Return whether a `var()` or `env()` function has been seen by the /// tokenizer since either `look_for_var_or_env_functions` was called, and /// stop looking. #[inline] pubfn seen_var_or_env_functions(&mutself) -> bool { self.input.tokenizer.seen_var_or_env_functions()
}
/// The old name of `try_parse`, which requires raw identifiers in the Rust 2018 edition. #[inline] pubfn r#try<F, T, E>(&mutself, thing: F) -> Result<T, E> where
F: FnOnce(&mut Parser<'i, 't>) -> Result<T, E>,
{ self.try_parse(thing)
}
/// Execute the given closure, passing it the parser. /// If the result (returned unchanged) is `Err`, /// the internal state of the parser (including position within the input) /// is restored to what it was before the call. #[inline] pubfn try_parse<F, T, E>(&mutself, thing: F) -> Result<T, E> where
F: FnOnce(&mut Parser<'i, 't>) -> Result<T, E>,
{ let start = self.state(); let result = thing(self); if result.is_err() { self.reset(&start)
}
result
}
/// Return a slice of the CSS input #[inline] pubfn slice(&self, range: Range<SourcePosition>) -> &'i str { self.input.tokenizer.slice(range)
}
/// Return a slice of the CSS input, from the given position to the current one. #[inline] pubfn slice_from(&self, start_position: SourcePosition) -> &style='color:blue'>'i str { self.input.tokenizer.slice_from(start_position)
}
/// Return the next token in the input that is neither whitespace or a comment, /// and advance the position accordingly. /// /// After returning a `Function`, `ParenthesisBlock`, /// `CurlyBracketBlock`, or `SquareBracketBlock` token, /// the next call will skip until after the matching `CloseParenthesis`, /// `CloseCurlyBracket`, or `CloseSquareBracket` token. /// /// See the `Parser::parse_nested_block` method to parse the content of functions or blocks. /// /// This only returns a closing token when it is unmatched (and therefore an error). #[allow(clippy::should_implement_trait)] pubfn next(&mutself) -> Result<&Token<'i>, BasicParseError<'i>> { self.skip_whitespace(); self.next_including_whitespace_and_comments()
}
/// Same as `Parser::next`, but does not skip whitespace tokens. pubfn next_including_whitespace(&mutself) -> Result<&Token<'i>, BasicParseError<'i>> { loop { matchself.next_including_whitespace_and_comments() {
Err(e) => return Err(e),
Ok(&Token::Comment(_)) => {}
_ => break,
}
}
Ok(self.input.cached_token_ref())
}
/// Same as `Parser::next`, but does not skip whitespace or comment tokens. /// /// **Note**: This should only be used in contexts like a CSS pre-processor /// where comments are preserved. /// When parsing higher-level values, per the CSS Syntax specification, /// comments should always be ignored between tokens. pubfn next_including_whitespace_and_comments(
&mutself,
) -> Result<&Token<'i>, BasicParseError<'i>> { iflet Some(block_type) = self.at_start_of.take() {
consume_until_end_of_block(block_type, &mutself.input.tokenizer);
}
let byte = self.input.tokenizer.next_byte(); ifself.stop_before.contains(Delimiters::from_byte(byte)) { return Err(self.new_basic_error(BasicParseErrorKind::EndOfInput));
}
/// Have the given closure parse something, then check the the input is exhausted. /// The result is overridden to an `Err(..)` if some input remains. /// /// This can help tell e.g. `color: green;` from `color: green 4px;` #[inline] pubfn parse_entirely<F, T, E>(&mutself, parse: F) -> Result<T, ParseError<'i, E>> where
F: FnOnce(&mut Parser<'i, 't>) -> Result<T, ParseError<'i, E>>,
{ let result = parse(self)?; self.expect_exhausted()?;
Ok(result)
}
/// Parse a list of comma-separated values, all with the same syntax. /// /// The given closure is called repeatedly with a "delimited" parser /// (see the `Parser::parse_until_before` method) so that it can over /// consume the input past a comma at this block/function nesting level. /// /// Successful results are accumulated in a vector. /// /// This method returns an`Err(..)` the first time that a closure call does, /// or if a closure call leaves some input before the next comma or the end /// of the input. #[inline] pubfn parse_comma_separated<F, T, E>(
&mutself,
parse_one: F,
) -> Result<Vec<T>, ParseError<'i, E>> where
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{ self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ false)
}
/// Like `parse_comma_separated`, but ignores errors on unknown components, /// rather than erroring out in the whole list. /// /// Caller must deal with the fact that the resulting list might be empty, /// if there's no valid component on the list. #[inline] pubfn parse_comma_separated_ignoring_errors<F, T, E: 'i>(&mut self, parse_one: F) -> Vec<T> where
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{ matchself.parse_comma_separated_internal(parse_one, /* ignore_errors = */ true) {
Ok(values) => values,
Err(..) => unreachable!(),
}
}
#[inline] fn parse_comma_separated_internal<F, T, E>(
&mutself, mut parse_one: F,
ignore_errors: bool,
) -> Result<Vec<T>, ParseError<'i, E>> where
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{ // 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 // way overallocate. Note that we always push at least one item if // parsing succeeds. letmut values = Vec::with_capacity(1); loop { self.skip_whitespace(); // Unnecessary for correctness, but may help try() in parse_one rewind less. matchself.parse_until_before(Delimiter::Comma, &mut parse_one) {
Ok(v) => values.push(v),
Err(e) if !ignore_errors => return Err(e),
Err(_) => {}
} matchself.next() {
Err(_) => return Ok(values),
Ok(&Token::Comma) => continue,
Ok(_) => unreachable!(),
}
}
}
/// Parse the content of a block or function. /// /// This method panics if the last token yielded by this parser /// (from one of the `next*` methods) /// is not a on that marks the start of a block or function: /// a `Function`, `ParenthesisBlock`, `CurlyBracketBlock`, or `SquareBracketBlock`. /// /// The given closure is called with a "delimited" parser /// that stops at the end of the block or function (at the matching closing token). /// /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. #[inline] pubfn parse_nested_block<F, T, E>(&mutself, parse: F) -> Result<T, ParseError<'i, E>> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{
parse_nested_block(self, parse)
}
/// Limit parsing to until a given delimiter or the end of the input. (E.g. /// a semicolon for a property value.) /// /// The given closure is called with a "delimited" parser /// that stops before the first character at this block/function nesting level /// that matches the given set of delimiters, or at the end of the input. /// /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. #[inline] pubfn parse_until_before<F, T, E>(
&mutself,
delimiters: Delimiters,
parse: F,
) -> Result<T, ParseError<'i, E>> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{
parse_until_before(self, delimiters, ParseUntilErrorBehavior::Consume, parse)
}
/// Like `parse_until_before`, but also consume the delimiter token. /// /// This can be useful when you don’t need to know which delimiter it was /// (e.g. if these is only one in the given set) /// or if it was there at all (as opposed to reaching the end of the input). #[inline] pubfn parse_until_after<F, T, E>(
&mutself,
delimiters: Delimiters,
parse: F,
) -> Result<T, ParseError<'i, E>> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{
parse_until_after(self, delimiters, ParseUntilErrorBehavior::Consume, parse)
}
/// Parse a <whitespace-token> and return its value. #[inline] pubfn expect_whitespace(&mutself) -> Result<&'i str, BasicParseError<'i>> { let start_location = self.current_source_location(); match *self.next_including_whitespace()? {
Token::WhiteSpace(value) => Ok(value), ref t => Err(start_location.new_basic_unexpected_token_error(t.clone())),
}
}
/// Parse a <ident-token> and return the unescaped value. #[inline] pubfn expect_ident(&mutself) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
expect! {self,
Token::Ident(ref value) => Ok(value),
}
}
/// expect_ident, but clone the CowRcStr #[inline] pubfn expect_ident_cloned(&mutself) -> Result<CowRcStr<'i>, BasicParseError<'i>> { self.expect_ident().cloned()
}
/// Parse a <ident-token> whose unescaped value is an ASCII-insensitive match for the given value. #[inline] pubfn expect_ident_matching(
&mutself,
expected_value: &str,
) -> Result<(), BasicParseError<'i>> {
expect! {self,
Token::Ident(ref value) if value.eq_ignore_ascii_case(expected_value) => Ok(()),
}
}
/// Parse a <string-token> and return the unescaped value. #[inline] pubfn expect_string(&mutself) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
expect! {self,
Token::QuotedString(ref value) => Ok(value),
}
}
/// expect_string, but clone the CowRcStr #[inline] pubfn expect_string_cloned(&mutself) -> Result<CowRcStr<'i>, BasicParseError<'i>> { self.expect_string().cloned()
}
/// Parse either a <ident-token> or a <string-token>, and return the unescaped value. #[inline] pubfn expect_ident_or_string(&mutself) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
expect! {self,
Token::Ident(ref value) => Ok(value),
Token::QuotedString(ref value) => Ok(value),
}
}
/// Parse a <url-token> and return the unescaped value. #[inline] pubfn expect_url(&mutself) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
expect! {self,
Token::UnquotedUrl(ref value) => Ok(value.clone()),
Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { self.parse_nested_block(|input| {
input.expect_string().map_err(Into::into).cloned()
})
.map_err(ParseError::<()>::basic)
}
}
}
/// Parse either a <url-token> or a <string-token>, and return the unescaped value. #[inline] pubfn expect_url_or_string(&mutself) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
expect! {self,
Token::UnquotedUrl(ref value) => Ok(value.clone()),
Token::QuotedString(ref value) => Ok(value.clone()),
Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { self.parse_nested_block(|input| {
input.expect_string().map_err(Into::into).cloned()
})
.map_err(ParseError::<()>::basic)
}
}
}
/// Parse a <number-token> and return the integer value. #[inline] pubfn expect_number(&mutself) -> Result<f32, BasicParseError<'i>> {
expect! {self,
Token::Number { value, .. } => Ok(value),
}
}
/// Parse a <number-token> that does not have a fractional part, and return the integer value. #[inline] pubfn expect_integer(&mutself) -> Result<i32, BasicParseError<'i>> {
expect! {self,
Token::Number { int_value: Some(int_value), .. } => Ok(int_value),
}
}
/// Parse a <percentage-token> and return the value. /// `0%` and `100%` map to `0.0` and `1.0` (not `100.0`), respectively. #[inline] pubfn expect_percentage(&mutself) -> Result<f32, BasicParseError<'i>> {
expect! {self,
Token::Percentage { unit_value, .. } => Ok(unit_value),
}
}
/// Parse a <delim-token> with the given value. #[inline] pubfn expect_delim(&mutself, expected_value: char) -> Result<(), BasicParseError<'i>> {
expect! {self,
Token::Delim(value) if value == expected_value => Ok(()),
}
}
/// Parse a `{ /* ... */ }` curly brackets block. /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] pubfn expect_curly_bracket_block(&mutself) -> Result<(), BasicParseError<'i>> {
expect! {self,
Token::CurlyBracketBlock => Ok(()),
}
}
/// Parse a `[ /* ... */ ]` square brackets block. /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] pubfn expect_square_bracket_block(&mutself) -> Result<(), BasicParseError<'i>> {
expect! {self,
Token::SquareBracketBlock => Ok(()),
}
}
/// Parse a `( /* ... */ )` parenthesis block. /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] pubfn expect_parenthesis_block(&mutself) -> Result<(), BasicParseError<'i>> {
expect! {self,
Token::ParenthesisBlock => Ok(()),
}
}
/// Parse a <function> token and return its name. /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] pubfn expect_function(&mutself) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
expect! {self,
Token::Function(ref name) => Ok(name),
}
}
/// Parse a <function> token whose name is an ASCII-insensitive match for the given value. /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] pubfn expect_function_matching(
&mutself,
expected_name: &str,
) -> Result<(), BasicParseError<'i>> {
expect! {self,
Token::Function(ref name) if name.eq_ignore_ascii_case(expected_name) => Ok(()),
}
}
/// Parse the input until exhaustion and check that it contains no “error” token. /// /// See `Token::is_parse_error`. This also checks nested blocks and functions recursively. #[inline] pubfn expect_no_error_token(&mutself) -> Result<(), BasicParseError<'i>> { loop { matchself.next_including_whitespace_and_comments() {
Ok(&Token::Function(_))
| Ok(&Token::ParenthesisBlock)
| Ok(&Token::SquareBracketBlock)
| Ok(&Token::CurlyBracketBlock) => self
.parse_nested_block(|input| input.expect_no_error_token().map_err(Into::into))
.map_err(ParseError::<()>::basic)?,
Ok(t) => { // FIXME: maybe these should be separate variants of // BasicParseError instead? if t.is_parse_error() { let token = t.clone(); return Err(self.new_basic_unexpected_token_error(token));
}
}
Err(_) => return Ok(()),
}
}
}
}
pubfn parse_until_before<'i: 't, 't, F, T, E>(
parser: &mut Parser<'i, 't>,
delimiters: Delimiters,
error_behavior: ParseUntilErrorBehavior,
parse: F,
) -> Result<T, ParseError<'i, E>> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{ let delimiters = parser.stop_before | delimiters; let result; // Introduce a new scope to limit duration of nested_parser’s borrow
{ letmut 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;
} iflet Some(block_type) = delimited_parser.at_start_of {
consume_until_end_of_block(block_type, &mut delimited_parser.input.tokenizer);
}
} // FIXME: have a special-purpose tokenizer method for this that does less work. loop { if delimiters.contains(Delimiters::from_byte(parser.input.tokenizer.next_byte())) { break;
} iflet Ok(token) = parser.input.tokenizer.next() { iflet Some(block_type) = BlockType::opening(&token) {
consume_until_end_of_block(block_type, &mut parser.input.tokenizer);
}
} else { break;
}
}
result
}
pubfn parse_until_after<'i: 't, 't, F, T, E>(
parser: &mut Parser<'i, 't>,
delimiters: Delimiters,
error_behavior: ParseUntilErrorBehavior,
parse: F,
) -> Result<T, ParseError<'i, E>> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{ let result = parse_until_before(parser, delimiters, error_behavior, parse); if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { return result;
} let next_byte = parser.input.tokenizer.next_byte(); if next_byte.is_some()
&& !parser
.stop_before
.contains(Delimiters::from_byte(next_byte))
{
debug_assert!(delimiters.contains(Delimiters::from_byte(next_byte))); // We know this byte is ASCII.
parser.input.tokenizer.advance(1); if next_byte == Some(b'{') {
consume_until_end_of_block(BlockType::CurlyBracket, &mut parser.input.tokenizer);
}
}
result
}
pubfn parse_nested_block<'i: 't, 't, F, T, E>(
parser: &mut Parser<'i, 't>,
parse: F,
) -> Result<T, ParseError<'i, E>> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
{ let block_type = parser.at_start_of.take().expect( "\
A nested parser can only be created when a Function, \
ParenthesisBlock, SquareBracketBlock, or CurlyBracketBlock \
token was just consumed.\ ",
); let closing_delimiter = 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
{ letmut nested_parser = Parser {
input: parser.input,
at_start_of: None,
stop_before: closing_delimiter,
};
result = nested_parser.parse_entirely(parse); iflet Some(block_type) = nested_parser.at_start_of {
consume_until_end_of_block(block_type, &mut nested_parser.input.tokenizer);
}
}
consume_until_end_of_block(block_type, &mut parser.input.tokenizer);
result
}
// FIXME: have a special-purpose tokenizer method for this that does less work. whilelet Ok(ref token) = tokenizer.next() { iflet Some(b) = BlockType::closing(token) { if *stack.last().unwrap() == b {
stack.pop(); if stack.is_empty() { return;
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.