Beginning work on DataTree and associated parsing.
This commit is contained in:
parent
a569586455
commit
d769ccecee
292
src/datatree.rs
Normal file
292
src/datatree.rs
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
pub enum Node {
|
||||||
|
Internal {
|
||||||
|
type_name: String,
|
||||||
|
ident: Option<String>,
|
||||||
|
children: Vec<Node>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Leaf {
|
||||||
|
type_name: String,
|
||||||
|
contents: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
fn from_string(text: &str) -> Node {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
|
||||||
|
let mut ti = token_iter(text);
|
||||||
|
while let Some(node) = parse_node(&mut ti) {
|
||||||
|
nodes.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::Internal {
|
||||||
|
type_name: "ROOT".to_string(),
|
||||||
|
ident: None,
|
||||||
|
children: nodes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_node(ti: &mut TokenIter) -> Option<Node> {
|
||||||
|
let type_name = if let Some(Token::TypeName(token)) = ti.next() {
|
||||||
|
token
|
||||||
|
} else {
|
||||||
|
panic!("Parse error")
|
||||||
|
};
|
||||||
|
|
||||||
|
let ident = match ti.next() {
|
||||||
|
Some(Token::Ident(token)) => Some(token),
|
||||||
|
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn token_iter<'a>(text: &'a str) -> TokenIter<'a> {
|
||||||
|
TokenIter {
|
||||||
|
text: text,
|
||||||
|
after_open_leaf: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// /////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum Token<'a> {
|
||||||
|
TypeName(&'a str),
|
||||||
|
Ident(&'a str),
|
||||||
|
OpenInner,
|
||||||
|
CloseInner,
|
||||||
|
OpenLeaf,
|
||||||
|
CloseLeaf,
|
||||||
|
LeafContents(&'a str),
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TokenIter<'a> {
|
||||||
|
text: &'a str,
|
||||||
|
after_open_leaf: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for TokenIter<'a> {
|
||||||
|
type Item = Token<'a>;
|
||||||
|
fn next(&mut self) -> Option<Token<'a>> {
|
||||||
|
let mut token = None;
|
||||||
|
let mut iter = self.text.char_indices().peekable();
|
||||||
|
|
||||||
|
if !self.after_open_leaf {
|
||||||
|
// Skip newlines, whitespace, and comments
|
||||||
|
loop {
|
||||||
|
let mut skipped = false;
|
||||||
|
|
||||||
|
while let Some(&(_, c)) = iter.peek() {
|
||||||
|
if is_ws_char(c) || is_nl_char(c) {
|
||||||
|
iter.next();
|
||||||
|
skipped = true;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(&(_, c)) = iter.peek() {
|
||||||
|
if is_comment_char(c) {
|
||||||
|
iter.next();
|
||||||
|
skipped = true;
|
||||||
|
while let Some(&(_, c)) = iter.peek() {
|
||||||
|
if !is_nl_char(c) {
|
||||||
|
iter.next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !skipped {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the meat of the token
|
||||||
|
if let Some(&(i, c)) = iter.peek() {
|
||||||
|
// TypeName
|
||||||
|
if is_ident_char(c) {
|
||||||
|
iter.next();
|
||||||
|
let i1 = i;
|
||||||
|
let i2 = {
|
||||||
|
let mut i2 = 0;
|
||||||
|
while let Some(&(i, c)) = iter.peek() {
|
||||||
|
if is_ident_char(c) {
|
||||||
|
iter.next();
|
||||||
|
} else {
|
||||||
|
i2 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i2
|
||||||
|
};
|
||||||
|
token = Some(Token::TypeName(&self.text[i1..i2]));
|
||||||
|
}
|
||||||
|
// Ident
|
||||||
|
// TODO: handle escaping
|
||||||
|
else if c == '$' {
|
||||||
|
iter.next();
|
||||||
|
let i1 = i;
|
||||||
|
let i2 = {
|
||||||
|
let mut i2 = 0;
|
||||||
|
while let Some(&(i, c)) = iter.peek() {
|
||||||
|
if is_ident_char(c) {
|
||||||
|
iter.next();
|
||||||
|
} else {
|
||||||
|
i2 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i2
|
||||||
|
};
|
||||||
|
token = Some(Token::Ident(&self.text[i1..i2]));
|
||||||
|
}
|
||||||
|
// Structural characters
|
||||||
|
else if is_reserved_char(c) {
|
||||||
|
iter.next();
|
||||||
|
match c {
|
||||||
|
'{' => {
|
||||||
|
token = Some(Token::OpenInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
'}' => {
|
||||||
|
token = Some(Token::CloseInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
'[' => {
|
||||||
|
self.after_open_leaf = true;
|
||||||
|
token = Some(Token::OpenLeaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
']' => {
|
||||||
|
token = Some(Token::CloseLeaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
token = Some(Token::Unknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Leaf contents
|
||||||
|
// TODO: handle escaping
|
||||||
|
else if let Some(&(i, _)) = iter.peek() {
|
||||||
|
self.after_open_leaf = false;
|
||||||
|
let i1 = i;
|
||||||
|
let i2 = {
|
||||||
|
let mut i2 = 0;
|
||||||
|
while let Some(&(i, c)) = iter.peek() {
|
||||||
|
if c != ']' {
|
||||||
|
iter.next();
|
||||||
|
} else {
|
||||||
|
i2 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i2
|
||||||
|
};
|
||||||
|
token = Some(Token::LeafContents(&self.text[i1..i2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish up
|
||||||
|
match iter.peek() {
|
||||||
|
Some(&(i, _)) => {
|
||||||
|
self.text = &self.text[i..];
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
self.text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// /////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// Returns whether the given unicode character is whitespace or not.
|
||||||
|
fn is_ws_char(c: char) -> bool {
|
||||||
|
match c {
|
||||||
|
' ' | '\t' => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns whether the given utf character is a newline or not.
|
||||||
|
fn is_nl_char(c: char) -> bool {
|
||||||
|
match c {
|
||||||
|
'\n' | '\r' => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns whether the given utf character is a comment starter or not.
|
||||||
|
fn is_comment_char(c: char) -> bool {
|
||||||
|
c == '#'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns whether the given utf character is a reserved character or not.
|
||||||
|
fn is_reserved_char(c: char) -> bool {
|
||||||
|
match c {
|
||||||
|
'{' | '}' | '[' | ']' | '\\' | '$' => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns whether the given utf character is a legal identifier character or not.
|
||||||
|
fn is_ident_char(c: char) -> bool {
|
||||||
|
// Anything that isn't whitespace, reserved, or an operator character
|
||||||
|
if !is_ws_char(c) && !is_nl_char(c) && !is_reserved_char(c) && !is_comment_char(c) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{token_iter, Token};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn token_iter_1() {
|
||||||
|
let s = r#"
|
||||||
|
# This is a comment and should be skipped
|
||||||
|
MyThing $ident { # This is another comment
|
||||||
|
MyProp [Some content]
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut ti = token_iter(s);
|
||||||
|
assert_eq!(ti.next(), Some(Token::TypeName("MyThing")));
|
||||||
|
assert_eq!(ti.next(), Some(Token::Ident("$ident")));
|
||||||
|
assert_eq!(ti.next(), Some(Token::OpenInner));
|
||||||
|
assert_eq!(ti.next(), Some(Token::TypeName("MyProp")));
|
||||||
|
assert_eq!(ti.next(), Some(Token::OpenLeaf));
|
||||||
|
assert_eq!(ti.next(), Some(Token::LeafContents("Some content")));
|
||||||
|
assert_eq!(ti.next(), Some(Token::CloseLeaf));
|
||||||
|
assert_eq!(ti.next(), Some(Token::CloseInner));
|
||||||
|
assert_eq!(ti.next(), None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ mod ray;
|
||||||
mod bbox;
|
mod bbox;
|
||||||
mod camera;
|
mod camera;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
mod datatree;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod image;
|
mod image;
|
||||||
mod triangle;
|
mod triangle;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user