Skip to content

Commit 41fa928

Browse files
committed
Rewrite ALL THE THINGS!
The tokenizer is now fully incremental, rather than yielding a block/function at a time. The convention is now to take `&mut Parser` as input for parsing, and only consume as necessary.
1 parent d211431 commit 41fa928

10 files changed

+1301
-916
lines changed

src/ast.rs

+11-62
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
use std::fmt;
6-
use std::slice;
7-
use std::vec;
86

97

108
#[deriving(PartialEq, Show)]
@@ -22,18 +20,18 @@ pub struct SourceLocation {
2220
}
2321

2422

25-
pub type Node = (ComponentValue, SourceLocation); // TODO this is not a good name
23+
pub type Node = (Token, SourceLocation); // TODO this is not a good name
2624

2725

2826
#[deriving(PartialEq, Show)]
29-
pub enum ComponentValue {
27+
pub enum Token {
3028
// Preserved tokens.
3129
Ident(String),
3230
AtKeyword(String),
3331
Hash(String),
3432
IDHash(String), // Hash that is a valid ID selector.
3533
QuotedString(String),
36-
URL(String),
34+
Url(String),
3735
Delim(char),
3836
Number(NumericValue),
3937
Percentage(NumericValue),
@@ -53,15 +51,15 @@ pub enum ComponentValue {
5351
CDC, // -->
5452

5553
// Function
56-
Function(String, Vec<ComponentValue>), // name, arguments
54+
Function(String), // name
5755

5856
// Simple block
59-
ParenthesisBlock(Vec<ComponentValue>), // (…)
60-
SquareBracketBlock(Vec<ComponentValue>), // […]
61-
CurlyBracketBlock(Vec<Node>), // {…}
57+
ParenthesisBlock, // (…)
58+
SquareBracketBlock, // […]
59+
CurlyBracketBlock, // {…}
6260

6361
// These are always invalid
64-
BadURL,
62+
BadUrl,
6563
BadString,
6664
CloseParenthesis, // )
6765
CloseSquareBracket, // ]
@@ -73,22 +71,22 @@ pub enum ComponentValue {
7371
pub struct Declaration {
7472
pub location: SourceLocation,
7573
pub name: String,
76-
pub value: Vec<ComponentValue>,
74+
pub value: Vec<Token>,
7775
pub important: bool,
7876
}
7977

8078
#[deriving(PartialEq)]
8179
pub struct QualifiedRule {
8280
pub location: SourceLocation,
83-
pub prelude: Vec<ComponentValue>,
81+
pub prelude: Vec<Token>,
8482
pub block: Vec<Node>,
8583
}
8684

8785
#[deriving(PartialEq)]
8886
pub struct AtRule {
8987
pub location: SourceLocation,
9088
pub name: String,
91-
pub prelude: Vec<ComponentValue>,
89+
pub prelude: Vec<Token>,
9290
pub block: Option<Vec<Node>>,
9391
}
9492

@@ -125,52 +123,3 @@ impl fmt::Show for SyntaxError {
125123
write!(f, "{}:{} {}", self.location.line, self.location.column, self.reason)
126124
}
127125
}
128-
129-
130-
pub trait SkipWhitespaceIterable<'a> {
131-
fn skip_whitespace(self) -> SkipWhitespaceIterator<'a>;
132-
}
133-
134-
impl<'a> SkipWhitespaceIterable<'a> for &'a [ComponentValue] {
135-
fn skip_whitespace(self) -> SkipWhitespaceIterator<'a> {
136-
SkipWhitespaceIterator{ iter_with_whitespace: self.iter() }
137-
}
138-
}
139-
140-
#[deriving(Clone)]
141-
pub struct SkipWhitespaceIterator<'a> {
142-
pub iter_with_whitespace: slice::Items<'a, ComponentValue>,
143-
}
144-
145-
impl<'a> Iterator<&'a ComponentValue> for SkipWhitespaceIterator<'a> {
146-
fn next(&mut self) -> Option<&'a ComponentValue> {
147-
for component_value in self.iter_with_whitespace {
148-
if component_value != &ComponentValue::WhiteSpace { return Some(component_value) }
149-
}
150-
None
151-
}
152-
}
153-
154-
155-
pub trait MoveSkipWhitespaceIterable {
156-
fn move_skip_whitespace(self) -> MoveSkipWhitespaceIterator;
157-
}
158-
159-
impl MoveSkipWhitespaceIterable for Vec<ComponentValue> {
160-
fn move_skip_whitespace(self) -> MoveSkipWhitespaceIterator {
161-
MoveSkipWhitespaceIterator{ iter_with_whitespace: self.into_iter() }
162-
}
163-
}
164-
165-
pub struct MoveSkipWhitespaceIterator {
166-
iter_with_whitespace: vec::MoveItems<ComponentValue>,
167-
}
168-
169-
impl Iterator<ComponentValue> for MoveSkipWhitespaceIterator {
170-
fn next(&mut self) -> Option<ComponentValue> {
171-
for component_value in self.iter_with_whitespace {
172-
if component_value != ComponentValue::WhiteSpace { return Some(component_value) }
173-
}
174-
None
175-
}
176-
}

