From 10827f160ba43f6b66e859325fca07db242f1d7d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 8 Aug 2013 17:10:51 +0100 Subject: [PATCH] Initial pass at a stylesheet and selector parser --- .gitignore | 8 +++ Makefile.in | 32 +++++++++++ configure | 4 ++ properties.rs | 12 ++++ selectors.rs | 151 +++++++++++++++++++++++++++++++++++++++++++++++++ servo-style.rc | 25 ++++++++ stylesheets.rs | 58 +++++++++++++++++++ 7 files changed, 290 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.in create mode 100755 configure create mode 100644 properties.rs create mode 100644 selectors.rs create mode 100644 servo-style.rc create mode 100644 stylesheets.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..504bab8100a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.o +*.a +*.so +*.dylib +*.dll +*.dummy +*-test +Makefile diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 00000000000..ba8c321ade9 --- /dev/null +++ b/Makefile.in @@ -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 diff --git a/configure b/configure new file mode 100755 index 00000000000..62a0f4cd3e6 --- /dev/null +++ b/configure @@ -0,0 +1,4 @@ +#!/bin/bash + +SRCDIR="$(cd $(dirname $0) && pwd)" +sed "s#%VPATH%#${SRCDIR}#" ${SRCDIR}/Makefile.in > Makefile diff --git a/properties.rs b/properties.rs new file mode 100644 index 00000000000..f10e8870de9 --- /dev/null +++ b/properties.rs @@ -0,0 +1,12 @@ +use cssparser::*; + + +pub struct PropertyDeclaration; // TODO + + +pub fn parse_property_declaration_list(input: &[Node]) -> ~[PropertyDeclaration] { + let _ = input; + ~[] +} + + diff --git a/selectors.rs b/selectors.rs new file mode 100644 index 00000000000..8407d61eb20 --- /dev/null +++ b/selectors.rs @@ -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 +} diff --git a/servo-style.rc b/servo-style.rc new file mode 100644 index 00000000000..60cf87b0e8f --- /dev/null +++ b/servo-style.rc @@ -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; diff --git a/stylesheets.rs b/stylesheets.rs new file mode 100644 index 00000000000..7f27be155bd --- /dev/null +++ b/stylesheets.rs @@ -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); + +impl>> Iterator for ErrorLogger { + fn next(&mut self) -> Option { + 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) +}