diff options
Diffstat (limited to 'third_party/rust/cssparser/src/nth.rs')
-rw-r--r-- | third_party/rust/cssparser/src/nth.rs | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/third_party/rust/cssparser/src/nth.rs b/third_party/rust/cssparser/src/nth.rs new file mode 100644 index 0000000000..a0c0a0070c --- /dev/null +++ b/third_party/rust/cssparser/src/nth.rs @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 matches::matches; + +/// Parse the *An+B* notation, as found in the `:nth-child()` selector. +/// The input is typically the arguments of a function, +/// in which case the caller needs to check if the arguments’ parser is exhausted. +/// Return `Ok((A, B))`, or `Err(())` for a syntax error. +pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), BasicParseError<'i>> { + match *input.next()? { + Token::Number { + int_value: Some(b), .. + } => Ok((0, b)), + Token::Dimension { + int_value: Some(a), + ref unit, + .. + } => { + match_ignore_ascii_case! { + unit, + "n" => Ok(parse_b(input, a)?), + "n-" => Ok(parse_signless_b(input, a, -1)?), + _ => match parse_n_dash_digits(&*unit) { + Ok(b) => Ok((a, b)), + Err(()) => { + let unit = unit.clone(); + Err(input.new_basic_unexpected_token_error(Token::Ident(unit))) + } + } + } + } + Token::Ident(ref value) => { + match_ignore_ascii_case! { value, + "even" => Ok((2, 0)), + "odd" => Ok((2, 1)), + "n" => Ok(parse_b(input, 1)?), + "-n" => Ok(parse_b(input, -1)?), + "n-" => Ok(parse_signless_b(input, 1, -1)?), + "-n-" => Ok(parse_signless_b(input, -1, -1)?), + _ => { + let (slice, a) = if value.starts_with("-") { + (&value[1..], -1) + } else { + (&**value, 1) + }; + match parse_n_dash_digits(slice) { + Ok(b) => Ok((a, b)), + Err(()) => { + let value = value.clone(); + Err(input.new_basic_unexpected_token_error(Token::Ident(value))) + } + } + } + } + } + Token::Delim('+') => match *input.next_including_whitespace()? { + Token::Ident(ref value) => { + match_ignore_ascii_case! { value, + "n" => parse_b(input, 1), + "n-" => parse_signless_b(input, 1, -1), + _ => match parse_n_dash_digits(value) { + Ok(b) => Ok((1, b)), + Err(()) => { + let value = value.clone(); + Err(input.new_basic_unexpected_token_error(Token::Ident(value))) + } + } + } + } + ref token => { + let token = token.clone(); + Err(input.new_basic_unexpected_token_error(token)) + } + }, + ref token => { + let token = token.clone(); + Err(input.new_basic_unexpected_token_error(token)) + } + } +} + +fn parse_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> { + let start = input.state(); + match input.next() { + Ok(&Token::Delim('+')) => parse_signless_b(input, a, 1), + Ok(&Token::Delim('-')) => parse_signless_b(input, a, -1), + Ok(&Token::Number { + has_sign: true, + int_value: Some(b), + .. + }) => Ok((a, b)), + _ => { + input.reset(&start); + Ok((a, 0)) + } + } +} + +fn parse_signless_b<'i, 't>( + input: &mut Parser<'i, 't>, + a: i32, + b_sign: i32, +) -> Result<(i32, i32), BasicParseError<'i>> { + // FIXME: remove .clone() when lifetimes are non-lexical. + match input.next()?.clone() { + Token::Number { + has_sign: false, + int_value: Some(b), + .. + } => Ok((a, b_sign * b)), + token => Err(input.new_basic_unexpected_token_error(token)), + } +} + +fn parse_n_dash_digits(string: &str) -> Result<i32, ()> { + let bytes = string.as_bytes(); + if bytes.len() >= 3 + && bytes[..2].eq_ignore_ascii_case(b"n-") + && bytes[2..].iter().all(|&c| matches!(c, b'0'..=b'9')) + { + Ok(parse_number_saturate(&string[1..]).unwrap()) // Include the minus sign + } else { + Err(()) + } +} + +fn parse_number_saturate(string: &str) -> Result<i32, ()> { + let mut input = ParserInput::new(string); + let mut parser = Parser::new(&mut input); + let int = if let Ok(&Token::Number { + int_value: Some(int), + .. + }) = parser.next_including_whitespace_and_comments() + { + int + } else { + return Err(()); + }; + if !parser.is_exhausted() { + return Err(()); + } + Ok(int) +} |