Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 24 additions & 33 deletions src/builtin/functions/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Error, ErrorKind, TulispContext, TulispObject, TulispValue};
use crate::{Error, TulispContext, TulispObject, TulispValue};

pub(crate) fn eval_1_arg_special_form(
ctx: &mut TulispContext,
Expand All @@ -8,22 +8,19 @@ pub(crate) fn eval_1_arg_special_form(
lambda: fn(&mut TulispContext, &TulispObject, &TulispObject) -> Result<TulispObject, Error>,
) -> Result<TulispObject, Error> {
if args.null() {
return Err(Error::new(
ErrorKind::MissingArgument,
if has_rest {
format!("{}: expected at least 1 argument.", name)
} else {
format!("{}: expected 1 argument.", name)
},
));
return Err(Error::missing_argument(if has_rest {
format!("{}: expected at least 1 argument.", name)
} else {
format!("{}: expected 1 argument.", name)
}));
}
args.car_and_then(|arg1| {
args.cdr_and_then(|rest| {
if !has_rest && !rest.null() {
return Err(Error::new(
ErrorKind::MissingArgument,
format!("{}: expected only 1 argument.", name),
));
return Err(Error::missing_argument(format!(
"{}: expected only 1 argument.",
name
)));
}
lambda(ctx, arg1, rest)
})
Expand All @@ -43,33 +40,27 @@ pub(crate) fn eval_2_arg_special_form(
) -> Result<TulispObject, Error>,
) -> Result<TulispObject, Error> {
let TulispValue::List { cons: args, .. } = &*args.inner_ref() else {
return Err(Error::new(
ErrorKind::MissingArgument,
if has_rest {
format!("{}: expected at least 2 arguments.", name)
} else {
format!("{}: expected 2 arguments.", name)
},
));
return Err(Error::missing_argument(if has_rest {
format!("{}: expected at least 2 arguments.", name)
} else {
format!("{}: expected 2 arguments.", name)
}));
};
if args.cdr().null() {
return Err(Error::new(
ErrorKind::MissingArgument,
if has_rest {
format!("{}: expected at least 2 arguments.", name)
} else {
format!("{}: expected 2 arguments.", name)
},
));
return Err(Error::missing_argument(if has_rest {
format!("{}: expected at least 2 arguments.", name)
} else {
format!("{}: expected 2 arguments.", name)
}));
}
let arg1 = args.car();
args.cdr().car_and_then(|arg2| {
args.cdr().cdr_and_then(|rest| {
if !has_rest && !rest.null() {
return Err(Error::new(
ErrorKind::MissingArgument,
format!("{}: expected only 2 arguments.", name),
));
return Err(Error::missing_argument(format!(
"{}: expected only 2 arguments.",
name
)));
}
lambda(ctx, arg1, arg2, rest)
})
Expand Down
20 changes: 10 additions & 10 deletions src/builtin/functions/comparison_of_strings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Error, ErrorKind, TulispContext, TulispObject, TulispValue, destruct_eval_bind};
use crate::{Error, TulispContext, TulispObject, TulispValue, destruct_eval_bind};

fn string_cmp(
ctx: &mut TulispContext,
Expand All @@ -13,15 +13,15 @@ fn string_cmp(
(TulispValue::String { value: string1 }, TulispValue::String { value: string2 }) => {
Ok(oper(string1, string2).into())
}
(_, _) => Err(Error::new(
ErrorKind::TypeMismatch,
"Both arguments need to be strings".to_string(),
)
.with_trace(if string1.stringp() {
args.cadr()?
} else {
args.car()?
})),
(_, _) => Err(
Error::type_mismatch("Both arguments need to be strings".to_string()).with_trace(
if string1.stringp() {
args.cadr()?
} else {
args.car()?
},
),
),
}
}

Expand Down
13 changes: 6 additions & 7 deletions src/builtin/functions/conditionals.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
Error, ErrorKind, TulispContext, TulispObject, TulispValue,
Error, TulispContext, TulispObject, TulispValue,
builtin::functions::common::eval_2_arg_special_form,
destruct_bind, destruct_eval_bind,
eval::{eval_and_then, eval_basic},
Expand Down Expand Up @@ -49,8 +49,7 @@ pub(crate) fn add(ctx: &mut TulispContext) {
// Constructs for combining conditions
fn not(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
if args.cdr_and_then(|x| Ok(!x.null()))? {
return Err(Error::new(
ErrorKind::SyntaxError,
return Err(Error::syntax_error(
"not: expected one argument".to_string(),
));
}
Expand Down Expand Up @@ -180,10 +179,10 @@ fn build_binding(
};

if length(&binding)? > 2 {
return Err(Error::new(
ErrorKind::SyntaxError,
format!("`let` bindings can have only one value-form {}", &binding),
));
return Err(Error::syntax_error(format!(
"`let` bindings can have only one value-form {}",
&binding
)));
}

let var = binding.car()?;
Expand Down
46 changes: 34 additions & 12 deletions src/builtin/functions/errors.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use crate::{Error, ErrorKind, TulispContext, TulispObject, destruct_bind};
use crate::{Error, ErrorKind, TulispContext, destruct_bind};

pub(crate) fn add(ctx: &mut TulispContext) {
ctx.add_special_form("error", |ctx, args| {
destruct_bind!((msg) = args);
Err(Error::new(
ErrorKind::LispError,
ctx.eval(&msg)?.as_string()?,
))
Err(Error::lisp_error(ctx.eval(&msg)?.as_string()?))
});

ctx.add_special_form("catch", |ctx, args| {
Expand All @@ -15,18 +12,43 @@ pub(crate) fn add(ctx: &mut TulispContext) {
if let Err(ref e) = res {
let tag = ctx.eval(&tag)?;
if let ErrorKind::Throw(obj) = e.kind_ref()
&& let Ok(true) = obj.car_and_then(|e_tag| Ok(e_tag.eq(&tag))) {
return obj.cdr();
}
&& let Ok(true) = obj.car_and_then(|e_tag| Ok(e_tag.eq(&tag)))
{
return obj.cdr();
}
}
res
});

ctx.add_special_form("throw", |ctx, args| {
destruct_bind!((tag value) = args);
Err(Error::new(
ErrorKind::Throw(TulispObject::cons(ctx.eval(&tag)?, ctx.eval(&value)?)),
String::new(),
))
Err(Error::throw(ctx.eval(&tag)?, ctx.eval(&value)?))
});
}

#[cfg(test)]
mod tests {
use crate::TulispContext;
use crate::test_utils::{eval_assert_equal, eval_assert_error};

#[test]
fn test_error_handling() {
let mut ctx = TulispContext::new();
eval_assert_equal(&mut ctx, "(catch 'my-tag (throw 'my-tag 42))", "42");
eval_assert_error(
&mut ctx,
"(catch 'my-tag (throw 'other-tag 42))",
r#"ERR Throw((other-tag . 42)):
<eval_string>:1.16-1.36: at (throw 'other-tag 42)
<eval_string>:1.1-1.37: at (catch 'my-tag (throw 'other-tag 42))
"#,
);
eval_assert_error(
&mut ctx,
r#"(error "Something went wrong!")"#,
r#"ERR LispError: Something went wrong!
<eval_string>:1.1-1.31: at (error "Something went wrong!")
"#,
);
}
}
73 changes: 25 additions & 48 deletions src/builtin/functions/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::context::Scope;
use crate::context::TulispContext;
use crate::destruct_eval_bind;
use crate::error::Error;
use crate::error::ErrorKind;
use crate::eval::DummyEval;
use crate::eval::Eval;
use crate::eval::eval;
Expand Down Expand Up @@ -117,8 +116,7 @@ pub(crate) fn add(ctx: &mut TulispContext) {
if prefix.stringp() {
prefix.as_string()?
} else {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"gensym: prefix must be a string".to_string(),
));
}
Expand Down Expand Up @@ -148,10 +146,7 @@ pub(crate) fn add(ctx: &mut TulispContext) {
match ele.as_string() {
Ok(ref s) => ret.push_str(s),
_ => {
return Err(Error::new(
ErrorKind::TypeMismatch,
format!("Not a string: {}", ele),
));
return Err(Error::type_mismatch(format!("Not a string: {}", ele)));
}
}
}
Expand All @@ -178,8 +173,7 @@ pub(crate) fn add(ctx: &mut TulispContext) {
continue;
}
let Some(next_arg) = args.next() else {
return Err(Error::new(
ErrorKind::MissingArgument,
return Err(Error::missing_argument(
"format has missing args".to_string(),
));
};
Expand All @@ -191,10 +185,10 @@ pub(crate) fn add(ctx: &mut TulispContext) {
'd' => output.push_str(&next_arg.try_int()?.to_string()),
'f' => output.push_str(&next_arg.try_float()?.to_string()),
_ => {
return Err(Error::new(
ErrorKind::SyntaxError,
format!("Invalid format operation: %{}", ch),
));
return Err(Error::syntax_error(format!(
"Invalid format operation: %{}",
ch
)));
}
}
}
Expand Down Expand Up @@ -230,15 +224,13 @@ pub(crate) fn add(ctx: &mut TulispContext) {
fn setq(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
let value = args.cdr_and_then(|args| {
if args.null() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"setq requires exactly 2 arguments".to_string(),
));
}
args.cdr_and_then(|x| {
if !x.null() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"setq requires exactly 2 arguments".to_string(),
));
}
Expand All @@ -253,15 +245,13 @@ pub(crate) fn add(ctx: &mut TulispContext) {
fn set(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
let value = args.cdr_and_then(|args| {
if args.null() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"setq requires exactly 2 arguments".to_string(),
));
}
args.cdr_and_then(|x| {
if !x.null() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"setq requires exactly 2 arguments".to_string(),
));
}
Expand All @@ -278,8 +268,7 @@ pub(crate) fn add(ctx: &mut TulispContext) {
fn impl_let(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
destruct_bind!((varlist &rest rest) = args);
if !rest.consp() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"let: expected varlist and body".to_string(),
));
}
Expand All @@ -290,26 +279,19 @@ pub(crate) fn add(ctx: &mut TulispContext) {
} else if varitem.consp() {
destruct_bind!((&optional name value &rest rest) = varitem);
if name.null() {
return Err(Error::new(
ErrorKind::Undefined,
"let varitem requires name".to_string(),
));
return Err(Error::undefined("let varitem requires name".to_string()));
}
if !rest.null() {
return Err(Error::new(
ErrorKind::Undefined,
return Err(Error::undefined(
"let varitem has too many values".to_string(),
));
}
local.set(name, eval(ctx, &value)?)?;
} else {
return Err(Error::new(
ErrorKind::SyntaxError,
format!(
"varitems inside a let-varlist should be a var or a binding: {}",
varitem
),
));
return Err(Error::syntax_error(format!(
"varitems inside a let-varlist should be a var or a binding: {}",
varitem
)));
};
}

Expand Down Expand Up @@ -513,15 +495,13 @@ pub(crate) fn add(ctx: &mut TulispContext) {
fn impl_cons(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
let cdr = args.cdr_and_then(|args| {
if args.null() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"cons requires exactly 2 arguments".to_string(),
));
}
args.cdr_and_then(|x| {
if !x.null() {
return Err(Error::new(
ErrorKind::TypeMismatch,
return Err(Error::type_mismatch(
"cons requires exactly 2 arguments".to_string(),
));
}
Expand Down Expand Up @@ -632,14 +612,11 @@ pub(crate) fn add(ctx: &mut TulispContext) {
match args.cdr_and_then(|x| Ok(x.null())) {
Err(err) => return Err(err),
Ok(false) => {
return Err(Error::new(
ErrorKind::TypeMismatch,
format!(
"Expected exatly 1 argument for {}. Got args: {}",
stringify!($name),
args
),
))
return Err(Error::type_mismatch(format!(
"Expected exatly 1 argument for {}. Got args: {}",
stringify!($name),
args
)))
}
Ok(true) => {}
}
Expand Down
Loading