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:
Ava Apples Affine 2023-05-23 22:06:11 +00:00
parent 91ad4eed12
commit 789349df48
24 changed files with 837 additions and 374 deletions

View file

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