From c53c9684e6b98370b47ae25048455a39e7254a8a Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Thu, 11 Aug 2022 17:35:40 -0400 Subject: [PATCH] presumably using macros everywhere now --- src/main.rs | 3 +- src/vcd/parse/combinator_atoms.rs | 82 +++++----- src/vcd/parse/events.rs | 5 +- src/vcd/parse/metadata.rs | 225 ++++++++++++++------------ src/vcd/parse/scopes.rs | 254 +++++++++++++++++------------- src/vcd/reader.rs | 115 ++++++++------ src/vcd/types.rs | 6 + src/vcd/utilities.rs | 2 +- 8 files changed, 384 insertions(+), 308 deletions(-) diff --git a/src/main.rs b/src/main.rs index c69e331..f6d9d4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,8 @@ fn main() -> std::io::Result<()> { let now = Instant::now(); let file = File::open(&args.path)?; - parse_vcd(file).unwrap(); + let vcd = parse_vcd(file).unwrap(); + // vcd. let elapsed = now.elapsed(); println!("Elapsed: {:.2?}", elapsed); diff --git a/src/vcd/parse/combinator_atoms.rs b/src/vcd/parse/combinator_atoms.rs index db77e94..7ac4217 100644 --- a/src/vcd/parse/combinator_atoms.rs +++ b/src/vcd/parse/combinator_atoms.rs @@ -1,88 +1,78 @@ +use super::reader::{next_word, WordReader}; use super::types::ParseResult; -use super::reader::WordReader; -pub(super) fn digit(chr : u8) -> bool { +pub(super) fn digit(chr: u8) -> bool { let zero = b'0' as u8; let nine = b'9' as u8; let between_zero_and_nine = (chr >= zero) && (nine >= chr); - return between_zero_and_nine + return between_zero_and_nine; } -pub(super) fn take_until<'a>(word : &'a str, pattern : u8) -> ParseResult<'a> { - let mut new_start = 0; +pub(super) fn take_until<'a>(word: &'a str, pattern: u8) -> ParseResult<'a> { + let mut new_start = 0; for chr in word.as_bytes() { if *chr == pattern { - break - } - else { + break; + } else { new_start += 1; } } - return - ParseResult{ - matched : &word[0..new_start], - residual : &word[new_start..] - }; - + return ParseResult { + matched: &word[0..new_start], + residual: &word[new_start..], + }; } -pub(super) fn take_while<'a>(word : &'a str, cond : fn(u8) -> bool) -> ParseResult<'a> { - let mut new_start = 0; +pub(super) fn take_while<'a>(word: &'a str, cond: fn(u8) -> bool) -> ParseResult<'a> { + let mut new_start = 0; for chr in word.as_bytes() { if cond(*chr) { new_start += 1; - } - else { - break + } else { + break; } } - return - ParseResult{ - matched : &word[0..new_start], - residual : &word[new_start..] - }; - + return ParseResult { + matched: &word[0..new_start], + residual: &word[new_start..], + }; } -pub(super) fn tag<'a>(word : &'a str, pattern : &'a str) -> ParseResult<'a> { - let lhs = word.as_bytes().iter(); - let rhs = pattern.as_bytes(); - let iter = lhs.zip(rhs); +pub(super) fn tag<'a>(word: &'a str, pattern: &'a str) -> ParseResult<'a> { + let lhs = word.as_bytes().iter(); + let rhs = pattern.as_bytes(); + let iter = lhs.zip(rhs); let mut new_start = 0; let mut res = true; for (c_lhs, c_rhs) in iter { res = res && (c_lhs == c_rhs); - if !res {break} + if !res { + break; + } new_start += 1; } - return - ParseResult{ - matched : &word[0..new_start], - residual : &word[new_start..] - }; + return ParseResult { + matched: &word[0..new_start], + residual: &word[new_start..], + }; } -pub(super) fn ident( - word_reader : &mut WordReader, - keyword : &str, -) -> Result<(), String> { +pub(super) fn ident(word_reader: &mut WordReader, keyword: &str) -> Result<(), String> { // let keyword = "module"; - - let (word, cursor) = word_reader.next_word()?; + let (word, cursor) = next_word!(word_reader)?; if word == keyword { - return Ok(()) - } - else { + return Ok(()); + } else { let err = format!("found keyword `{word}` but expected `{keyword}` on {cursor:?}"); - return Err(err) + return Err(err); } -} \ No newline at end of file +} diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index 1ff5829..062aa41 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -10,10 +10,11 @@ pub(super) fn parse_events<'a>( loop { let next_word = word_reader.next_word(); + // The following is the only case where eof is not an error. // If we've reached the end of the file, then there is obviously // nothing left to do... - if next_word.is_err() { + if next_word.is_none() { break; }; @@ -77,7 +78,7 @@ pub(super) fn parse_events<'a>( } // this word should be the signal alias - let (word, cursor) = word_reader.next_word().unwrap(); + let (word, cursor) = next_word!(word_reader)?; // lookup signal idx let SignalIdx(ref signal_idx) = signal_map.get(word).ok_or(()).map_err(|_| { diff --git a/src/vcd/parse/metadata.rs b/src/vcd/parse/metadata.rs index 7f62fcf..855f3ba 100644 --- a/src/vcd/parse/metadata.rs +++ b/src/vcd/parse/metadata.rs @@ -4,23 +4,22 @@ use itertools::Itertools; use super::*; pub(super) 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), + 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_and_ctx1; - + let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; if !days.contains(&word) { - let msg = format!("Error near {}:{}.", file!(), line!()); + let msg = format!("Error near {}:{}.", file!(), line!()); let msg2 = format!("{word} is not a valid weekday : expected one of {days:?}\n"); let msg3 = format!("failure location: {cursor:?}"); - return Err(format!("{}{}{}", msg, msg2, msg3)) + return Err(format!("{}{}{}", msg, msg2, msg3)); } word.to_string() @@ -31,16 +30,14 @@ pub(super) fn parse_date( let (word, cursor) = word_and_ctx2; let months = [ - "Jan", "Feb", "Mar", "Apr", - "May", "Jun", "Jul", "Aug", - "Sept", "Oct", "Nov", "Dec", - ]; + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec", + ]; if !months.contains(&word) { - let msg = format!("Error near {}:{}.", file!(), line!()); + let msg = format!("Error near {}:{}.", file!(), line!()); let msg2 = format!("{word} is not a valid month : expected one of {months:?}\n"); let msg3 = format!("failure location: {cursor:?}"); - return Err(format!("{}{}{}", msg, msg2, msg3)) + return Err(format!("{}{}{}", msg, msg2, msg3)); } word.to_string() @@ -50,17 +47,16 @@ pub(super) fn parse_date( // check for another word in the file let (word, cursor) = word_and_ctx3; - let date : u8 = match word.to_string().parse() { + let date: u8 = match word.to_string().parse() { Ok(date) => date, - Err(e) => {return Err(format!("Error near {}:{}. {e}", file!(), line!()))} + Err(e) => return Err(format!("Error near {}:{}. {e}", file!(), line!())), }; if date > 31 { - let msg = format!("Error near {}:{}.", file!(), line!()); + let msg = format!("Error near {}:{}.", file!(), line!()); let msg2 = format!("{word} is not a valid date : must be between 0 and 31\n"); let msg3 = format!("failure location: {cursor:?}"); - return Err(format!("{}{}{}", msg, msg2, msg3)) - + return Err(format!("{}{}{}", msg, msg2, msg3)); } date.to_string() @@ -72,45 +68,50 @@ pub(super) fn parse_date( let res = take_until(word, b':'); res.assert_match()?; - let hh : u8 = res.matched.to_string() - .parse() - .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; + let hh: u8 = res + .matched + .to_string() + .parse() + .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; if hh > 23 { - let msg = format!("Error near {}:{}.", file!(), line!()); + let msg = format!("Error near {}:{}.", file!(), line!()); 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)) + return Err(format!("{}{}{}", msg, msg2, msg3)); } // get minute let word = &res.residual[1..]; // chop off colon which is at index 0 let res = take_until(word, b':'); res.assert_match()?; - let mm : u8 = res.matched.to_string() - .parse() - .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; + let mm: u8 = res + .matched + .to_string() + .parse() + .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; if mm > 60 { - let msg = format!("Error near {}:{}.", file!(), line!()); + let msg = format!("Error near {}:{}.", file!(), line!()); 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)) + return Err(format!("{}{}{}", msg, msg2, msg3)); } // get second // let ss : u8 = remainder.to_string().parse().unwrap(); res.assert_residual()?; let residual = &res.residual[1..]; // chop of colon which is at index 0 - let ss : u8 = residual.to_string() - .parse() - .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; + let ss: u8 = residual + .to_string() + .parse() + .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; if ss > 60 { - let msg = format!("Error near {}:{}.", file!(), line!()); + let msg = format!("Error near {}:{}.", file!(), line!()); 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)) + return Err(format!("{}{}{}", msg, msg2, msg3)); } (hh.to_string(), mm.to_string(), ss.to_string()) }; @@ -121,107 +122,123 @@ pub(super) fn parse_date( word.to_string() }; - // unfortunately, the minutes, seconds, and hour could occur in an + // unfortunately, the minutes, seconds, and hour could occur in an // unexpected order let full_date = format!("{day} {month} {date} {hh}:{mm}:{ss} {year}"); let full_date = Utc.datetime_from_str(full_date.as_str(), "%a %b %e %T %Y"); if full_date.is_ok() { - return Ok(full_date.unwrap()) + return Ok(full_date.unwrap()); } - Err(format!("Error near {}:{}. Failed to parse date.", file!(), line!())) - + Err(format!( + "Error near {}:{}. Failed to parse date.", + file!(), + line!() + )) } -pub(super) fn parse_version(word_reader : &mut WordReader) -> Result { +pub(super) fn parse_version(word_reader: &mut WordReader) -> Result { let mut version = String::new(); loop { - let (word, _) = word_reader.next_word()?; + let (word, _) = next_word!(word_reader)?; if word == "$end" { // truncate trailing whitespace - let version = version[0..(version.len() - 1)].to_string(); - return Ok(Version(version)) - - } - else { + let version = version[0..(version.len() - 1)].to_string(); + return Ok(Version(version)); + } else { version.push_str(word); version.push_str(" "); } } } -pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option, Timescale), String> { - +pub(super) fn parse_timescale( + word_reader: &mut WordReader, +) -> Result<(Option, Timescale), String> { // we might see `1ps $end` or `1 ps $end` // first get timescale - let (word, _) = word_reader.next_word()?; - let word = word.to_string(); - let ParseResult{matched, residual} = take_while(word.as_str(), digit); + let (word, _) = next_word!(word_reader)?; + let ParseResult { matched, residual } = take_while(word, digit); let scalar = matched; - let scalar : u32 = scalar.to_string().parse() - .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; + let scalar: u32 = scalar + .to_string() + .parse() + .map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?; let timescale = { if residual == "" { - let (word, _) = word_reader.next_word()?; + let (word, _) = next_word!(word_reader)?; let unit = match word { - "fs" => {Ok(Timescale::Fs)} - "ps" => {Ok(Timescale::Ps)} - "ns" => {Ok(Timescale::Ns)} - "us" => {Ok(Timescale::Us)} - "ms" => {Ok(Timescale::Ms)} - "s" => {Ok(Timescale::S)} - _ => {Err(format!("Error near {}:{}. Unknown unit {word}.", file!(), line!()))} - }.unwrap(); - + "fs" => Ok(Timescale::Fs), + "ps" => Ok(Timescale::Ps), + "ns" => Ok(Timescale::Ns), + "us" => Ok(Timescale::Us), + "ms" => Ok(Timescale::Ms), + "s" => Ok(Timescale::S), + _ => Err(format!( + "Error near {}:{}. Unknown unit {word}.", + file!(), + line!() + )), + } + .unwrap(); + (Some(scalar), unit) - } - else { + } else { let unit = match residual { - "fs" => {Ok(Timescale::Fs)} - "ps" => {Ok(Timescale::Ps)} - "ns" => {Ok(Timescale::Ns)} - "us" => {Ok(Timescale::Us)} - "ms" => {Ok(Timescale::Ms)} - "s" => {Ok(Timescale::S)} - _ => {Err(format!("Error near {}:{}. Unknown unit {residual}.", file!(), line!()))} - }.unwrap(); - + "fs" => Ok(Timescale::Fs), + "ps" => Ok(Timescale::Ps), + "ns" => Ok(Timescale::Ns), + "us" => Ok(Timescale::Us), + "ms" => Ok(Timescale::Ms), + "s" => Ok(Timescale::S), + _ => Err(format!( + "Error near {}:{}. Unknown unit {residual}.", + file!(), + line!() + )), + } + .unwrap(); + (Some(scalar), unit) } }; // then check for the `$end` keyword - let (end, _) = word_reader.next_word()?; - tag(end, "$end").assert_match()?; + let (word, _) = next_word!(word_reader)?; + tag(word, "$end").assert_match()?; return Ok(timescale); - } -pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result { - +pub(super) fn parse_metadata(word_reader: &mut WordReader) -> Result { let mut metadata = Metadata { - date : None, - version : None, - timescale : (None, Timescale::Unit) + date: None, + version: None, + timescale: (None, Timescale::Unit), }; loop { // check for another word in the file - let (word, _) = word_reader.next_word()?; + let (word, _) = word_reader.next_word().ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. Did not expect to reach end of file here.", + file!(), + line!() + ) + })?; - let ParseResult{matched, residual} = tag(word, "$"); + let ParseResult { matched, residual } = tag(word, "$"); match matched { // we hope that this word stars with a `$` - "$" => { + "$" => { match residual { - "date" => { - // a date is typically composed of the 5 following words which can - // occur in any order: + "date" => { + // 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. @@ -231,14 +248,14 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result = Vec::new(); + let mut lookahead_5_words: Vec<(String, Cursor)> = Vec::new(); for _ in 0..5 { - let (word, cursor) = word_reader.next_word()?; + let (word, cursor) = next_word!(word_reader)?; let word = word.to_string(); match word.as_str() { "$end" => { @@ -253,12 +270,14 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result Result { + "version" => { let version = parse_version(word_reader); if version.is_ok() { metadata.version = Some(version.unwrap()); @@ -298,8 +316,8 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result {break} - "var" => {break} + "scope" => break, + "var" => break, // we keep searching for words until we've found one of the following // keywords, ["version", "timescale", "scope", "var"] _ => {} @@ -308,7 +326,6 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result {} } - } - return Ok(metadata) -} \ No newline at end of file + return Ok(metadata); +} diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index fa62188..09e1f05 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -3,63 +3,80 @@ use super::*; pub(super) fn parse_var<'a>( - word_reader : &mut WordReader, - parent_scope_idx : ScopeIdx, - vcd : &'a mut VCD, - signal_map : &mut HashMap + word_reader: &mut WordReader, + parent_scope_idx: ScopeIdx, + vcd: &'a mut VCD, + signal_map: &mut HashMap, ) -> Result<(), String> { - let (word, cursor) = word_reader.next_word()?; - let expected_types = ["integer", "parameter", "real", "reg", "string", "wire", "tri1", "time"]; + let (word, cursor) = next_word!(word_reader)?; + let expected_types = [ + "integer", + "parameter", + "real", + "reg", + "string", + "wire", + "tri1", + "time", + ]; // $var parameter 3 a IDLE $end // ^^^^^^^^^ - var_type let var_type = match word { - "integer" => {Ok(SigType::Integer)} - "parameter" => {Ok(SigType::Parameter)} - "real" => {Ok(SigType::Real)} - "reg" => {Ok(SigType::Reg)} - "string" => {Ok(SigType::Str)} - "wire" => {Ok(SigType::Wire)} - "tri1" => {Ok(SigType::Tri1)} - "time" => {Ok(SigType::Time)} + "integer" => Ok(SigType::Integer), + "parameter" => Ok(SigType::Parameter), + "real" => Ok(SigType::Real), + "reg" => Ok(SigType::Reg), + "string" => Ok(SigType::Str), + "wire" => Ok(SigType::Wire), + "tri1" => Ok(SigType::Tri1), + "time" => Ok(SigType::Time), _ => { - let err = format!("Error near {}:{} \ + let err = format!( + "Error near {}:{} \ found keyword `{word}` but expected one of \ - {expected_types:?} on {cursor:?}", file!(), line!()); + {expected_types:?} on {cursor:?}", + file!(), + line!() + ); Err(err) } }?; - let (word, cursor) = word_reader.next_word()?; + let (word, cursor) = next_word!(word_reader)?; + let parse_err = format!("failed to parse as usize on {cursor:?}"); // $var parameter 3 a IDLE $end // ^ - no_bits let no_bits = match var_type { - SigType::Integer | SigType::Parameter | - SigType::Real | SigType::Reg | - SigType::Wire | SigType::Tri1 | - SigType::Time => { + SigType::Integer + | SigType::Parameter + | SigType::Real + | SigType::Reg + | SigType::Wire + | SigType::Tri1 + | SigType::Time => { let no_bits = word.parse::().expect(parse_err.as_str()); Some(no_bits) } // for strings, we don't really care what the number of bits is - _ => {None} + _ => None, }; // $var parameter 3 a IDLE $end // ^ - signal_alias - let (word, _) = word_reader.next_word()?; + let (word, _) = next_word!(word_reader)?; let signal_alias = word.to_string(); // $var parameter 3 a IDLE $end // ^^^^ - full_signal_name(can extend until $end) let mut full_signal_name = Vec::::new(); loop { - let (word, _) = word_reader.next_word()?; + let (word, _) = next_word!(word_reader)?; match word { - "$end" => {break} - _ => {full_signal_name.push(word.to_string())} + "$end" => break, + _ => full_signal_name.push(word.to_string()), } } let full_signal_name = full_signal_name.join(" "); @@ -70,15 +87,16 @@ pub(super) fn parse_var<'a>( let (signal, signal_idx) = match signal_map.get(&signal_alias) { Some(ref_signal_idx) => { let signal_idx = SignalIdx(vcd.all_signals.len()); - let signal = Signal::Alias{ + let signal = Signal::Alias { name: full_signal_name, - signal_alias: *ref_signal_idx}; + signal_alias: *ref_signal_idx, + }; (signal, signal_idx) } None => { let signal_idx = SignalIdx(vcd.all_signals.len()); signal_map.insert(signal_alias.to_string(), signal_idx); - let signal = Signal::Data{ + let signal = Signal::Data { name: full_signal_name, sig_type: var_type, signal_error: None, @@ -88,7 +106,8 @@ pub(super) fn parse_var<'a>( u8_timeline_markers: vec![], string_timeline: vec![], string_timeline_markers: vec![], - scope_parent: parent_scope_idx }; + scope_parent: parent_scope_idx, + }; (signal, signal_idx) } }; @@ -104,9 +123,9 @@ pub(super) fn parse_var<'a>( /// Sometimes, variables can be listed outside of scopes. /// We call these orphaned vars. fn parse_orphaned_vars<'a>( - word_reader : &mut WordReader, - vcd : &'a mut VCD, - signal_map : &mut HashMap + word_reader: &mut WordReader, + vcd: &'a mut VCD, + signal_map: &mut HashMap, ) -> Result<(), String> { // create scope for unscoped signals if such a scope does not // yet exist @@ -124,39 +143,41 @@ fn parse_orphaned_vars<'a>( if scope.name == scope_name { scope_idx = scope.self_idx; scope_already_exists = true; - break + break; } } if !scope_already_exists { - vcd.all_scopes.push( - Scope { - name: scope_name.to_string(), - parent_idx: None, - self_idx: scope_idx, - child_signals: vec![], - child_scopes: vec![] - } - ); + vcd.all_scopes.push(Scope { + name: scope_name.to_string(), + parent_idx: None, + self_idx: scope_idx, + child_signals: vec![], + child_scopes: vec![], + }); vcd.scope_roots.push(scope_idx); } - + // we can go ahead and parse the current var as we've already encountered // "$var" before now. parse_var(word_reader, scope_idx, vcd, signal_map)?; loop { - let (word, cursor) = word_reader.next_word()?; + let (word, cursor) = next_word!(word_reader)?; match word { "$var" => { parse_var(word_reader, scope_idx, vcd, signal_map)?; } - "$scope" => {break} + "$scope" => break, _ => { - let msg = format!("Error near {}:{}.\ + let msg = format!( + "Error near {}:{}.\ Expected $scope or $var, found \ - {word} at {cursor:?}", file!(), line!()); + {word} at {cursor:?}", + file!(), + line!() + ); Err(msg)?; } }; @@ -165,33 +186,36 @@ fn parse_orphaned_vars<'a>( Ok(()) } -pub(super) fn parse_signal_tree<'a>( - word_reader : &mut WordReader, - parent_scope_idx : Option, - vcd : &'a mut VCD, - signal_map : &mut HashMap +fn parse_scopes_inner<'a>( + word_reader: &mut WordReader, + parent_scope_idx: Option, + vcd: &'a mut VCD, + signal_map: &mut HashMap, ) -> Result<(), String> { - // $scope module reg_mag_i $end // ^^^^^^ - module keyword - let (keyword, cursor) = word_reader.next_word()?; + let (keyword, cursor) = next_word!(word_reader)?; let expected = ["module", "begin", "task", "function"]; if expected.contains(&keyword) { Ok(()) } else { - let err = format!("Error near {}:{}. \ + let err = format!( + "Error near {}:{}. \ found keyword `{keyword}` but expected one of \ - {expected:?} on {cursor:?}", file!(), line!()); + {expected:?} on {cursor:?}", + file!(), + line!() + ); Err(err) }?; // $scope module reg_mag_i $end // ^^^^^^^^^ - scope name - let (scope_name, _) = word_reader.next_word()?; + let (scope_name, _) = next_word!(word_reader)?; let curr_scope_idx = ScopeIdx(vcd.all_scopes.len()); - + // register this scope as a child of the current parent scope // if there is a parent scope, or else we register this scope as // root scope @@ -200,64 +224,68 @@ pub(super) fn parse_signal_tree<'a>( let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap(); parent_scope.child_scopes.push(curr_scope_idx); } - None => { - vcd.scope_roots.push(curr_scope_idx) - } + None => vcd.scope_roots.push(curr_scope_idx), } // add this scope to list of existing scopes - vcd.all_scopes.push( - Scope { - name: scope_name.to_string(), - parent_idx: parent_scope_idx, - self_idx: curr_scope_idx, - child_signals: vec![], - child_scopes: vec![] - } - ); + vcd.all_scopes.push(Scope { + name: scope_name.to_string(), + parent_idx: parent_scope_idx, + self_idx: curr_scope_idx, + child_signals: vec![], + child_scopes: vec![], + }); // $scope module reg_mag_i $end // ^^^^ - end keyword ident(word_reader, "$end")?; loop { - let (word, cursor) = word_reader.next_word()?; - let ParseResult{matched, residual} = tag(word, "$"); + let (word, cursor) = next_word!(word_reader)?; + let ParseResult { matched, residual } = tag(word, "$"); match matched { // we hope that this word starts with a `$` - "$" => { + "$" => { match residual { "scope" => { // recursive - parse inside of current scope tree - parse_signal_tree(word_reader, Some(curr_scope_idx), vcd, signal_map)?; + parse_scopes_inner(word_reader, Some(curr_scope_idx), vcd, signal_map)?; } "var" => { parse_var(word_reader, curr_scope_idx, vcd, signal_map)?; } "upscope" => { ident(word_reader, "$end")?; - break + break; } // we ignore comments - "comment" => { - loop { - if ident(word_reader, "$end").is_ok() {break} + "comment" => loop { + if ident(word_reader, "$end").is_ok() { + break; } - } + }, _ => { - let err = format!("Error near {}:{}. \ + let err = format!( + "Error near {}:{}. \ found keyword `{residual}` but expected \ `$scope`, `$var`, `$comment`, or `$upscope` \ - on {cursor:?}", file!(), line!()); - return Err(err) + on {cursor:?}", + file!(), + line!() + ); + return Err(err); } } } _ => { - let err = format!("Error near {}:{}. \ + let err = format!( + "Error near {}:{}. \ found keyword `{matched}` but \ - expected `$` on {cursor:?}", file!(), line!()); - return Err(err) + expected `$` on {cursor:?}", + file!(), + line!() + ); + return Err(err); } } } @@ -266,68 +294,78 @@ pub(super) fn parse_signal_tree<'a>( } pub(super) fn parse_scopes<'a>( - word_reader : &mut WordReader, - vcd : &'a mut VCD, - signal_map : &mut HashMap + word_reader: &mut WordReader, + vcd: &'a mut VCD, + signal_map: &mut HashMap, ) -> Result<(), String> { // get the current word - let (word, _) = word_reader.curr_word()?; + let (word, cursor) = curr_word!(word_reader)?; // we may have orphaned vars that occur before the first scope if word == "$var" { parse_orphaned_vars(word_reader, vcd, signal_map)?; - } - + } + // get the current word - let (word, cursor) = word_reader.curr_word()?; + let (word, cursor) = curr_word!(word_reader)?; // the current word should be "scope", as `parse_orphaned_vars`(if it // was called), should have terminated upon encountering "$scope". // If `parse_orphaned_vars` was not called, `parse_scopes` should still // have only been called if the caller encountered the word "$scope" if word != "$scope" { - let msg = format!("Error near {}:{}.\ + let msg = format!( + "Error near {}:{}.\ Expected $scope or $var, found \ - {word} at {cursor:?}", file!(), line!()); - return Err(msg) + {word} at {cursor:?}", + file!(), + line!() + ); + return Err(msg); } // now for the interesting part - parse_signal_tree(word_reader, None, vcd, signal_map)?; + parse_scopes_inner(word_reader, None, vcd, signal_map)?; // let err = format!("reached end of file without parser leaving {}", function_name!()); let expected_keywords = ["$scope", "$enddefinitions"]; // there could be multiple signal trees, and unfortunately, we - // can't merge the earlier call to `parse_signal_tree` into this loop - // because this loop gets a word from `next_word` instead of + // can't merge the earlier call to `parse_scopes_inner` into this loop + // because this loop gets a word from `next_word` instead of // `curr_word()`. loop { - let (word, cursor) = word_reader.next_word()?; + let (word, cursor) = next_word!(word_reader)?; + match word { "$scope" => { - parse_signal_tree(word_reader, None, vcd, signal_map)?; + parse_scopes_inner(word_reader, None, vcd, signal_map)?; } "$enddefinitions" => { ident(word_reader, "$end")?; - break + break; } "comment" => { // although we don't store comments, we still need to advance the // word_reader cursor to the end of the comment loop { - if ident(word_reader, "$end").is_ok() {break} + if ident(word_reader, "$end").is_ok() { + break; + } } } _ => { - let err = format!("Error near {}:{} \ + let err = format!( + "Error near {}:{} \ found keyword `{word}` but expected one of \ - {expected_keywords:?} on {cursor:?}", file!(), line!()); - return Err(err) - + {expected_keywords:?} on {cursor:?}", + file!(), + line!() + ); + return Err(err); } } } Ok(()) -} \ No newline at end of file +} diff --git a/src/vcd/reader.rs b/src/vcd/reader.rs index e1640f2..4a8f45f 100644 --- a/src/vcd/reader.rs +++ b/src/vcd/reader.rs @@ -13,12 +13,8 @@ pub(super) struct Line(pub(super) usize); pub(super) struct Word(pub(super) usize); #[derive(Debug, Clone)] pub(super) struct Cursor(pub(super) Line, pub(super) Word); -#[derive(Debug)] -pub(super) enum FileStatus { - Eof, -} -pub struct WordReader { +pub(super) struct WordReader { reader: io::BufReader, eof: bool, buffers: Vec, @@ -40,7 +36,7 @@ impl WordReader { } } - pub(super) fn next_word(&mut self) -> Result<(&str, Cursor), FileStatus> { + pub(super) fn next_word(&mut self) -> Option<(&str, Cursor)> { // although reaching the eof is not technically an error, in most cases, // we treat it like one in the rest of the codebase. @@ -50,7 +46,7 @@ impl WordReader { self.buffers.clear(); if self.eof { - return Err(FileStatus::Eof); + return None; } let num_buffers = 10; @@ -81,7 +77,7 @@ impl WordReader { // if after we've attempted to read in more content from the file, // there are still no words... if self.str_slices.is_empty() { - return Err(FileStatus::Eof); + return None; } // if we make it here, we return the next word @@ -89,57 +85,84 @@ impl WordReader { let (ptr, len, position) = self.str_slices.pop_front().unwrap(); let slice = slice::from_raw_parts(ptr, len); self.curr_slice = Some((ptr, len, position.clone())); - return Ok((str::from_utf8(slice).unwrap(), position)); + return Some((str::from_utf8(slice).unwrap(), position)); }; } - pub(super) fn curr_word(&mut self) -> Result<(&str, Cursor), FileStatus> { + pub(super) fn curr_word(&mut self) -> Option<(&str, Cursor)> { match &self.curr_slice { Some(slice) => unsafe { let (ptr, len, position) = slice.clone(); let slice = slice::from_raw_parts(ptr, len); - Ok((str::from_utf8(slice).unwrap(), position)) + return Some((str::from_utf8(slice).unwrap(), position)); }, - None => Err(FileStatus::Eof), + None => None, } } } -fn previous_symbol(level: u32) -> Option { - let (trace, curr_file, curr_line) = (Backtrace::new(), file!(), line!()); - let frames = trace.frames(); - frames - .iter() - .flat_map(BacktraceFrame::symbols) - .skip_while(|s| { - s.filename() - .map(|p| !p.ends_with(curr_file)) - .unwrap_or(true) - || s.lineno() != Some(curr_line) +macro_rules! next_word { + ($word_reader:ident) => { + $word_reader.next_word().ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. Did not expect to reach end of file here.", + file!(), + line!() + ) }) - .nth(1 + level as usize) - .cloned() + }; } -impl From for String { - fn from(f: FileStatus) -> String { - let sym = previous_symbol(1); - let filename = sym - .as_ref() - .and_then(BacktraceSymbol::filename) - .map_or(None, |path| path.to_str()) - .unwrap_or("(Couldn't determine filename)"); - let lineno = sym - .as_ref() - .and_then(BacktraceSymbol::lineno) - .map_or(None, |path| Some(path.to_string())) - .unwrap_or("(Couldn't determine line number)".to_string()); - - match f { - FileStatus::Eof => format!( - "Error near {filename}:{lineno} \ - No more words left in vcd file." - ), - } - } +macro_rules! curr_word { + ($word_reader:ident) => { + $word_reader.curr_word().ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. A call to curr_word! shouldn't fail unless next_word has not yet been invoked.", + file!(), + line!() + ) + }) + }; } + +pub(super) use curr_word; +pub(super) use next_word; + +// fn previous_symbol(level: u32) -> Option { +// let (trace, curr_file, curr_line) = (Backtrace::new(), file!(), line!()); +// let frames = trace.frames(); +// frames +// .iter() +// .flat_map(BacktraceFrame::symbols) +// .skip_while(|s| { +// s.filename() +// .map(|p| !p.ends_with(curr_file)) +// .unwrap_or(true) +// || s.lineno() != Some(curr_line) +// }) +// .nth(1 + level as usize) +// .cloned() +// } + +// impl From for String { +// fn from(f: FileStatus) -> String { +// let sym = previous_symbol(1); +// let filename = sym +// .as_ref() +// .and_then(BacktraceSymbol::filename) +// .map_or(None, |path| path.to_str()) +// .unwrap_or("(Couldn't determine filename)"); +// let lineno = sym +// .as_ref() +// .and_then(BacktraceSymbol::lineno) +// .map_or(None, |path| Some(path.to_string())) +// .unwrap_or("(Couldn't determine line number)".to_string()); + +// match f { +// FileStatus::Eof => format!( +// "Error near {filename}:{lineno} \ +// No more words left in vcd file." +// ), +// } +// } +// } diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 6bcd66e..cb67d68 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -87,7 +87,13 @@ pub(super) struct Scope { #[derive(Debug)] pub struct VCD { pub(super) metadata: Metadata, + // since we only need to store values when there is an actual change + // in the timeline, we keep a vector that stores the time at which an + // event occurs. Time t is always stored as the minimum length sequence + // of u8. pub timeline: Vec, + // we need to keep track of where a given time t sequence of u8 begins + // and ends in the timeline vector. pub timeline_markers: Vec, pub(super) all_signals: Vec, pub(super) all_scopes: Vec, diff --git a/src/vcd/utilities.rs b/src/vcd/utilities.rs index cb050d8..a0c1abb 100644 --- a/src/vcd/utilities.rs +++ b/src/vcd/utilities.rs @@ -12,7 +12,7 @@ pub(super) enum BinaryParserErrTypes { } // We build a quick and not so dirty bit string parser. -pub(super) fn base2_str_to_byte(word: &[u8]) -> Result { +fn base2_str_to_byte(word: &[u8]) -> Result { let mut val = 0u8; // shouldn't have more than 8 chars in str