mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Initial pass at a stylesheet and selector parser
This commit is contained in:
commit
10827f160b
7 changed files with 290 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.dummy
|
||||
*-test
|
||||
Makefile
|
32
Makefile.in
Normal file
32
Makefile.in
Normal 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
4
configure
vendored
Executable 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
12
properties.rs
Normal 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
151
selectors.rs
Normal 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
25
servo-style.rc
Normal 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
58
stylesheets.rs
Normal 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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue