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