trying to get filename and linenumber

This commit is contained in:
Yehowshua Immanuel 2022-08-04 13:19:52 -04:00
parent fa25bad391
commit ba9208277b
7 changed files with 200 additions and 83 deletions

View file

@ -9,4 +9,5 @@ edition = "2021"
num = "0.4"
clap = { version = "3.1.8", features = ["derive"] }
chrono = "0.4"
itertools = "0.10.3"
itertools = "0.10.3"
backtrace = "0.3"

View file

@ -76,9 +76,7 @@ pub(super) fn ident(
) -> Result<(), String> {
// let keyword = "module";
let err : Result<(), String> = Err(format!("reached end of file without parser leaving ident"));
let word = word_reader.next_word();
let (word, cursor) = word.ok_or(err).unwrap();
let (word, cursor) = word_reader.next_word()?;
if word == keyword {
return Ok(())

View file

@ -84,9 +84,10 @@ pub(super) fn parse_events<'a>(
loop {
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...
if next_word.is_none() {break};
if next_word.is_err() {break};
let (word, cursor) = next_word.unwrap();
let Cursor(Line(_), Word(word_in_line_idx)) = cursor;

View file

@ -17,8 +17,7 @@ pub(super) fn parse_date(
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
if !days.contains(&word) {
let msg = format!("Error near {}:{}. Reached end of file without \
terminating parser", 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))
@ -38,8 +37,7 @@ pub(super) fn parse_date(
];
if !months.contains(&word) {
let msg = format!("Error near {}:{}. Reached end of file without \
terminating parser", 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))
@ -54,12 +52,11 @@ pub(super) fn parse_date(
let date : u8 = match word.to_string().parse() {
Ok(date) => date,
Err(_) => {return Err("".to_string())}
Err(e) => {return Err(format!("Error near {}:{}. {e}", file!(), line!()))}
};
if date > 31 {
let msg = format!("Error near {}:{}. Reached end of file without \
terminating parser", 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))
@ -77,7 +74,7 @@ pub(super) fn parse_date(
res.assert_match()?;
let hh : u8 = res.matched.to_string()
.parse()
.map_err(|_| "failed to parse".to_string())?;
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
if hh > 23 {
let msg = format!("Error near {}:{}.", file!(), line!());
@ -92,7 +89,7 @@ pub(super) fn parse_date(
res.assert_match()?;
let mm : u8 = res.matched.to_string()
.parse()
.map_err(|_| "failed to parse".to_string())?;
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
if mm > 60 {
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 ss : u8 = residual.to_string()
.parse()
.map_err(|_| "failed to parse".to_string())?;
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
if ss > 60 {
let msg = format!("Error near {}:{}.", file!(), line!());
@ -132,7 +129,7 @@ pub(super) fn parse_date(
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();
loop {
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();
let (word, _) = word_reader.next_word()?;
if word == "$end" {
// 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> {
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
let (word, _) = word_reader.next_word().ok_or(&err_msg)?;
let (word, _) = word_reader.next_word()?;
let word = word.to_string();
let ParseResult{matched, residual} = take_while(word.as_str(), digit);
let scalar = matched;
let scalar : u32 = scalar.to_string().parse()
.map_err(|_| &err_msg)?;
.map_err(|e| format!("Error near {}:{}. {e}", file!(), line!()))?;
let timescale = {
if residual == "" {
let (word, _) = word_reader.next_word().ok_or(&err_msg)?;
let (word, _) = word_reader.next_word()?;
let unit = match word {
"fs" => {Ok(Timescale::Fs)}
"ps" => {Ok(Timescale::Ps)}
@ -189,19 +174,20 @@ pub(super) fn parse_timescale(word_reader : &mut WordReader) -> Result<(Option<u
"us" => {Ok(Timescale::Us)}
"ms" => {Ok(Timescale::Ms)}
"s" => {Ok(Timescale::S)}
_ => {Err(err_msg.to_string())}
_ => {Err(format!("Error near {}:{}. Unknown unit {word}.", file!(), line!()))}
}.unwrap();
(Some(scalar), unit)
}
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(err_msg.to_string())}
_ => {Err(format!("Error near {}:{}. Unknown unit {residual}.", file!(), line!()))}
}.unwrap();
(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
let (end, _) = word_reader.next_word().ok_or(&err_msg)?;
let (end, _) = word_reader.next_word()?;
tag(end, "$end").assert_match()?;
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> {
let err_msg = format!("Error near {}:{}. No more words left in vcd file.",
file!(), line!());
let mut metadata = Metadata {
date : None,
@ -228,7 +212,7 @@ pub(super) fn parse_metadata(word_reader : &mut WordReader) -> Result<Metadata,
loop {
// 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, "$");
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();
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();
match word.as_str() {
"$end" => {

View file

@ -8,8 +8,7 @@ pub(super) fn parse_var<'a>(
vcd : &'a mut VCD,
signal_map : &mut HashMap<String, SignalIdx>
) -> Result<(), String> {
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
let (word, cursor) = word_reader.next_word()?;
let expected_types = ["integer", "parameter", "real", "reg", "string", "wire", "tri1", "time"];
// $var parameter 3 a IDLE $end
@ -24,12 +23,14 @@ pub(super) fn parse_var<'a>(
"tri1" => {Ok(SigType::Tri1)}
"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)
}
}?;
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:?}");
// $var parameter 3 a IDLE $end
@ -48,14 +49,14 @@ pub(super) fn parse_var<'a>(
// $var parameter 3 a IDLE $end
// ^ - signal_alias
let (word, _) = word_reader.next_word().ok_or(&err)?;
let (word, _) = word_reader.next_word()?;
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::<String>::new();
loop {
let (word, _) = word_reader.next_word().ok_or(&err)?;
let (word, _) = word_reader.next_word()?;
match word {
"$end" => {break}
_ => {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)?;
loop {
let next_word = 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();
let (word, cursor) = word_reader.next_word()?;
match word {
"$var" => {
@ -161,9 +154,9 @@ fn parse_orphaned_vars<'a>(
}
"$scope" => {break}
_ => {
let (f, l )= (file!(), line!());
let msg = format!("Error near {f}:{l}.\
Expected $scope or $var, found {word} at {cursor:?}");
let msg = format!("Error near {}:{}.\
Expected $scope or $var, found \
{word} at {cursor:?}", file!(), line!());
Err(msg)?;
}
};
@ -181,20 +174,21 @@ pub(super) fn parse_signal_tree<'a>(
// $scope module reg_mag_i $end
// ^^^^^^ - module keyword
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
let (keyword, cursor) = word_reader.next_word().ok_or(&err)?;
let (keyword, cursor) = word_reader.next_word()?;
let expected = ["module", "begin", "task", "function"];
if expected.contains(&keyword) {
Ok(())
} 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)
}?;
// $scope module reg_mag_i $end
// ^^^^^^^^^ - 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());
@ -227,7 +221,7 @@ pub(super) fn parse_signal_tree<'a>(
ident(word_reader, "$end")?;
loop {
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
let (word, cursor) = word_reader.next_word()?;
let ParseResult{matched, residual} = tag(word, "$");
match matched {
// 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)
}
}
}
_ => {
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)
}
}
@ -272,8 +271,7 @@ pub(super) fn parse_scopes<'a>(
signal_map : &mut HashMap<String, SignalIdx>
) -> Result<(), String> {
// get the current word
let err = format!("Error near {}:{}. No more words left in vcd file.", file!(), line!());
let (word, _) = word_reader.curr_word().ok_or(&err)?;
let (word, _) = word_reader.curr_word()?;
// we may have orphaned vars that occur before the first scope
if word == "$var" {
@ -281,16 +279,16 @@ pub(super) fn parse_scopes<'a>(
}
// 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
// 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 (f, l )= (file!(), line!());
let msg = format!("Error near {f}:{l}.\
Expected $scope or $var, found {word} at {cursor:?}");
let msg = format!("Error near {}:{}.\
Expected $scope or $var, found \
{word} at {cursor:?}", file!(), line!());
return Err(msg)
}
@ -305,7 +303,7 @@ pub(super) fn parse_scopes<'a>(
// because this loop gets a word from `next_word` instead of
// `curr_word()`.
loop {
let (word, cursor) = word_reader.next_word().ok_or(&err)?;
let (word, cursor) = word_reader.next_word()?;
match word {
"$scope" => {
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 \
of `{expected_keywords:?}` on {cursor:?}");
let err = format!("Error near {}:{} \
found keyword `{word}` but expected one of \
{expected_keywords:?} on {cursor:?}", file!(), line!());
return Err(err)
}

View file

@ -5,12 +5,16 @@ use std::str;
use std::io::prelude::*;
use std::io;
use backtrace::{ Backtrace, BacktraceFrame, BacktraceSymbol };
#[derive(Debug, Clone)]
pub(super) struct Line(pub(super) usize);
#[derive(Debug, Clone)]
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 {
reader : io::BufReader<File>,
@ -21,6 +25,7 @@ pub struct WordReader {
curr_slice : Option<(*const u8, usize, Cursor)>,
}
impl WordReader {
pub(super) fn new(file : File) -> WordReader {
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
if self.str_slices.is_empty() {
self.buffers.clear();
if self.eof {return None}
if self.eof {return Err(FileStatus::Eof)}
let num_buffers = 10;
@ -70,7 +80,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 None
return Err(FileStatus::Eof)
}
// 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 slice = slice::from_raw_parts(ptr, len);
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 {
Some(slice) => {
unsafe {
let (ptr, len, position) = slice.clone();
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."),
}
}
}

View 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