mirror of
https://github.com/servo/servo.git
synced 2025-06-29 19:43:39 +01:00
Factor out parsing of various types of rules.
This commit is contained in:
parent
ff1f4e62ec
commit
9b22acf2f3
5 changed files with 152 additions and 104 deletions
26
errors.rs
Normal file
26
errors.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use cssparser::{SyntaxError, SourceLocation};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ErrorLoggerIterator<I>(I);
|
||||||
|
|
||||||
|
impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLoggerIterator<I> {
|
||||||
|
pub fn next(&mut self) -> Option<T> {
|
||||||
|
for result in **self {
|
||||||
|
match result {
|
||||||
|
Ok(v) => return Some(v),
|
||||||
|
Err(error) => log_css_error(error.location, fmt!("%?", error.reason))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn log_css_error(location: SourceLocation, message: &str) {
|
||||||
|
// TODO eventually this will got into a "web console" or something.
|
||||||
|
info!("%u:%u %s", location.line, location.column, message)
|
||||||
|
}
|
63
namespaces.rs
Normal file
63
namespaces.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::hashmap::HashMap;
|
||||||
|
use cssparser::*;
|
||||||
|
use errors::log_css_error;
|
||||||
|
|
||||||
|
pub struct NamespaceMap {
|
||||||
|
default: Option<~str>, // Optional URL
|
||||||
|
prefix_map: HashMap<~str, ~str>, // prefix -> URL
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl NamespaceMap {
|
||||||
|
pub fn new() -> NamespaceMap {
|
||||||
|
NamespaceMap { default: None, prefix_map: HashMap::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) {
|
||||||
|
let location = rule.location;
|
||||||
|
macro_rules! syntax_error(
|
||||||
|
() => {{
|
||||||
|
log_css_error(location, "Invalid @namespace rule");
|
||||||
|
return
|
||||||
|
}};
|
||||||
|
);
|
||||||
|
if rule.block.is_some() { syntax_error!() }
|
||||||
|
let mut prefix: Option<~str> = None;
|
||||||
|
let mut url: Option<~str> = None;
|
||||||
|
let mut iter = rule.prelude.consume_skip_whitespace();
|
||||||
|
for component_value in iter {
|
||||||
|
match component_value {
|
||||||
|
Ident(value) => {
|
||||||
|
if prefix.is_some() { syntax_error!() }
|
||||||
|
prefix = Some(value);
|
||||||
|
},
|
||||||
|
URL(value) | String(value) => {
|
||||||
|
if url.is_some() { syntax_error!() }
|
||||||
|
url = Some(value);
|
||||||
|
break
|
||||||
|
},
|
||||||
|
_ => syntax_error!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if iter.next().is_some() { syntax_error!() }
|
||||||
|
match (prefix, url) {
|
||||||
|
(Some(prefix), Some(url)) => {
|
||||||
|
if namespaces.prefix_map.swap(prefix, url).is_some() {
|
||||||
|
log_css_error(location, "Duplicate @namespace rule");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(None, Some(url)) => {
|
||||||
|
if namespaces.default.is_some() {
|
||||||
|
log_css_error(location, "Duplicate @namespace rule");
|
||||||
|
}
|
||||||
|
namespaces.default = Some(url);
|
||||||
|
},
|
||||||
|
_ => syntax_error!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
use std::{vec, iterator};
|
use std::{vec, iterator};
|
||||||
use std::ascii::to_ascii_lower;
|
use std::ascii::to_ascii_lower;
|
||||||
use cssparser::*;
|
use cssparser::*;
|
||||||
use stylesheets::NamespaceMap;
|
use namespaces::NamespaceMap;
|
||||||
|
|
||||||
|
|
||||||
pub struct Selector {
|
pub struct Selector {
|
||||||
|
@ -68,7 +68,7 @@ pub struct AttrSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Iter = iterator::PeekableIterator<ComponentValue, vec::ConsumeIterator<ComponentValue>>;
|
type Iter = iterator::Peekable<ComponentValue, vec::ConsumeIterator<ComponentValue>>;
|
||||||
|
|
||||||
|
|
||||||
// None means invalid selector
|
// None means invalid selector
|
||||||
|
|
|
@ -9,5 +9,7 @@ extern mod extra;
|
||||||
extern mod cssparser;
|
extern mod cssparser;
|
||||||
|
|
||||||
pub mod stylesheets;
|
pub mod stylesheets;
|
||||||
|
pub mod errors;
|
||||||
pub mod selectors;
|
pub mod selectors;
|
||||||
pub mod properties;
|
pub mod properties;
|
||||||
|
pub mod namespaces;
|
||||||
|
|
161
stylesheets.rs
161
stylesheets.rs
|
@ -3,29 +3,31 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::iterator::Iterator;
|
use std::iterator::Iterator;
|
||||||
use std::hashmap::HashMap;
|
|
||||||
use std::ascii::to_ascii_lower;
|
use std::ascii::to_ascii_lower;
|
||||||
use cssparser::*;
|
use cssparser::*;
|
||||||
use selectors;
|
use selectors;
|
||||||
use properties;
|
use properties;
|
||||||
|
use errors::{ErrorLoggerIterator, log_css_error};
|
||||||
|
use namespaces::{NamespaceMap, parse_namespace_rule};
|
||||||
|
|
||||||
|
|
||||||
pub struct Stylesheet {
|
pub struct Stylesheet {
|
||||||
style_rules: ~[StyleRule],
|
style_rules: ~[CSSRule],
|
||||||
namespaces: NamespaceMap,
|
namespaces: NamespaceMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub enum CSSRule {
|
||||||
|
CSSStyleRule(StyleRule),
|
||||||
|
// CSSMediaRule(MediaRule),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct StyleRule {
|
pub struct StyleRule {
|
||||||
selectors: ~[selectors::Selector],
|
selectors: ~[selectors::Selector],
|
||||||
declarations: ~[properties::PropertyDeclaration],
|
declarations: ~[properties::PropertyDeclaration],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NamespaceMap {
|
|
||||||
default: Option<~str>, // Optional URL
|
|
||||||
prefix_map: HashMap<~str, ~str>, // prefix -> URL
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn parse_stylesheet(css: &str) -> Stylesheet {
|
fn parse_stylesheet(css: &str) -> Stylesheet {
|
||||||
static STATE_CHARSET: uint = 1;
|
static STATE_CHARSET: uint = 1;
|
||||||
|
@ -35,113 +37,68 @@ fn parse_stylesheet(css: &str) -> Stylesheet {
|
||||||
let mut state: uint = STATE_CHARSET;
|
let mut state: uint = STATE_CHARSET;
|
||||||
|
|
||||||
let mut rules = ~[];
|
let mut rules = ~[];
|
||||||
let mut namespaces = NamespaceMap { default: None, prefix_map: HashMap::new() };
|
let mut namespaces = NamespaceMap::new();
|
||||||
|
|
||||||
for rule in ErrorLogger(parse_stylesheet_rules(tokenize(css))) {
|
for rule in ErrorLoggerIterator(parse_stylesheet_rules(tokenize(css))) {
|
||||||
|
let next_state; // Unitialized to force each branch to set it.
|
||||||
match rule {
|
match rule {
|
||||||
AtRule(rule) => {
|
AtRule(rule) => {
|
||||||
let name = to_ascii_lower(rule.name);
|
let name: &str = to_ascii_lower(rule.name);
|
||||||
if "charset" == name {
|
match name {
|
||||||
if state > STATE_CHARSET { log_css_error(rule.location,
|
"charset" => {
|
||||||
"@charset must be the first rule") }
|
if state > STATE_CHARSET {
|
||||||
// Valid @charset rules are just ignored
|
log_css_error(rule.location, "@charset must be the first rule")
|
||||||
loop;
|
|
||||||
}
|
|
||||||
if "import" == name {
|
|
||||||
if state > STATE_IMPORTS { log_css_error(
|
|
||||||
rule.location, "@import must be before any rule but @charset") }
|
|
||||||
else {
|
|
||||||
state = STATE_IMPORTS;
|
|
||||||
log_css_error(rule.location, "@import is not supported yet") // TODO
|
|
||||||
}
|
|
||||||
loop;
|
|
||||||
}
|
|
||||||
if "namespace" == name {
|
|
||||||
if state > STATE_NAMESPACES { log_css_error(rule.location,
|
|
||||||
"@namespace must be before any rule but @charset and @import") }
|
|
||||||
else {
|
|
||||||
state = STATE_NAMESPACES;
|
|
||||||
let location = rule.location;
|
|
||||||
if !parse_namespace_rule(rule, &mut namespaces) {
|
|
||||||
log_css_error(location, "Invalid @namespace rule")
|
|
||||||
}
|
}
|
||||||
}
|
// Valid @charset rules are just ignored
|
||||||
loop;
|
next_state = state;
|
||||||
|
},
|
||||||
|
"import" => {
|
||||||
|
if state > STATE_IMPORTS {
|
||||||
|
next_state = state;
|
||||||
|
log_css_error(rule.location,
|
||||||
|
"@import must be before any rule but @charset")
|
||||||
|
} else {
|
||||||
|
next_state = STATE_IMPORTS;
|
||||||
|
log_css_error(rule.location, "@import is not supported yet") // TODO
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"namespace" => {
|
||||||
|
if state > STATE_NAMESPACES {
|
||||||
|
next_state = state;
|
||||||
|
log_css_error(
|
||||||
|
rule.location,
|
||||||
|
"@namespace must be before any rule but @charset and @import"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
next_state = STATE_NAMESPACES;
|
||||||
|
parse_namespace_rule(rule, &mut namespaces)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
next_state = STATE_BODY;
|
||||||
|
log_css_error(rule.location, fmt!("Unsupported at-rule: @%s", name))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
state = STATE_BODY;
|
|
||||||
log_css_error(rule.location, fmt!("Unsupported at-rule: @%s", name))
|
|
||||||
},
|
},
|
||||||
QualifiedRule(QualifiedRule{location: location, prelude: prelude, block: block}) => {
|
QualifiedRule(rule) => {
|
||||||
state = STATE_BODY;
|
next_state = STATE_BODY;
|
||||||
match selectors::parse_selector_list(prelude, &namespaces) {
|
parse_style_rule(rule, &mut rules, &namespaces)
|
||||||
Some(selectors) => rules.push(StyleRule{
|
|
||||||
selectors: selectors,
|
|
||||||
declarations: properties::parse_property_declaration_list(block)
|
|
||||||
}),
|
|
||||||
None => log_css_error(location, "Unsupported CSS selector."),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
state = next_state;
|
||||||
}
|
}
|
||||||
Stylesheet{ style_rules: rules, namespaces: namespaces }
|
Stylesheet{ style_rules: rules, namespaces: namespaces }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) -> bool {
|
fn parse_style_rule(rule: QualifiedRule, rule_list: &mut ~[CSSRule],
|
||||||
if rule.block.is_some() { return false }
|
namespaces: &NamespaceMap) {
|
||||||
let location = rule.location;
|
let QualifiedRule{location: location, prelude: prelude, block: block} = rule;
|
||||||
let mut prefix: Option<~str> = None;
|
match selectors::parse_selector_list(prelude, namespaces) {
|
||||||
let mut url: Option<~str> = None;
|
Some(selectors) => rule_list.push(CSSStyleRule(StyleRule{
|
||||||
let mut iter = rule.prelude.consume_skip_whitespace();
|
selectors: selectors,
|
||||||
for component_value in iter {
|
declarations: properties::parse_property_declaration_list(block)
|
||||||
match component_value {
|
})),
|
||||||
Ident(value) => {
|
None => log_css_error(location, "Unsupported CSS selector."),
|
||||||
if prefix.is_some() { return false }
|
|
||||||
prefix = Some(value);
|
|
||||||
},
|
|
||||||
URL(value) | String(value) => {
|
|
||||||
if url.is_some() { return false }
|
|
||||||
url = Some(value);
|
|
||||||
break
|
|
||||||
},
|
|
||||||
_ => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if iter.next().is_some() { return false }
|
|
||||||
match (prefix, url) {
|
|
||||||
(Some(prefix), Some(url)) => {
|
|
||||||
if namespaces.prefix_map.swap(prefix, url).is_some() {
|
|
||||||
log_css_error(location, "Duplicate @namespace rule");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(None, Some(url)) => {
|
|
||||||
if namespaces.default.is_some() {
|
|
||||||
log_css_error(location, "Duplicate @namespace rule");
|
|
||||||
}
|
|
||||||
namespaces.default = Some(url);
|
|
||||||
},
|
|
||||||
_ => return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct ErrorLogger<I>(I);
|
|
||||||
|
|
||||||
impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLogger<I> {
|
|
||||||
fn next(&mut self) -> Option<T> {
|
|
||||||
for result in **self {
|
|
||||||
match result {
|
|
||||||
Ok(v) => return Some(v),
|
|
||||||
Err(error) => log_css_error(error.location, fmt!("%?", error.reason))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn log_css_error(location: SourceLocation, message: &str) {
|
|
||||||
// TODO eventually this will got into a "web console" or something.
|
|
||||||
info!("%u:%u %s", location.line, location.column, message)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue