can now convert from html to ir and back
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "b"
|
name = "r"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["hellerve <veit@veitheller.de>"]
|
authors = ["hellerve <veit@veitheller.de>"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "b"
|
name = "r"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
getopts = "0.2.3"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# b
|
# r
|
||||||
|
|
||||||
A browser engine written in Rust. I heard that’s what the cool kids do these
|
A browser engine written in Rust. I heard that’s what the cool kids do these
|
||||||
days.
|
days.
|
||||||
|
283
src/css.rs
283
src/css.rs
@@ -1,7 +1,4 @@
|
|||||||
enum Selector {
|
use std;
|
||||||
Simple(SimpleSelector),
|
|
||||||
Chain(ChainSelector),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SimpleSelector {
|
struct SimpleSelector {
|
||||||
tag_name: Option<String>,
|
tag_name: Option<String>,
|
||||||
@@ -15,10 +12,57 @@ struct ChainSelector {
|
|||||||
class: Vec<Vec<String>>,
|
class: Vec<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Selector {
|
||||||
|
Simple(SimpleSelector),
|
||||||
|
//Chain(ChainSelector),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Specificity = (usize, usize, usize);
|
||||||
|
|
||||||
|
impl Selector {
|
||||||
|
pub fn specificity(&self) -> Specificity {
|
||||||
|
let Selector::Simple(ref simple) = *self;
|
||||||
|
let a = simple.id.iter().count();
|
||||||
|
let b = simple.class.len();
|
||||||
|
let c = simple.tag_name.iter().count();
|
||||||
|
(a, b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Selector {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Selector::Simple(ref s) => {
|
||||||
|
let tag = s.tag_name.clone().unwrap_or("".to_string());
|
||||||
|
let id = s.id.clone().unwrap_or("".to_string());
|
||||||
|
write!(f, "{}{}{}", tag,
|
||||||
|
id,
|
||||||
|
s.class.join(" "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Unit {
|
enum Unit {
|
||||||
Px,
|
Px,
|
||||||
Em,
|
Em,
|
||||||
Rm,
|
Rm,
|
||||||
|
Vw,
|
||||||
|
Vh,
|
||||||
|
Perc
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Unit {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Unit::Px => write!(f, "px"),
|
||||||
|
Unit::Em => write!(f, "em"),
|
||||||
|
Unit::Rm => write!(f, "rm"),
|
||||||
|
Unit::Vw => write!(f, "vw"),
|
||||||
|
Unit::Vh => write!(f, "vh"),
|
||||||
|
Unit::Perc => write!(f, "%"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
@@ -34,16 +78,245 @@ enum Value {
|
|||||||
ColorValue(Color),
|
ColorValue(Color),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Value {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Value::Keyword(ref s) => write!(f, "{}", s),
|
||||||
|
Value::Length(n, ref u) => write!(f, "{}{}", n, u),
|
||||||
|
Value::ColorValue(ref c) => write!(f, "rgba({}, {}, {}, {})", c.r, c.g, c.b, c.a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Declaration {
|
struct Declaration {
|
||||||
name: String,
|
name: String,
|
||||||
value: Value,
|
value: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Declaration {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}: {};", self.name, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Rule {
|
struct Rule {
|
||||||
selectors: Vec<Selector>,
|
selectors: Vec<Selector>,
|
||||||
declarations: Vec<Declaration>,
|
declarations: Vec<Declaration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Stylesheet {
|
impl std::fmt::Display for Rule {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
let mut selector = "".to_string();
|
||||||
|
for s in self.selectors.iter() {
|
||||||
|
if selector.len() == 0 {
|
||||||
|
selector = format!("{}", s);
|
||||||
|
} else {
|
||||||
|
selector = format!("{}, {}", selector, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, "{}", selector);
|
||||||
|
write!(f, "{{ ");
|
||||||
|
for decl in self.declarations.iter() {
|
||||||
|
write!(f, "{}", decl);
|
||||||
|
}
|
||||||
|
write!(f, " }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stylesheet {
|
||||||
rules: Vec<Rule>,
|
rules: Vec<Rule>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Stylesheet {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
for rule in self.rules.iter() {
|
||||||
|
write!(f, "{}", rule);
|
||||||
|
}
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser {
|
||||||
|
pos: usize,
|
||||||
|
input: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
fn parse_rules(&mut self) -> Vec<Rule> {
|
||||||
|
let mut rules = Vec::new();
|
||||||
|
loop {
|
||||||
|
self.consume_ws();
|
||||||
|
if self.eof() { break }
|
||||||
|
rules.push(self.parse_rule());
|
||||||
|
}
|
||||||
|
rules
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_rule(&mut self) -> Rule {
|
||||||
|
Rule {
|
||||||
|
selectors: self.parse_selectors(),
|
||||||
|
declarations: self.parse_declarations(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_selectors(&mut self) -> Vec<Selector> {
|
||||||
|
let mut selectors = Vec::new();
|
||||||
|
loop {
|
||||||
|
selectors.push(Selector::Simple(self.parse_simple_selector()));
|
||||||
|
self.consume_ws();
|
||||||
|
match self.next() {
|
||||||
|
',' => { self.consume(); self.consume_ws(); }
|
||||||
|
'{' => break,
|
||||||
|
c => panic!("Unexpected character {} in selector list", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectors.sort_by(|a,b| b.specificity().cmp(&a.specificity()));
|
||||||
|
selectors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_simple_selector(&mut self) -> SimpleSelector {
|
||||||
|
let mut selector = SimpleSelector { tag_name: None, id: None, class: Vec::new() };
|
||||||
|
while !self.eof() {
|
||||||
|
match self.next() {
|
||||||
|
'#' => {
|
||||||
|
self.consume();
|
||||||
|
selector.id = Some(self.parse_id());
|
||||||
|
}
|
||||||
|
'.' => {
|
||||||
|
self.consume();
|
||||||
|
selector.class.push(self.parse_id());
|
||||||
|
}
|
||||||
|
'*' => {
|
||||||
|
self.consume();
|
||||||
|
}
|
||||||
|
c if valid_identifier_char(c) => {
|
||||||
|
selector.tag_name = Some(self.parse_id());
|
||||||
|
}
|
||||||
|
_ => break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selector
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_declarations(&mut self) -> Vec<Declaration> {
|
||||||
|
assert!(self.consume() == '{');
|
||||||
|
let mut declarations = Vec::new();
|
||||||
|
loop {
|
||||||
|
self.consume_ws();
|
||||||
|
if self.next() == '}' {
|
||||||
|
self.consume();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
declarations.push(self.parse_declaration());
|
||||||
|
}
|
||||||
|
declarations
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_declaration(&mut self) -> Declaration {
|
||||||
|
let property_name = self.parse_id();
|
||||||
|
self.consume_ws();
|
||||||
|
assert!(self.consume() == ':');
|
||||||
|
self.consume_ws();
|
||||||
|
let value = self.parse_value();
|
||||||
|
self.consume_ws();
|
||||||
|
assert!(self.consume() == ';');
|
||||||
|
|
||||||
|
Declaration {
|
||||||
|
name: property_name,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_value(&mut self) -> Value {
|
||||||
|
match self.next() {
|
||||||
|
'0'...'9' => self.parse_length(),
|
||||||
|
'#' => self.parse_color(),
|
||||||
|
_ => Value::Keyword(self.parse_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_length(&mut self) -> Value {
|
||||||
|
Value::Length(self.parse_float(), self.parse_unit())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_float(&mut self) -> f32 {
|
||||||
|
let s = self.consume_while(|c| match c {
|
||||||
|
'0'...'9' | '.' => true,
|
||||||
|
_ => false
|
||||||
|
});
|
||||||
|
s.parse().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_unit(&mut self) -> Unit {
|
||||||
|
match &*self.parse_id().to_lowercase() {
|
||||||
|
"px" => Unit::Px,
|
||||||
|
"em" => Unit::Em,
|
||||||
|
"rm" => Unit::Rm,
|
||||||
|
"vw" => Unit::Vw,
|
||||||
|
"vh" => Unit::Vh,
|
||||||
|
"%" => Unit::Perc,
|
||||||
|
_ => panic!("unrecognized unit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_color(&mut self) -> Value {
|
||||||
|
assert!(self.consume() == '#');
|
||||||
|
Value::ColorValue(Color {
|
||||||
|
r: self.parse_hex_pair(),
|
||||||
|
g: self.parse_hex_pair(),
|
||||||
|
b: self.parse_hex_pair(),
|
||||||
|
a: 255 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_hex_pair(&mut self) -> u8 {
|
||||||
|
let s = &self.input[self.pos .. self.pos + 2];
|
||||||
|
self.pos += 2;
|
||||||
|
u8::from_str_radix(s, 16).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_id(&mut self) -> String {
|
||||||
|
self.consume_while(valid_identifier_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_ws(&mut self) {
|
||||||
|
self.consume_while(char::is_whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_while<F>(&mut self, test: F) -> String
|
||||||
|
where F: Fn(char) -> bool {
|
||||||
|
let mut result = String::new();
|
||||||
|
while !self.eof() && test(self.next()) {
|
||||||
|
result.push(self.consume());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self) -> char {
|
||||||
|
let mut iter = self.input[self.pos..].char_indices();
|
||||||
|
let (_, cur_char) = iter.next().unwrap();
|
||||||
|
let (next_pos, _) = iter.next().unwrap_or((1, ' '));
|
||||||
|
self.pos += next_pos;
|
||||||
|
cur_char
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self) -> char {
|
||||||
|
self.input[self.pos..].chars().next().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eof(&self) -> bool {
|
||||||
|
self.pos >= self.input.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_identifier_char(c: char) -> bool {
|
||||||
|
match c {
|
||||||
|
'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(source: String) -> Stylesheet {
|
||||||
|
let mut parser = Parser { pos: 0, input: source };
|
||||||
|
Stylesheet { rules: parser.parse_rules() }
|
||||||
|
}
|
||||||
|
|
||||||
|
45
src/dom.rs
45
src/dom.rs
@@ -1,7 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std;
|
||||||
|
|
||||||
|
use css;
|
||||||
|
|
||||||
struct Attr {
|
pub struct Attr {
|
||||||
attrs: HashMap<String, String>,
|
attrs: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10,27 +12,32 @@ struct EData {
|
|||||||
attr: Attr,
|
attr: Attr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SData {
|
||||||
|
attr: Attr,
|
||||||
|
content: css::Stylesheet,
|
||||||
|
}
|
||||||
|
|
||||||
enum NType {
|
enum NType {
|
||||||
Text(String),
|
Text(String),
|
||||||
Comment(String),
|
Comment(String),
|
||||||
Element(EData),
|
Element(EData),
|
||||||
|
Stylesheet(SData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Node {
|
||||||
struct Node {
|
|
||||||
children: Vec<Node>,
|
children: Vec<Node>,
|
||||||
ntype: NType,
|
ntype: NType,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text(d: String) -> Node {
|
pub fn text(d: String) -> Node {
|
||||||
Node { children: Vec::new(), ntype: NType::Text(d) }
|
Node { children: Vec::new(), ntype: NType::Text(d) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comment(d: String) -> Node {
|
pub fn comment(d: String) -> Node {
|
||||||
Node { children: Vec::new(), ntype: NType::Comment(d) }
|
Node { children: Vec::new(), ntype: NType::Comment(d) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elem(name: String, attr: Attr, children: Vec<Node>) -> Node {
|
pub fn elem(name: String, attr: Attr, children: Vec<Node>) -> Node {
|
||||||
Node {
|
Node {
|
||||||
children: children,
|
children: children,
|
||||||
ntype: NType::Element(EData {
|
ntype: NType::Element(EData {
|
||||||
@@ -40,20 +47,35 @@ fn elem(name: String, attr: Attr, children: Vec<Node>) -> Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn attr(attrs: HashMap<String, String>) -> Attr {
|
||||||
|
Attr { attrs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn style(style: String, attr: Attr) -> Node {
|
||||||
|
Node {
|
||||||
|
children: Vec::new(),
|
||||||
|
ntype: NType::Stylesheet(SData {
|
||||||
|
content: css::parse(style),
|
||||||
|
attr: attr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Node {
|
impl std::fmt::Display for Node {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self.ntype {
|
match self.ntype {
|
||||||
NType::Text(ref s) => write!(f, "{}", s),
|
NType::Text(ref s) => write!(f, "{}", s),
|
||||||
NType::Comment(ref s) => write!(f, "<!--{}-->", s),
|
NType::Comment(ref s) => write!(f, "<!--{}-->", s),
|
||||||
NType::Element(ref d) => {
|
NType::Element(ref d) => {
|
||||||
write!(f, "<{}", d.name);
|
write!(f, "<{}{}>", d.name, d.attr);
|
||||||
write!(f, "{}", d.attr);
|
|
||||||
write!(f, ">");
|
|
||||||
for child in self.children.iter() {
|
for child in self.children.iter() {
|
||||||
write!(f, "{}", child);
|
write!(f, "{}", child);
|
||||||
}
|
}
|
||||||
write!(f, "</{}>", d.name)
|
write!(f, "</{}>", d.name)
|
||||||
},
|
},
|
||||||
|
NType::Stylesheet(ref s) => {
|
||||||
|
write!(f, "<style {}>{}</style>", s.attr, s.content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,8 +88,3 @@ impl std::fmt::Display for Attr {
|
|||||||
return Result::Ok(())
|
return Result::Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn main() {
|
|
||||||
let e = elem("html".to_string(), Attr{attrs:"ab".chars().map(|c| (c.to_string(), c.to_string())).collect::<HashMap<_, _>>()}, vec![elem("body".to_string(), Attr{attrs:HashMap::new()}, vec![text("hi".to_string())])]);
|
|
||||||
println!("{}", e)
|
|
||||||
}*/
|
|
||||||
|
78
src/html.rs
78
src/html.rs
@@ -1,3 +1,7 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use dom;
|
||||||
|
|
||||||
struct Parser {
|
struct Parser {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
input: String,
|
input: String,
|
||||||
@@ -29,20 +33,20 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn consume_while<F>(&mut self, test: F) -> String
|
fn consume_while<F>(&mut self, test: F) -> String
|
||||||
where F: Fn(char) -> bool {
|
where F: Fn(char, &mut Parser) -> bool {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
while !self.eof() && test(self.next()) {
|
while !self.eof() && test(self.next(), self) {
|
||||||
result.push(self.consume());
|
result.push(self.consume());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_ws(&mut self) {
|
fn consume_ws(&mut self) {
|
||||||
self.consume_while(CharExt::is_whitespace);
|
self.consume_while(|x, _| char::is_whitespace(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tag_name(&mut self) -> String {
|
fn parse_tag_name(&mut self) -> String {
|
||||||
self.consume_while(|c| match c {
|
self.consume_while(|c, _| match c {
|
||||||
'a'...'z' | 'A'...'Z' | '0'...'9' => true,
|
'a'...'z' | 'A'...'Z' | '0'...'9' => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
@@ -60,63 +64,73 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_text(&mut self) -> dom::Node {
|
fn parse_text(&mut self) -> dom::Node {
|
||||||
dom::text(self.consume_while(|c| c != '<'))
|
dom::text(self.consume_while(|c, _| c != '<'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_element(&mut self) -> dom::Node {
|
fn parse_element(&mut self) -> dom::Node {
|
||||||
// Opening tag.
|
assert!(self.consume() == '<');
|
||||||
assert!(self.consume_char() == '<');
|
|
||||||
let tag_name = self.parse_tag_name();
|
let tag_name = self.parse_tag_name();
|
||||||
let attrs = self.parse_attributes();
|
let attrs = self.parse_attributes();
|
||||||
assert!(self.consume_char() == '>');
|
assert!(self.consume() == '>');
|
||||||
|
|
||||||
|
if tag_name == "style" {
|
||||||
|
// TODO: make safe
|
||||||
|
let contents = self.consume_while(|c, s| c != '<' &&
|
||||||
|
s.next() != '/');
|
||||||
|
|
||||||
|
assert!(self.consume() == '<');
|
||||||
|
assert!(self.consume() == '/');
|
||||||
|
assert!(self.parse_tag_name() == tag_name);
|
||||||
|
assert!(self.consume() == '>');
|
||||||
|
|
||||||
|
return dom::style(contents, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
// Contents.
|
|
||||||
let children = self.parse_nodes();
|
let children = self.parse_nodes();
|
||||||
|
|
||||||
// Closing tag.
|
assert!(self.consume() == '<');
|
||||||
assert!(self.consume_char() == '<');
|
assert!(self.consume() == '/');
|
||||||
assert!(self.consume_char() == '/');
|
|
||||||
assert!(self.parse_tag_name() == tag_name);
|
assert!(self.parse_tag_name() == tag_name);
|
||||||
assert!(self.consume_char() == '>');
|
assert!(self.consume() == '>');
|
||||||
|
|
||||||
return dom::elem(tag_name, attrs, children);
|
return dom::elem(tag_name, attrs, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_comment(&mut self) -> dom::Node {
|
fn parse_comment(&mut self) -> dom::Node {
|
||||||
assert!(self.consume_char() == '<');
|
assert!(self.consume() == '<');
|
||||||
assert!(self.consume_char() == '!');
|
assert!(self.consume() == '!');
|
||||||
assert!(self.consume_char() == '-');
|
assert!(self.consume() == '-');
|
||||||
assert!(self.consume_char() == '-');
|
assert!(self.consume() == '-');
|
||||||
|
|
||||||
let contents = consume_while(|c| c != '-' &&
|
let contents = self.consume_while(|c, s| c != '-' &&
|
||||||
self.next() != '-' &&
|
s.next() != '-' &&
|
||||||
self.nth(1) != '>')
|
s.nth(1) != '>');
|
||||||
|
|
||||||
assert!(self.consume_char() == '-');
|
assert!(self.consume() == '-');
|
||||||
assert!(self.consume_char() == '-');
|
assert!(self.consume() == '-');
|
||||||
assert!(self.consume_char() == '>');
|
assert!(self.consume() == '>');
|
||||||
|
|
||||||
return dom::comment(contents);
|
return dom::comment(contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_attr(&mut self) -> (String, String) {
|
fn parse_attr(&mut self) -> (String, String) {
|
||||||
let name = self.parse_tag_name();
|
let name = self.parse_tag_name();
|
||||||
assert!(self.consume_char() == '=');
|
assert!(self.consume() == '=');
|
||||||
let value = self.parse_attr_value();
|
let value = self.parse_attr_value();
|
||||||
return (name, value);
|
return (name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a quoted value.
|
// Parse a quoted value.
|
||||||
fn parse_attr_value(&mut self) -> String {
|
fn parse_attr_value(&mut self) -> String {
|
||||||
let open_quote = self.consume_char();
|
let open_quote = self.consume();
|
||||||
assert!(open_quote == '"' || open_quote == '\'');
|
assert!(open_quote == '"' || open_quote == '\'');
|
||||||
let value = self.consume_while(|c| c != open_quote);
|
let value = self.consume_while(|c, _| c != open_quote);
|
||||||
assert!(self.consume_char() == open_quote);
|
assert!(self.consume() == open_quote);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a list of name="value" pairs, separated by whitespace.
|
// Parse a list of name="value" pairs, separated by whitespace.
|
||||||
fn parse_attributes(&mut self) -> dom::AttrMap {
|
fn parse_attributes(&mut self) -> dom::Attr {
|
||||||
let mut attributes = HashMap::new();
|
let mut attributes = HashMap::new();
|
||||||
loop {
|
loop {
|
||||||
self.consume_ws();
|
self.consume_ws();
|
||||||
@@ -126,7 +140,7 @@ impl Parser {
|
|||||||
let (name, value) = self.parse_attr();
|
let (name, value) = self.parse_attr();
|
||||||
attributes.insert(name, value);
|
attributes.insert(name, value);
|
||||||
}
|
}
|
||||||
return attributes;
|
return dom::attr(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_nodes(&mut self) -> Vec<dom::Node> {
|
fn parse_nodes(&mut self) -> Vec<dom::Node> {
|
||||||
@@ -140,14 +154,14 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse(source: String) -> dom::Node {
|
pub fn parse(source: String) -> dom::Node {
|
||||||
let mut nodes = Parser { pos: 0, input: source }.parse_nodes();
|
let mut nodes = Parser { pos: 0, input: source }.parse_nodes();
|
||||||
|
|
||||||
if nodes.len() == 1 {
|
if nodes.len() == 1 {
|
||||||
nodes.swap_remove(0)
|
nodes.swap_remove(0)
|
||||||
} else {
|
} else {
|
||||||
dom::elem("html".to_string(), HashMap::new(), nodes)
|
dom::elem("html".to_string(), dom::attr(HashMap::new()), nodes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user