commit
3ec1aa44e9
|
@ -3,6 +3,9 @@ name = "fastwave"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = 1
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -10,3 +13,4 @@ num = "0.4"
|
||||||
clap = { version = "3.1.8", features = ["derive"] }
|
clap = { version = "3.1.8", features = ["derive"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
|
backtrace = "0.3"
|
|
@ -76,9 +76,7 @@ pub(super) fn ident(
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// let keyword = "module";
|
// let keyword = "module";
|
||||||
|
|
||||||
let err : Result<(), String> = Err(format!("reached end of file without parser leaving ident"));
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
let word = word_reader.next_word();
|
|
||||||
let (word, cursor) = word.ok_or(err).unwrap();
|
|
||||||
|
|
||||||
if word == keyword {
|
if word == keyword {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
|
|
|
@ -84,9 +84,10 @@ pub(super) fn parse_events<'a>(
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next_word = word_reader.next_word();
|
let next_word = word_reader.next_word();
|
||||||
// if we've reached the end of the file, then there is obviously
|
// 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...
|
// nothing left to do...
|
||||||
if next_word.is_none() {break};
|
if next_word.is_err() {break};
|
||||||
|
|
||||||
let (word, cursor) = next_word.unwrap();
|
let (word, cursor) = next_word.unwrap();
|
||||||
let Cursor(Line(_), Word(word_in_line_idx)) = cursor;
|
let Cursor(Line(_), Word(word_in_line_idx)) = cursor;
|
||||||
|
|
|
@ -17,8 +17,7 @@ pub(super) fn parse_date(
|
||||||
|
|
||||||
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||||
if !days.contains(&word) {
|
if !days.contains(&word) {
|
||||||
let msg = format!("Error near {}:{}. Reached end of file without \
|
let msg = format!("Error near {}:{}.", file!(), line!());
|
||||||
terminating parser", file!(), line!());
|
|
||||||
let msg2 = format!("{word} is not a valid weekday : expected one of {days:?}\n");
|
let msg2 = format!("{word} is not a valid weekday : expected one of {days:?}\n");
|
||||||
let msg3 = format!("failure location: {cursor:?}");
|
let msg3 = format!("failure location: {cursor:?}");
|
||||||
return Err(format!("{}{}{}", msg, msg2, msg3))
|
return Err(format!("{}{}{}", msg, msg2, msg3))
|
||||||
|
@ -38,8 +37,7 @@ pub(super) fn parse_date(
|
||||||
];
|
];
|
||||||
|
|
||||||
if !months.contains(&word) {
|
if !months.contains(&word) {
|
||||||
let msg = format!("Error near {}:{}. Reached end of file without \
|
let msg = format!("Error near {}:{}.", file!(), line!());
|
||||||
terminating parser", file!(), line!());
|
|
||||||
let msg2 = format!("{word} is not a valid month : expected one of {months:?}\n");
|
let msg2 = format!("{word} is not a valid month : expected one of {months:?}\n");
|
||||||
let msg3 = format!("failure location: {cursor:?}");
|
let msg3 = format!("failure location: {cursor:?}");
|
||||||
return Err(format!("{}{}{}", msg, msg2, msg3))
|
return Err(format!("{}{}{}", msg, msg2, msg3))
|
||||||
|
@ -54,12 +52,11 @@ pub(super) fn parse_date(
|
||||||
|
|
||||||
let date : u8 = match word.to_string().parse() {
|
let date : u8 = match word.to_string().parse() {
|
||||||
Ok(date) => date,
|
Ok(date) => date,
|
||||||
Err(_) => {return Err("".to_string())}
|
Err(e) => {return Err(format!("Error near {}:{}. {e}", file!(), line!()))}
|
||||||
};
|
};
|
||||||
|
|
||||||
if date > 31 {
|
if date > 31 {
|
||||||
let msg = format!("Error near {}:{}. Reached end of file without \
|
let msg = format!("Error near {}:{}.", file!(), line!());
|
||||||
terminating parser", file!(), line!());
|
|
||||||
let msg2 = format!("{word} is not a valid date : must be between 0 and 31\n");
|
let msg2 = format!("{word} is not a valid date : must be between 0 and 31\n");
|
||||||
let msg3 = format!("failure location: {cursor:?}");
|
let msg3 = format!("failure location: {cursor:?}");
|
||||||
return Err(format!("{}{}{}", msg, msg2, msg3))
|
return Err(format!("{}{}{}", msg, msg2, msg3))
|
||||||
|
@ -77,7 +74,7 @@ pub(super) fn parse_date(
|
||||||
res.assert_match()?;
|
res.assert_match()?;
|
||||||
let hh : u8 = res.matched.to_string()
|
let hh : u8 = res.matched.to_string()
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| "failed to parse".to_string())?;
|
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
|
||||||
|
|
||||||
if hh > 23 {
|
if hh > 23 {
|
||||||
let msg = format!("Error near {}:{}.", file!(), line!());
|
let msg = format!("Error near {}:{}.", file!(), line!());
|
||||||
|
@ -92,7 +89,7 @@ pub(super) fn parse_date(
|
||||||
res.assert_match()?;
|
res.assert_match()?;
|
||||||
let mm : u8 = res.matched.to_string()
|
let mm : u8 = res.matched.to_string()
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| "failed to parse".to_string())?;
|
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
|
||||||
|
|
||||||
if mm > 60 {
|
if mm > 60 {
|
||||||
let msg = format!("Error near {}:{}.", file!(), line!());
|
let msg = format!("Error near {}:{}.", file!(), line!());
|
||||||
|
@ -107,7 +104,7 @@ pub(super) fn parse_date(
|
||||||
let residual = &res.residual[1..]; // chop of colon which is at index 0
|
let residual = &res.residual[1..]; // chop of colon which is at index 0
|
||||||
let ss : u8 = residual.to_string()
|
let ss : u8 = residual.to_string()
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| "failed to parse".to_string())?;
|
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
|
||||||
|
|
||||||
if ss > 60 {
|
if ss > 60 {
|
||||||
let msg = format!("Error near {}:{}.", file!(), line!());
|
let msg = format!("Error near {}:{}.", file!(), line!());
|
||||||
|
@ -132,7 +129,7 @@ pub(super) fn parse_date(
|
||||||
return Ok(full_date.unwrap())
|
return Ok(full_date.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
Err("failed to parse date".to_string())
|
Err(format!("Error near {}:{}. Failed to parse date.", file!(), line!()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,16 +137,7 @@ pub(super) fn parse_version(word_reader : &mut WordReader) -> Result<Version, St
|
||||||
let mut version = String::new();
|
let mut version = String::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let word = word_reader.next_word();
|
let (word, _) = word_reader.next_word()?;
|
||||||
|
|
||||||
// if there isn't another word left in the file, then we exit
|
|
||||||
if word.is_none() {
|
|
||||||
let msg = format!("Error near {}:{}. Reached end of file without \
|
|
||||||
terminating parser", file!(), line!());
|
|
||||||
return Err(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
let (word, _) = word.unwrap();
|
|
||||||
|
|
||||||
if word == "$end" {
|
if word == "$end" {
|
||||||
// truncate trailing whitespace
|
// truncate trailing whitespace
|
||||||
|
@ -165,23 +153,20 @@ pub(super) fn parse_version(word_reader : &mut WordReader) -> Result<Version, St
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option<u32>, Timescale), String> {
|
pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option<u32>, Timescale), String> {
|
||||||
let err_msg = format!("Error near {}:{}. No more words left in vcd file.",
|
|
||||||
file!(), line!());
|
|
||||||
|
|
||||||
// we might see `scalarunit $end` or `scalar unit $end`
|
|
||||||
|
|
||||||
|
// we might see `1ps $end` or `1 ps $end`
|
||||||
// first get timescale
|
// first get timescale
|
||||||
let (word, _) = word_reader.next_word().ok_or(&err_msg)?;
|
let (word, _) = word_reader.next_word()?;
|
||||||
let word = word.to_string();
|
let word = word.to_string();
|
||||||
let ParseResult{matched, residual} = take_while(word.as_str(), digit);
|
let ParseResult{matched, residual} = take_while(word.as_str(), digit);
|
||||||
let scalar = matched;
|
let scalar = matched;
|
||||||
|
|
||||||
let scalar : u32 = scalar.to_string().parse()
|
let scalar : u32 = scalar.to_string().parse()
|
||||||
.map_err(|_| &err_msg)?;
|
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
|
||||||
|
|
||||||
let timescale = {
|
let timescale = {
|
||||||
if residual == "" {
|
if residual == "" {
|
||||||
let (word, _) = word_reader.next_word().ok_or(&err_msg)?;
|
let (word, _) = word_reader.next_word()?;
|
||||||
let unit = match word {
|
let unit = match word {
|
||||||
"fs" => {Ok(Timescale::Fs)}
|
"fs" => {Ok(Timescale::Fs)}
|
||||||
"ps" => {Ok(Timescale::Ps)}
|
"ps" => {Ok(Timescale::Ps)}
|
||||||
|
@ -189,19 +174,20 @@ pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option<u
|
||||||
"us" => {Ok(Timescale::Us)}
|
"us" => {Ok(Timescale::Us)}
|
||||||
"ms" => {Ok(Timescale::Ms)}
|
"ms" => {Ok(Timescale::Ms)}
|
||||||
"s" => {Ok(Timescale::S)}
|
"s" => {Ok(Timescale::S)}
|
||||||
_ => {Err(err_msg.to_string())}
|
_ => {Err(format!("Error near {}:{}. Unknown unit {word}.", file!(), line!()))}
|
||||||
}.unwrap();
|
}.unwrap();
|
||||||
|
|
||||||
(Some(scalar), unit)
|
(Some(scalar), unit)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let unit = match residual {
|
let unit = match residual {
|
||||||
|
"fs" => {Ok(Timescale::Fs)}
|
||||||
"ps" => {Ok(Timescale::Ps)}
|
"ps" => {Ok(Timescale::Ps)}
|
||||||
"ns" => {Ok(Timescale::Ns)}
|
"ns" => {Ok(Timescale::Ns)}
|
||||||
"us" => {Ok(Timescale::Us)}
|
"us" => {Ok(Timescale::Us)}
|
||||||
"ms" => {Ok(Timescale::Ms)}
|
"ms" => {Ok(Timescale::Ms)}
|
||||||
"s" => {Ok(Timescale::S)}
|
"s" => {Ok(Timescale::S)}
|
||||||
_ => {Err(err_msg.to_string())}
|
_ => {Err(format!("Error near {}:{}. Unknown unit {residual}.", file!(), line!()))}
|
||||||
}.unwrap();
|
}.unwrap();
|
||||||
|
|
||||||
(Some(scalar), unit)
|
(Some(scalar), unit)
|
||||||
|
@ -209,7 +195,7 @@ pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option<u
|
||||||
};
|
};
|
||||||
|
|
||||||
// then check for the `$end` keyword
|
// then check for the `$end` keyword
|
||||||
let (end, _) = word_reader.next_word().ok_or(&err_msg)?;
|
let (end, _) = word_reader.next_word()?;
|
||||||
tag(end, "$end").assert_match()?;
|
tag(end, "$end").assert_match()?;
|
||||||
|
|
||||||
return Ok(timescale);
|
return Ok(timescale);
|
||||||
|
@ -217,8 +203,6 @@ pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option<u
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result<Metadata, String> {
|
pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result<Metadata, String> {
|
||||||
let err_msg = format!("Error near {}:{}. No more words left in vcd file.",
|
|
||||||
file!(), line!());
|
|
||||||
|
|
||||||
let mut metadata = Metadata {
|
let mut metadata = Metadata {
|
||||||
date : None,
|
date : None,
|
||||||
|
@ -228,7 +212,7 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result<Metadata,
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// check for another word in the file
|
// check for another word in the file
|
||||||
let (word, _) = word_reader.next_word().ok_or(&err_msg)?;
|
let (word, _) = word_reader.next_word()?;
|
||||||
|
|
||||||
let ParseResult{matched, residual} = tag(word, "$");
|
let ParseResult{matched, residual} = tag(word, "$");
|
||||||
match matched {
|
match matched {
|
||||||
|
@ -254,7 +238,7 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result<Metadata,
|
||||||
let mut lookahead_5_words : Vec<(String, Cursor)> = Vec::new();
|
let mut lookahead_5_words : Vec<(String, Cursor)> = Vec::new();
|
||||||
|
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
let (word, cursor) = word_reader.next_word().expect(err_msg.as_str());
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
let word = word.to_string();
|
let word = word.to_string();
|
||||||
match word.as_str() {
|
match word.as_str() {
|
||||||
"$end" => {
|
"$end" => {
|
||||||
|
|
|
@ -8,8 +8,7 @@ pub(super) fn parse_var<'a>(
|
||||||
vcd : &'a mut VCD,
|
vcd : &'a mut VCD,
|
||||||
signal_map : &mut HashMap<String, SignalIdx>
|
signal_map : &mut HashMap<String, SignalIdx>
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
|
|
||||||
let expected_types = ["integer", "parameter", "real", "reg", "string", "wire", "tri1", "time"];
|
let expected_types = ["integer", "parameter", "real", "reg", "string", "wire", "tri1", "time"];
|
||||||
|
|
||||||
// $var parameter 3 a IDLE $end
|
// $var parameter 3 a IDLE $end
|
||||||
|
@ -24,12 +23,14 @@ pub(super) fn parse_var<'a>(
|
||||||
"tri1" => {Ok(SigType::Tri1)}
|
"tri1" => {Ok(SigType::Tri1)}
|
||||||
"time" => {Ok(SigType::Time)}
|
"time" => {Ok(SigType::Time)}
|
||||||
_ => {
|
_ => {
|
||||||
let err = format!("found keyword `{word}` but expected one of {expected_types:?} on {cursor:?}");
|
let err = format!("Error near {}:{} \
|
||||||
|
found keyword `{word}` but expected one of \
|
||||||
|
{expected_types:?} on {cursor:?}", file!(), line!());
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
let parse_err = format!("failed to parse as usize on {cursor:?}");
|
let parse_err = format!("failed to parse as usize on {cursor:?}");
|
||||||
|
|
||||||
// $var parameter 3 a IDLE $end
|
// $var parameter 3 a IDLE $end
|
||||||
|
@ -48,14 +49,14 @@ pub(super) fn parse_var<'a>(
|
||||||
|
|
||||||
// $var parameter 3 a IDLE $end
|
// $var parameter 3 a IDLE $end
|
||||||
// ^ - signal_alias
|
// ^ - signal_alias
|
||||||
let (word, _) = word_reader.next_word().ok_or(&err)?;
|
let (word, _) = word_reader.next_word()?;
|
||||||
let signal_alias = word.to_string();
|
let signal_alias = word.to_string();
|
||||||
|
|
||||||
// $var parameter 3 a IDLE $end
|
// $var parameter 3 a IDLE $end
|
||||||
// ^^^^ - full_signal_name(can extend until $end)
|
// ^^^^ - full_signal_name(can extend until $end)
|
||||||
let mut full_signal_name = Vec::<String>::new();
|
let mut full_signal_name = Vec::<String>::new();
|
||||||
loop {
|
loop {
|
||||||
let (word, _) = word_reader.next_word().ok_or(&err)?;
|
let (word, _) = word_reader.next_word()?;
|
||||||
match word {
|
match word {
|
||||||
"$end" => {break}
|
"$end" => {break}
|
||||||
_ => {full_signal_name.push(word.to_string())}
|
_ => {full_signal_name.push(word.to_string())}
|
||||||
|
@ -145,15 +146,7 @@ fn parse_orphaned_vars<'a>(
|
||||||
parse_var(word_reader, scope_idx, vcd, signal_map)?;
|
parse_var(word_reader, scope_idx, vcd, signal_map)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next_word = word_reader.next_word();
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
|
|
||||||
// we shouldn't reach the end of the file here...
|
|
||||||
if next_word.is_none() {
|
|
||||||
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
|
|
||||||
Err(err)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
let (word, cursor) = next_word.unwrap();
|
|
||||||
|
|
||||||
match word {
|
match word {
|
||||||
"$var" => {
|
"$var" => {
|
||||||
|
@ -161,9 +154,9 @@ fn parse_orphaned_vars<'a>(
|
||||||
}
|
}
|
||||||
"$scope" => {break}
|
"$scope" => {break}
|
||||||
_ => {
|
_ => {
|
||||||
let (f, l )= (file!(), line!());
|
let msg = format!("Error near {}:{}.\
|
||||||
let msg = format!("Error near {f}:{l}.\
|
Expected $scope or $var, found \
|
||||||
Expected $scope or $var, found {word} at {cursor:?}");
|
{word} at {cursor:?}", file!(), line!());
|
||||||
Err(msg)?;
|
Err(msg)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -181,20 +174,21 @@ pub(super) fn parse_signal_tree<'a>(
|
||||||
|
|
||||||
// $scope module reg_mag_i $end
|
// $scope module reg_mag_i $end
|
||||||
// ^^^^^^ - module keyword
|
// ^^^^^^ - module keyword
|
||||||
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
|
let (keyword, cursor) = word_reader.next_word()?;
|
||||||
let (keyword, cursor) = word_reader.next_word().ok_or(&err)?;
|
|
||||||
|
|
||||||
let expected = ["module", "begin", "task", "function"];
|
let expected = ["module", "begin", "task", "function"];
|
||||||
if expected.contains(&keyword) {
|
if expected.contains(&keyword) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let err = format!("found keyword `{keyword}` but expected one of `{expected:?}` on {cursor:?}");
|
let err = format!("Error near {}:{}. \
|
||||||
|
found keyword `{keyword}` but expected one of \
|
||||||
|
{expected:?} on {cursor:?}", file!(), line!());
|
||||||
Err(err)
|
Err(err)
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
// $scope module reg_mag_i $end
|
// $scope module reg_mag_i $end
|
||||||
// ^^^^^^^^^ - scope name
|
// ^^^^^^^^^ - scope name
|
||||||
let (scope_name, _) = word_reader.next_word().ok_or(&err)?;
|
let (scope_name, _) = word_reader.next_word()?;
|
||||||
|
|
||||||
let curr_scope_idx = ScopeIdx(vcd.all_scopes.len());
|
let curr_scope_idx = ScopeIdx(vcd.all_scopes.len());
|
||||||
|
|
||||||
|
@ -227,7 +221,7 @@ pub(super) fn parse_signal_tree<'a>(
|
||||||
ident(word_reader, "$end")?;
|
ident(word_reader, "$end")?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
let ParseResult{matched, residual} = tag(word, "$");
|
let ParseResult{matched, residual} = tag(word, "$");
|
||||||
match matched {
|
match matched {
|
||||||
// we hope that this word starts with a `$`
|
// we hope that this word starts with a `$`
|
||||||
|
@ -251,13 +245,18 @@ pub(super) fn parse_signal_tree<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let err = format!("found keyword `{residual}` but expected `$scope`, `$var`, `$comment`, or `$upscope` on {cursor:?}");
|
let err = format!("Error near {}:{}. \
|
||||||
|
found keyword `{residual}` but expected \
|
||||||
|
`$scope`, `$var`, `$comment`, or `$upscope` \
|
||||||
|
on {cursor:?}", file!(), line!());
|
||||||
return Err(err)
|
return Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let err = format!("found keyword `{matched}` but expected `$` on {cursor:?}");
|
let err = format!("Error near {}:{}. \
|
||||||
|
found keyword `{matched}` but \
|
||||||
|
expected `$` on {cursor:?}", file!(), line!());
|
||||||
return Err(err)
|
return Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,8 +271,7 @@ pub(super) fn parse_scopes<'a>(
|
||||||
signal_map : &mut HashMap<String, SignalIdx>
|
signal_map : &mut HashMap<String, SignalIdx>
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// get the current word
|
// get the current word
|
||||||
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
|
let (word, _) = word_reader.curr_word()?;
|
||||||
let (word, _) = word_reader.curr_word().ok_or(&err)?;
|
|
||||||
|
|
||||||
// we may have orphaned vars that occur before the first scope
|
// we may have orphaned vars that occur before the first scope
|
||||||
if word == "$var" {
|
if word == "$var" {
|
||||||
|
@ -281,16 +279,16 @@ pub(super) fn parse_scopes<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the current word
|
// get the current word
|
||||||
let (word, cursor) = word_reader.curr_word().ok_or(&err)?;
|
let (word, cursor) = word_reader.curr_word()?;
|
||||||
|
|
||||||
// the current word should be "scope", as `parse_orphaned_vars`(if it
|
// the current word should be "scope", as `parse_orphaned_vars`(if it
|
||||||
// was called), should have terminated upon encountering "$scope".
|
// was called), should have terminated upon encountering "$scope".
|
||||||
// If `parse_orphaned_vars` was not called, `parse_scopes` should still
|
// If `parse_orphaned_vars` was not called, `parse_scopes` should still
|
||||||
// have only been called if the caller encountered the word "$scope"
|
// have only been called if the caller encountered the word "$scope"
|
||||||
if word != "$scope" {
|
if word != "$scope" {
|
||||||
let (f, l )= (file!(), line!());
|
let msg = format!("Error near {}:{}.\
|
||||||
let msg = format!("Error near {f}:{l}.\
|
Expected $scope or $var, found \
|
||||||
Expected $scope or $var, found {word} at {cursor:?}");
|
{word} at {cursor:?}", file!(), line!());
|
||||||
return Err(msg)
|
return Err(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +303,7 @@ pub(super) fn parse_scopes<'a>(
|
||||||
// because this loop gets a word from `next_word` instead of
|
// because this loop gets a word from `next_word` instead of
|
||||||
// `curr_word()`.
|
// `curr_word()`.
|
||||||
loop {
|
loop {
|
||||||
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
|
let (word, cursor) = word_reader.next_word()?;
|
||||||
match word {
|
match word {
|
||||||
"$scope" => {
|
"$scope" => {
|
||||||
parse_signal_tree(word_reader, None, vcd, signal_map)?;
|
parse_signal_tree(word_reader, None, vcd, signal_map)?;
|
||||||
|
@ -322,8 +320,9 @@ pub(super) fn parse_scopes<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let err = format!("found keyword `{word}` but expected one \
|
let err = format!("Error near {}:{} \
|
||||||
of `{expected_keywords:?}` on {cursor:?}");
|
found keyword `{word}` but expected one of \
|
||||||
|
{expected_keywords:?} on {cursor:?}", file!(), line!());
|
||||||
return Err(err)
|
return Err(err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,16 @@ use std::str;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use backtrace::{ Backtrace, BacktraceFrame, BacktraceSymbol };
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct Line(pub(super) usize);
|
pub(super) struct Line(pub(super) usize);
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct Word(pub(super) usize);
|
pub(super) struct Word(pub(super) usize);
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct Cursor(pub(super) Line, pub(super) Word);
|
pub(super) struct Cursor(pub(super) Line, pub(super) Word);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) enum FileStatus{Eof}
|
||||||
|
|
||||||
pub struct WordReader {
|
pub struct WordReader {
|
||||||
reader : io::BufReader<File>,
|
reader : io::BufReader<File>,
|
||||||
|
@ -21,6 +25,7 @@ pub struct WordReader {
|
||||||
curr_slice : Option<(*const u8, usize, Cursor)>,
|
curr_slice : Option<(*const u8, usize, Cursor)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl WordReader {
|
impl WordReader {
|
||||||
pub(super) fn new(file : File) -> WordReader {
|
pub(super) fn new(file : File) -> WordReader {
|
||||||
let reader = io::BufReader::new(file);
|
let reader = io::BufReader::new(file);
|
||||||
|
@ -34,13 +39,18 @@ impl WordReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn next_word(&mut self) -> Option<(&str, Cursor)> {
|
|
||||||
// if there are no more words, attempt to read more content
|
pub(super) fn next_word(&mut self) -> Result<(&str, Cursor), FileStatus> {
|
||||||
|
|
||||||
|
// although reaching the eof is not technically an error, in most cases,
|
||||||
|
// we treat it like one in the rest of the codebase.
|
||||||
|
|
||||||
|
// if there are no more words in the buffer, attempt to read more content
|
||||||
// from the file
|
// from the file
|
||||||
if self.str_slices.is_empty() {
|
if self.str_slices.is_empty() {
|
||||||
self.buffers.clear();
|
self.buffers.clear();
|
||||||
|
|
||||||
if self.eof {return None}
|
if self.eof {return Err(FileStatus::Eof)}
|
||||||
|
|
||||||
let num_buffers = 10;
|
let num_buffers = 10;
|
||||||
|
|
||||||
|
@ -70,7 +80,7 @@ impl WordReader {
|
||||||
// if after we've attempted to read in more content from the file,
|
// if after we've attempted to read in more content from the file,
|
||||||
// there are still no words...
|
// there are still no words...
|
||||||
if self.str_slices.is_empty() {
|
if self.str_slices.is_empty() {
|
||||||
return None
|
return Err(FileStatus::Eof)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we make it here, we return the next word
|
// if we make it here, we return the next word
|
||||||
|
@ -78,21 +88,53 @@ impl WordReader {
|
||||||
let (ptr, len, position) = self.str_slices.pop_front().unwrap();
|
let (ptr, len, position) = self.str_slices.pop_front().unwrap();
|
||||||
let slice = slice::from_raw_parts(ptr, len);
|
let slice = slice::from_raw_parts(ptr, len);
|
||||||
self.curr_slice = Some((ptr, len, position.clone()));
|
self.curr_slice = Some((ptr, len, position.clone()));
|
||||||
return Some((str::from_utf8(slice).unwrap(), position));
|
return Ok((str::from_utf8(slice).unwrap(), position));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn curr_word(&mut self) -> Option<(&str, Cursor)> {
|
pub(super) fn curr_word(&mut self) -> Result<(&str, Cursor), FileStatus> {
|
||||||
match &self.curr_slice {
|
match &self.curr_slice {
|
||||||
Some(slice) => {
|
Some(slice) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (ptr, len, position) = slice.clone();
|
let (ptr, len, position) = slice.clone();
|
||||||
let slice = slice::from_raw_parts(ptr, len);
|
let slice = slice::from_raw_parts(ptr, len);
|
||||||
Some((str::from_utf8(slice).unwrap(), position))
|
Ok((str::from_utf8(slice).unwrap(), position))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
None => {None}
|
None => {Err(FileStatus::Eof)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn previous_symbol(level: u32) -> Option<BacktraceSymbol> {
|
||||||
|
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<FileStatus> 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."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
92
test-vcd-files/VCD_file_with_errors.vcd
Normal file
92
test-vcd-files/VCD_file_with_errors.vcd
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
$date
|
||||||
|
Thu Dec 17 17:19:03 2020
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Aldec HDL Simulator Version 10.03.3558
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1 ps
|
||||||
|
$end
|
||||||
|
|
||||||
|
$scope module tb $end
|
||||||
|
|
||||||
|
$scope module t $end
|
||||||
|
$var wire 1 ! CLK $end
|
||||||
|
$var wire 1 " LED $end
|
||||||
|
$var wire 1 # PIN_10 $end
|
||||||
|
$var wire 1 $ PIN_11 $end
|
||||||
|
$var wire 1 % PIN_12 $end
|
||||||
|
$var wire 1 & PIN_13 $end
|
||||||
|
$var wire 1 ' SPI_In $end
|
||||||
|
$var wire 1 ( SPI_Out $end
|
||||||
|
$var wire 1 ) SPI_Data_Available $end
|
||||||
|
$var wire 1 * RegMap_In $end
|
||||||
|
$var wire 1 + RegMap_Out $end
|
||||||
|
$var wire 1 , RegMap_Data_Available $end
|
||||||
|
$var wire 8 - AddrBus [7:0] $end
|
||||||
|
$var wire 8 . DataBus [7:0] $end
|
||||||
|
|
||||||
|
$scope module controller $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 " LED $end
|
||||||
|
$var wire 8 . DataBus [7:0] $end
|
||||||
|
$var wire 1 ) SPI_Data_Available $end
|
||||||
|
$var wire 1 , RegMap_Data_available $end
|
||||||
|
$var wire 8 / addr [7:0] $end
|
||||||
|
$var wire 8 0 data [7:0] $end
|
||||||
|
$var wire 1 1 BusActive $end
|
||||||
|
$var reg 8 2 AddrBus [7:0] $end
|
||||||
|
$var reg 1 3 SPI_In $end
|
||||||
|
$var reg 1 4 SPI_Out $end
|
||||||
|
$var reg 1 5 RegMap_In $end
|
||||||
|
$var reg 1 6 RegMap_Out $end
|
||||||
|
$var reg 1 7 LED_state $end
|
||||||
|
$var reg 3 8 block [2:0] $end
|
||||||
|
$var reg 3 9 doing [2:0] $end
|
||||||
|
$var parameter 3 : IDLE $end
|
||||||
|
$var parameter 3 ; READ_ADDR $end
|
||||||
|
$var parameter 3 < READ_DATA $end
|
||||||
|
$var parameter 3 = TX $end
|
||||||
|
$var parameter 3 > SPI $end
|
||||||
|
$upscope $end
|
||||||
|
|
||||||
|
|
||||||
|
$scope module reg_mag_i $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 + RegMap_Out $end
|
||||||
|
$var wire 1 * RegMap_In $end
|
||||||
|
$var wire 8 - AddrBus [7:0] $end
|
||||||
|
$var wire 8 . DataBus [7:0] $end
|
||||||
|
$var wire 1 ? r_w $end
|
||||||
|
$var wire 1 @ outputData $end
|
||||||
|
$var wire 1 A inputData $end
|
||||||
|
$var reg 1 B RegMap_Data_Available $end
|
||||||
|
$var reg 8 C inData [7:0] $end
|
||||||
|
$var reg 8 D inAddr [7:0] $end
|
||||||
|
$var reg 8 E outData [7:0] $end
|
||||||
|
$var reg 1 F addr_rcv $end
|
||||||
|
$var reg 1 G data_rcv $end
|
||||||
|
$var reg 2 H state [1:0] $end
|
||||||
|
$var parameter 2 I INIT $end
|
||||||
|
$var parameter 2 J IDLE $end
|
||||||
|
$var parameter 2 K RX $end
|
||||||
|
$var parameter 2 L TX $end
|
||||||
|
$var parameter 32 M MAXADDRESS $end
|
||||||
|
$upscope $end
|
||||||
|
|
||||||
|
|
||||||
|
$scope module SPI_i $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 # SCK $end
|
||||||
|
$var wire 1 $ SSEL $end
|
||||||
|
$var wire 1 % MOSI $end
|
||||||
|
$var wire 1 & MISO $end
|
||||||
|
$var wire 1 ' SPI_In $end
|
||||||
|
$var wire 1 ( SPI_Out $end
|
||||||
|
$var wire 8 - AddrBus [7:0] $end
|
||||||
|
$var wire 8 . DataBus [7:0] $end
|
||||||
|
$var wire 1 N SCK_risingedge $end
|
||||||
|
$var wire 1 O SCK_fallingedge $end
|
||||||
|
$var wire 1 P SSEL_active $end
|
||||||
|
$var wire 1 Q MOSI_data $end
|
||||||
|
$var reg 1 R SPI_Data_Available $end
|
Loading…
Reference in a new issue