use std::io::prelude::*; use std::io; use std::fs::File; use std::collections::BTreeMap; use chrono::prelude::*; use ::function_name::named; use num::*; use clap::Parser; #[derive(Parser)] struct Cli { /// The path to the file to read #[clap(parse(from_os_str))] path: std::path::PathBuf} // TODO: implement any timescales greater than a second #[derive(Debug)] enum Timescale {ps, ns, us, ms, s, unit} #[derive(Debug)] struct Scope_Idx(usize); #[derive(Debug)] struct Signal_Idx(usize); #[derive(Debug)] struct Version(String); #[derive(Debug)] struct Metadata { date : Option>, version : Option, timescale : (Option, Timescale)} #[derive(Debug)] enum SignalGeneric{ Signal{ name : String, timeline : BTreeMap, scope_parent : Scope_Idx}, SignalAlias{ name : String, signal_alias : Signal_Idx} } #[derive(Debug)] struct Scope { name : String, child_signals : Vec, child_scopes : Vec} #[derive(Debug)] struct VCD { metadata : Metadata, all_signals : Vec, // the root scope should always be placed at index 0 all_scopes : Vec} #[derive(Debug)] enum Date_Parser_State {Begin, Parsing} #[derive(Debug)] enum Version_Parser_State {Begin, Parsing} #[derive(Debug)] enum Timescale_Parser_State {Begin, Parsing} #[derive(Debug)] enum Signal_Tree_Parser_State {Begin, Parsing} #[derive(Debug)] enum Parser_State { Date(Date_Parser_State), Version(Version_Parser_State), Timescale(Timescale_Parser_State), Signal_Tree(Signal_Tree_Parser_State), Parse_Signal_Values} struct VCD_Parser<'a> { vcd_parser_state : Parser_State, buffer : Option, vcd : &'a mut VCD, curr_scope : Option<&'a Scope>, curr_parent_scope : Option<&'a Scope>} impl VCD { pub fn new() -> Self { let metadata = Metadata { date : None, version : None, timescale : (None, Timescale::unit)}; VCD { metadata : metadata, all_signals : Vec::::new(), all_scopes : Vec::::new()} } } impl<'a> VCD_Parser<'a> { pub fn new(vcd : &'a mut VCD) -> Self { VCD_Parser { vcd_parser_state : Parser_State::Date(Date_Parser_State::Begin), buffer : None, vcd : vcd, curr_scope : None, curr_parent_scope : None } } pub fn parse_word(&mut self, word : &str) -> Result<(), String> { let mut state = &mut self.vcd_parser_state; let t = &self.vcd; match state { Parser_State::Date(_) => self.parse_date(word), Parser_State::Version(_) => self.parse_version(word), Parser_State::Timescale(_) => self.parse_timescale(word), // TODO : Enable the following in production // _ => Err(format!("parser in bad state : {state:?}")) // TODO : Disable the following in production _ => { Err(format!("parser in bad state : {state:?}; {t:?}")) } } } #[named] pub fn parse_date(&mut self, word : &str) -> Result<(), String> { let mut state = &mut self.vcd_parser_state; match state { Parser_State::Date(Date_Parser_State::Begin) => match word { "$date" => { *state = Parser_State::Date(Date_Parser_State::Parsing); Ok(()) } _ => { *state = Parser_State::Version(Version_Parser_State::Begin); self.parse_version(word) } } Parser_State::Date(Date_Parser_State::Parsing) => match word { "$end" => { let s = self.buffer.take().unwrap(); let dt = Utc.datetime_from_str(s.as_str(), "%a %b %e %T %Y") .expect(&format!("invalid date {s}").as_str()); *state = Parser_State::Version(Version_Parser_State::Begin); self.vcd.metadata.date = Some(dt); Ok(()) } _ => { if let Some(ref mut buffer) = self.buffer { buffer.push_str(" "); buffer.push_str(word); } else { self.buffer = Some(word.to_string()); } Ok(()) } } _ => Err(format!("{state:?} should be unreachable within {}.",function_name!())), } } #[named] pub fn parse_version(&mut self, word : &str) -> Result<(), String> { let mut state = &mut self.vcd_parser_state; match state { Parser_State::Version(Version_Parser_State::Begin) => match word { "$version" => { *state = Parser_State::Version(Version_Parser_State::Parsing); Ok(()) } _ => { *state = Parser_State::Timescale(Timescale_Parser_State::Begin); Ok(()) } } Parser_State::Version(Version_Parser_State::Parsing) => match word { "$end" => { let s = self.buffer.take().unwrap(); self.vcd.metadata.version = Some(Version(s)); *state = Parser_State::Timescale(Timescale_Parser_State::Begin); Ok(()) } _ => { if let Some(ref mut buffer) = self.buffer { buffer.push_str(" "); buffer.push_str(word); } else { self.buffer = Some(word.to_string()); } Ok(()) } } _ => Err(format!("{state:?} should be unreachable within {}.",function_name!())), } } #[named] pub fn parse_timescale(&mut self, word : &str) -> Result<(), String> { let mut state = &mut self.vcd_parser_state; match state { Parser_State::Timescale(Timescale_Parser_State::Begin) => match word { "$timescale" => { *state = Parser_State::Timescale(Timescale_Parser_State::Parsing); Ok(()) } _ => { *state = Parser_State::Signal_Tree(Signal_Tree_Parser_State::Begin); Ok(()) } } Parser_State::Timescale(Timescale_Parser_State::Parsing) => match word { "$end" => { let s = self.buffer.take().unwrap(); let s = s.split_ascii_whitespace(); let s = s.collect::>(); let scalar = s[0].to_string().parse::().unwrap(); let unit = s[1]; let unit = match unit { "ps" => Ok(Timescale::ps), "ns" => Ok(Timescale::ns), "us" => Ok(Timescale::us), "ms" => Ok(Timescale::ms), "s" => Ok(Timescale::s), // TODO : see if there is a way to easily print out all enum variants // _ => Err(format!("{word} is not a valid unit of time in {Timescale}")) _ => Err(format!("{unit} is not a valid unit")) }.unwrap(); dbg!(s); self.vcd.metadata.timescale = (Some(scalar), unit); *state = Parser_State::Timescale(Timescale_Parser_State::Begin); Ok(()) } _ => { if let Some(ref mut buffer) = self.buffer { buffer.push_str(" "); buffer.push_str(word); } else { self.buffer = Some(word.to_string()); } Ok(()) } } _ => Err(format!("{state:?} should be unreachable within {}.",function_name!())), } } } fn yield_word_and_apply(file : File, mut f : impl FnMut(&str) -> Result<(), String>) { let mut reader = io::BufReader::new(file); let mut buffer = String::new(); let mut EOF = false; let line_chunk_size = 25; while {!EOF} { for _ in 0..line_chunk_size { let bytes_read = reader.read_line(&mut buffer).unwrap(); if bytes_read == 0 { EOF = true; break } } let words = buffer.split_ascii_whitespace(); for word in words { f(word).unwrap(); } buffer.clear(); } } fn main() -> std::io::Result<()> { let args = Cli::parse(); let file = File::open(&args.path)?; let mut vcd = VCD::new(); let mut parser = VCD_Parser::new(&mut vcd); yield_word_and_apply(file, |word| {parser.parse_word(word)}); dbg!(&vcd); Ok(()) }