commit 10d910d8dc42188eb982da389bf5ed43b23a9e99 Author: hellerve Date: Fri Aug 25 15:39:53 2017 +0200 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4308d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1e87aad --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "b" +version = "0.1.0" +authors = ["hellerve "] + +[[bin]] +name = "b" +path = "src/main.rs" + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..0314970 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# b + +A browser engine written in Rust. I heard that’s what the cool kids do these +days. diff --git a/src/css.rs b/src/css.rs new file mode 100644 index 0000000..4777a3e --- /dev/null +++ b/src/css.rs @@ -0,0 +1,49 @@ +enum Selector { + Simple(SimpleSelector), + Chain(ChainSelector), +} + +struct SimpleSelector { + tag_name: Option, + id: Option, + class: Vec, +} + +struct ChainSelector { + tag_name: Option>, + id: Option>, + class: Vec>, +} + +enum Unit { + Px, + Em, + Rm, +} + +struct Color { + r: u8, + g: u8, + b: u8, + a: u8, +} + +enum Value { + Keyword(String), + Length(f32, Unit), + ColorValue(Color), +} + +struct Declaration { + name: String, + value: Value, +} + +struct Rule { + selectors: Vec, + declarations: Vec, +} + +struct Stylesheet { + rules: Vec, +} diff --git a/src/dom.rs b/src/dom.rs new file mode 100644 index 0000000..1eebd0d --- /dev/null +++ b/src/dom.rs @@ -0,0 +1,73 @@ +use std::collections::HashMap; + + +struct Attr { + attrs: HashMap, +} + +struct EData { + name: String, + attr: Attr, +} + +enum NType { + Text(String), + Comment(String), + Element(EData), +} + + +struct Node { + children: Vec, + ntype: NType, +} + +fn text(d: String) -> Node { + Node { children: Vec::new(), ntype: NType::Text(d) } +} + +fn comment(d: String) -> Node { + Node { children: Vec::new(), ntype: NType::Comment(d) } +} + +fn elem(name: String, attr: Attr, children: Vec) -> Node { + Node { + children: children, + ntype: NType::Element(EData { + name: name, + attr: attr, + }), + } +} + +impl std::fmt::Display for Node { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self.ntype { + NType::Text(ref s) => write!(f, "{}", s), + NType::Comment(ref s) => write!(f, "", s), + NType::Element(ref d) => { + write!(f, "<{}", d.name); + write!(f, "{}", d.attr); + write!(f, ">"); + for child in self.children.iter() { + write!(f, "{}", child); + } + write!(f, "", d.name) + }, + } + } +} + +impl std::fmt::Display for Attr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for (k, v) in self.attrs.iter() { + write!(f, " {}=\"{}\"", k, v); + } + return Result::Ok(()) + } +} + +/*fn main() { + let e = elem("html".to_string(), Attr{attrs:"ab".chars().map(|c| (c.to_string(), c.to_string())).collect::>()}, vec![elem("body".to_string(), Attr{attrs:HashMap::new()}, vec![text("hi".to_string())])]); + println!("{}", e) +}*/ diff --git a/src/html.rs b/src/html.rs new file mode 100644 index 0000000..a393b10 --- /dev/null +++ b/src/html.rs @@ -0,0 +1,153 @@ +struct Parser { + pos: usize, + input: String, +} + +impl Parser { + fn next(&self) -> char { + self.input[self.pos..].chars().next().unwrap() + } + + fn nth(&self, n: usize) -> char { + self.input[self.pos..].chars().nth(n).unwrap() + } + + fn starts_with(&self, s: &str) -> bool { + self.input[self.pos ..].starts_with(s) + } + + fn eof(&self) -> bool { + self.pos >= self.input.len() + } + + fn consume(&mut self) -> char { + let mut iter = self.input[self.pos..].char_indices(); + let (_, cur) = iter.next().unwrap(); + let (inc, _) = iter.next().unwrap_or((1, ' ')); + self.pos += inc; + return cur; + } + + fn consume_while(&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()); + } + return result; + } + + fn consume_ws(&mut self) { + self.consume_while(CharExt::is_whitespace); + } + + fn parse_tag_name(&mut self) -> String { + self.consume_while(|c| match c { + 'a'...'z' | 'A'...'Z' | '0'...'9' => true, + _ => false, + }) + } + fn parse_node(&mut self) -> dom::Node { + match self.next() { + '<' => { + match self.next() { + '!' => self.parse_comment(), + _ => self.parse_element(), + } + }, + _ => self.parse_text() + } + } + + fn parse_text(&mut self) -> dom::Node { + dom::text(self.consume_while(|c| c != '<')) + } + + fn parse_element(&mut self) -> dom::Node { + // Opening tag. + assert!(self.consume_char() == '<'); + let tag_name = self.parse_tag_name(); + let attrs = self.parse_attributes(); + assert!(self.consume_char() == '>'); + + // Contents. + let children = self.parse_nodes(); + + // Closing tag. + assert!(self.consume_char() == '<'); + assert!(self.consume_char() == '/'); + assert!(self.parse_tag_name() == tag_name); + assert!(self.consume_char() == '>'); + + return dom::elem(tag_name, attrs, children); + } + + fn parse_comment(&mut self) -> dom::Node { + assert!(self.consume_char() == '<'); + assert!(self.consume_char() == '!'); + assert!(self.consume_char() == '-'); + assert!(self.consume_char() == '-'); + + let contents = consume_while(|c| c != '-' && + self.next() != '-' && + self.nth(1) != '>') + + assert!(self.consume_char() == '-'); + assert!(self.consume_char() == '-'); + assert!(self.consume_char() == '>'); + + return dom::comment(contents); + } + + fn parse_attr(&mut self) -> (String, String) { + let name = self.parse_tag_name(); + assert!(self.consume_char() == '='); + let value = self.parse_attr_value(); + return (name, value); + } + + // Parse a quoted value. + fn parse_attr_value(&mut self) -> String { + let open_quote = self.consume_char(); + assert!(open_quote == '"' || open_quote == '\''); + let value = self.consume_while(|c| c != open_quote); + assert!(self.consume_char() == open_quote); + return value; + } + + // Parse a list of name="value" pairs, separated by whitespace. + fn parse_attributes(&mut self) -> dom::AttrMap { + let mut attributes = HashMap::new(); + loop { + self.consume_ws(); + if self.next() == '>' { + break; + } + let (name, value) = self.parse_attr(); + attributes.insert(name, value); + } + return attributes; + } + + fn parse_nodes(&mut self) -> Vec { + let mut nodes = Vec::new(); + loop { + self.consume_ws(); + if self.eof() || self.starts_with(" dom::Node { + let mut nodes = Parser { pos: 0, input: source }.parse_nodes(); + + if nodes.len() == 1 { + nodes.swap_remove(0) + } else { + dom::elem("html".to_string(), HashMap::new(), nodes) + } + } +}