mirror of
https://github.com/servo/servo.git
synced 2025-06-25 01:24:37 +01:00
Move css parser and lexer to rust-css
This commit is contained in:
parent
37d45c6872
commit
bf19709645
7 changed files with 4 additions and 789 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit 2a61c1cb0d1878f238cd2e33377a254d29ff7f2e
|
Subproject commit b050d795e69328e75e1870d95506005e4c552463
|
|
@ -1,266 +0,0 @@
|
||||||
//! Code to lex and tokenize css files
|
|
||||||
|
|
||||||
use option::is_none;
|
|
||||||
use str::from_bytes;
|
|
||||||
use vec::push;
|
|
||||||
|
|
||||||
use pipes::{Port, Chan};
|
|
||||||
|
|
||||||
use lexer_util::*;
|
|
||||||
|
|
||||||
use std::net::url::Url;
|
|
||||||
use std::cell::Cell;
|
|
||||||
|
|
||||||
enum ParserState {
|
|
||||||
CssElement,
|
|
||||||
CssRelation,
|
|
||||||
CssDescription,
|
|
||||||
CssAttribute
|
|
||||||
}
|
|
||||||
|
|
||||||
type CssLexer = {
|
|
||||||
input_state: InputState,
|
|
||||||
mut parser_state: ParserState
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum Token {
|
|
||||||
StartDescription,
|
|
||||||
EndDescription,
|
|
||||||
Descendant,
|
|
||||||
Child,
|
|
||||||
Sibling,
|
|
||||||
Comma,
|
|
||||||
Element(~str),
|
|
||||||
Attr(newcss::values::Attr),
|
|
||||||
Description(~str, ~str),
|
|
||||||
Eof
|
|
||||||
}
|
|
||||||
|
|
||||||
trait CssLexerMethods {
|
|
||||||
fn parse_css() -> Token;
|
|
||||||
fn parse_css_relation(c : u8) -> Token;
|
|
||||||
fn parse_css_element(c : u8) -> Token;
|
|
||||||
fn parse_css_attribute(c : u8) -> Token;
|
|
||||||
fn parse_css_description(c: u8) -> Token;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CssLexer : CssLexerMethods {
|
|
||||||
fn parse_css() -> Token {
|
|
||||||
let mut ch: u8;
|
|
||||||
match self.input_state.get() {
|
|
||||||
CoeChar(c) => ch = c,
|
|
||||||
CoeEof => { return Eof; }
|
|
||||||
}
|
|
||||||
|
|
||||||
let token = match self.parser_state {
|
|
||||||
CssDescription => self.parse_css_description(ch),
|
|
||||||
CssAttribute => self.parse_css_attribute(ch),
|
|
||||||
CssElement => self.parse_css_element(ch),
|
|
||||||
CssRelation => self.parse_css_relation(ch)
|
|
||||||
};
|
|
||||||
|
|
||||||
#debug["token=%?", token];
|
|
||||||
return move token;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_css_relation(c : u8) -> Token {
|
|
||||||
self.parser_state = CssElement;
|
|
||||||
|
|
||||||
let token = match c {
|
|
||||||
'{' as u8 => { self.parser_state = CssDescription; StartDescription }
|
|
||||||
'>' as u8 => { Child }
|
|
||||||
'+' as u8 => { Sibling }
|
|
||||||
',' as u8 => { Comma }
|
|
||||||
_ => { self.input_state.unget(c); Descendant }
|
|
||||||
};
|
|
||||||
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
|
|
||||||
return move token;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_css_element(c : u8) -> Token {
|
|
||||||
assert is_none(&self.input_state.lookahead);
|
|
||||||
|
|
||||||
/* Check for special attributes with an implied element,
|
|
||||||
or a wildcard which is not a alphabet character.*/
|
|
||||||
if c == '.' as u8 || c == '#' as u8 {
|
|
||||||
self.parser_state = CssAttribute;
|
|
||||||
self.input_state.unget(c);
|
|
||||||
return Element(~"*");
|
|
||||||
} else if c == '*' as u8 {
|
|
||||||
self.parser_state = CssAttribute;
|
|
||||||
return Element(~"*");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.input_state.unget(c);
|
|
||||||
let element = self.input_state.parse_ident();
|
|
||||||
|
|
||||||
self.parser_state = CssAttribute;
|
|
||||||
|
|
||||||
return move Element(move element);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_css_attribute(c : u8) -> Token {
|
|
||||||
let mut ch = c;
|
|
||||||
|
|
||||||
/* If we've reached the end of this list of attributes,
|
|
||||||
look for the relation to the next element.*/
|
|
||||||
if c.is_whitespace() {
|
|
||||||
self.parser_state = CssRelation;
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
|
|
||||||
match self.input_state.get() {
|
|
||||||
CoeChar(c) => { ch = c }
|
|
||||||
CoeEof => { fail ~"File ended before description of style" }
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.parse_css_relation(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
match ch {
|
|
||||||
'.' as u8 => return Attr(newcss::values::Includes(~"class", self.input_state.parse_ident())),
|
|
||||||
'#' as u8 => return Attr(newcss::values::Includes(~"id", self.input_state.parse_ident())),
|
|
||||||
'[' as u8 => {
|
|
||||||
let attr_name = self.input_state.parse_ident();
|
|
||||||
|
|
||||||
match self.input_state.get() {
|
|
||||||
CoeChar(c) => { ch = c; }
|
|
||||||
CoeEof => { fail ~"File ended before description finished"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if ch == ']' as u8 {
|
|
||||||
return Attr(newcss::values::Exists(move attr_name));
|
|
||||||
} else if ch == '=' as u8 {
|
|
||||||
let attr_val = self.input_state.parse_ident();
|
|
||||||
self.input_state.expect(']' as u8);
|
|
||||||
return Attr(newcss::values::Exact(move attr_name, move attr_val));
|
|
||||||
} else if ch == '~' as u8 {
|
|
||||||
self.input_state.expect('=' as u8);
|
|
||||||
let attr_val = self.input_state.parse_ident();
|
|
||||||
self.input_state.expect(']' as u8);
|
|
||||||
return Attr(newcss::values::Includes(move attr_name, move attr_val));
|
|
||||||
} else if ch == '|' as u8 {
|
|
||||||
self.input_state.expect('=' as u8);
|
|
||||||
let attr_val = self.input_state.parse_ident();
|
|
||||||
self.input_state.expect(']' as u8);
|
|
||||||
return Attr(newcss::values::StartsWith(move attr_name, move attr_val));
|
|
||||||
}
|
|
||||||
|
|
||||||
fail #fmt("Unexpected symbol %c in attribute", ch as char);
|
|
||||||
}
|
|
||||||
_ => { fail #fmt("Unexpected symbol %c in attribute", ch as char); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_css_description(c: u8) -> Token {
|
|
||||||
let mut ch = c;
|
|
||||||
|
|
||||||
if ch == '}' as u8 {
|
|
||||||
self.parser_state = CssElement;
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
return EndDescription;
|
|
||||||
} else if ch.is_whitespace() {
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
|
|
||||||
match self.input_state.get() {
|
|
||||||
CoeChar(c) => { ch = c }
|
|
||||||
CoeEof => { fail ~"Reached end of file in CSS description" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut desc_name = ~[];
|
|
||||||
|
|
||||||
// Get the name of the descriptor
|
|
||||||
loop {
|
|
||||||
if ch.is_whitespace() {
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
} else if ch == ':' as u8 {
|
|
||||||
if desc_name.len() == 0u {
|
|
||||||
fail ~"Expected descriptor name";
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
push(&mut desc_name, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.input_state.get() {
|
|
||||||
CoeChar(c) => { ch = c }
|
|
||||||
CoeEof => { fail ~"Reached end of file in CSS description" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
let mut desc_val = ~[];
|
|
||||||
|
|
||||||
// Get the value of the descriptor
|
|
||||||
loop {
|
|
||||||
match self.input_state.get() {
|
|
||||||
CoeChar(c) => { ch = c }
|
|
||||||
CoeEof => { fail ~"Reached end of file in CSS description" }
|
|
||||||
}
|
|
||||||
|
|
||||||
if ch.is_whitespace() {
|
|
||||||
self.input_state.eat_whitespace();
|
|
||||||
} else if ch == '}' as u8 {
|
|
||||||
if desc_val.len() == 0u {
|
|
||||||
fail ~"Expected descriptor value";
|
|
||||||
} else {
|
|
||||||
self.input_state.unget('}' as u8);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if ch == ';' as u8 {
|
|
||||||
if desc_val.len() == 0u {
|
|
||||||
fail ~"Expected descriptor value";
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
push(&mut desc_val, ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Description(from_bytes(desc_name), from_bytes(desc_val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parser(input: DataStream, state : ParserState) -> CssLexer {
|
|
||||||
return {
|
|
||||||
input_state: {
|
|
||||||
mut lookahead: None,
|
|
||||||
mut buffer: ~[],
|
|
||||||
input: input,
|
|
||||||
mut eof: false
|
|
||||||
},
|
|
||||||
mut parser_state: state
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lex_css_from_bytes(input_stream: DataStream, result_chan : &Chan<Token>) {
|
|
||||||
let lexer = parser(input_stream, CssElement);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let token = lexer.parse_css();
|
|
||||||
let should_break = match token { Eof => true, _ => false };
|
|
||||||
|
|
||||||
result_chan.send(move token);
|
|
||||||
|
|
||||||
if should_break {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_css_lexer_from_string(content : ~str) -> pipes::Port<Token> {
|
|
||||||
let (result_chan, result_port) = pipes::stream();
|
|
||||||
|
|
||||||
do task::spawn |move result_chan, move content| {
|
|
||||||
let content = str::to_bytes(content);
|
|
||||||
let content = Cell(copy content);
|
|
||||||
let input = |move content| if !content.is_empty() { Some(content.take()) } else { None };
|
|
||||||
lex_css_from_bytes(input, &result_chan);
|
|
||||||
}
|
|
||||||
|
|
||||||
return move result_port;
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
/*!
|
|
||||||
A collection of functions that are useful for both css and html parsing
|
|
||||||
*/
|
|
||||||
|
|
||||||
use option::is_none;
|
|
||||||
use str::from_bytes;
|
|
||||||
use vec::push;
|
|
||||||
use comm::Port;
|
|
||||||
|
|
||||||
enum CharOrEof {
|
|
||||||
CoeChar(u8),
|
|
||||||
CoeEof
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type DataStream = @fn() -> Option<~[u8]>;
|
|
||||||
|
|
||||||
impl CharOrEof: cmp::Eq {
|
|
||||||
pure fn eq(other: &CharOrEof) -> bool {
|
|
||||||
match (self, *other) {
|
|
||||||
(CoeChar(a), CoeChar(b)) => a == b,
|
|
||||||
(CoeChar(*), _) | (_, CoeChar(*)) => false,
|
|
||||||
(CoeEof, CoeEof) => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pure fn ne(other: &CharOrEof) -> bool {
|
|
||||||
return !self.eq(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputState = {
|
|
||||||
mut lookahead: Option<CharOrEof>,
|
|
||||||
mut buffer: ~[u8],
|
|
||||||
input: DataStream,
|
|
||||||
mut eof: bool
|
|
||||||
};
|
|
||||||
|
|
||||||
trait U8Methods {
|
|
||||||
fn is_whitespace() -> bool;
|
|
||||||
fn is_alpha() -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl u8 : U8Methods {
|
|
||||||
fn is_whitespace() -> bool {
|
|
||||||
return self == ' ' as u8 || self == '\n' as u8 || self == '\t' as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_alpha() -> bool {
|
|
||||||
return (self >= ('A' as u8) && self <= ('Z' as u8)) ||
|
|
||||||
(self >= ('a' as u8) && self <= ('z' as u8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait InputStateUtil {
|
|
||||||
fn get() -> CharOrEof;
|
|
||||||
fn unget(ch: u8);
|
|
||||||
fn parse_err(+err: ~str) -> !;
|
|
||||||
fn expect(ch: u8);
|
|
||||||
fn parse_ident() -> ~str;
|
|
||||||
fn expect_ident(+expected: ~str);
|
|
||||||
fn eat_whitespace();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputState : InputStateUtil {
|
|
||||||
fn get() -> CharOrEof {
|
|
||||||
match copy self.lookahead {
|
|
||||||
Some(coe) => {
|
|
||||||
let rv = coe;
|
|
||||||
self.lookahead = None;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
/* fall through */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Lots of copies here
|
|
||||||
|
|
||||||
if self.buffer.len() > 0 {
|
|
||||||
return CoeChar(vec::shift(&mut self.buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.eof {
|
|
||||||
return CoeEof;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.input() {
|
|
||||||
Some(data) => {
|
|
||||||
// TODO: change copy to move once we have match move
|
|
||||||
self.buffer = copy data;
|
|
||||||
return CoeChar(vec::shift(&mut self.buffer));
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.eof = true;
|
|
||||||
return CoeEof;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unget(ch: u8) {
|
|
||||||
assert is_none(&self.lookahead);
|
|
||||||
self.lookahead = Some(CoeChar(ch));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_err(err: ~str) -> ! {
|
|
||||||
fail err
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expect(ch: u8) {
|
|
||||||
match self.get() {
|
|
||||||
CoeChar(c) => { if c != ch { self.parse_err(#fmt("expected '%c'", ch as char)); } }
|
|
||||||
CoeEof => { self.parse_err(#fmt("expected '%c' at eof", ch as char)); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_ident() -> ~str {
|
|
||||||
let mut result: ~[u8] = ~[];
|
|
||||||
loop {
|
|
||||||
match self.get() {
|
|
||||||
CoeChar(c) => {
|
|
||||||
if (c.is_alpha()) { push(&mut result, c); }
|
|
||||||
else if result.len() == 0u { self.parse_err(~"expected ident"); }
|
|
||||||
else {
|
|
||||||
self.unget(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CoeEof => {
|
|
||||||
self.parse_err(~"expected ident");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str::from_bytes(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expect_ident(expected: ~str) {
|
|
||||||
let actual = self.parse_ident();
|
|
||||||
if expected != actual {
|
|
||||||
self.parse_err(#fmt("expected '%s' but found '%s'", expected, actual));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eat_whitespace() {
|
|
||||||
loop {
|
|
||||||
match self.get() {
|
|
||||||
CoeChar(c) => {
|
|
||||||
if !c.is_whitespace() {
|
|
||||||
self.unget(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CoeEof => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
/**
|
|
||||||
Constructs a list of css style rules from a token stream
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: fail according to the css spec instead of failing when things
|
|
||||||
// are not as expected
|
|
||||||
|
|
||||||
use newcss::values::*;
|
|
||||||
// Disambiguate parsed Selector, Rule values from tokens
|
|
||||||
use css = newcss::values;
|
|
||||||
use tok = lexer;
|
|
||||||
use lexer::Token;
|
|
||||||
use comm::recv;
|
|
||||||
use option::{map, is_none};
|
|
||||||
use vec::push;
|
|
||||||
use parser_util::*;
|
|
||||||
use newcss::color::parsing::parse_color;
|
|
||||||
use vec::push;
|
|
||||||
|
|
||||||
type TokenReader = {stream : pipes::Port<Token>, mut lookahead : Option<Token>};
|
|
||||||
|
|
||||||
trait TokenReaderMethods {
|
|
||||||
fn get() -> Token;
|
|
||||||
fn unget(+tok : Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TokenReader : TokenReaderMethods {
|
|
||||||
fn get() -> Token {
|
|
||||||
match copy self.lookahead {
|
|
||||||
Some(tok) => { self.lookahead = None; copy tok }
|
|
||||||
None => { self.stream.recv() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unget(tok : Token) {
|
|
||||||
assert is_none(&self.lookahead);
|
|
||||||
self.lookahead = Some(move tok);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait ParserMethods {
|
|
||||||
fn parse_element() -> Option<~css::Selector>;
|
|
||||||
fn parse_selector() -> Option<~[~css::Selector]>;
|
|
||||||
fn parse_description() -> Option<~[StyleDeclaration]>;
|
|
||||||
fn parse_rule() -> Option<~css::Rule>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TokenReader : ParserMethods {
|
|
||||||
fn parse_element() -> Option<~css::Selector> {
|
|
||||||
// Get the current element type
|
|
||||||
let elmt_name = match self.get() {
|
|
||||||
lexer::Element(tag) => { copy tag }
|
|
||||||
lexer::Eof => { return None; }
|
|
||||||
_ => { fail ~"Expected an element" }
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut attr_list = ~[];
|
|
||||||
|
|
||||||
// Get the attributes associated with that element
|
|
||||||
loop {
|
|
||||||
let token = self.get();
|
|
||||||
match token {
|
|
||||||
lexer::Attr(attr) => { push(&mut attr_list, copy attr); }
|
|
||||||
tok::StartDescription | tok::Descendant | tok::Child | tok::Sibling | tok::Comma => {
|
|
||||||
self.unget(move token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tok::Eof => { return None; }
|
|
||||||
tok::Element(_) => fail ~"Unexpected second element without relation to first element",
|
|
||||||
tok::EndDescription => fail ~"Unexpected '}'",
|
|
||||||
tok::Description(_, _) => fail ~"Unexpected description"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Some(~css::Element(move elmt_name, move attr_list));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_selector() -> Option<~[~css::Selector]> {
|
|
||||||
let mut sel_list = ~[];
|
|
||||||
|
|
||||||
// Collect all the selectors that this rule applies to
|
|
||||||
loop {
|
|
||||||
let mut cur_sel;
|
|
||||||
|
|
||||||
match self.parse_element() {
|
|
||||||
Some(elmt) => { cur_sel = copy elmt; }
|
|
||||||
None => { return None; } // we hit an eof in the middle of a rule
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let tok = self.get();
|
|
||||||
let built_sel = move cur_sel;
|
|
||||||
|
|
||||||
match tok {
|
|
||||||
tok::Descendant => {
|
|
||||||
match self.parse_element() {
|
|
||||||
Some(elmt) => {
|
|
||||||
let new_sel = copy elmt;
|
|
||||||
cur_sel = ~css::Descendant(move built_sel, move new_sel)
|
|
||||||
}
|
|
||||||
None => { return None; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tok::Child => {
|
|
||||||
match self.parse_element() {
|
|
||||||
Some(elmt) => {
|
|
||||||
let new_sel = copy elmt;
|
|
||||||
cur_sel = ~css::Child(move built_sel, move new_sel)
|
|
||||||
}
|
|
||||||
None => { return None; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tok::Sibling => {
|
|
||||||
match self.parse_element() {
|
|
||||||
Some(elmt) => {
|
|
||||||
let new_sel = copy elmt;
|
|
||||||
cur_sel = ~css::Sibling(move built_sel, move new_sel)
|
|
||||||
}
|
|
||||||
None => { return None; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tok::StartDescription => {
|
|
||||||
push(&mut sel_list, move built_sel);
|
|
||||||
self.unget(tok::StartDescription);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tok::Comma => {
|
|
||||||
push(&mut sel_list, move built_sel);
|
|
||||||
self.unget(tok::Comma);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tok::Attr(_) | tok::EndDescription | tok::Element(_) | tok::Description(_, _) => {
|
|
||||||
fail #fmt["Unexpected token %? in elements", tok];
|
|
||||||
}
|
|
||||||
tok::Eof => { return None; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we should break out of the nesting loop as well
|
|
||||||
// TODO: fix this when rust gets labelled loops
|
|
||||||
let tok = self.get();
|
|
||||||
match tok {
|
|
||||||
tok::StartDescription => { break; }
|
|
||||||
tok::Comma => { }
|
|
||||||
_ => { self.unget(move tok); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(move sel_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_description() -> Option<~[StyleDeclaration]> {
|
|
||||||
let mut desc_list : ~[StyleDeclaration]= ~[];
|
|
||||||
|
|
||||||
// Get the description to be applied to the selector
|
|
||||||
loop {
|
|
||||||
let tok = self.get();
|
|
||||||
match tok {
|
|
||||||
tok::EndDescription => { break; }
|
|
||||||
tok::Description(prop, val) => {
|
|
||||||
let desc : Option<StyleDeclaration> = match prop {
|
|
||||||
// TODO: have color parsing return a ParseResult instead of a real value
|
|
||||||
~"background-color" => parse_color(val).map(|res| BackgroundColor(Specified(BgColor(*res)))),
|
|
||||||
~"color" => parse_color(val).map(|res| Color(Specified(TextColor(*res)))),
|
|
||||||
~"display" => parse_display_type(val).extract(|res| Display(res)),
|
|
||||||
~"font-size" => parse_font_size(val).extract(|res| FontSize(res)),
|
|
||||||
~"height" => parse_box_sizing(val).extract(|res| Height(res)),
|
|
||||||
~"width" => parse_box_sizing(val).extract(|res| Width(res)),
|
|
||||||
~"border-width" => parse_length(val).map(|res| BorderWidth(Specified(*res))),
|
|
||||||
~"border-color" => parse_color(val).map(|res| BorderColor(Specified(BdrColor(*res)))),
|
|
||||||
~"position" => parse_position(val).extract(|res| Position(res)),
|
|
||||||
~"top" => parse_length(val).map(|res| Top(Specified(*res))),
|
|
||||||
~"right" => parse_length(val).map(|res| Right(Specified(*res))),
|
|
||||||
~"bottom" => parse_length(val).map(|res| Bottom(Specified(*res))),
|
|
||||||
~"left" => parse_length(val).map(|res| Left(Specified(*res))),
|
|
||||||
_ => { #debug["Recieved unknown style property '%s'", val]; None }
|
|
||||||
};
|
|
||||||
match desc {
|
|
||||||
Some(d) => push(&mut desc_list, d),
|
|
||||||
None => { #debug["Couldn't parse value '%s' for property '%s'", val, prop] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tok::Eof => { return None; }
|
|
||||||
tok::StartDescription | tok::Descendant | tok::Child | tok::Sibling
|
|
||||||
| tok::Comma | tok::Element(_) | tok::Attr(_) => {
|
|
||||||
fail #fmt["Unexpected token %? in description", tok];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(move desc_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_rule() -> Option<~css::Rule> {
|
|
||||||
// TODO: get rid of copies once match move works
|
|
||||||
let sel_list = match self.parse_selector() {
|
|
||||||
Some(list) => { copy list }
|
|
||||||
None => { return None; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#debug("sel_list: %?", sel_list);
|
|
||||||
|
|
||||||
// Get the description to be applied to the selector
|
|
||||||
let desc_list = match self.parse_description() {
|
|
||||||
Some(list) => { copy list }
|
|
||||||
None => { return None; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#debug("desc_list: %?", desc_list);
|
|
||||||
|
|
||||||
return Some(~(move sel_list, move desc_list));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_stylesheet(stream : pipes::Port<Token>) -> ~[~css::Rule] {
|
|
||||||
let mut rule_list = ~[];
|
|
||||||
let reader = {stream : move stream, mut lookahead : None};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match reader.parse_rule() {
|
|
||||||
Some(rule) => { push(&mut rule_list, copy rule); }
|
|
||||||
None => { break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return move rule_list;
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
//! Helper functions to parse values of specific attributes
|
|
||||||
|
|
||||||
use newcss::values::*;
|
|
||||||
use str::{pop_char, from_chars};
|
|
||||||
use float::from_str;
|
|
||||||
use option::map;
|
|
||||||
|
|
||||||
export parse_font_size;
|
|
||||||
export parse_size;
|
|
||||||
export parse_box_sizing;
|
|
||||||
export parse_display_type;
|
|
||||||
|
|
||||||
|
|
||||||
fn parse_length(str : &str) -> Option<Length> {
|
|
||||||
// TODO: use these once we stop lexing below
|
|
||||||
const PTS_PER_INCH: float = 72.0;
|
|
||||||
const CM_PER_INCH: float = 2.54;
|
|
||||||
const PX_PER_PT: float = 1.0 / 0.75;
|
|
||||||
|
|
||||||
match str {
|
|
||||||
s if s.ends_with("in") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(1.0/0.75 * 72.0 * *f)),
|
|
||||||
s if s.ends_with("cm") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(*f / 2.54 * 72.0 * 1.0/0.75)),
|
|
||||||
s if s.ends_with("mm") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(*f * 0.1 / 2.54 * 72.0 * 1.0/0.75)),
|
|
||||||
s if s.ends_with("pt") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(1.0/0.75 * *f)),
|
|
||||||
s if s.ends_with("pc") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(1.0/0.75 * 12.0 * *f)),
|
|
||||||
s if s.ends_with("px") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(*f)),
|
|
||||||
s if s.ends_with("em") => from_str(str.substr(0, str.len() - 2)).map(|f| Em(*f)),
|
|
||||||
s if s.ends_with("ex") => from_str(str.substr(0, str.len() - 2)).map(|f| Em(0.5 * *f)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_absolute_size(str : &str) -> ParseResult<AbsoluteSize> {
|
|
||||||
// FIXME: Bad copy. Can't match &str
|
|
||||||
match str.to_str() {
|
|
||||||
~"xx-small" => Value(XXSmall),
|
|
||||||
~"x-small" => Value(XSmall),
|
|
||||||
~"small" => Value(Small),
|
|
||||||
~"medium" => Value(Medium),
|
|
||||||
~"large" => Value(Large),
|
|
||||||
~"x-large" => Value(XLarge),
|
|
||||||
~"xx-large" => Value(XXLarge),
|
|
||||||
_ => Fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_position(str: &str) -> ParseResult<CSSPosition> {
|
|
||||||
// FIXME: Bad copy
|
|
||||||
match str.to_str() {
|
|
||||||
~"static" => Value(PosStatic),
|
|
||||||
~"relative" => Value(PosRelative),
|
|
||||||
~"absolute" => Value(PosAbsolute),
|
|
||||||
~"fixed" => Value(PosFixed),
|
|
||||||
_ => Fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_relative_size(str: &str) -> ParseResult<RelativeSize> {
|
|
||||||
// FIXME: Bad copy. Can't match &str
|
|
||||||
match str.to_str() {
|
|
||||||
~"smaller" => Value(Smaller),
|
|
||||||
~"larger" => Value(Larger),
|
|
||||||
_ => Fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_font_size(_str: &str) -> ParseResult<CSSFontSize> {
|
|
||||||
// TODO: complete me
|
|
||||||
Value(LengthSize(Px(14.0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// For width / height, and anything else with the same attribute values
|
|
||||||
fn parse_box_sizing(str : &str) -> ParseResult<BoxSizing> {
|
|
||||||
// FIXME: Bad copy. Can't match &str
|
|
||||||
match str.to_str() {
|
|
||||||
~"auto" => Value(BoxAuto),
|
|
||||||
~"inherit" => CSSInherit,
|
|
||||||
_ => Fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_display_type(str : &str) -> ParseResult<CSSDisplay> {
|
|
||||||
// FIXME: Bad copy. Can't match &str
|
|
||||||
match str.to_str() {
|
|
||||||
~"inline" => Value(DisplayInline),
|
|
||||||
~"block" => Value(DisplayBlock),
|
|
||||||
~"none" => Value(DisplayNone),
|
|
||||||
_ => { #debug["Recieved unknown display value '%s'", str]; Fail }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use css::lexer::spawn_css_lexer_from_string;
|
|
||||||
use css::parser::build_stylesheet;
|
|
||||||
use newcss::values::{Stylesheet, Element, FontSize, Width, Height};
|
|
||||||
|
|
||||||
// TODO: use helper methods to create test values
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_match_font_sizes() {
|
|
||||||
let input = ~"* {font-size:12px; font-size:inherit; font-size:200%; font-size:x-small}";
|
|
||||||
let token_port = spawn_css_lexer_from_string(move input);
|
|
||||||
let _actual_rule = build_stylesheet(move token_port);
|
|
||||||
let _expected_rule : Stylesheet = ~[~(~[~Element(~"*", ~[])],
|
|
||||||
~[FontSize(Specified(LengthSize(Px(12.0)))),
|
|
||||||
FontSize(Specified(PercentSize(100.0))),
|
|
||||||
FontSize(Specified(PercentSize(200.0))),
|
|
||||||
FontSize(Specified(LengthSize(Px(12.0))))])];
|
|
||||||
|
|
||||||
// TODO: fix me once StyleDeclaration is a trait, not an enum
|
|
||||||
//assert actual_rule == expected_rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_match_width_height() {
|
|
||||||
let input = ~"* {width:20%; height:auto; width:20px; width:3in; height:70px; height:30px}";
|
|
||||||
let token_port = spawn_css_lexer_from_string(move input);
|
|
||||||
let _actual_rule = build_stylesheet(move token_port);
|
|
||||||
let _expected_rule : Stylesheet = ~[~(~[~Element(~"*", ~[])],
|
|
||||||
~[Width(Specified(BoxPercent(20.0))),
|
|
||||||
Height(Specified(BoxAuto)),
|
|
||||||
Width(Specified(BoxLength(Px(20.0)))),
|
|
||||||
Width(Specified(BoxLength(Px(216.0)))),
|
|
||||||
Height(Specified(BoxLength(Px(70.0)))),
|
|
||||||
Height(Specified(BoxLength(Px(30.0))))])];
|
|
||||||
|
|
||||||
// TODO: fix me once StyleDeclaration is a trait, not an enum
|
|
||||||
//assert actual_rule == expected_rule;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,8 +5,8 @@ Some little helpers for hooking up the HTML parser with the CSS parser
|
||||||
use std::net::url::Url;
|
use std::net::url::Url;
|
||||||
use resource::resource_task::{ResourceTask, ProgressMsg, Load, Payload, Done};
|
use resource::resource_task::{ResourceTask, ProgressMsg, Load, Payload, Done};
|
||||||
use newcss::values::Rule;
|
use newcss::values::Rule;
|
||||||
use css::lexer_util::DataStream;
|
use newcss::lexer_util::DataStream;
|
||||||
use css::lexer::{Token, lex_css_from_bytes};
|
use newcss::lexer::{Token, lex_css_from_bytes};
|
||||||
|
|
||||||
pub fn spawn_css_parser(url: Url, resource_task: ResourceTask) -> comm::Port<~[~Rule]> {
|
pub fn spawn_css_parser(url: Url, resource_task: ResourceTask) -> comm::Port<~[~Rule]> {
|
||||||
let result_port = comm::Port();
|
let result_port = comm::Port();
|
||||||
|
@ -15,7 +15,7 @@ pub fn spawn_css_parser(url: Url, resource_task: ResourceTask) -> comm::Port<~[~
|
||||||
let url = copy url;
|
let url = copy url;
|
||||||
do task::spawn |move url, copy resource_task| {
|
do task::spawn |move url, copy resource_task| {
|
||||||
let css_stream = spawn_css_lexer_task(copy url, resource_task);
|
let css_stream = spawn_css_lexer_task(copy url, resource_task);
|
||||||
let mut css_rules = css::parser::build_stylesheet(move css_stream);
|
let mut css_rules = newcss::parser::build_stylesheet(move css_stream);
|
||||||
result_chan.send(move css_rules);
|
result_chan.send(move css_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,6 @@ pub mod content {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod css {
|
pub mod css {
|
||||||
pub mod lexer;
|
|
||||||
pub mod lexer_util;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod parser_util;
|
|
||||||
|
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub mod resolve {
|
pub mod resolve {
|
||||||
pub mod apply;
|
pub mod apply;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue