Skip to content

Commit

Permalink
Support more than 2 operands for comparison operators (#43)
Browse files Browse the repository at this point in the history
Closes #26
  • Loading branch information
shsms authored Nov 10, 2023
2 parents ac4af55 + 3da565d commit 4e97692
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 13 deletions.
59 changes: 46 additions & 13 deletions src/builtin/functions/comparison_of_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,59 @@ use crate::{
};
use std::rc::Rc;

macro_rules! compare_ops {
($oper:expr) => {{
|selfobj: &TulispObject, other: &TulispObject| -> Result<bool, Error> {
if selfobj.floatp() {
let s: f64 = selfobj.as_float().unwrap();
let o: f64 = other.try_into()?;
Ok($oper(&s, &o))
} else if other.floatp() {
let o: f64 = other.as_float().unwrap();
let s: f64 = selfobj.try_into()?;
Ok($oper(&s, &o))
} else {
let s: i64 = selfobj.try_into()?;
let o: i64 = other.try_into()?;
Ok($oper(&s, &o))
}
}
}};
}

macro_rules! compare_impl {
($name:ident) => {
fn $name(ctx: &mut TulispContext, rest: &TulispObject) -> Result<TulispObject, Error> {
let items: Vec<TulispObject> = rest.base_iter().collect();
if items.len() < 2 {
return Err(Error::new(
crate::ErrorKind::OutOfRange,
format!("{} requires at least 2 arguments", stringify!($name)),
));
}
for items in items.windows(2) {
let a = ctx.eval(&items[0])?;
let b = ctx.eval(&items[1])?;
if !compare_ops!(std::cmp::PartialOrd::$name)(&a, &b)? {
return Ok(TulispObject::nil());
}
}
Ok(TulispObject::t())
}
};
}

pub(crate) fn add(ctx: &mut TulispContext) {
// // TODO: >, >=, <, <= - need to be able to support more than 2 args
fn gt(ctx: &mut TulispContext, rest: &TulispObject) -> Result<TulispObject, Error> {
reduce_with(ctx, rest, binary_ops!(std::cmp::PartialOrd::gt))
}
compare_impl!(gt);
intern_set_func!(ctx, gt, ">");

fn ge(ctx: &mut TulispContext, rest: &TulispObject) -> Result<TulispObject, Error> {
reduce_with(ctx, rest, binary_ops!(std::cmp::PartialOrd::ge))
}
compare_impl!(ge);
intern_set_func!(ctx, ge, ">=");

fn lt(ctx: &mut TulispContext, rest: &TulispObject) -> Result<TulispObject, Error> {
reduce_with(ctx, rest, binary_ops!(std::cmp::PartialOrd::lt))
}
compare_impl!(lt);
intern_set_func!(ctx, lt, "<");

fn le(ctx: &mut TulispContext, args: &TulispObject) -> Result<TulispObject, Error> {
reduce_with(ctx, args, binary_ops!(std::cmp::PartialOrd::le))
}
compare_impl!(le);
intern_set_func!(ctx, le, "<=");

fn max(ctx: &mut TulispContext, rest: &TulispObject) -> Result<TulispObject, Error> {
Expand Down
65 changes: 65 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,71 @@ macro_rules! tulisp_assert {
};
}

#[test]
fn test_comparison_of_numbers() -> Result<(), Error> {
// Greater than
tulisp_assert! { program: "(> 10 10)", result: "nil" }
tulisp_assert! { program: "(> 10 5)", result: "t" }
tulisp_assert! { program: "(> 5 10)", result: "nil" }
tulisp_assert! { program: "(> 2 4 6)", result: "nil" }
tulisp_assert! { program: "(> 2 6 4)", result: "nil" }
tulisp_assert! { program: "(> 6 2 4)", result: "nil" }
tulisp_assert! { program: "(> 6 4 2)", result: "t" }
tulisp_assert! { program: "(> 10.0 5.0)", result: "t" }
tulisp_assert! { program: "(> 5.0 10.0)", result: "nil" }
tulisp_assert! {
program: "(let ((a 10)) (> a))",
error: "<eval_string>:1.14-1.19: ERR OutOfRange: gt requires at least 2 arguments"
}

// Greater than or equal
tulisp_assert! { program: "(>= 10 10)", result: "t" }
tulisp_assert! { program: "(>= 10 5)", result: "t" }
tulisp_assert! { program: "(>= 5 10)", result: "nil" }
tulisp_assert! { program: "(>= 2 4 6)", result: "nil" }
tulisp_assert! { program: "(>= 2 6 4)", result: "nil" }
tulisp_assert! { program: "(>= 6 2 4)", result: "nil" }
tulisp_assert! { program: "(>= 6 4 2)", result: "t" }
tulisp_assert! { program: "(>= 10.0 5.0)", result: "t" }
tulisp_assert! { program: "(>= 5.0 10.0)", result: "nil" }
tulisp_assert! {
program: "(let ((a 10)) (>= a))",
error: "<eval_string>:1.14-1.20: ERR OutOfRange: ge requires at least 2 arguments"
}

// Less than
tulisp_assert! { program: "(< 10 10)", result: "nil" }
tulisp_assert! { program: "(< 10 5)", result: "nil" }
tulisp_assert! { program: "(< 5 10)", result: "t" }
tulisp_assert! { program: "(< 2 4 6)", result: "t" }
tulisp_assert! { program: "(< 2 6 4)", result: "nil" }
tulisp_assert! { program: "(< 6 2 4)", result: "nil" }
tulisp_assert! { program: "(< 6 4 2)", result: "nil" }
tulisp_assert! { program: "(< 10.0 5.0)", result: "nil" }
tulisp_assert! { program: "(< 5.0 10.0)", result: "t" }
tulisp_assert! {
program: "(let ((a 10)) (< a))",
error: "<eval_string>:1.14-1.19: ERR OutOfRange: lt requires at least 2 arguments"
}

// Less than or equal
tulisp_assert! { program: "(<= 10 10)", result: "t" }
tulisp_assert! { program: "(<= 10 5)", result: "nil" }
tulisp_assert! { program: "(<= 5 10)", result: "t" }
tulisp_assert! { program: "(<= 2 4 6)", result: "t" }
tulisp_assert! { program: "(<= 2 6 4)", result: "nil" }
tulisp_assert! { program: "(<= 6 2 4)", result: "nil" }
tulisp_assert! { program: "(<= 6 4 2)", result: "nil" }
tulisp_assert! { program: "(<= 10.0 5.0)", result: "nil" }
tulisp_assert! { program: "(<= 5.0 10.0)", result: "t" }
tulisp_assert! {
program: "(let ((a 10)) (<= a))",
error: "<eval_string>:1.14-1.20: ERR OutOfRange: le requires at least 2 arguments"
}

Ok(())
}

#[test]
fn test_conditionals() -> Result<(), Error> {
tulisp_assert! { program: "(if t 10 15 20)", result: "10" }
Expand Down

0 comments on commit 4e97692

Please sign in to comment.