From 38d33ed144d8ac665b898faaab6b89a8f3d0a6eb Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Thu, 10 Mar 2016 23:58:19 -0800 Subject: [PATCH] More work on scene parsing. Using Nom to help with parsing the contents of leaf nodes. --- Cargo.lock | 40 ++++-- Cargo.toml | 3 +- src/main.rs | 2 + src/parse/basics.rs | 289 +++++++++++++++++++++++++++++++++++++++++ src/parse/data_tree.rs | 55 +++++++- src/parse/mod.rs | 1 + src/parse/psy.rs | 133 ++++++++++++++++++- src/renderer.rs | 1 + 8 files changed, 503 insertions(+), 21 deletions(-) create mode 100644 src/parse/basics.rs diff --git a/Cargo.lock b/Cargo.lock index 8fb7f80..c8d24c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,15 +3,16 @@ name = "psychopath" version = "0.1.0" dependencies = [ "docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aho-corasick" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -19,42 +20,48 @@ name = "docopt" version = "0.6.78" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "regex 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.55 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" -version = "0.2.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "0.1.7" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nom" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "regex" -version = "0.1.44" +version = "0.1.55" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-serialize" -version = "0.3.16" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -62,3 +69,8 @@ name = "strsim" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/Cargo.toml b/Cargo.toml index b1f99c6..75bfd32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ authors = ["Nathan Vegdahl "] [dependencies] docopt = "0.6" -rustc-serialize = "0.3" \ No newline at end of file +rustc-serialize = "0.3" +nom = "1.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 31da264..abf7cbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ extern crate rustc_serialize; extern crate docopt; +#[macro_use] +extern crate nom; mod math; mod algorithm; diff --git a/src/parse/basics.rs b/src/parse/basics.rs new file mode 100644 index 0000000..127aedb --- /dev/null +++ b/src/parse/basics.rs @@ -0,0 +1,289 @@ +//! Some basic nom parsers +#![allow(dead_code)] + +use std::str; + +use nom::{IResult, Needed, digit, multispace}; +use nom::IResult::*; + + +// Parsers for numbers surrounded by whitespace +named!(pub ws_u32, delimited!(opt!(multispace), u32_utf8, opt!(multispace))); +named!(pub ws_u64, delimited!(opt!(multispace), u64_utf8, opt!(multispace))); +named!(pub ws_i32, delimited!(opt!(multispace), i32_utf8, opt!(multispace))); +named!(pub ws_i64, delimited!(opt!(multispace), i64_utf8, opt!(multispace))); +named!(pub ws_f32, delimited!(opt!(multispace), f32_utf8, opt!(multispace))); +named!(pub ws_f64, delimited!(opt!(multispace), f64_utf8, opt!(multispace))); + + + + +// ======================================================== + +named!(pub u32_utf8, chain!( + bytes: digit, + || { str::from_utf8(bytes).unwrap().parse::().unwrap() } +)); + +named!(pub i32_utf8, chain!( + sign: one_of!("-+")? ~ + bytes: digit, + || { + match sign { + Some(s) if s == '-' => -str::from_utf8(bytes).unwrap().parse::().unwrap(), + _ => str::from_utf8(bytes).unwrap().parse::().unwrap(), + } + } +)); + +named!(pub u64_utf8, chain!( + bytes: digit, + || { str::from_utf8(bytes).unwrap().parse::().unwrap() } +)); + +named!(pub i64_utf8, chain!( + sign: one_of!("-+")? ~ + bytes: digit, + || { + match sign { + Some(s) if s == '-' => -str::from_utf8(bytes).unwrap().parse::().unwrap(), + _ => str::from_utf8(bytes).unwrap().parse::().unwrap(), + } + } +)); + +named!(pub f32_utf8, chain!( + bytes: take_decimal_real, + || { + str::from_utf8(bytes).unwrap().parse::().unwrap() + } +)); + +named!(pub f64_utf8, chain!( + bytes: take_decimal_real, + || { + str::from_utf8(bytes).unwrap().parse::().unwrap() + } +)); + +fn take_decimal_integer(i: &[u8]) -> IResult<&[u8], &[u8]> { + named!(rr<&[u8], ()>, chain!( + one_of!("-+")? ~ + digit, + ||{()} + )); + + match rr(i) { + Done(remaining, _) => { + let m = i.len() - remaining.len(); + if m == 0 { + Incomplete(Needed::Unknown) + } else { + Done(&i[m..], &i[0..m]) + } + } + + Error(e) => Error(e), + + Incomplete(e) => Incomplete(e), + } +} + +fn take_decimal_real(i: &[u8]) -> IResult<&[u8], &[u8]> { + named!(rr<&[u8], ()>, chain!( + one_of!("-+")? ~ + digit ~ + complete!(chain!(tag!(".") ~ digit, ||{()}))?, + ||{()} + )); + + match rr(i) { + Done(remaining, _) => { + let m = i.len() - remaining.len(); + if m == 0 { + Incomplete(Needed::Unknown) + } else { + Done(&i[m..], &i[0..m]) + } + } + + Error(e) => Error(e), + + Incomplete(e) => Incomplete(e), + } +} + + + + +// ======================================================== + +#[cfg(test)] +mod test { + use nom::IResult; + use nom::IResult::*; + use super::take_decimal_real; + use super::*; + + #[test] + fn ws_u32_1() { + assert_eq!(ws_u32(b"42"), Done(&b""[..], 42)); + assert_eq!(ws_u32(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_u32(b"42 "), Done(&b""[..], 42)); + assert_eq!(ws_u32(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_u32(b" 42 53"), Done(&b"53"[..], 42)); + } + + #[test] + fn ws_i32_1() { + assert_eq!(ws_i32(b"42"), Done(&b""[..], 42)); + assert_eq!(ws_i32(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_i32(b"42 "), Done(&b""[..], 42)); + assert_eq!(ws_i32(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_i32(b" 42 53"), Done(&b"53"[..], 42)); + } + + #[test] + fn ws_i32_2() { + assert_eq!(ws_i32(b"-42"), Done(&b""[..], -42)); + assert_eq!(ws_i32(b" -42"), Done(&b""[..], -42)); + assert_eq!(ws_i32(b"-42 "), Done(&b""[..], -42)); + assert_eq!(ws_i32(b" -42 "), Done(&b""[..], -42)); + assert_eq!(ws_i32(b" -42 53"), Done(&b"53"[..], -42)); + } + + #[test] + fn ws_u64_1() { + assert_eq!(ws_u64(b"42"), Done(&b""[..], 42)); + assert_eq!(ws_u64(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_u64(b"42 "), Done(&b""[..], 42)); + assert_eq!(ws_u64(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_u64(b" 42 53"), Done(&b"53"[..], 42)); + } + + #[test] + fn ws_i64_1() { + assert_eq!(ws_i64(b"42"), Done(&b""[..], 42)); + assert_eq!(ws_i64(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_i64(b"42 "), Done(&b""[..], 42)); + assert_eq!(ws_i64(b" 42"), Done(&b""[..], 42)); + assert_eq!(ws_i64(b" 42 53"), Done(&b"53"[..], 42)); + } + + #[test] + fn ws_i64_2() { + assert_eq!(ws_i64(b"-42"), Done(&b""[..], -42)); + assert_eq!(ws_i64(b" -42"), Done(&b""[..], -42)); + assert_eq!(ws_i64(b"-42 "), Done(&b""[..], -42)); + assert_eq!(ws_i64(b" -42 "), Done(&b""[..], -42)); + assert_eq!(ws_i64(b" -42 53"), Done(&b"53"[..], -42)); + } + + #[test] + fn ws_f32_1() { + assert_eq!(ws_f32(b"42"), Done(&b""[..], 42.0)); + assert_eq!(ws_f32(b" 42"), Done(&b""[..], 42.0)); + assert_eq!(ws_f32(b"42 "), Done(&b""[..], 42.0)); + assert_eq!(ws_f32(b" 42"), Done(&b""[..], 42.0)); + assert_eq!(ws_f32(b" 42 53"), Done(&b"53"[..], 42.0)); + } + + #[test] + fn ws_f32_2() { + assert_eq!(ws_f32(b"42.5"), Done(&b""[..], 42.5)); + assert_eq!(ws_f32(b" 42.5"), Done(&b""[..], 42.5)); + assert_eq!(ws_f32(b"42.5 "), Done(&b""[..], 42.5)); + assert_eq!(ws_f32(b" 42.5"), Done(&b""[..], 42.5)); + assert_eq!(ws_f32(b" 42.5 53"), Done(&b"53"[..], 42.5)); + } + + #[test] + fn ws_f32_3() { + assert_eq!(ws_f32(b"-42.5"), Done(&b""[..], -42.5)); + assert_eq!(ws_f32(b" -42.5"), Done(&b""[..], -42.5)); + assert_eq!(ws_f32(b"-42.5 "), Done(&b""[..], -42.5)); + assert_eq!(ws_f32(b" -42.5"), Done(&b""[..], -42.5)); + assert_eq!(ws_f32(b" -42.5 53"), Done(&b"53"[..], -42.5)); + } + + #[test] + fn ws_f64_1() { + assert_eq!(ws_f64(b"42"), Done(&b""[..], 42.0)); + assert_eq!(ws_f64(b" 42"), Done(&b""[..], 42.0)); + assert_eq!(ws_f64(b"42 "), Done(&b""[..], 42.0)); + assert_eq!(ws_f64(b" 42"), Done(&b""[..], 42.0)); + assert_eq!(ws_f64(b" 42 53"), Done(&b"53"[..], 42.0)); + } + + #[test] + fn ws_f64_2() { + assert_eq!(ws_f64(b"42.5"), Done(&b""[..], 42.5)); + assert_eq!(ws_f64(b" 42.5"), Done(&b""[..], 42.5)); + assert_eq!(ws_f64(b"42.5 "), Done(&b""[..], 42.5)); + assert_eq!(ws_f64(b" 42.5"), Done(&b""[..], 42.5)); + assert_eq!(ws_f64(b" 42.5 53"), Done(&b"53"[..], 42.5)); + } + + #[test] + fn ws_f64_3() { + assert_eq!(ws_f64(b"-42.5"), Done(&b""[..], -42.5)); + assert_eq!(ws_f64(b" -42.5"), Done(&b""[..], -42.5)); + assert_eq!(ws_f64(b"-42.5 "), Done(&b""[..], -42.5)); + assert_eq!(ws_f64(b" -42.5"), Done(&b""[..], -42.5)); + assert_eq!(ws_f64(b" -42.5 53"), Done(&b"53"[..], -42.5)); + } + + #[test] + fn take_decimal_real_1() { + assert_eq!(take_decimal_real(b"-42.3"), Done(&b""[..], &b"-42.3"[..])); + assert_eq!(take_decimal_real(b"42.3"), Done(&b""[..], &b"42.3"[..])); + assert_eq!(take_decimal_real(b"-42"), Done(&b""[..], &b"-42"[..])); + assert_eq!(take_decimal_real(b"+42.3"), Done(&b""[..], &b"+42.3"[..])); + } + + #[test] + fn u32_utf8_1() { + assert_eq!(u32_utf8(b"42"), Done(&b""[..], 42)); + assert_eq!(u32_utf8(b"-42").is_err(), true); + } + + #[test] + fn i32_utf8_1() { + assert_eq!(i32_utf8(b"42"), Done(&b""[..], 42)); + assert_eq!(i32_utf8(b"-42"), Done(&b""[..], -42)); + assert_eq!(i32_utf8(b"+42"), Done(&b""[..], 42)); + assert_eq!(i32_utf8(b"--42").is_err(), true); + assert_eq!(i32_utf8(b"+-42").is_err(), true); + } + + #[test] + fn u64_utf8_1() { + assert_eq!(u64_utf8(b"42"), Done(&b""[..], 42)); + assert_eq!(u64_utf8(b"-42").is_err(), true); + } + + #[test] + fn i64_utf8_1() { + assert_eq!(i64_utf8(b"42"), Done(&b""[..], 42)); + assert_eq!(i64_utf8(b"-42"), Done(&b""[..], -42)); + assert_eq!(i64_utf8(b"+42"), Done(&b""[..], 42)); + assert_eq!(i64_utf8(b"--42").is_err(), true); + assert_eq!(i64_utf8(b"+-42").is_err(), true); + } + + #[test] + fn f32_utf8_1() { + assert_eq!(f32_utf8(b"-42.3"), Done(&b""[..], -42.3)); + assert_eq!(f32_utf8(b"+42.3"), Done(&b""[..], 42.3)); + assert_eq!(f32_utf8(b"42.3"), Done(&b""[..], 42.3)); + assert_eq!(f32_utf8(b"42"), Done(&b""[..], 42.0)); + } + + #[test] + fn f64_utf8_1() { + assert_eq!(f64_utf8(b"-42.3"), Done(&b""[..], -42.3)); + assert_eq!(f64_utf8(b"+42.3"), Done(&b""[..], 42.3)); + assert_eq!(f64_utf8(b"42.3"), Done(&b""[..], 42.3)); + assert_eq!(f64_utf8(b"42"), Done(&b""[..], 42.0)); + } +} diff --git a/src/parse/data_tree.rs b/src/parse/data_tree.rs index 2ed51e9..ac50c18 100644 --- a/src/parse/data_tree.rs +++ b/src/parse/data_tree.rs @@ -42,15 +42,62 @@ impl<'a> DataTree<'a> { } } + pub fn get_first_child_with_type_name(&'a self, type_name: &str) -> Option<&'a DataTree> { + if let &DataTree::Internal { ref children, .. } = self { + for child in children.iter() { + match child { + &DataTree::Internal { type_name: tn, .. } => { + if tn == type_name { + return Some(child); + } + } + + &DataTree::Leaf { type_name: tn, .. } => { + if tn == type_name { + return Some(child); + } + } + } + } + return None; + } else { + return None; + } + } + + pub fn count_children_with_type_name(&self, type_name: &str) -> usize { + if let &DataTree::Internal { ref children, .. } = self { + let mut count = 0; + for child in children.iter() { + match child { + &DataTree::Internal { type_name: tn, .. } => { + if tn == type_name { + count += 1; + } + } + + &DataTree::Leaf { type_name: tn, .. } => { + if tn == type_name { + count += 1; + } + } + } + } + return count; + } else { + return 0; + } + } + // For unit tests - fn internal_data(&'a self) -> (&'a str, Option<&'a str>, &'a Vec>) { + fn internal_data_or_panic(&'a self) -> (&'a str, Option<&'a str>, &'a Vec>) { if let DataTree::Internal { type_name, ident, ref children } = *self { (type_name, ident, children) } else { panic!("Expected DataTree::Internal, found DataTree::Leaf") } } - fn leaf_data(&'a self) -> (&'a str, &'a str) { + fn leaf_data_or_panic(&'a self) -> (&'a str, &'a str) { if let DataTree::Leaf { type_name, contents } = *self { (type_name, contents) } else { @@ -451,13 +498,13 @@ mod tests { let dt = DataTree::from_str(input).unwrap(); // Root - let (t, i, c) = dt.internal_data(); + let (t, i, c) = dt.internal_data_or_panic(); assert_eq!(t, "ROOT"); assert_eq!(i, None); assert_eq!(c.len(), 1); // First (and only) child - let (t, i, c) = c[0].internal_data(); + let (t, i, c) = c[0].internal_data_or_panic(); assert_eq!(t, "Thing"); assert_eq!(i, None); assert_eq!(c.len(), 0); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index b704633..b4eae1d 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,4 +1,5 @@ mod data_tree; mod psy; +pub mod basics; pub use self::data_tree::DataTree; diff --git a/src/parse/psy.rs b/src/parse/psy.rs index 4f7855a..90b71ab 100644 --- a/src/parse/psy.rs +++ b/src/parse/psy.rs @@ -1,8 +1,137 @@ #![allow(dead_code)] +use std::result::Result; + +use nom; +use nom::IResult; + use renderer::Renderer; use super::DataTree; +use super::basics::ws_u32; -pub fn parse_frame(tree: &DataTree) -> Renderer { - unimplemented!() + +/// Takes in a DataTree representing a Scene node and returns +/// a renderer. +pub fn parse_frame(tree: &DataTree) -> Result { + // Verify we have the right number of each section + if tree.count_children_with_type_name("Output") != 1 { + return Err(()); + } + if tree.count_children_with_type_name("RenderSettings") != 1 { + return Err(()); + } + if tree.count_children_with_type_name("Camera") != 1 { + return Err(()); + } + if tree.count_children_with_type_name("World") != 1 { + return Err(()); + } + if tree.count_children_with_type_name("Assembly") != 1 { + return Err(()); + } + + // Parse output info + let output_info = if let &DataTree::Internal{ref children, ..} = + tree.get_first_child_with_type_name("Output").unwrap() { + let mut found_path = false; + let mut path = String::new(); + + for child in children { + match child { + &DataTree::Leaf { type_name, contents } if type_name == "Path" => { + // TODO: proper string escaping and quotes stripping + found_path = true; + path = contents.to_string(); + } + + &DataTree::Leaf { type_name, contents } if type_name == "FileFormat" => { + // TODO + unimplemented!() + } + + &DataTree::Leaf { type_name, contents } if type_name == "ColorSpace" => { + // TODO + unimplemented!() + } + + &DataTree::Leaf { type_name, contents } if type_name == "Dither" => { + // TODO + unimplemented!() + } + + _ => {} + } + } + + if found_path { + (path) + } else { + return Err(()); + } + } else { + return Err(()); + }; + + // Parse render settings + let render_settings = if let &DataTree::Internal{ref children, ..} = + tree.get_first_child_with_type_name("RenderSettings").unwrap() { + let mut found_res = false; + let mut found_spp = false; + let mut res = (0, 0); + let mut spp = 0; + let mut seed = 0; + + for child in children { + match child { + // Resolution + &DataTree::Leaf { type_name, contents } if type_name == "Resolution" => { + if let IResult::Done(_, (w, h)) = closure!(terminated!(tuple!(ws_u32, ws_u32), + nom::eof))(contents.as_bytes()) { + found_res = true; + res = (w, h); + } else { + // Found Resolution, but its contents is not in the right format + return Err(()); + } + } + + &DataTree::Leaf { type_name, contents } if type_name == "SamplesPerPixel" => { + if let IResult::Done(_, n) = ws_u32(contents.as_bytes()) { + found_spp = true; + spp = n; + } else { + // Found SamplesPerPixel, but its contents is not in the right format + return Err(()); + } + } + + &DataTree::Leaf { type_name, contents } if type_name == "Seed" => { + if let IResult::Done(_, n) = ws_u32(contents.as_bytes()) { + seed = n; + } else { + // Found Seed, but its contents is not in the right format + return Err(()); + } + } + + _ => {} + } + } + + if found_res && found_spp { + (res, spp) + } else { + return Err(()); + } + } else { + return Err(()); + }; + + // Parse camera + + // Parse world + + // Parse root scene assembly + + return Err(()); } diff --git a/src/renderer.rs b/src/renderer.rs index fffb6ed..4e78cc5 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -8,6 +8,7 @@ use math::fast_logit; use image::Image; use surface; use surface::Surface; +use scene::Scene; pub struct Renderer { pub output_file: String,