|
| 1 | +use std::i32; |
| 2 | +use std::ascii::to_ascii_lower; |
| 3 | +use ast::*; |
| 4 | + |
| 5 | + |
| 6 | +/// Parse the An+B notation, as found in the ``:nth-child()`` selector. |
| 7 | +/// The input is typically the arguments of a function component value. |
| 8 | +/// Return Some((A, B)), or None for a syntax error. |
| 9 | +pub fn parse_nth(input: &[ComponentValue]) -> Option<(i32, i32)> { |
| 10 | + let iter = &mut input.skip_whitespace(); |
| 11 | + match iter.next() { |
| 12 | + Some(&Number(ref value)) => match value.int_value { |
| 13 | + Some(b) => parse_end(iter, 0, b as i32), |
| 14 | + _ => None, |
| 15 | + }, |
| 16 | + Some(&Dimension(ref value, ref unit)) => match value.int_value { |
| 17 | + Some(a) => { |
| 18 | + let unit: &str = to_ascii_lower(unit.as_slice()); |
| 19 | + match unit { |
| 20 | + "n" => parse_b(iter, a as i32), |
| 21 | + "n-" => parse_signless_b(iter, a as i32, -1), |
| 22 | + _ => match(parse_n_dash_digits(unit)) { |
| 23 | + Some(b) => parse_end(iter, a as i32, b), |
| 24 | + _ => None |
| 25 | + }, |
| 26 | + } |
| 27 | + }, |
| 28 | + _ => None, |
| 29 | + }, |
| 30 | + Some(&Ident(ref value)) => { |
| 31 | + let ident: &str = to_ascii_lower(value.as_slice()); |
| 32 | + match ident { |
| 33 | + "even" => parse_end(iter, 2, 0), |
| 34 | + "odd" => parse_end(iter, 2, 1), |
| 35 | + "n" => parse_b(iter, 1), |
| 36 | + "-n" => parse_b(iter, -1), |
| 37 | + "n-" => parse_signless_b(iter, 1, -1), |
| 38 | + "-n-" => parse_signless_b(iter, -1, -1), |
| 39 | + _ if ident.starts_with("-") => match(parse_n_dash_digits(ident.slice_from(1))) { |
| 40 | + Some(b) => parse_end(iter, -1, b), |
| 41 | + _ => None |
| 42 | + }, |
| 43 | + _ => match(parse_n_dash_digits(ident)) { |
| 44 | + Some(b) => parse_end(iter, 1, b), |
| 45 | + _ => None |
| 46 | + }, |
| 47 | + } |
| 48 | + }, |
| 49 | + Some(&Delim('+')) => match iter.iter_with_whitespace.next() { |
| 50 | + Some(&Ident(ref value)) => { |
| 51 | + let ident: &str = to_ascii_lower(value.as_slice()); |
| 52 | + match ident { |
| 53 | + "n" => parse_b(iter, 1), |
| 54 | + "n-" => parse_signless_b(iter, 1, -1), |
| 55 | + _ => match(parse_n_dash_digits(ident)) { |
| 56 | + Some(b) => parse_end(iter, 1, b), |
| 57 | + _ => None |
| 58 | + }, |
| 59 | + } |
| 60 | + }, |
| 61 | + _ => None |
| 62 | + }, |
| 63 | + _ => None |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +type Nth = Option<(i32, i32)>; |
| 69 | +type Iter<'self> = SkipWhitespaceIterator<'self>; |
| 70 | + |
| 71 | +fn parse_b(iter: &mut Iter, a: i32) -> Nth { |
| 72 | + match iter.next() { |
| 73 | + None => Some((a, 0)), |
| 74 | + Some(&Delim('+')) => parse_signless_b(iter, a, 1), |
| 75 | + Some(&Delim('-')) => parse_signless_b(iter, a, -1), |
| 76 | + Some(&Number(ref value)) => match value.int_value { |
| 77 | + Some(b) if has_sign(value) => parse_end(iter, a, b as i32), |
| 78 | + _ => None, |
| 79 | + }, |
| 80 | + _ => None |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +fn parse_signless_b(iter: &mut Iter, a: i32, b_sign: i32) -> Nth { |
| 85 | + match iter.next() { |
| 86 | + Some(&Number(ref value)) => match value.int_value { |
| 87 | + Some(b) if !has_sign(value) => parse_end(iter, a, b_sign * (b as i32)), |
| 88 | + _ => None, |
| 89 | + }, |
| 90 | + _ => None |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +fn parse_end(iter: &mut Iter, a: i32, b: i32) -> Nth { |
| 95 | + match iter.next() { |
| 96 | + None => Some((a, b)), |
| 97 | + Some(_) => None, |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +fn parse_n_dash_digits(string: &str) -> Option<i32> { |
| 102 | + if string.len() >= 3 |
| 103 | + && string.starts_with("n-") |
| 104 | + && string.slice_from(2).iter().all(|c| match c { '0'..'9' => true, _ => false }) |
| 105 | + { |
| 106 | + let result = i32::from_str(string.slice_from(1)); // Include the minus sign |
| 107 | + assert!(result.is_some()); |
| 108 | + result |
| 109 | + } |
| 110 | + else { None } |
| 111 | +} |
| 112 | + |
| 113 | +#[inline] |
| 114 | +fn has_sign(value: &NumericValue) -> bool { |
| 115 | + match value.representation[0] as char { |
| 116 | + '+' | '-' => true, |
| 117 | + _ => false |
| 118 | + } |
| 119 | +} |
0 commit comments