Error Messaging Redesign
This commit contains the following: * New data types to support full tracebacks * New traceback data type used across stl and ast * Updates to tests * fixes for error messaging in sym and some stl functions
This commit is contained in:
parent
91ad4eed12
commit
789349df48
24 changed files with 837 additions and 374 deletions
81
src/sym.rs
81
src/sym.rs
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
use crate::eval::eval;
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use crate::segment::{Ctr, Seg, Type};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
|
@ -39,7 +40,7 @@ pub struct UserFn {
|
|||
*/
|
||||
#[derive(Clone)]
|
||||
pub enum ValueType {
|
||||
Internal(Rc<dyn Fn(&Seg, &mut SymTable) -> Result<Ctr, String>>),
|
||||
Internal(Rc<dyn Fn(&Seg, &mut SymTable) -> Result<Ctr, Traceback>>),
|
||||
FuncForm(UserFn),
|
||||
VarForm(Box<Ctr>),
|
||||
}
|
||||
|
|
@ -127,10 +128,13 @@ impl SymTable {
|
|||
name: &String,
|
||||
args: &Seg,
|
||||
call_func: bool,
|
||||
) -> Result<Box<Ctr>, String> {
|
||||
) -> Result<Box<Ctr>, Traceback> {
|
||||
let mut symbol = match self.remove(name) {
|
||||
Some(s) => s,
|
||||
None => return Err(format!("undefined symbol: {}", name)),
|
||||
None => return Err(
|
||||
Traceback::new()
|
||||
.with_trace((name, "(is an undefined symbol)").into())
|
||||
),
|
||||
};
|
||||
// will re-increment when inserted
|
||||
// but we dont want to increment it
|
||||
|
|
@ -175,17 +179,21 @@ impl Default for SymTable {
|
|||
}
|
||||
|
||||
impl Args {
|
||||
fn validate_inputs(&self, args: &Seg) -> Result<(), String> {
|
||||
fn validate_inputs(&self, args: &Seg, caller: &String) -> Result<(), Traceback> {
|
||||
match self {
|
||||
Args::None => {
|
||||
if args.len() == 1 {
|
||||
if let Ctr::None = *args.car {
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err("expected no args".to_string());
|
||||
return Err(start_trace(
|
||||
(caller, "expected no args")
|
||||
.into()))
|
||||
}
|
||||
} else {
|
||||
return Err("expected no args".to_string());
|
||||
return Err(start_trace(
|
||||
(caller, "expected no args")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +201,9 @@ impl Args {
|
|||
if !args.is_empty() {
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err("expected args but none were provided".to_string());
|
||||
return Err(start_trace(
|
||||
(caller, "expected args, but none were provided")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,12 +213,18 @@ impl Args {
|
|||
if let Ctr::None = *args.car {
|
||||
//pass
|
||||
} else {
|
||||
return Err("expected 0 args. Got one or more.".to_string());
|
||||
return Err(start_trace(
|
||||
(caller, "expected no args, got 1 or more")
|
||||
.into()))
|
||||
}
|
||||
} else if *num != called_arg_count {
|
||||
return Err(format!("expected {} args. Got {}.", num, called_arg_count));
|
||||
return Err(start_trace(
|
||||
(caller, format!("expected {} args. Got {}.", num, called_arg_count))
|
||||
.into()))
|
||||
} else if let Ctr::None = *args.car {
|
||||
return Err(format!("expected {} args. Got 0.", num,));
|
||||
return Err(start_trace(
|
||||
(caller, format!("expected {} args. Got 0.", num))
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,18 +247,26 @@ impl Args {
|
|||
});
|
||||
|
||||
if passes && idx < (arg_types.len() - 1) {
|
||||
return Err(format!("{} too few arguments", arg_types.len() - (idx + 1)));
|
||||
return Err(start_trace(
|
||||
(caller, format!("{} too few arguments", arg_types.len() - (idx + 1)))
|
||||
.into()))
|
||||
}
|
||||
|
||||
if !passes {
|
||||
if mismatch {
|
||||
return Err(format!("arg {} expected to be {}", idx + 1, arg_types[idx]));
|
||||
return Err(start_trace(
|
||||
(caller, format!("arg {} expected to be {}", idx + 1, arg_types[idx]))
|
||||
.into()))
|
||||
}
|
||||
if idx > (arg_types.len() - 1) {
|
||||
return Err("too many arguments".to_string());
|
||||
return Err(start_trace(
|
||||
(caller, "too many arguments".to_string())
|
||||
.into()))
|
||||
}
|
||||
if idx < (arg_types.len() - 1) {
|
||||
return Err("too few arguments".to_string());
|
||||
return Err(start_trace(
|
||||
(caller, "too few arguments".to_string())
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -306,10 +330,11 @@ impl Symbol {
|
|||
/* call
|
||||
* routine is called by eval when a symbol is expanded
|
||||
*/
|
||||
pub fn call(&self, args: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
||||
pub fn call(&self, args: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, Traceback> {
|
||||
let evaluated_args: &Seg;
|
||||
let mut outer_scope_seg_storage = Seg::new();
|
||||
let mut errcon: String = String::new();
|
||||
let mut cursor = 0;
|
||||
let mut errcon: Traceback = Traceback::new();
|
||||
if !self.conditional_branches {
|
||||
if !args.circuit(&mut |arg: &Ctr| -> bool {
|
||||
if let Ctr::Seg(ref s) = arg {
|
||||
|
|
@ -333,16 +358,22 @@ impl Symbol {
|
|||
} else {
|
||||
outer_scope_seg_storage.append(Box::new(arg.clone()));
|
||||
}
|
||||
cursor += 1;
|
||||
true
|
||||
}) {
|
||||
return Err(format!("error evaluating args: {}", errcon))
|
||||
return Err(
|
||||
errcon.with_trace((
|
||||
&self.name,
|
||||
format!("error evaluating arg {cursor}"),
|
||||
).into()))
|
||||
}
|
||||
evaluated_args = &outer_scope_seg_storage;
|
||||
} else {
|
||||
evaluated_args = args;
|
||||
}
|
||||
};
|
||||
|
||||
self.args.validate_inputs(evaluated_args, &self.name)?;
|
||||
|
||||
self.args.validate_inputs(evaluated_args)?;
|
||||
match &self.value {
|
||||
ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())),
|
||||
ValueType::Internal(ref f) => Ok(Box::new(f(evaluated_args, syms)?)),
|
||||
|
|
@ -377,7 +408,7 @@ impl Symbol {
|
|||
if let Ctr::Seg(ref data) = *iterate.car {
|
||||
match eval(data, syms) {
|
||||
Ok(ctr) => result = ctr,
|
||||
Err(e) => return Err(format!("evaluation failure\n{}", e)),
|
||||
Err(e) => return Err(e.with_trace((&self.name, "returned error").into())),
|
||||
}
|
||||
} else {
|
||||
let temp = Seg::from_mono(iterate.car.clone());
|
||||
|
|
@ -389,7 +420,9 @@ impl Symbol {
|
|||
result = ctr;
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(format!("evaluation failure\n{}", e)),
|
||||
Err(e) => return Err(
|
||||
e.with_trace((&self.name, "returned error").into())
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +481,7 @@ impl Default for Symbol {
|
|||
Symbol {
|
||||
value: ValueType::Internal(
|
||||
Rc::new(
|
||||
|_: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
||||
|_: &Seg, _: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
unimplemented!()
|
||||
}
|
||||
)
|
||||
|
|
@ -466,9 +499,9 @@ pub fn call_lambda(
|
|||
lam: &UserFn,
|
||||
args_ctr: &Box<Ctr>,
|
||||
syms: &mut SymTable,
|
||||
) -> Result<Box<Ctr>, String> {
|
||||
) -> Result<Box<Ctr>, Traceback> {
|
||||
let temp_sym = Symbol {
|
||||
name: String::from("anonymous"),
|
||||
name: String::from("<lambda>"),
|
||||
conditional_branches: false,
|
||||
docs: String::from("user defined lambda"),
|
||||
args: Args::Lazy(lam.arg_syms.len() as u128),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue