Merge branch 'parse_error_position'

This commit is contained in:
dalance 2019-10-16 17:08:22 +09:00
commit 675c11e5c7
12 changed files with 166 additions and 25 deletions

View File

@ -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

View File

@ -14,8 +14,8 @@ pub enum ErrorKind {
File(PathBuf),
#[fail(display = "Include error")]
Include,
#[fail(display = "Parse error")]
Parse,
#[fail(display = "Parse error: {:?}", _0)]
Parse(Option<(PathBuf, usize)>),
#[fail(display = "Preprocess error")]
Preprocess,
#[fail(display = "Define argument not found: {}", _0)]

View File

@ -20,9 +20,10 @@ trace = ["nom-tracable/trace"]
[dependencies]
nom = "5.0.0"
nom_locate = "1.0.0"
nom-greedyerror = "0.1.0"
nom-packrat = "0.3.0"
nom-recursive = {version = "0.1.1", features = ["tracer128"]}
nom-tracable = "0.4.0"
nom-tracable = "0.4.1"
str-concat = "0.1.4"
sv-parser-macros = {version = "0.1.0", path = "../sv-parser-macros"}
sv-parser-syntaxtree = {version = "0.1.0", path = "../sv-parser-syntaxtree"}

View File

@ -31,10 +31,12 @@ pub(crate) use nom::branch::*;
pub(crate) use nom::bytes::complete::*;
pub(crate) use nom::character::complete::*;
pub(crate) use nom::combinator::*;
pub(crate) use nom::error::{make_error, ErrorKind};
pub(crate) use nom::error::{context, make_error, ErrorKind};
pub(crate) use nom::multi::*;
pub(crate) use nom::sequence::*;
pub(crate) use nom::{Err, IResult};
//pub(crate) use nom::{Err, IResult};
pub(crate) use nom::Err;
pub(crate) use nom_greedyerror::GreedyError;
pub(crate) use nom_packrat::{self, packrat_parser, HasExtraState};
pub(crate) use nom_recursive::{recursive_parser, HasRecursiveInfo, RecursiveInfo};
pub(crate) use nom_tracable::tracable_parser;
@ -52,6 +54,7 @@ pub struct SpanInfo {
}
pub type Span<'a> = nom_locate::LocatedSpanEx<&'a str, SpanInfo>;
pub type IResult<T, U> = nom::IResult<T, U, GreedyError<T>>;
impl HasRecursiveInfo for SpanInfo {
fn get_recursive_info(&self) -> RecursiveInfo {

View File

@ -5,7 +5,7 @@ use crate::*;
#[tracable_parser]
#[packrat_parser]
pub(crate) fn config_declaration(s: Span) -> IResult<Span, ConfigDeclaration> {
let (s, a) = keyword("config")(s)?;
let (s, a) = context("config", keyword("config"))(s)?;
let (s, b) = config_identifier(s)?;
let (s, c) = symbol(";")(s)?;
let (s, d) = many0(pair(local_parameter_declaration, symbol(";")))(s)?;

View File

@ -7,8 +7,8 @@ use crate::*;
pub(crate) fn source_text(s: Span) -> IResult<Span, SourceText> {
let (s, a) = many0(white_space)(s)?;
let (s, b) = opt(timeunits_declaration)(s)?;
let (s, c) = many0(description)(s)?;
Ok((s, SourceText { nodes: (a, b, c) }))
let (s, c) = many_till(description, eof)(s)?;
Ok((s, SourceText { nodes: (a, b, c.0) }))
}
#[tracable_parser]

View File

@ -51,8 +51,8 @@ pub(crate) fn symbol<'a>(t: &'a str) -> impl Fn(Span<'a>) -> IResult<Span<'a>, S
#[cfg(not(feature = "trace"))]
pub(crate) fn symbol_exact<'a>(t: &'a str) -> impl Fn(Span<'a>) -> IResult<Span<'a>, Symbol> {
move |s: Span<'a>| {
let (s, x) = map(no_ws(map(tag(t.clone()), into_locate)), |x| {
Symbol { nodes: x }
let (s, x) = map(no_ws(map(tag(t.clone()), into_locate)), |x| Symbol {
nodes: x,
})(s)?;
Ok((s, x))
}
@ -63,8 +63,8 @@ pub(crate) fn symbol_exact<'a>(t: &'a str) -> impl Fn(Span<'a>) -> IResult<Span<
move |s: Span<'a>| {
let (depth, s) = nom_tracable::forward_trace(s, &format!("symbol(\"{}\")", t));
let body = || {
let (s, x) = map(no_ws(map(tag(t.clone()), into_locate)), |x| {
Symbol { nodes: x }
let (s, x) = map(no_ws(map(tag(t.clone()), into_locate)), |x| Symbol {
nodes: x,
})(s)?;
Ok((s, x))
};
@ -79,10 +79,7 @@ pub(crate) fn keyword<'a>(t: &'a str) -> impl Fn(Span<'a>) -> IResult<Span<'a>,
let (s, x) = map(
ws(alt((
all_consuming(map(tag(t.clone()), into_locate)),
terminated(
map(tag(t.clone()), into_locate),
peek(none_of(AZ09_)),
),
terminated(map(tag(t.clone()), into_locate), peek(none_of(AZ09_))),
))),
|x| Keyword { nodes: x },
)(s)?;
@ -98,10 +95,7 @@ pub(crate) fn keyword<'a>(t: &'a str) -> impl Fn(Span<'a>) -> IResult<Span<'a>,
let (s, x) = map(
ws(alt((
all_consuming(map(tag(t.clone()), into_locate)),
terminated(
map(tag(t.clone()), into_locate),
peek(none_of(AZ09_)),
),
terminated(map(tag(t.clone()), into_locate), peek(none_of(AZ09_))),
))),
|x| Keyword { nodes: x },
)(s)?;
@ -315,6 +309,16 @@ where
// -----------------------------------------------------------------------------
pub(crate) fn eof(s: Span) -> IResult<Span, Span> {
use nom::InputLength;
if s.input_len() == 0 {
Ok((s, s))
} else {
Err(Err::Error(make_error(s, ErrorKind::Eof)))
}
}
#[tracable_parser]
#[packrat_parser]
pub(crate) fn white_space(s: Span) -> IResult<Span, WhiteSpace> {

View File

@ -20,6 +20,7 @@ trace = ["sv-parser-parser/trace"]
[dependencies]
failure = "0.1.5"
nom = "5.0.0"
nom-greedyerror = "0.1.0"
sv-parser-error = {version = "0.1.0", path = "../sv-parser-error"}
sv-parser-parser = {version = "0.1.0", path = "../sv-parser-parser"}
sv-parser-syntaxtree = {version = "0.1.0", path = "../sv-parser-syntaxtree"}

View File

@ -1,5 +1,6 @@
use crate::range::Range;
use failure::ResultExt;
use nom_greedyerror::error_position;
use std::collections::{BTreeMap, HashMap};
use std::convert::TryInto;
use std::fs::File;
@ -107,7 +108,23 @@ fn preprocess_str<T: AsRef<Path>, U: AsRef<Path>>(
}
let span = Span::new_extra(&s, SpanInfo::default());
let (_, pp_text) = pp_parser(span).map_err(|_| ErrorKind::Parse)?;
let (_, pp_text) = pp_parser(span).map_err(|x| match x {
nom::Err::Incomplete(_) => ErrorKind::Parse(None),
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();

View File

@ -14,8 +14,17 @@ 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"
sv-parser-error = {version = "0.1.0", path = "../sv-parser-error"}
sv-parser-parser = {version = "0.1.0", path = "../sv-parser-parser"}
sv-parser-pp = {version = "0.1.0", path = "../sv-parser-pp"}

View File

@ -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) => {
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)
);
}
}
}

View File

@ -1,6 +1,7 @@
#![recursion_limit = "256"]
use nom::combinator::all_consuming;
use nom_greedyerror::error_position;
use std::collections::HashMap;
use std::fmt;
use std::path::Path;
@ -79,7 +80,23 @@ pub fn parse_sv<T: AsRef<Path>, U: AsRef<Path>>(
},
defines,
)),
Err(_) => Err(ErrorKind::Parse.into()),
Err(x) => {
let pos = match x {
nom::Err::Incomplete(_) => None,
nom::Err::Error(e) => error_position(&e),
nom::Err::Failure(e) => error_position(&e),
};
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())
}
}
}
@ -99,7 +116,23 @@ pub fn parse_lib<T: AsRef<Path>, U: AsRef<Path>>(
},
defines,
)),
Err(_) => Err(ErrorKind::Parse.into()),
Err(x) => {
let pos = match x {
nom::Err::Incomplete(_) => None,
nom::Err::Error(e) => error_position(&e),
nom::Err::Failure(e) => error_position(&e),
};
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())
}
}
}