diff --git a/CHANGELOG.md b/CHANGELOG.md index d86b2af..41e0707 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased](https://github.com/dalance/sv-parser/compare/v0.1.4...Unreleased) - ReleaseDate +* [Added] parse error position * [Fixed] spacing rule aroung text_macro_identifier * [Fixed] cond_predicate in cond_predicate * [Fixed] fixed_number priority in delay_value diff --git a/sv-parser-error/src/lib.rs b/sv-parser-error/src/lib.rs index d410666..8808ca5 100644 --- a/sv-parser-error/src/lib.rs +++ b/sv-parser-error/src/lib.rs @@ -15,7 +15,7 @@ pub enum ErrorKind { #[fail(display = "Include error")] Include, #[fail(display = "Parse error: {:?}", _0)] - Parse(Option), + Parse(Option<(PathBuf, usize)>), #[fail(display = "Preprocess error")] Preprocess, #[fail(display = "Define argument not found: {}", _0)] diff --git a/sv-parser-pp/src/preprocess.rs b/sv-parser-pp/src/preprocess.rs index 646f30b..72ace97 100644 --- a/sv-parser-pp/src/preprocess.rs +++ b/sv-parser-pp/src/preprocess.rs @@ -110,8 +110,20 @@ fn preprocess_str, U: AsRef>( let span = Span::new_extra(&s, SpanInfo::default()); let (_, pp_text) = pp_parser(span).map_err(|x| match x { nom::Err::Incomplete(_) => ErrorKind::Parse(None), - nom::Err::Error(e) => ErrorKind::Parse(error_position(&e)), - nom::Err::Failure(e) => ErrorKind::Parse(error_position(&e)), + nom::Err::Error(e) => { + if let Some(pos) = error_position(&e) { + ErrorKind::Parse(Some((PathBuf::from(path.as_ref()), pos))) + } else { + ErrorKind::Parse(None) + } + } + nom::Err::Failure(e) => { + if let Some(pos) = error_position(&e) { + ErrorKind::Parse(Some((PathBuf::from(path.as_ref()), pos))) + } else { + ErrorKind::Parse(None) + } + } })?; let mut ret = PreprocessedText::new(); diff --git a/sv-parser/Cargo.toml b/sv-parser/Cargo.toml index 3d0c4ce..a9c1a60 100644 --- a/sv-parser/Cargo.toml +++ b/sv-parser/Cargo.toml @@ -14,6 +14,14 @@ edition = "2018" default = [] trace = ["sv-parser-parser/trace"] +[package.metadata.release] +pre-release-replacements = [ + {file = "../README.md", search = "sv-parser = \"[a-z0-9\\.-]+\"", replace = "sv-parser = \"{{version}}\""}, + {file = "../CHANGELOG.md", search = "Unreleased", replace = "v{{version}}"}, + {file = "../CHANGELOG.md", search = "ReleaseDate", replace = "{{date}}"}, + {file = "../CHANGELOG.md", search = "Change Log", replace = "Change Log\n\n## [Unreleased](https://github.com/dalance/sv-parser/compare/v{{version}}...Unreleased) - ReleaseDate"}, +] + [dependencies] nom = "5.0.0" nom-greedyerror = "0.1.0" diff --git a/sv-parser/examples/parse_sv.rs b/sv-parser/examples/parse_sv.rs index 9a55c82..4926bf8 100644 --- a/sv-parser/examples/parse_sv.rs +++ b/sv-parser/examples/parse_sv.rs @@ -1,8 +1,11 @@ use std::collections::HashMap; +use std::fs::File; +use std::io::Read; use std::path::PathBuf; -use std::process; +use std::{cmp, process}; use structopt::StructOpt; use sv_parser::parse_sv; +use sv_parser_error::ErrorKind; #[derive(StructOpt)] struct Opt { @@ -37,10 +40,79 @@ fn main() { } } Err(x) => { - println!("parse failed: {:?} ({})", path, x); + match x.kind() { + ErrorKind::Parse(Some((origin_path, origin_pos))) => { + println!("parse failed: {:?}", path); + print_parse_error(origin_path, origin_pos); + } + x => { + println!("parse failed: {:?} ({})", path, x); + } + } exit = 1; } } } process::exit(exit); } + +static CHAR_CR: u8 = 0x0d; +static CHAR_LF: u8 = 0x0a; + +fn print_parse_error(origin_path: &PathBuf, origin_pos: &usize) { + let mut f = File::open(&origin_path).unwrap(); + let mut s = String::new(); + let _ = f.read_to_string(&mut s); + + let mut pos = 0; + let mut column = 1; + let mut last_lf = None; + while pos < s.len() { + if s.as_bytes()[pos] == CHAR_LF { + column += 1; + last_lf = Some(pos); + } + pos += 1; + + if *origin_pos == pos { + let row = if let Some(last_lf) = last_lf { + pos - last_lf + } else { + pos + 1 + }; + let mut next_crlf = pos; + while next_crlf < s.len() { + if s.as_bytes()[next_crlf] == CHAR_CR || s.as_bytes()[next_crlf] == CHAR_LF { + break; + } + next_crlf += 1; + } + + let column_len = format!("{}", column).len(); + + print!(" {}:{}:{}\n", origin_path.to_string_lossy(), column, row); + + print!("{}|\n", " ".repeat(column_len + 1)); + + print!("{} |", column); + + let beg = if let Some(last_lf) = last_lf { + last_lf + 1 + } else { + 0 + }; + print!( + " {}\n", + String::from_utf8_lossy(&s.as_bytes()[beg..next_crlf]) + ); + + print!("{}|", " ".repeat(column_len + 1)); + + print!( + " {}{}\n", + " ".repeat(pos - beg), + "^".repeat(cmp::min(origin_pos + 1, next_crlf) - origin_pos) + ); + } + } +} diff --git a/sv-parser/src/lib.rs b/sv-parser/src/lib.rs index 237a260..53d6dc5 100644 --- a/sv-parser/src/lib.rs +++ b/sv-parser/src/lib.rs @@ -86,7 +86,16 @@ pub fn parse_sv, U: AsRef>( nom::Err::Error(e) => error_position(&e), nom::Err::Failure(e) => error_position(&e), }; - Err(ErrorKind::Parse(pos).into()) + let origin = if let Some(pos) = pos { + if let Some(origin) = text.origin(pos) { + Some((origin.0.clone(), origin.1)) + } else { + None + } + } else { + None + }; + Err(ErrorKind::Parse(origin).into()) } } } @@ -113,7 +122,16 @@ pub fn parse_lib, U: AsRef>( nom::Err::Error(e) => error_position(&e), nom::Err::Failure(e) => error_position(&e), }; - Err(ErrorKind::Parse(pos).into()) + let origin = if let Some(pos) = pos { + if let Some(origin) = text.origin(pos) { + Some((origin.0.clone(), origin.1)) + } else { + None + } + } else { + None + }; + Err(ErrorKind::Parse(origin).into()) } } }