FEATURE: Error message improvements.

Selector path lookup errors render better.
We also include the actual file name we are parsing now.
This commit is contained in:
Jeremy Wall 2018-05-30 22:07:25 -05:00
parent e926bdd733
commit c008d689a1
5 changed files with 101 additions and 28 deletions

View File

@ -25,6 +25,8 @@ use std::convert::Into;
use std::hash::Hash;
use std::hash::Hasher;
use std::fmt;
macro_rules! enum_type_equality {
( $slf:ident, $r:expr, $( $l:pat ),* ) => {
match $slf {
@ -267,7 +269,7 @@ macro_rules! make_selector {
/// let berry = {best = "strawberry", unique = "acai"}.best;
/// let third = ["uno", "dos", "tres"].1;
/// '''
#[derive(Debug, PartialEq, Clone)]
#[derive(PartialEq, Clone)]
pub struct SelectorList {
pub head: Box<Expression>,
pub tail: Option<Vec<Token>>,
@ -280,6 +282,24 @@ impl SelectorList {
}
}
impl fmt::Debug for SelectorList {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!(w, "Selector({})", self)
}
}
impl fmt::Display for SelectorList {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
try!(write!(w, "{}", self.head));
if let Some(ref tok_vec) = self.tail {
for t in tok_vec.iter() {
try!(write!(w, ".{}", t.fragment));
}
}
return Ok(());
}
}
/// An ordered list of Name = Value pairs.
///
/// This is usually used as the body of a tuple in the UCG AST.
@ -709,6 +729,44 @@ impl Expression {
}
}
impl fmt::Display for Expression {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
match self {
&Expression::Simple(ref v) => {
try!(write!(w, "{}", v.to_string()));
}
&Expression::Binary(_) => {
try!(write!(w, "<Expr>"));
}
&Expression::Compare(_) => {
try!(write!(w, "<Expr>"));
}
&Expression::ListOp(_) => {
try!(write!(w, "<Expr>"));
}
&Expression::Copy(_) => {
try!(write!(w, "<Copy>"));
}
&Expression::Grouped(_) => {
try!(write!(w, "(<Expr>)"));
}
&Expression::Format(_) => {
try!(write!(w, "<Format Expr>"));
}
&Expression::Call(_) => {
try!(write!(w, "<MacroCall>"));
}
&Expression::Macro(_) => {
try!(write!(w, "<Macro>"));
}
&Expression::Select(_) => {
try!(write!(w, "<Select>"));
}
}
Ok(())
}
}
/// Encodes a let statement in the UCG AST.
#[derive(Debug, PartialEq)]
pub struct LetDef {

View File

@ -540,16 +540,15 @@ impl Builder {
next: (&Position, &str),
fs: &Vec<(Positioned<String>, Rc<Val>)>,
) -> Result<(), Box<Error>> {
// This unwrap is safe because we already checked for
// Tuple in the pattern match.
if let Some(vv) = Self::find_in_fieldlist(next.1, fs) {
stack.push_back(vv.clone());
} else {
return Err(Box::new(error::Error::new(
format!(
"Unable to \
match selector \
path {:?} in file: {}",
match element {} in selector \
path [{}] in file: {}",
next.1,
sl,
self.root.to_string_lossy(),
),
@ -574,8 +573,9 @@ impl Builder {
return Err(Box::new(error::Error::new(
format!(
"Unable to \
match selector \
path {:?} in file: {}",
match element {} in selector \
path [{}] in file: {}",
next.1,
sl,
self.root.to_string_lossy(),
),
@ -597,8 +597,7 @@ impl Builder {
&Val::List(_) => {
stack.push_back(first.clone());
}
val => {
eprintln!("Not a tuple or list! {:?}", val)
_ => {
// noop
}
}

View File

@ -92,25 +92,29 @@ impl Error {
_ => Self::new(msg, t, pos),
}
}
fn render(&self, w: &mut fmt::Formatter) -> fmt::Result {
try!(write!(
w,
"{}: \"{}\" at line: {} column: {}",
self.err_type, self.msg, self.pos.line, self.pos.column
));
if let Some(ref cause) = self.cause {
try!(write!(w, "\n\tCaused By: {}", cause));
}
Ok(())
}
}
impl fmt::Debug for Error {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!(
w,
"{}: \"{}\" at line: {} column: {}",
self.err_type, self.msg, self.pos.line, self.pos.column
)
self.render(w)
}
}
impl fmt::Display for Error {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!(
w,
"{}: \"{}\" at line: {} column: {}",
self.err_type, self.msg, self.pos.line, self.pos.column
)
self.render(w)
}
}

View File

@ -17,6 +17,7 @@ extern crate ucglib;
use std::fs::File;
use std::io;
use std::path::PathBuf;
use std::process;
use std::rc::Rc;
@ -60,12 +61,13 @@ fn main() {
let out = matches.value_of("out");
let sym = matches.value_of("sym");
let target = matches.value_of("target").unwrap();
let mut builder = build::Builder::new(std::env::current_dir().unwrap());
let root = PathBuf::from(file);
let mut builder = build::Builder::new(root);
match ConverterRunner::new(target) {
Ok(converter) => {
let result = builder.build_file(file);
if !result.is_ok() {
eprintln!("{:?}", result.err());
eprintln!("{:?}", result.err().unwrap());
process::exit(1);
}
let val = match sym {

View File

@ -119,6 +119,16 @@ macro_rules! do_tag_tok {
// rewrite your macro argumets for you by adding an initial argument
// for all their sub-macros. Which means we require this $i paramater
// on the first macro invocation but not the rest.
($i:expr, $type:expr, $tag:expr,WS) => {
do_parse!(
$i,
span: position!() >> frag: tag!($tag) >> alt!(whitespace | comment) >> (Token {
typ: $type,
pos: Position::from(span),
fragment: frag.fragment.to_string(),
})
)
};
($i:expr, $type:expr, $tag:expr) => {
do_parse!(
$i,
@ -224,31 +234,31 @@ named!(fatcommatok( Span ) -> Token,
);
named!(lettok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "let")
do_tag_tok!(TokenType::BAREWORD, "let", WS)
);
named!(selecttok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "select")
do_tag_tok!(TokenType::BAREWORD, "select", WS)
);
named!(macrotok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "macro")
do_tag_tok!(TokenType::BAREWORD, "macro", WS)
);
named!(importtok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "import")
do_tag_tok!(TokenType::BAREWORD, "import", WS)
);
named!(astok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "as")
do_tag_tok!(TokenType::BAREWORD, "as", WS)
);
named!(maptok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "map")
do_tag_tok!(TokenType::BAREWORD, "map", WS)
);
named!(filtertok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "filter")
do_tag_tok!(TokenType::BAREWORD, "filter", WS)
);
fn end_of_input(input: Span) -> nom::IResult<Span, Token> {