initial
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target/
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["hellerve <veit@veitheller.de>"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "b"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
4
README.md
Normal file
4
README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# b
|
||||||
|
|
||||||
|
A browser engine written in Rust. I heard that’s what the cool kids do these
|
||||||
|
days.
|
49
src/css.rs
Normal file
49
src/css.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
enum Selector {
|
||||||
|
Simple(SimpleSelector),
|
||||||
|
Chain(ChainSelector),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SimpleSelector {
|
||||||
|
tag_name: Option<String>,
|
||||||
|
id: Option<String>,
|
||||||
|
class: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChainSelector {
|
||||||
|
tag_name: Option<Vec<String>>,
|
||||||
|
id: Option<Vec<String>>,
|
||||||
|
class: Vec<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Selector>,
|
||||||
|
declarations: Vec<Declaration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stylesheet {
|
||||||
|
rules: Vec<Rule>,
|
||||||
|
}
|
73
src/dom.rs
Normal file
73
src/dom.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
||||||
|
struct Attr {
|
||||||
|
attrs: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EData {
|
||||||
|
name: String,
|
||||||
|
attr: Attr,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NType {
|
||||||
|
Text(String),
|
||||||
|
Comment(String),
|
||||||
|
Element(EData),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
children: Vec<Node>,
|
||||||
|
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 {
|
||||||
|
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::<HashMap<_, _>>()}, vec![elem("body".to_string(), Attr{attrs:HashMap::new()}, vec![text("hi".to_string())])]);
|
||||||
|
println!("{}", e)
|
||||||
|
}*/
|
153
src/html.rs
Normal file
153
src/html.rs
Normal file
@@ -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<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());
|
||||||
|
}
|
||||||
|
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<dom::Node> {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
loop {
|
||||||
|
self.consume_ws();
|
||||||
|
if self.eof() || self.starts_with("</") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nodes.push(self.parse_node());
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(source: String) -> 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user