Skip to content

Parse <unicode-range> based on tokens’s source representation #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "cssparser"
version = "0.12.2"
version = "0.12.3"
authors = [ "Simon Sapin <simon.sapin@exyr.org>" ]

description = "Rust implementation of CSS Syntax Level 3"
Expand Down
12 changes: 3 additions & 9 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,18 +399,12 @@ fn clamp_f32(val: f32) -> u8 {

#[inline]
fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
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());
Expand Down
38 changes: 26 additions & 12 deletions src/css-parsing-tests/make_color3_hsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(']')
4 changes: 4 additions & 0 deletions src/css-parsing-tests/urange.json
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
130 changes: 33 additions & 97 deletions src/unicode_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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 <unicode-range>,
// 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 {
Expand All @@ -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<i32, ()> {
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<i32, ()> { // 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<UnicodeRange, ()> {
fn parse_concatenated(text: &[u8]) -> Result<UnicodeRange, ()> {
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;
Expand Down Expand Up @@ -241,18 +192,3 @@ impl ToCss for UnicodeRange {
Ok(())
}
}

/// Make conversions from io::Error implicit in `?` syntax.
struct Error;

impl From<Error> for () {
fn from(_: Error) -> Self { () }
}

impl From<()> for Error {
fn from(_: ()) -> Self { Error }
}

impl From<io::Error> for Error {
fn from(_: io::Error) -> Self { Error }
}