diff --git a/Cargo.toml b/Cargo.toml index 74b4a006..27458b2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cssparser" -version = "0.12.2" +version = "0.12.3" authors = [ "Simon Sapin " ] description = "Rust implementation of CSS Syntax Level 3" diff --git a/src/color.rs b/src/color.rs index a1e2a496..23ff92fd 100644 --- a/src/color.rs +++ b/src/color.rs @@ -399,18 +399,12 @@ fn clamp_f32(val: f32) -> u8 { #[inline] fn parse_color_function(name: &str, arguments: &mut Parser) -> Result { - let is_rgb = match_ignore_ascii_case! { name, - "rgb" | "rgba" => true, - "hsl" | "hsla" => false, + let (red, green, blue, uses_commas) = match_ignore_ascii_case! { name, + "rgb" | "rgba" => parse_rgb_components_rgb(arguments)?, + "hsl" | "hsla" => parse_rgb_components_hsl(arguments)?, _ => return Err(()) }; - let (red, green, blue, uses_commas) = if is_rgb { - parse_rgb_components_rgb(arguments)? - } else { - parse_rgb_components_hsl(arguments)? - }; - let alpha = if !arguments.is_exhausted() { if uses_commas { try!(arguments.expect_comma()); diff --git a/src/css-parsing-tests/make_color3_hsl.py b/src/css-parsing-tests/make_color3_hsl.py index c037bc70..f8e911df 100644 --- a/src/css-parsing-tests/make_color3_hsl.py +++ b/src/css-parsing-tests/make_color3_hsl.py @@ -2,18 +2,32 @@ trim = lambda s: s if not s.endswith('.0') else s[:-2] print('[') print(',\n'.join( - f[0] % ( - (n, h, trim(str(s/10.)), trim(str(l/10.)), - f[1] % round(a / 255., 2) if a is not None else '') - + tuple(trim(str(min(255, v * 256))) - for v in colorsys.hls_to_rgb(h/360., l/1000., s/1000.)) - + (a if a is not None else 255,) + function_format % tuple( + [ + function_name, + hue, + trim(str(saturation / 10.)), + trim(str(lightness / 10.)), + alpha_format % round(alpha / 255., 2) if alpha is not None else '' + ] + [ + trim(str(min(255, component * 256))) + for component in colorsys.hls_to_rgb( + hue / 360., + lightness / 1000., + saturation / 1000. + ) + ] + [ + alpha if alpha is not None else 255 + ] ) - for f in [('"%s(%s, %s%%, %s%%%s)", [%s, %s, %s, %s]', ', %s'), ('"%s(%s %s%% %s%%%s)", [%s, %s, %s, %s]', ' / %s')] - for n in ["hsl", "hsla"] - for a in [None, 255, 64, 0] - for l in range(0, 1001, 125) - for s in range(0, 1001, 125) - for h in range(0, 360, 30) + for function_format, alpha_format in [ + ('"%s(%s, %s%%, %s%%%s)", [%s, %s, %s, %s]', ', %s'), + ('"%s(%s %s%% %s%%%s)", [%s, %s, %s, %s]', ' / %s') + ] + for function_name in ["hsl", "hsla"] + for alpha in [None, 255, 64, 0] + for lightness in range(0, 1001, 125) + for saturation in range(0, 1001, 125) + for hue in range(0, 360, 30) )) print(']') diff --git a/src/css-parsing-tests/urange.json b/src/css-parsing-tests/urange.json index 64fb7f35..857d1d62 100644 --- a/src/css-parsing-tests/urange.json +++ b/src/css-parsing-tests/urange.json @@ -60,6 +60,10 @@ [0, 4095] ], +"U+4E-9F", [ + [78, 159] +], + "u+20-3F, u+3F-3F, u+3F-3E, U+0-110000, U+0-10FFFF, U+100000-2, U+1000000-2, U+10-200000", [ [32, 63], [63, 63], diff --git a/src/unicode_range.rs b/src/unicode_range.rs index f13a1ad2..6a6dc022 100644 --- a/src/unicode_range.rs +++ b/src/unicode_range.rs @@ -8,8 +8,7 @@ use {Parser, ToCss}; use std::char; use std::cmp; use std::fmt; -use std::io::{self, Write}; -use tokenizer::{Token, NumericValue}; +use tokenizer::Token; /// One contiguous range of code points. /// @@ -35,22 +34,15 @@ impl UnicodeRange { // u '+' '?'+ input.expect_ident_matching("u")?; + let after_u = input.position(); + parse_tokens(input)?; - // Since start or end can’t be above 0x10FFFF, they can’t have more than 6 hex digits - // Conversely, input with more digits would end up returning Err anyway. - const MAX_LENGTH_AFTER_U_PLUS: usize = 6 + 1 + 6; // 6 digits, '-', 6 digits - let mut buffer = [0; MAX_LENGTH_AFTER_U_PLUS]; + // This deviates from the spec in case there are CSS comments + // between tokens in the middle of one , + // but oh well… + let concatenated_tokens = input.slice_from(after_u); - let remaining_len; - { - let mut remaining = &mut buffer[..]; - concatenate_tokens(input, &mut remaining)?; - remaining_len = remaining.len(); - } - - let text_len = buffer.len() - remaining_len; - let text = &buffer[..text_len]; - let range = parse_concatenated(text)?; + let range = parse_concatenated(concatenated_tokens.as_bytes())?; if range.end > char::MAX as u32 || range.start > range.end { Err(()) } else { @@ -59,93 +51,52 @@ impl UnicodeRange { } } -fn concatenate_tokens(input: &mut Parser, remaining: &mut &mut [u8]) -> Result<(), Error> { +fn parse_tokens(input: &mut Parser) -> Result<(), ()> { match input.next_including_whitespace()? { Token::Delim('+') => { match input.next_including_whitespace()? { - Token::Ident(ident) => remaining.write_all(ident.as_bytes())?, - Token::Delim('?') => remaining.write_all(b"?")?, - _ => return Err(Error) + Token::Ident(_) => {} + Token::Delim('?') => {} + _ => return Err(()) } - parse_question_marks(input, remaining) + parse_question_marks(input) } - - Token::Dimension(ref value, ref unit) => { - // Require a '+' sign as part of the number - let int_value = positive_integer_with_plus_sign(value)?; - write!(remaining, "{}{}", int_value, unit)?; - parse_question_marks(input, remaining) + Token::Dimension(..) => { + parse_question_marks(input) } - - Token::Number(ref value) => { - // Require a '+' sign as part of the number - let int_value = positive_integer_with_plus_sign(value)?; - write!(remaining, "{}", int_value)?; - + Token::Number(_) => { let after_number = input.position(); match input.next_including_whitespace() { - Ok(Token::Delim('?')) => { - // If `remaining` is already full, `int_value` has too many digits - // so we can use `result?` Rust syntax. - remaining.write_all(b"?")?; - parse_question_marks(input, remaining) - } - - Ok(Token::Dimension(ref value, ref unit)) => { - // Require a '-' sign as part of the number - let int_value = negative_integer(value)?; - write!(remaining, "{}{}", int_value, unit)? - } - - Ok(Token::Number(ref value)) => { - // Require a '-' sign as part of the number - let int_value = negative_integer(value)?; - write!(remaining, "{}", int_value)? - } - + Ok(Token::Delim('?')) => parse_question_marks(input), + Ok(Token::Dimension(..)) => {} + Ok(Token::Number(_)) => {} _ => input.reset(after_number) } } - - _ => return Err(Error) + _ => return Err(()) } Ok(()) } -/// Consume as many '?' as possible and write them to `remaining` until it’s full -fn parse_question_marks(input: &mut Parser, remaining: &mut &mut [u8]) { +/// Consume as many '?' as possible +fn parse_question_marks(input: &mut Parser) { loop { - let result = input.try(|input| { - match input.next_including_whitespace() { - Ok(Token::Delim('?')) => remaining.write_all(b"?").map_err(|_| ()), - _ => Err(()) + let position = input.position(); + match input.next_including_whitespace() { + Ok(Token::Delim('?')) => {} + _ => { + input.reset(position); + return } - }); - if result.is_err() { - return } } } -fn positive_integer_with_plus_sign(value: &NumericValue) -> Result { - let int_value = value.int_value.ok_or(())?; - if value.has_sign && int_value >= 0 { - Ok(int_value) - } else { - Err(()) - } -} - -fn negative_integer(value: &NumericValue) -> Result { // Necessarily had a negative sign. - let int_value = value.int_value.ok_or(())?; - if int_value <= 0 { - Ok(int_value) - } else { - Err(()) - } -} - -fn parse_concatenated(mut text: &[u8]) -> Result { +fn parse_concatenated(text: &[u8]) -> Result { + let mut text = match text.split_first() { + Some((&b'+', text)) => text, + _ => return Err(()) + }; let (first_hex_value, hex_digit_count) = consume_hex(&mut text); let question_marks = consume_question_marks(&mut text); let consumed = hex_digit_count + question_marks; @@ -241,18 +192,3 @@ impl ToCss for UnicodeRange { Ok(()) } } - -/// Make conversions from io::Error implicit in `?` syntax. -struct Error; - -impl From for () { - fn from(_: Error) -> Self { () } -} - -impl From<()> for Error { - fn from(_: ()) -> Self { Error } -} - -impl From for Error { - fn from(_: io::Error) -> Self { Error } -}