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::Hash;
use std::hash::Hasher; use std::hash::Hasher;
use std::fmt;
macro_rules! enum_type_equality { macro_rules! enum_type_equality {
( $slf:ident, $r:expr, $( $l:pat ),* ) => { ( $slf:ident, $r:expr, $( $l:pat ),* ) => {
match $slf { match $slf {
@ -267,7 +269,7 @@ macro_rules! make_selector {
/// let berry = {best = "strawberry", unique = "acai"}.best; /// let berry = {best = "strawberry", unique = "acai"}.best;
/// let third = ["uno", "dos", "tres"].1; /// let third = ["uno", "dos", "tres"].1;
/// ''' /// '''
#[derive(Debug, PartialEq, Clone)] #[derive(PartialEq, Clone)]
pub struct SelectorList { pub struct SelectorList {
pub head: Box<Expression>, pub head: Box<Expression>,
pub tail: Option<Vec<Token>>, 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. /// An ordered list of Name = Value pairs.
/// ///
/// This is usually used as the body of a tuple in the UCG AST. /// 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. /// Encodes a let statement in the UCG AST.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct LetDef { pub struct LetDef {

View File

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

View File

@ -92,25 +92,29 @@ impl Error {
_ => Self::new(msg, t, pos), _ => 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 { impl fmt::Debug for Error {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!( self.render(w)
w,
"{}: \"{}\" at line: {} column: {}",
self.err_type, self.msg, self.pos.line, self.pos.column
)
} }
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!( self.render(w)
w,
"{}: \"{}\" at line: {} column: {}",
self.err_type, self.msg, self.pos.line, self.pos.column
)
} }
} }

View File

@ -17,6 +17,7 @@ extern crate ucglib;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::path::PathBuf;
use std::process; use std::process;
use std::rc::Rc; use std::rc::Rc;
@ -60,12 +61,13 @@ fn main() {
let out = matches.value_of("out"); let out = matches.value_of("out");
let sym = matches.value_of("sym"); let sym = matches.value_of("sym");
let target = matches.value_of("target").unwrap(); 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) { match ConverterRunner::new(target) {
Ok(converter) => { Ok(converter) => {
let result = builder.build_file(file); let result = builder.build_file(file);
if !result.is_ok() { if !result.is_ok() {
eprintln!("{:?}", result.err()); eprintln!("{:?}", result.err().unwrap());
process::exit(1); process::exit(1);
} }
let val = match sym { 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 // rewrite your macro argumets for you by adding an initial argument
// for all their sub-macros. Which means we require this $i paramater // for all their sub-macros. Which means we require this $i paramater
// on the first macro invocation but not the rest. // 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) => { ($i:expr, $type:expr, $tag:expr) => {
do_parse!( do_parse!(
$i, $i,
@ -224,31 +234,31 @@ named!(fatcommatok( Span ) -> Token,
); );
named!(lettok( Span ) -> Token, named!(lettok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "let") do_tag_tok!(TokenType::BAREWORD, "let", WS)
); );
named!(selecttok( Span ) -> Token, named!(selecttok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "select") do_tag_tok!(TokenType::BAREWORD, "select", WS)
); );
named!(macrotok( Span ) -> Token, named!(macrotok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "macro") do_tag_tok!(TokenType::BAREWORD, "macro", WS)
); );
named!(importtok( Span ) -> Token, named!(importtok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "import") do_tag_tok!(TokenType::BAREWORD, "import", WS)
); );
named!(astok( Span ) -> Token, named!(astok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "as") do_tag_tok!(TokenType::BAREWORD, "as", WS)
); );
named!(maptok( Span ) -> Token, named!(maptok( Span ) -> Token,
do_tag_tok!(TokenType::BAREWORD, "map") do_tag_tok!(TokenType::BAREWORD, "map", WS)
); );
named!(filtertok( Span ) -> Token, 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> { fn end_of_input(input: Span) -> nom::IResult<Span, Token> {