src/color.rs

+38-65
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ use std::num::{Float, FloatMath};
88

99
use text_writer::{mod, TextWriter};
1010

11-
use ast::{ComponentValue, SkipWhitespaceIterable};
12-
use ast::ComponentValue::{Number, Percentage, Function, Ident, Hash, IDHash, Comma};
13-
use serializer::ToCss;
11+
use super::{Token, Parser, ToCss};
1412

1513

1614
#[deriving(Clone, PartialEq)]
@@ -66,13 +64,18 @@ impl fmt::Show for Color {
6664

6765
/// Return `Err(())` on invalid or unsupported value (not a color).
6866
impl Color {
69-
pub fn parse(component_value: &ComponentValue) -> Result<Color, ()> {
70-
match *component_value {
71-
Hash(ref value) | IDHash(ref value) => parse_color_hash(value.as_slice()),
72-
Ident(ref value) => parse_color_keyword(value.as_slice()),
73-
Function(ref name, ref arguments)
74-
=> parse_color_function(name.as_slice(), arguments.as_slice()),
75-
_ => Err(())
67+
pub fn parse(input: &mut Parser) -> Result<Color, ()> {
68+
match try!(input.next()) {
69+
Token::Hash(ref value) | Token::IDHash(ref value) => {
70+
parse_color_hash(value.as_slice())
71+
}
72+
Token::Ident(ref value) => parse_color_keyword(value.as_slice()),
73+
Token::Function(ref name) => {
74+
input.parse_nested_block().parse_entirely(|arguments| {
75+
parse_color_function(name.as_slice(), arguments)
76+
})
77+
}
78+
token => input.unexpected(token)
7679
}
7780
}
7881
}
@@ -279,70 +282,43 @@ fn parse_color_hash(value: &str) -> Result<Color, ()> {
279282

280283

281284
#[inline]
282-
fn parse_color_function(name: &str, arguments: &[ComponentValue])
283-
-> Result<Color, ()> {
284-
let lower_name = name.to_ascii_lower();
285-
let lower_name = lower_name.as_slice();
286-
285+
fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
287286
let (is_rgb, has_alpha) =
288-
if "rgba" == lower_name { (true, true) }
289-
else if "rgb" == lower_name { (true, false) }
290-
else if "hsl" == lower_name { (false, false) }
291-
else if "hsla" == lower_name { (false, true) }
287+
if name.eq_ignore_ascii_case("rgba") { (true, true) }
288+
else if name.eq_ignore_ascii_case("rgb") { (true, false) }
289+
else if name.eq_ignore_ascii_case("hsl") { (false, false) }
290+
else if name.eq_ignore_ascii_case("hsla") { (false, true) }
292291
else { return Err(()) };
293292

294-
let mut iter = arguments.skip_whitespace();
295-
macro_rules! expect_comma(
296-
() => ( match iter.next() { Some(&Comma) => {}, _ => { return Err(()) } } );
297-
)
298-
macro_rules! expect_percentage(
299-
() => ( match iter.next() {
300-
Some(&Percentage(ref v)) => v.value,
301-
_ => return Err(()),
302-
});
303-
)
304-
macro_rules! expect_integer(
305-
() => ( match iter.next() {
306-
Some(&Number(ref v)) if v.int_value.is_some() => v.value,
307-
_ => return Err(()),
308-
});
309-
)
310-
macro_rules! expect_number(
311-
() => ( match iter.next() {
312-
Some(&Number(ref v)) => v.value,
313-
_ => return Err(()),
314-
});
315-
)
316-
317293
let red: f32;
318294
let green: f32;
319295
let blue: f32;
320296
if is_rgb {
321297
// Either integers or percentages, but all the same type.
322-
match iter.next() {
323-
Some(&Number(ref v)) if v.int_value.is_some() => {
298+
match try!(arguments.next()) {
299+
Token::Number(ref v) if v.int_value.is_some() => {
324300
red = (v.value / 255.) as f32;
325-
expect_comma!();
326-
green = (expect_integer!() / 255.) as f32;
327-
expect_comma!();
328-
blue = (expect_integer!() / 255.) as f32;
301+
try!(arguments.expect_comma());
302+
green = try!(arguments.expect_integer()) as f32 / 255.;
303+
try!(arguments.expect_comma());
304+
blue = try!(arguments.expect_integer()) as f32 / 255.;
329305
}
330-
Some(&Percentage(ref v)) => {
306+
Token::Percentage(ref v) => {
331307
red = (v.value / 100.) as f32;
332-
expect_comma!();
333-
green = (expect_percentage!() / 100.) as f32;
334-
expect_comma!();
335-
blue = (expect_percentage!() / 100.) as f32;
308+
try!(arguments.expect_comma());
309+
green = (try!(arguments.expect_percentage()) / 100.) as f32;
310+
try!(arguments.expect_comma());
311+
blue = (try!(arguments.expect_percentage()) / 100.) as f32;
336312
}
337313
_ => return Err(())
338314
};
339315
} else {
340-
let hue = expect_number!() / 360.;
316+
let hue = try!(arguments.expect_number()) / 360.;
341317
let hue = hue - hue.floor();
342-
expect_comma!();
343-
let saturation = (expect_percentage!() / 100.).max(0.).min(1.);
344-
expect_comma!();
345-
let lightness = (expect_percentage!() / 100.).max(0.).min(1.);
318+
try!(arguments.expect_comma());
319+
let saturation = (try!(arguments.expect_percentage()) / 100.).max(0.).min(1.);
320+
try!(arguments.expect_comma());
321+
let lightness = (try!(arguments.expect_percentage()) / 100.).max(0.).min(1.);
346322

347323
// http://www.w3.org/TR/css3-color/#hsl-color
348324
fn hue_to_rgb(m1: f64, m2: f64, mut h: f64) -> f64 {
@@ -363,14 +339,11 @@ fn parse_color_function(name: &str, arguments: &[ComponentValue])
363339
}
364340

365341
let alpha = if has_alpha {
366-
expect_comma!();
367-
(expect_number!()).max(0.).min(1.) as f32
342+
try!(arguments.expect_comma());
343+
(try!(arguments.expect_number())).max(0.).min(1.) as f32
368344
} else {
369345
1.
370346
};
371-
if iter.next().is_none() {
372-
Ok(Color::RGBA(RGBA { red: red, green: green, blue: blue, alpha: alpha }))
373-
} else {
374-
Err(())
375-
}
347+
try!(arguments.expect_exhausted());
348+
Ok(Color::RGBA(RGBA { red: red, green: green, blue: blue, alpha: alpha }))
376349
}

src/from_bytes.rs

+30
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use encoding::label::encoding_from_whatwg_label;
88
use encoding::all::UTF_8;
99
use encoding::{EncodingRef, DecoderTrap, decode};
1010

11+
use super::{Parser, RuleListParser, QualifiedRuleParser, AtRuleParser};
12+
1113

1214
/// Determine the character encoding of a CSS stylesheet and decode it.
1315
///
@@ -65,3 +67,31 @@ fn decode_replace(input: &[u8], fallback_encoding: EncodingRef)-> (String, Encod
6567
let (result, used_encoding) = decode(input, DecoderTrap::Replace, fallback_encoding);
6668
(result.unwrap(), used_encoding)
6769
}
70+
71+
72+
/// Parse stylesheet from bytes.
73+
///
74+
/// * `css_bytes`: A byte string.
75+
/// * `protocol_encoding`: The encoding label, if any, defined by HTTP or equivalent protocol.
76+
/// (e.g. via the `charset` parameter of the `Content-Type` header.)
77+
/// * `environment_encoding`: An optional `Encoding` object for the [environment encoding]
78+
/// (http://www.w3.org/TR/css-syntax/#environment-encoding), if any.
79+
///
80+
/// Returns a 2-tuple of a `Iterator<Result<Rule, SyntaxError>>`
81+
/// and the `Encoding` object that was used.
82+
pub fn parse_stylesheet_rules_from_bytes<QP, AP, R, P, T>(
83+
css_bytes: &[u8],
84+
protocol_encoding_label: Option<&str>,
85+
environment_encoding: Option<EncodingRef>,
86+
rules_parser: P,
87+
parse: |EncodingRef, RuleListParser<R, QP, AP, P>| -> T)
88+
-> T
89+
where P: QualifiedRuleParser<QP, R> + AtRuleParser<AP, R> {
90+
let (css_unicode, encoding) = decode_stylesheet_bytes(
91+
css_bytes, protocol_encoding_label, environment_encoding);
92+
// FIXME: Remove option dance when unboxed closures permit.
93+
let mut rules_parser = Some(rules_parser);
94+
Parser::parse_str(css_unicode.as_slice(), |input| {
95+
parse(encoding, RuleListParser::new_for_stylesheet(input, rules_parser.take().unwrap()))
96+
})
97+
}

src/lib.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#![crate_name = "cssparser"]
66
#![crate_type = "rlib"]
77

8-
#![feature(globs, macro_rules)]
8+
#![feature(globs, macro_rules, if_let, while_let, unsafe_destructor)]
99

1010
extern crate encoding;
1111
extern crate text_writer;
@@ -16,18 +16,18 @@ extern crate test;
1616
#[cfg(test)]
1717
extern crate serialize;
1818

19-
pub use color::{parse_color_keyword};
20-
pub use tokenizer::{tokenize, Tokenizer};
21-
pub use parser::{parse_stylesheet_rules, StylesheetParser,
22-
parse_rule_list, RuleListParser,
23-
parse_declaration_list, DeclarationListParser,
24-
parse_one_rule, parse_one_declaration, parse_one_component_value};
25-
pub use from_bytes::decode_stylesheet_bytes;
26-
pub use color::{RGBA, Color};
19+
pub use tokenizer::{Tokenizer, Token, NumericValue};
20+
pub use rules_and_declarations::{Priority, parse_important};
21+
pub use rules_and_declarations::{DeclarationParser, DeclarationListParser, parse_one_declaration};
22+
pub use rules_and_declarations::{RuleListParser, parse_one_rule};
23+
pub use rules_and_declarations::{AtRulePrelude, QualifiedRuleParser, AtRuleParser};
24+
pub use from_bytes::{decode_stylesheet_bytes, parse_stylesheet_rules_from_bytes};
25+
pub use color::{RGBA, Color, parse_color_keyword};
2726
pub use nth::parse_nth;
2827
pub use serializer::{ToCss, CssStringWriter, serialize_identifier, serialize_string};
28+
pub use parser::{Parser, Delimiter, Delimiters};
2929

30-
pub mod ast;
30+
mod rules_and_declarations;
3131
mod tokenizer;
3232
mod parser;
3333
mod from_bytes;

0 commit comments

Comments
 (0)