diff --git a/Cargo.toml b/Cargo.toml index 05b725b..18c1c50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" num = "0.4" clap = { version = "3.1.8", features = ["derive"] } chrono = "0.4" -function_name = "0.3.0" \ No newline at end of file +function_name = "0.3.0" +itertools = "0.10.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b98e7eb..e077fa8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn main() -> std::io::Result<()> { let args = Cli::parse(); let file = File::open(&args.path)?; - dbg!(["hello", "goodbye", "myworld"].contains(&"myworlde")); + // dbg!(["hello", "goodbye", "myworld"].contains(&"myworlde")); // let mut word_gen = WordReader::new(file); // let mut word_count = 0; @@ -26,8 +26,8 @@ fn main() -> std::io::Result<()> { // let word1 = "hello world"; // let word2 = "hello planet"; // dbg!(&word1[0..6].len()); - dbg!(take_until("tea time now: and later", b':')); - // parse_vcd(file); + // dbg!(take_until("tea time now: and later", b':')); + parse_vcd(file); // tag("my oh my"); diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index 9771b07..74ffb4c 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -1,4 +1,6 @@ use super::*; +use chrono::prelude::*; +use itertools::Itertools; use std::fs::File; use ::function_name::named; @@ -10,7 +12,7 @@ pub fn take_until<'a>(word : &'a str, pattern : u8) -> Option<(&'a str, Residual for chr in word.as_bytes() { if (*chr == pattern) { - return Some((&word[0..new_start], Residual(&word[new_start..]))); + return Some((&word[0..new_start], Residual(&word[new_start+1..]))); } else { new_start += 1; @@ -37,21 +39,17 @@ fn tag<'a>(word : &'a str, pattern : &'a str) -> Option<&'a str> { } #[named] -fn parse_date(word_reader : &mut WordReader) -> Result<(), String> { - let mut parsed_day = false; - let mut parsed_month = false; - let mut parsed_date = false; - let mut parsed_hh = false; - let mut parsed_mm = false; - let mut parsed_ss = false; - let mut parsed_year = false; - let mut parsed_end = false; +fn parse_date( + word_and_ctx1 : (&str, Cursor), + word_and_ctx2 : (&str, Cursor), + word_and_ctx3 : (&str, Cursor), + word_and_ctx4 : (&str, Cursor), + word_and_ctx5 : (&str, Cursor), +) -> Result, String> { let day = { // check for another word in the file - let (word, cursor) = word_reader.next_word().expect( - format!("reached end of file without parser leaving {}", function_name!()).as_str() - ); + let (word, cursor) = word_and_ctx1; let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; if !days.contains(&word) { @@ -66,9 +64,7 @@ fn parse_date(word_reader : &mut WordReader) -> Result<(), String> { let month = { // check for another word in the file - let (word, cursor) = word_reader.next_word().expect( - format!("reached end of file without parser leaving {}", function_name!()).as_str() - ); + let (word, cursor) = word_and_ctx2; let months = [ "Jan", "Feb", "Mar", "Apr", @@ -88,9 +84,7 @@ fn parse_date(word_reader : &mut WordReader) -> Result<(), String> { let date = { // check for another word in the file - let (word, cursor) = word_reader.next_word().expect( - format!("reached end of file without parser leaving {}", function_name!()).as_str() - ); + let (word, cursor) = word_and_ctx3; let date : u8 = word.to_string().parse().unwrap(); @@ -106,48 +100,63 @@ fn parse_date(word_reader : &mut WordReader) -> Result<(), String> { }; let (hh, mm, ss) = { - // check for another word in the file - let (word, cursor) = word_reader.next_word().expect( - format!("reached end of file without parser leaving {}", function_name!()).as_str() - ); + // get hour + let (word, cursor) = word_and_ctx4; - let date : u8 = word.to_string().parse().unwrap(); - // let hh = take_until(word, b':').unwrap(); + let (hh, Residual(remainder)) = take_until(word, b':').unwrap(); + let hh : u8 = hh.to_string().parse().unwrap(); - if date > 31 { + if hh > 23 { let msg = format!("reached end of file without parser leaving {}\n", function_name!()); - let msg2 = format!("{word} is not a valid date : must be between 0 and 31\n"); + let msg2 = format!("{hh} is not a valid hour : must be between 0 and 23\n"); let msg3 = format!("failure location: {cursor:?}"); return Err(format!("{}{}{}", msg, msg2, msg3)) - } - ("", "", "") + + // get minute + let (mm, Residual(remainder)) = take_until(remainder, b':').unwrap(); + let mm : u8 = mm.to_string().parse().unwrap(); + + if mm > 60 { + let msg = format!("reached end of file without parser leaving {}\n", function_name!()); + let msg2 = format!("{mm} is not a valid minute : must be between 0 and 60\n"); + let msg3 = format!("failure location: {cursor:?}"); + return Err(format!("{}{}{}", msg, msg2, msg3)) + } + + // get second + let ss : u8 = remainder.to_string().parse().unwrap(); + + if ss > 60 { + let msg = format!("reached end of file without parser leaving {}\n", function_name!()); + let msg2 = format!("{ss} is not a valid second : must be between 0 and 60\n"); + let msg3 = format!("failure location: {cursor:?}"); + return Err(format!("{}{}{}", msg, msg2, msg3)) + } + (hh.to_string(), mm.to_string(), ss.to_string()) }; - // else if !parsed_date { + let year = { + // check for another word in the file + let (word, cursor) = word_and_ctx5; + word.to_string() + }; - // } - // else if !parsed_hh { + let date = Utc.datetime_from_str( + format!("{day} {month} {date} {mm}:{hh}:{ss} {year}").as_str(), + "%a %b %e %T %Y").unwrap(); - // } - // else if !parsed_mm { - - // } - // else if !parsed_ss { - - // } - // else if !parsed_year { - - // } - // else if !parsed_end { - - // } - - Ok(()) + Ok(date) } #[named] -fn parse_header(word_reader : &mut WordReader) -> Result<(), String> { +fn parse_header(word_reader : &mut WordReader) -> Result { + let mut header = Metadata { + date : None, + version : None, + timescale : (None, Timescale::unit) + }; + loop { // check for another word in the file let word = word_reader.next_word(); @@ -165,10 +174,47 @@ fn parse_header(word_reader : &mut WordReader) -> Result<(), String> { // we hope that this word stars with a `$` Some(ident) => { match ident { - "date" => {println!("got date")} + "date" => { + let err_msg = format!("reached end of file without parser leaving {}", function_name!()); + // a date is typically composed of the 5 following words which can + // occur in any order: + // {Day, Month, Date(number in month), hh:mm:ss, year}. + // Thus, we must lookahead read the 5 next words, and try our date + // parser on 5! = 120 permutations of the 5 words. + // + // While looking ahead, if one of the 5 words in `$end`, we have to + // immediately stop trying to get more words. + + let mut found_end = false; + let mut lookahead_5_words : Vec<(String, Cursor)> = Vec::new(); + + for word in 0..5 { + let (word, cursor) = word_reader.next_word().expect(err_msg.as_str()); + let word = word.to_string(); + match word.as_str() { + "$end" => { + found_end = true; + break; + } + _ => { + lookahead_5_words.push((word, cursor)); + } + }; + } + + // we no longer attempt to parse date if we weren't able to lookahead 5 + // words + if found_end {continue} + + let iter = lookahead_5_words + .iter() + .permutations(lookahead_5_words.len()); + // let parsed_date = parse_date(word_reader).unwrap(); + // header.date = Some(parsed_date); + } "version" => {println!("got version")} "timescale" => {println!("got timescale")} - "scope" => {return Ok(())} + "scope" => {break} _ => {} } } @@ -176,13 +222,13 @@ fn parse_header(word_reader : &mut WordReader) -> Result<(), String> { None => {} } - } - // Ok() + return Ok(header) } pub fn parse_vcd(file : File) { let mut word_gen = WordReader::new(file); - parse_header(&mut word_gen); + let header = parse_header(&mut word_gen).unwrap(); + dbg!(header); } \ No newline at end of file diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 336601a..3e5dd21 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -3,16 +3,16 @@ use chrono::prelude::*; use num::BigInt; #[derive(Debug)] -struct Version(String); +pub(super) struct Version(String); #[derive(Debug)] -enum Timescale {ps, ns, us, ms, s, unit} +pub(super) enum Timescale {ps, ns, us, ms, s, unit} #[derive(Debug)] pub(super) struct Metadata { - date : Option>, - version : Option, - timescale : (Option, Timescale)} + pub(super) date : Option>, + pub(super) version : Option, + pub(super) timescale : (Option, Timescale)} #[derive(Debug)] struct Scope_Idx(usize);