mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-23 18:29:50 -04:00
Feature: Better error reporting.
* Common Error Type with line and column reporting. * Removed some todos.
This commit is contained in:
parent
3acf8e4679
commit
7a0a194fb9
@ -11,7 +11,6 @@ license = "Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nom = "^3.2"
|
nom = "^3.2"
|
||||||
nom_locate = "^0.1.1"
|
nom_locate = "^0.1.1"
|
||||||
quick-error = "^1.2.0"
|
|
||||||
clap = "~2.26.0"
|
clap = "~2.26.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
213
src/build.rs
213
src/build.rs
@ -28,6 +28,7 @@ use tokenizer::Span;
|
|||||||
use ast::*;
|
use ast::*;
|
||||||
use format;
|
use format;
|
||||||
use parse::parse;
|
use parse::parse;
|
||||||
|
use error;
|
||||||
|
|
||||||
impl MacroDef {
|
impl MacroDef {
|
||||||
pub fn eval(&self,
|
pub fn eval(&self,
|
||||||
@ -36,8 +37,9 @@ impl MacroDef {
|
|||||||
// Error conditions. If the args don't match the length and types of the argdefs then this is
|
// Error conditions. If the args don't match the length and types of the argdefs then this is
|
||||||
// macro call error.
|
// macro call error.
|
||||||
if args.len() > self.argdefs.len() {
|
if args.len() > self.argdefs.len() {
|
||||||
return Err(Box::new(BuildError::BadArgLen("Macro called with too many args"
|
return Err(Box::new(error::Error::new("Macro called with too many args",
|
||||||
.to_string())));
|
error::ErrorType::BadArgLen,
|
||||||
|
self.pos.clone())));
|
||||||
}
|
}
|
||||||
// If the args don't match the types required by the expressions then that is a TypeFail.
|
// If the args don't match the types required by the expressions then that is a TypeFail.
|
||||||
// If the expressions reference Symbols not defined in the MacroDef that is also an error.
|
// If the expressions reference Symbols not defined in the MacroDef that is also an error.
|
||||||
@ -59,44 +61,6 @@ impl MacroDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quick_error! {
|
|
||||||
#[derive(Debug,PartialEq)]
|
|
||||||
pub enum BuildError {
|
|
||||||
TypeFail(msg: String) {
|
|
||||||
description("Type Error")
|
|
||||||
display("Type Error {}", msg)
|
|
||||||
}
|
|
||||||
DuplicateBinding(msg: String) {
|
|
||||||
description("Atttempt to add duplicate binding in file")
|
|
||||||
display("Atttempt to add duplicate binding in file {}", msg)
|
|
||||||
}
|
|
||||||
IncompleteParse(msg: String) {
|
|
||||||
description("Incomplete Parse of file")
|
|
||||||
display("Incomplete Parse of file {}", msg)
|
|
||||||
}
|
|
||||||
Unsupported(msg: String) {
|
|
||||||
description("Unsupported Operation")
|
|
||||||
display("Unsupported Operation {}", msg)
|
|
||||||
}
|
|
||||||
NoSuchSymbol(msg: String) {
|
|
||||||
description("Eval Error")
|
|
||||||
display("No Such Variable {}", msg)
|
|
||||||
}
|
|
||||||
BadArgLen(msg: String) {
|
|
||||||
description("Eval Error")
|
|
||||||
display("Bad Argument Length {}", msg)
|
|
||||||
}
|
|
||||||
FormatError(msg: String) {
|
|
||||||
description("String Format Error")
|
|
||||||
display("String format Error {}", msg)
|
|
||||||
}
|
|
||||||
TODO(msg: String) {
|
|
||||||
description("TODO Error")
|
|
||||||
display("TODO Error {}", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// BuildResult is the result of a build.
|
/// BuildResult is the result of a build.
|
||||||
type BuildResult = Result<(), Box<Error>>;
|
type BuildResult = Result<(), Box<Error>>;
|
||||||
|
|
||||||
@ -213,15 +177,15 @@ pub struct Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! eval_binary_expr {
|
macro_rules! eval_binary_expr {
|
||||||
($case:pat, $rside:ident, $result:expr, $msg:expr) => {
|
($case:pat, $pos:ident, $rside:ident, $result:expr, $msg:expr) => {
|
||||||
match $rside.as_ref() {
|
match $rside.as_ref() {
|
||||||
$case => {
|
$case => {
|
||||||
return Ok(Rc::new($result))
|
return Ok(Rc::new($result));
|
||||||
},
|
},
|
||||||
val => {
|
val => {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
BuildError::TypeFail(
|
error::Error::new(
|
||||||
format!("Expected {} but got {}", $msg, val))))
|
format!("Expected {} but got {}", $msg, val), error::ErrorType::TypeFail, $pos.clone())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +217,9 @@ impl Builder {
|
|||||||
&Value::String(ref s) => Ok(Rc::new(Val::String(s.val.to_string()))),
|
&Value::String(ref s) => Ok(Rc::new(Val::String(s.val.to_string()))),
|
||||||
&Value::Symbol(ref s) => {
|
&Value::Symbol(ref s) => {
|
||||||
self.lookup_sym(&(s.into()))
|
self.lookup_sym(&(s.into()))
|
||||||
.ok_or(Box::new(BuildError::NoSuchSymbol(format!("Unable to find {}", s.val))))
|
.ok_or(Box::new(error::Error::new(format!("Unable to find {}", s.val),
|
||||||
|
error::ErrorType::NoSuchSymbol,
|
||||||
|
v.pos().clone())))
|
||||||
}
|
}
|
||||||
&Value::List(ref def) => self.list_to_val(def),
|
&Value::List(ref def) => self.list_to_val(def),
|
||||||
&Value::Tuple(ref tuple) => self.tuple_to_val(&tuple.val),
|
&Value::Tuple(ref tuple) => self.tuple_to_val(&tuple.val),
|
||||||
@ -306,9 +272,11 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
nom::IResult::Error(err) => Err(Box::new(err)),
|
nom::IResult::Error(err) => Err(Box::new(err)),
|
||||||
nom::IResult::Incomplete(_) => {
|
nom::IResult::Incomplete(_) => {
|
||||||
Err(Box::new(BuildError::IncompleteParse(format!("Could not parse input from \
|
Err(Box::new(error::Error::new(
|
||||||
file: {}",
|
format!("Could not parse input from file: {}", name),
|
||||||
name))))
|
error::ErrorType::IncompleteParsing,
|
||||||
|
Position{line: 0, column: 0}
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,10 +313,12 @@ impl Builder {
|
|||||||
let name = &def.name;
|
let name = &def.name;
|
||||||
match self.out.entry(name.into()) {
|
match self.out.entry(name.into()) {
|
||||||
Entry::Occupied(e) => {
|
Entry::Occupied(e) => {
|
||||||
return Err(Box::new(BuildError::DuplicateBinding(format!("Let binding \
|
return Err(Box::new(error::Error::new(format!("Let binding \
|
||||||
for {:?} already \
|
for {:?} already \
|
||||||
exists",
|
exists",
|
||||||
e.key()))));
|
e.key()),
|
||||||
|
error::ErrorType::DuplicateBinding,
|
||||||
|
def.name.pos.clone())));
|
||||||
}
|
}
|
||||||
Entry::Vacant(e) => {
|
Entry::Vacant(e) => {
|
||||||
e.insert(val);
|
e.insert(val);
|
||||||
@ -403,10 +373,12 @@ impl Builder {
|
|||||||
stack.push_back(vv.clone());
|
stack.push_back(vv.clone());
|
||||||
} else {
|
} else {
|
||||||
// TODO(jwall): A better error for this would be nice.
|
// TODO(jwall): A better error for this would be nice.
|
||||||
return Err(Box::new(BuildError::NoSuchSymbol(format!("Unable to \
|
return Err(Box::new(error::Error::new(format!("Unable to \
|
||||||
match selector \
|
match selector \
|
||||||
path {:?}",
|
path {:?}",
|
||||||
sl))));
|
sl),
|
||||||
|
error::ErrorType::NoSuchSymbol,
|
||||||
|
next.pos.clone())));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -423,10 +395,12 @@ impl Builder {
|
|||||||
stack.push_back(elems[idx].clone());
|
stack.push_back(elems[idx].clone());
|
||||||
} else {
|
} else {
|
||||||
// TODO(jwall): A better error for this would be nice.
|
// TODO(jwall): A better error for this would be nice.
|
||||||
return Err(Box::new(BuildError::NoSuchSymbol(format!("Unable to \
|
return Err(Box::new(error::Error::new(format!("Unable to \
|
||||||
match selector \
|
match selector \
|
||||||
path {:?}",
|
path {:?}",
|
||||||
sl))));
|
sl),
|
||||||
|
error::ErrorType::NoSuchSymbol,
|
||||||
|
next.pos.clone())));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -457,29 +431,42 @@ impl Builder {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Box::new(BuildError::TypeFail(format!("{} is not a Tuple or List",
|
return Err(Box::new(error::Error::new(format!("{} is not a Tuple or List",
|
||||||
sl[0].fragment))));
|
sl[0].fragment),
|
||||||
|
error::ErrorType::TypeFail, next.pos.clone())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(Box::new(BuildError::NoSuchSymbol(format!("Unable to find Symbol {}",
|
return Err(Box::new(error::Error::new(format!("Unable to find Symbol {}",
|
||||||
sl[0].fragment))));
|
sl[0].fragment),
|
||||||
|
error::ErrorType::NoSuchSymbol,
|
||||||
|
pos_sl.pos.clone())));
|
||||||
}
|
}
|
||||||
return Err(Box::new(BuildError::NoSuchSymbol("Attempted to lookup an empty selector"
|
return Err(Box::new(error::Error::new("Attempted to lookup an empty selector",
|
||||||
.to_string())));
|
error::ErrorType::NoSuchSymbol,
|
||||||
|
Position {
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_vals(&self, left: Rc<Val>, right: Rc<Val>) -> Result<Rc<Val>, Box<Error>> {
|
fn add_vals(&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>)
|
||||||
|
-> Result<Rc<Val>, Box<Error>> {
|
||||||
match *left {
|
match *left {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii),
|
eval_binary_expr!(&Val::Int(ii),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Int(i + ii),
|
Val::Int(i + ii),
|
||||||
"Integer")
|
"Integer")
|
||||||
}
|
}
|
||||||
Val::Float(f) => {
|
Val::Float(f) => {
|
||||||
eval_binary_expr!(&Val::Float(ff),
|
eval_binary_expr!(&Val::Float(ff),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Float(f + ff),
|
Val::Float(f + ff),
|
||||||
"Float")
|
"Float")
|
||||||
@ -490,11 +477,13 @@ impl Builder {
|
|||||||
return Ok(Rc::new(Val::String([s.to_string(), ss.clone()].concat())))
|
return Ok(Rc::new(Val::String([s.to_string(), ss.clone()].concat())))
|
||||||
}
|
}
|
||||||
val => {
|
val => {
|
||||||
return Err(Box::new(BuildError::TypeFail(format!("Expected \
|
return Err(Box::new(error::Error::new(format!("Expected \
|
||||||
String \
|
String \
|
||||||
but got \
|
but got \
|
||||||
{:?}",
|
{:?}",
|
||||||
val))))
|
val),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,84 +496,112 @@ impl Builder {
|
|||||||
return Ok(Rc::new(Val::List(new_vec)));
|
return Ok(Rc::new(Val::List(new_vec)));
|
||||||
}
|
}
|
||||||
val => {
|
val => {
|
||||||
return Err(Box::new(BuildError::TypeFail(format!("Expected \
|
return Err(Box::new(error::Error::new(format!("Expected \
|
||||||
List \
|
List \
|
||||||
but got \
|
but got \
|
||||||
{:?}",
|
{:?}",
|
||||||
val))))
|
val),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ref expr => {
|
ref expr => {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
BuildError::Unsupported(
|
error::Error::new(
|
||||||
format!("{} does not support the '+' operation", expr.type_name()))))
|
format!("{} does not support the '+' operation", expr.type_name()),
|
||||||
|
error::ErrorType::Unsupported,
|
||||||
|
pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtract_vals(&self, left: Rc<Val>, right: Rc<Val>) -> Result<Rc<Val>, Box<Error>> {
|
fn subtract_vals(&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>)
|
||||||
|
-> Result<Rc<Val>, Box<Error>> {
|
||||||
match *left {
|
match *left {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii),
|
eval_binary_expr!(&Val::Int(ii),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Int(i - ii),
|
Val::Int(i - ii),
|
||||||
"Integer")
|
"Integer")
|
||||||
}
|
}
|
||||||
Val::Float(f) => {
|
Val::Float(f) => {
|
||||||
eval_binary_expr!(&Val::Float(ff),
|
eval_binary_expr!(&Val::Float(ff),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Float(f - ff),
|
Val::Float(f - ff),
|
||||||
"Float")
|
"Float")
|
||||||
}
|
}
|
||||||
ref expr => {
|
ref expr => {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
BuildError::Unsupported(
|
error::Error::new(
|
||||||
format!("{} does not support the '-' operation", expr.type_name()))))
|
format!("{} does not support the '-' operation", expr.type_name()),
|
||||||
|
error::ErrorType::Unsupported,
|
||||||
|
pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multiply_vals(&self, left: Rc<Val>, right: Rc<Val>) -> Result<Rc<Val>, Box<Error>> {
|
fn multiply_vals(&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>)
|
||||||
|
-> Result<Rc<Val>, Box<Error>> {
|
||||||
match *left {
|
match *left {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii),
|
eval_binary_expr!(&Val::Int(ii),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Int(i * ii),
|
Val::Int(i * ii),
|
||||||
"Integer")
|
"Integer")
|
||||||
}
|
}
|
||||||
Val::Float(f) => {
|
Val::Float(f) => {
|
||||||
eval_binary_expr!(&Val::Float(ff),
|
eval_binary_expr!(&Val::Float(ff),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Float(f * ff),
|
Val::Float(f * ff),
|
||||||
"Float")
|
"Float")
|
||||||
}
|
}
|
||||||
ref expr => {
|
ref expr => {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
BuildError::Unsupported(
|
error::Error::new(
|
||||||
format!("{} does not support the '*' operation", expr.type_name()))))
|
format!("{} does not support the '*' operation", expr.type_name()),
|
||||||
|
error::ErrorType::Unsupported,
|
||||||
|
pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn divide_vals(&self, left: Rc<Val>, right: Rc<Val>) -> Result<Rc<Val>, Box<Error>> {
|
fn divide_vals(&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>)
|
||||||
|
-> Result<Rc<Val>, Box<Error>> {
|
||||||
match *left {
|
match *left {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii),
|
eval_binary_expr!(&Val::Int(ii),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Int(i / ii),
|
Val::Int(i / ii),
|
||||||
"Integer")
|
"Integer")
|
||||||
}
|
}
|
||||||
Val::Float(f) => {
|
Val::Float(f) => {
|
||||||
eval_binary_expr!(&Val::Float(ff),
|
eval_binary_expr!(&Val::Float(ff),
|
||||||
|
pos,
|
||||||
right,
|
right,
|
||||||
Val::Float(f / ff),
|
Val::Float(f / ff),
|
||||||
"Float")
|
"Float")
|
||||||
}
|
}
|
||||||
ref expr => {
|
ref expr => {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
BuildError::Unsupported(
|
error::Error::new(
|
||||||
format!("{} does not support the '*' operation", expr.type_name()))))
|
format!("{} does not support the '*' operation", expr.type_name()),
|
||||||
|
error::ErrorType::Unsupported,
|
||||||
|
pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -596,10 +613,10 @@ impl Builder {
|
|||||||
let right = try!(self.eval_expr(expr));
|
let right = try!(self.eval_expr(expr));
|
||||||
let left = try!(self.value_to_val(v));
|
let left = try!(self.value_to_val(v));
|
||||||
match kind {
|
match kind {
|
||||||
&BinaryExprType::Add => self.add_vals(left, right),
|
&BinaryExprType::Add => self.add_vals(&def.pos, left, right),
|
||||||
&BinaryExprType::Sub => self.subtract_vals(left, right),
|
&BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right),
|
||||||
&BinaryExprType::Mul => self.multiply_vals(left, right),
|
&BinaryExprType::Mul => self.multiply_vals(&def.pos, left, right),
|
||||||
&BinaryExprType::Div => self.divide_vals(left, right),
|
&BinaryExprType::Div => self.divide_vals(&def.pos, left, right),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,10 +629,12 @@ impl Builder {
|
|||||||
if let Entry::Vacant(v) = m.entry(key.clone()) {
|
if let Entry::Vacant(v) = m.entry(key.clone()) {
|
||||||
v.insert(val.clone());
|
v.insert(val.clone());
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(BuildError::TypeFail(format!("Duplicate \
|
return Err(Box::new(error::Error::new(format!("Duplicate \
|
||||||
field: {} in \
|
field: {} in \
|
||||||
tuple",
|
tuple",
|
||||||
key.val))));
|
key.val),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
def.pos.clone())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for &(ref key, ref val) in def.fields.iter() {
|
for &(ref key, ref val) in def.fields.iter() {
|
||||||
@ -631,9 +650,11 @@ impl Builder {
|
|||||||
v.insert(expr_result);
|
v.insert(expr_result);
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
BuildError::TypeFail(
|
error::Error::new(
|
||||||
format!("Expected type {} for field {} but got {}",
|
format!("Expected type {} for field {} but got {}",
|
||||||
src_val.type_name(), key.fragment, expr_result.type_name()))));
|
src_val.type_name(), key.fragment, expr_result.type_name()),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
key.pos.clone())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -644,7 +665,9 @@ impl Builder {
|
|||||||
new_fields.sort_by(|a, b| a.0.cmp(&b.0));
|
new_fields.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
return Ok(Rc::new(Val::Tuple(new_fields)));
|
return Ok(Rc::new(Val::Tuple(new_fields)));
|
||||||
}
|
}
|
||||||
Err(Box::new(BuildError::TypeFail(format!("Expected Tuple got {}", v))))
|
Err(Box::new(error::Error::new(format!("Expected Tuple got {}", v),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
def.selector.pos.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_format(&self, def: &FormatDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_format(&self, def: &FormatDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
@ -656,7 +679,7 @@ impl Builder {
|
|||||||
vals.push(rcv.deref().clone());
|
vals.push(rcv.deref().clone());
|
||||||
}
|
}
|
||||||
let formatter = format::Formatter::new(tmpl.clone(), vals);
|
let formatter = format::Formatter::new(tmpl.clone(), vals);
|
||||||
Ok(Rc::new(Val::String(try!(formatter.render()))))
|
Ok(Rc::new(Val::String(try!(formatter.render(&def.pos)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_call(&self, def: &CallDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_call(&self, def: &CallDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
@ -672,17 +695,21 @@ impl Builder {
|
|||||||
let fields = try!(m.eval(argvals));
|
let fields = try!(m.eval(argvals));
|
||||||
return Ok(Rc::new(Val::Tuple(fields)));
|
return Ok(Rc::new(Val::Tuple(fields)));
|
||||||
}
|
}
|
||||||
Err(Box::new(BuildError::TypeFail(// We should pretty print the selectors here.
|
Err(Box::new(error::Error::new(// We should pretty print the selectors here.
|
||||||
format!("{} is not a Macro", v))))
|
format!("{} is not a Macro", v),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
def.pos.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_macro_def(&self, def: &MacroDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_macro_def(&self, def: &MacroDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match def.validate_symbols() {
|
match def.validate_symbols() {
|
||||||
Ok(()) => Ok(Rc::new(Val::Macro(def.clone()))),
|
Ok(()) => Ok(Rc::new(Val::Macro(def.clone()))),
|
||||||
Err(set) => {
|
Err(set) => {
|
||||||
Err(Box::new(BuildError::NoSuchSymbol(format!("Macro has the following \
|
Err(Box::new(error::Error::new(format!("Macro has the following \
|
||||||
undefined symbols: {:?}",
|
undefined symbols: {:?}",
|
||||||
set))))
|
set),
|
||||||
|
error::ErrorType::NoSuchSymbol,
|
||||||
|
def.pos.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -705,9 +732,11 @@ impl Builder {
|
|||||||
// Otherwise return the default
|
// Otherwise return the default
|
||||||
return self.eval_expr(def_expr);
|
return self.eval_expr(def_expr);
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(BuildError::TypeFail(format!("Expected String but got \
|
return Err(Box::new(error::Error::new(format!("Expected String but got \
|
||||||
{} in Select expression",
|
{} in Select expression",
|
||||||
v.type_name()))));
|
v.type_name()),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
def.pos.clone())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
86
src/error.rs
Normal file
86
src/error.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
use std::error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use ast::*;
|
||||||
|
|
||||||
|
pub enum ErrorType {
|
||||||
|
// Build Errors
|
||||||
|
TypeFail,
|
||||||
|
DuplicateBinding,
|
||||||
|
IncompleteParsing,
|
||||||
|
Unsupported,
|
||||||
|
NoSuchSymbol,
|
||||||
|
BadArgLen,
|
||||||
|
FormatError,
|
||||||
|
// Parsing Errors
|
||||||
|
UnexpectedToken,
|
||||||
|
EmptyExpression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ErrorType {
|
||||||
|
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let name = match self {
|
||||||
|
&ErrorType::TypeFail => "TypeFail",
|
||||||
|
&ErrorType::DuplicateBinding => "DuplicateBinding",
|
||||||
|
&ErrorType::IncompleteParsing => "IncompleteParsing",
|
||||||
|
&ErrorType::Unsupported => "Unsupported",
|
||||||
|
&ErrorType::NoSuchSymbol => "NoSuchSymbol",
|
||||||
|
&ErrorType::BadArgLen => "BadArgLen",
|
||||||
|
&ErrorType::FormatError => "FormatError",
|
||||||
|
&ErrorType::UnexpectedToken => "UnexpectedToken",
|
||||||
|
&ErrorType::EmptyExpression => "EmptyExpression",
|
||||||
|
};
|
||||||
|
w.write_str(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Error {
|
||||||
|
pub err_type: ErrorType,
|
||||||
|
pub pos: Position,
|
||||||
|
pub msg: String,
|
||||||
|
_pkgonly: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn new<S: Into<String>>(msg: S, t: ErrorType, pos: Position) -> Self {
|
||||||
|
Error {
|
||||||
|
err_type: t,
|
||||||
|
pos: pos,
|
||||||
|
msg: msg.into(),
|
||||||
|
_pkgonly: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Error {
|
||||||
|
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"{}: \"{}\" {}:{}",
|
||||||
|
self.err_type,
|
||||||
|
self.msg,
|
||||||
|
self.pos.line,
|
||||||
|
self.pos.column
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"{}: \"{}\" {}:{}",
|
||||||
|
self.err_type,
|
||||||
|
self.msg,
|
||||||
|
self.pos.line,
|
||||||
|
self.pos.column
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
&self.msg
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,8 @@
|
|||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use build::BuildError;
|
use ast::*;
|
||||||
|
use error;
|
||||||
|
|
||||||
pub struct Formatter<V: Into<String> + Clone> {
|
pub struct Formatter<V: Into<String> + Clone> {
|
||||||
tmpl: String,
|
tmpl: String,
|
||||||
@ -30,16 +31,17 @@ impl<V: Into<String> + Clone> Formatter<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self) -> Result<String, Box<Error>> {
|
pub fn render(&self, pos: &Position) -> Result<String, Box<Error>> {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let mut should_escape = false;
|
let mut should_escape = false;
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for c in self.tmpl.chars() {
|
for c in self.tmpl.chars() {
|
||||||
if c == '@' && !should_escape {
|
if c == '@' && !should_escape {
|
||||||
if count == self.args.len() {
|
if count == self.args.len() {
|
||||||
return Err(Box::new(BuildError::FormatError("Too few arguments to string \
|
return Err(Box::new(error::Error::new("Too few arguments to string \
|
||||||
formatter."
|
formatter.",
|
||||||
.to_string())));
|
error::ErrorType::FormatError,
|
||||||
|
pos.clone())));
|
||||||
}
|
}
|
||||||
let arg = self.args[count].clone();
|
let arg = self.args[count].clone();
|
||||||
let strval = arg.into();
|
let strval = arg.into();
|
||||||
@ -52,9 +54,10 @@ impl<V: Into<String> + Clone> Formatter<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.args.len() != count {
|
if self.args.len() != count {
|
||||||
return Err(Box::new(BuildError::FormatError("Too many arguments to string \
|
return Err(Box::new(error::Error::new("Too many arguments to string \
|
||||||
formatter."
|
formatter.",
|
||||||
.to_string())));
|
error::ErrorType::FormatError,
|
||||||
|
pos.clone())));
|
||||||
}
|
}
|
||||||
return Ok(buf);
|
return Ok(buf);
|
||||||
}
|
}
|
||||||
@ -63,22 +66,35 @@ impl<V: Into<String> + Clone> Formatter<V> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::Formatter;
|
use super::Formatter;
|
||||||
|
use ast::Position;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_happy_path() {
|
fn test_format_happy_path() {
|
||||||
let formatter = Formatter::new("foo @ @ \\@", vec!["bar", "quux"]);
|
let formatter = Formatter::new("foo @ @ \\@", vec!["bar", "quux"]);
|
||||||
assert_eq!(formatter.render().unwrap(), "foo bar quux @");
|
let pos = Position {
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
};
|
||||||
|
assert_eq!(formatter.render(&pos).unwrap(), "foo bar quux @");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_happy_wrong_too_few_args() {
|
fn test_format_happy_wrong_too_few_args() {
|
||||||
let formatter = Formatter::new("foo @ @ \\@", vec!["bar"]);
|
let formatter = Formatter::new("foo @ @ \\@", vec!["bar"]);
|
||||||
assert!(formatter.render().is_err());
|
let pos = Position {
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
};
|
||||||
|
assert!(formatter.render(&pos).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_happy_wrong_too_many_args() {
|
fn test_format_happy_wrong_too_many_args() {
|
||||||
let formatter = Formatter::new("foo @ @ \\@", vec!["bar", "quux", "baz"]);
|
let formatter = Formatter::new("foo @ @ \\@", vec!["bar", "quux", "baz"]);
|
||||||
assert!(formatter.render().is_err());
|
let pos = Position {
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
};
|
||||||
|
assert!(formatter.render(&pos).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,14 @@ extern crate nom;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate nom_locate;
|
extern crate nom_locate;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate quick_error;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod tokenizer;
|
pub mod tokenizer;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
mod format;
|
mod format;
|
||||||
|
|
||||||
pub use ast::Value;
|
pub use ast::Value;
|
||||||
|
62
src/parse.rs
62
src/parse.rs
@ -20,23 +20,7 @@ use nom::InputLength;
|
|||||||
|
|
||||||
use ast::*;
|
use ast::*;
|
||||||
use tokenizer::*;
|
use tokenizer::*;
|
||||||
|
use error as E;
|
||||||
quick_error! {
|
|
||||||
#[derive(Debug,PartialEq)]
|
|
||||||
pub enum ParseError {
|
|
||||||
UnexpectedToken(expected: String, actual: String) {
|
|
||||||
description("Unexpected Token")
|
|
||||||
display("Unexpected Token Expected {} Got {}", expected, actual)
|
|
||||||
}
|
|
||||||
EmptyExpression(msg: String ) {
|
|
||||||
description("EmptyExpression")
|
|
||||||
display("Unexpected EmptyExpression {}", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO(jwall): Error Reporting with Line and Column information.
|
|
||||||
|
|
||||||
type ParseResult<O> = Result<O, Box<Error>>;
|
type ParseResult<O> = Result<O, Box<Error>>;
|
||||||
|
|
||||||
@ -171,7 +155,6 @@ pub fn selector_or_symbol(input: Span) -> IResult<Span, Value> {
|
|||||||
return IResult::Incomplete(i);
|
return IResult::Incomplete(i);
|
||||||
}
|
}
|
||||||
IResult::Error(_) => {
|
IResult::Error(_) => {
|
||||||
// TODO(jwall): Maybe be smarter about the error reporting here?
|
|
||||||
return ws!(input, selector_value);
|
return ws!(input, selector_value);
|
||||||
}
|
}
|
||||||
IResult::Done(rest, val) => {
|
IResult::Done(rest, val) => {
|
||||||
@ -275,16 +258,27 @@ named!(grouped_expression( Span ) -> Expression,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
fn assert_nonempty_list<T>(v: Vec<T>) -> ParseResult<Vec<T>> {
|
// TODO(jwall): This can go away once the non_empty_separated_list in nom is fixed to work
|
||||||
if v.is_empty() {
|
// with nom_locate.
|
||||||
return Err(Box::new(ParseError::EmptyExpression("Selectors can't be empty.".to_string())));
|
fn assert_nonempty_list<T>(t: (Span, Vec<T>)) -> ParseResult<Vec<T>> {
|
||||||
|
if t.1.is_empty() {
|
||||||
|
return Err(Box::new(E::Error::new("Selectors can't be empty.",
|
||||||
|
E::ErrorType::EmptyExpression,
|
||||||
|
Position {
|
||||||
|
line: t.0.line as usize,
|
||||||
|
column: t.0.offset as usize,
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
return Ok(v);
|
return Ok(t.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(selector_list( Span ) -> SelectorList,
|
named!(selector_list( Span ) -> SelectorList,
|
||||||
map_res!(
|
map_res!(
|
||||||
separated_list!(dottok, barewordtok),
|
do_parse!(
|
||||||
|
pos: position!() >>
|
||||||
|
list: separated_list!(dottok, barewordtok) >>
|
||||||
|
(pos, list)
|
||||||
|
),
|
||||||
assert_nonempty_list
|
assert_nonempty_list
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -330,7 +324,12 @@ fn tuple_to_macro(mut t: (Span, Vec<Value>, Value)) -> ParseResult<Expression> {
|
|||||||
}
|
}
|
||||||
// TODO(jwall): Show a better version of the unexpected parsed value.
|
// TODO(jwall): Show a better version of the unexpected parsed value.
|
||||||
val => {
|
val => {
|
||||||
Err(Box::new(ParseError::UnexpectedToken("{ .. }".to_string(), format!("{:?}", val))))
|
Err(Box::new(E::Error::new(format!("Expected Tuple Got {:?}", val),
|
||||||
|
E::ErrorType::UnexpectedToken,
|
||||||
|
Position {
|
||||||
|
line: t.0.line as usize,
|
||||||
|
column: t.0.offset as usize,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,9 +362,13 @@ fn tuple_to_select(t: (Span, Expression, Expression, Value)) -> ParseResult<Expr
|
|||||||
pos: Position::new(t.0.line as usize, t.0.offset as usize),
|
pos: Position::new(t.0.line as usize, t.0.offset as usize),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
// TODO(jwall): Show a better version of the unexpected parsed value.
|
|
||||||
val => {
|
val => {
|
||||||
Err(Box::new(ParseError::UnexpectedToken("{ .. }".to_string(), format!("{:?}", val))))
|
Err(Box::new(E::Error::new(format!("Expected Tuple Got {:?}", val),
|
||||||
|
E::ErrorType::UnexpectedToken,
|
||||||
|
Position {
|
||||||
|
line: t.0.line as usize,
|
||||||
|
column: t.0.offset as usize,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -414,7 +417,12 @@ fn tuple_to_call(t: (Span, Value, Vec<Expression>)) -> ParseResult<Expression> {
|
|||||||
pos: Position::new(t.0.line as usize, t.0.offset as usize),
|
pos: Position::new(t.0.line as usize, t.0.offset as usize),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(ParseError::UnexpectedToken("Selector".to_string(), format!("{:?}", t.0))))
|
Err(Box::new(E::Error::new(format!("Expected Selector Got {:?}", t.0),
|
||||||
|
E::ErrorType::UnexpectedToken,
|
||||||
|
Position {
|
||||||
|
line: t.0.line as usize,
|
||||||
|
column: t.0.offset as usize,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user