Initial pass at a stylesheet and selector parser

This commit is contained in:
Simon Sapin 2013-08-08 17:10:51 +01:00
commit 10827f160b
7 changed files with 290 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
*.o
*.a
*.so
*.dylib
*.dll
*.dummy
*-test
Makefile

32
Makefile.in Normal file
View file

@ -0,0 +1,32 @@
VPATH=%VPATH%
CC ?= gcc
CXX ?= g++
CXXFLAGS ?=
AR ?= ar
RUSTC ?= rustc
RUSTFLAGS ?=
RUST_SRC=$(shell find $(VPATH)/. -type f -name '*.rs')
.PHONY: all
all: libservo-style.dummy
libservo-style.dummy: servo-style.rc $(RUST_SRC)
$(RUSTC) $(RUSTFLAGS) $<
touch $@
servo-style-test: servo-style.rc $(RUST_SRC)
$(RUSTC) $(RUSTFLAGS) $< -o $@ --test
.PHONY: check
check: servo-style-test
./servo-style-test
.PHONY: check-debug
check-debug: servo-style-tests
echo -e "start\n break upcall_fail\n continue\n where\n continue" | gdb -q ./servo-style-tests
.PHONY: clean
clean:
rm -f *.o *.a *.so *.dylib *.dll *.dummy *-test

4
configure vendored Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
SRCDIR="$(cd $(dirname $0) && pwd)"
sed "s#%VPATH%#${SRCDIR}#" ${SRCDIR}/Makefile.in > Makefile

12
properties.rs Normal file
View file

@ -0,0 +1,12 @@
use cssparser::*;
pub struct PropertyDeclaration; // TODO
pub fn parse_property_declaration_list(input: &[Node]) -> ~[PropertyDeclaration] {
let _ = input;
~[]
}

151
selectors.rs Normal file
View file

@ -0,0 +1,151 @@
use cssparser::*;
pub struct Selector {
compound_selectors: CompoundSelector,
pseudo_element: PseudoElement,
specificity: u32,
}
pub enum PseudoElement {
Element, // No pseudo-element
Before,
After,
FirstLine,
FirstLetter,
}
pub struct CompoundSelector {
simple_selectors: ~[SimpleSelector],
next: Option<(~CompoundSelector, Combinator)>, // c.next is left of c
}
pub enum Combinator {
Child, // >
Descendant, // space
NextSibling, // +
LaterSibling, // ~
}
pub enum SimpleSelector {
IDSelector(~str),
ClassSelector(~str),
LocalNameSelector{lowercase_name: ~str, cased_name: ~str},
// NamespaceSelector(Namespace)
// Attribute selectors
AttrExists(AttrSelector), // [foo]
AttrEqual(AttrSelector, ~str), // [foo=bar]
AttrIncludes(AttrSelector, ~str), // [foo~=bar]
AttrDashMatch(AttrSelector, ~str), // [foo|=bar]
AttrPrefixMatch(AttrSelector, ~str), // [foo^=bar]
AttrSubstringMatch(AttrSelector, ~str), // [foo*=bar]
AttrSuffixMatch(AttrSelector, ~str), // [foo$=bar]
// Pseudo-classes
// Empty,
// Root,
// Lang(~str),
// NthChild(u32, u32),
// NthLastChild(u32, u32),
// NthOfType(u32, u32),
// NthLastOfType(u32, u32),
// Lang(~str),
// Negation(~Selector),
// ...
}
pub struct AttrSelector {
lowercase_name: ~str,
cased_name: ~str,
// namespace: Option<~str>,
}
pub fn parse_selector_list(input: &[ComponentValue]) -> Option<~[Selector]> {
let len = input.len();
let (first, pos) = match parse_selector(input, 0) {
None => return None,
Some(result) => result
};
let mut results = ~[first];
let mut pos = pos;
loop {
pos = skip_whitespace(input, pos);
if pos >= len { break } // EOF
if input[pos] != Comma { return None }
pos = skip_whitespace(input, pos);
match parse_selector(input, pos) {
None => return None,
Some((selector, next_pos)) => {
results.push(selector);
pos = next_pos;
}
}
}
Some(results)
}
fn parse_selector(input: &[ComponentValue], pos: uint) -> Option<(Selector, uint)> {
let len = input.len();
let (first, pos) = match parse_simple_selectors(input, pos) {
None => return None,
Some(result) => result
};
let mut compound = CompoundSelector{ simple_selectors: first, next: None };
let mut pos = pos;
loop {
let pre_whitespace_pos = pos;
pos = skip_whitespace(input, pos);
if pos >= len { break } // EOF
let combinator = match input[pos] {
Delim('>') => { pos += 1; Child },
Delim('+') => { pos += 1; NextSibling },
Delim('~') => { pos += 1; LaterSibling },
_ => {
if pos > pre_whitespace_pos { Descendant }
else { return None }
}
};
pos = skip_whitespace(input, pos);
match parse_simple_selectors(input, pos) {
None => return None,
Some((simple_selectors, next_pos)) => {
compound = CompoundSelector {
simple_selectors: simple_selectors,
next: Some((~compound, combinator))
};
pos = next_pos;
}
}
}
let selector = Selector{
compound_selectors: compound,
pseudo_element: Element,
specificity: 0, // TODO
};
Some((selector, pos))
}
fn parse_simple_selectors(input: &[ComponentValue], pos: uint)
-> Option<(~[SimpleSelector], uint)> {
let _ = input;
let _ = pos;
None // TODO
}
#[inline]
fn skip_whitespace(input: &[ComponentValue], mut pos: uint) -> uint {
let len = input.len();
while pos < len {
if input[pos] == WhiteSpace { break }
pos += 1;
}
pos
}

25
servo-style.rc Normal file
View file

@ -0,0 +1,25 @@
#[link(name = "servo-style", vers = "0.1")];
#[crate_type = "lib"];
extern mod extra;
#[path = "../rust-cssparser/ast.rs"]
mod ast;
#[path = "../rust-cssparser/tokenizer.rs"]
mod tokenizer;
#[path = "../rust-cssparser/parser.rs"]
mod syntax;
#[path = "../rust-cssparser/color.rs"]
mod color;
mod cssparser {
pub use super::ast::*;
pub use super::tokenizer::*;
pub use super::syntax::*;
}
pub mod stylesheets;
pub mod selectors;
pub mod properties;

58
stylesheets.rs Normal file
View file

@ -0,0 +1,58 @@
use std::iterator::Iterator;
use cssparser::*;
use selectors;
use properties;
struct Stylesheet {
style_rules: ~[StyleRule],
}
struct StyleRule {
selectors: ~[selectors::Selector],
declarations: ~[properties::PropertyDeclaration],
}
fn parse_stylesheet(css: &str) -> Stylesheet {
let mut rules = ~[];
for rule in ErrorLogger(parse_stylesheet_rules(tokenize(css))) {
match rule {
AtRule(rule) => {
log_css_error(fmt!("Unsupported at-rule: @%s", rule.name))
},
QualifiedRule(rule) => {
match selectors::parse_selector_list(rule.prelude) {
Some(selectors) => rules.push(StyleRule{
selectors: selectors,
declarations: properties::parse_property_declaration_list(rule.block)
}),
None => log_css_error("Unsupported CSS selector."),
}
},
}
}
Stylesheet{ style_rules: rules }
}
struct ErrorLogger<I>(I);
impl<T, I: Iterator<Result<T, ErrorReason>>> Iterator<T> for ErrorLogger<I> {
fn next(&mut self) -> Option<T> {
for result in **self {
match result {
Ok(v) => return Some(v),
Err(e) => log_css_error(fmt!("%?", e))
}
}
None
}
}
fn log_css_error(message: &str) {
// TODO eventually this will got into a "web console" or something.
info!(message)
}