Skip to content

Commit a6fb2f9

Browse files
committed
divide, multiply, subtract with unittests, clojure inc and dec functions, temporary _slash_ name for division function in environment
1 parent 265148c commit a6fb2f9

File tree

7 files changed

+346
-0
lines changed

7 files changed

+346
-0
lines changed

src/clojure/core.clj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@
1010

1111
(defn println [& more]
1212
(print-string (apply str more)))
13+
14+
(defn inc [x]
15+
(+ x 1))
16+
17+
(defn dec [x]
18+
(- x 1))

src/environment.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ impl Environment {
7171
pub fn clojure_core_environment() -> Rc<Environment> {
7272
// Register our macros / functions ahead of time
7373
let add_fn = rust_core::AddFn {};
74+
let subtract_fn = rust_core::_subtract_::SubtractFn {};
75+
let multiply_fn = rust_core::_multiply_::MultiplyFn {};
76+
let divide_fn = rust_core::_divide_::DivideFn {};
7477
let str_fn = rust_core::StrFn {};
7578
let do_fn = rust_core::DoFn {};
7679
let nth_fn = rust_core::NthFn {};
@@ -94,6 +97,9 @@ impl Environment {
9497
let eval_fn = rust_core::EvalFn::new(Rc::clone(&environment));
9598

9699
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
100+
environment.insert(Symbol::intern("-"), subtract_fn.to_rc_value());
101+
environment.insert(Symbol::intern("*"), multiply_fn.to_rc_value());
102+
environment.insert(Symbol::intern("_slash_"), divide_fn.to_rc_value());
97103
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
98104
environment.insert(Symbol::intern("str"), str_fn.to_rc_value());
99105
environment.insert(Symbol::intern("quote"), quote_macro.to_rc_value());

src/error_message.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ pub fn wrong_varg_count(expected: &[usize], got: usize) -> Value {
2525
))
2626
}
2727

28+
pub fn zero_arg_count(got: usize) -> Value {
29+
Value::Condition(format!(
30+
"Wrong number of arguments given to function (Given: {})", got))
31+
}
32+
2833
pub fn index_out_of_bounds(ind: usize, count: usize) -> Value {
2934
Value::Condition(format!(
3035
"Index out of bounds: Index ({}), Length: ({})",

src/rust_core.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ use crate::error_message;
1919

2020
use crate::util::IsEven;
2121

22+
pub(crate) mod _subtract_;
23+
pub(crate) mod _divide_;
24+
pub(crate) mod _multiply_;
25+
2226
// This module will hold core function and macro primitives that aren't special cases
2327
// (like the quote macro, or let), and can't be implemented in clojure itself
2428

src/rust_core/_divide_.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{Value, ToValue};
3+
use std::rc::Rc;
4+
5+
use crate::error_message;
6+
7+
/// (/ x y & xys)
8+
///
9+
#[derive(Debug, Clone)]
10+
pub struct DivideFn {}
11+
impl ToValue for DivideFn {
12+
fn to_value(&self) -> Value {
13+
Value::IFn(Rc::new(self.clone()))
14+
}
15+
}
16+
impl IFn for DivideFn {
17+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
18+
match args.len() {
19+
0 => { error_message::zero_arg_count(args.len()) },
20+
1 => {
21+
let val = args.get(0).unwrap().to_value();
22+
match val {
23+
Value::I32(a_) => Value::F64(1.0 / a_ as f64),
24+
Value::F64(f_) => Value::F64(1.0 / f_),
25+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
26+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
27+
val.type_tag()
28+
))
29+
}
30+
},
31+
_ => {
32+
let mut args_iterator = args.into_iter();
33+
let first_arg = args_iterator.next().unwrap();
34+
args_iterator.fold(first_arg.to_value(), |a, b| match a {
35+
Value::I32(a_) => match *b {
36+
Value::I32(b_) => Value::I32(a_ / b_),
37+
Value::F64(b_) => Value::F64(a_ as f64 / b_),
38+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
39+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
40+
b.type_tag()
41+
)),
42+
},
43+
Value::F64(a_) => match *b {
44+
Value::I32(b_) => Value::F64(a_ / b_ as f64),
45+
Value::F64(b_) => Value::F64(a_ / b_),
46+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
47+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
48+
b.type_tag()
49+
)),
50+
},
51+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
52+
"Type mismatch: Expecting: (i32 | i64 | f32 | f64), Found: {}",
53+
a.type_tag()
54+
)),
55+
})
56+
}
57+
}
58+
}
59+
}
60+
61+
#[cfg(test)]
62+
mod tests {
63+
mod Divide_tests {
64+
use crate::rust_core::_divide_::DivideFn;
65+
use crate::ifn::IFn;
66+
use crate::value::Value;
67+
use std::rc::Rc;
68+
69+
#[test]
70+
fn Divide_without_arguments_returns_one() {
71+
let Divide = DivideFn {};
72+
let args = vec![];
73+
assert_eq!(Value::Condition(String::from("Wrong number of arguments given to function (Given: 0)")),
74+
Divide.invoke(args));
75+
}
76+
77+
#[test]
78+
fn Divide_with_one_positive_argument_returns_reciprocal() {
79+
let divide = DivideFn {};
80+
let args = vec![Rc::new(Value::I32(5))];
81+
assert_eq!(Value::F64(1.0/5.0), divide.invoke(args));
82+
}
83+
84+
#[test]
85+
fn Divide_with_one_negative_argument_returns_reciprocal() {
86+
let divide = DivideFn {};
87+
let args = vec![Rc::new(Value::I32(-5))];
88+
assert_eq!(Value::F64(1.0 / -5.0), divide.invoke(args));
89+
}
90+
91+
#[test]
92+
fn Divide_with_two_integer_argument_returns_quotient() {
93+
let divide = DivideFn {};
94+
let args = vec![Rc::new(Value::I32(24)), Rc::new(Value::I32(6))];
95+
assert_eq!(Value::I32(4), divide.invoke(args));
96+
}
97+
98+
#[test]
99+
fn Divide_with_one_double_argument_returns_quotient() {
100+
let divide = DivideFn {};
101+
let args = vec![Rc::new(Value::I32(24)), Rc::new(Value::F64(1.5))];
102+
assert_eq!(Value::F64(16.0), divide.invoke(args));
103+
}
104+
105+
#[test]
106+
fn Divide_with_multiple_integer_arguments_returns_quotient() {
107+
let Divide = DivideFn {};
108+
let args = vec![
109+
Rc::new(Value::I32(100)),
110+
Rc::new(Value::I32(5)),
111+
Rc::new(Value::I32(4))];
112+
assert_eq!(Value::I32(5), Divide.invoke(args));
113+
}
114+
115+
#[test]
116+
fn Divide_with_multiple_mixed_arguments_returns_quotient() {
117+
let Divide = DivideFn {};
118+
let args = vec![
119+
Rc::new(Value::I32(100)),
120+
Rc::new(Value::I32(5)),
121+
Rc::new(Value::I32(4)),
122+
Rc::new(Value::F64(2.0))];
123+
assert_eq!(Value::F64(2.5), Divide.invoke(args));
124+
}
125+
126+
}
127+
}

src/rust_core/_multiply_.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{Value, ToValue};
3+
use std::rc::Rc;
4+
5+
/// (* x y & xys)
6+
///
7+
#[derive(Debug, Clone)]
8+
pub struct MultiplyFn {}
9+
impl ToValue for MultiplyFn {
10+
fn to_value(&self) -> Value {
11+
Value::IFn(Rc::new(self.clone()))
12+
}
13+
}
14+
impl IFn for MultiplyFn {
15+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
16+
args.into_iter().fold(1_i32.to_value(), |a, b| match a {
17+
Value::I32(a_) => match *b {
18+
Value::I32(b_) => Value::I32(a_ * b_),
19+
Value::F64(b_) => Value::F64(a_ as f64 * b_),
20+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
21+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
22+
b.type_tag()
23+
)),
24+
},
25+
Value::F64(a_) => match *b {
26+
Value::I32(b_) => Value::F64(a_ * b_ as f64),
27+
Value::F64(b_) => Value::F64(a_ * b_),
28+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
29+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
30+
b.type_tag()
31+
)),
32+
},
33+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
34+
"Type mismatch: Expecting: (i32 | i64 | f32 | f64), Found: {}",
35+
a.type_tag()
36+
)),
37+
})
38+
}
39+
}
40+
41+
#[cfg(test)]
42+
mod tests {
43+
mod multiply_tests {
44+
use crate::rust_core::_multiply_::MultiplyFn;
45+
use crate::ifn::IFn;
46+
use crate::value::Value;
47+
use std::rc::Rc;
48+
49+
#[test]
50+
fn multiply_without_arguments_returns_one() {
51+
let multiply = MultiplyFn {};
52+
let args = vec![];
53+
assert_eq!(Value::I32(1), multiply.invoke(args));
54+
}
55+
56+
#[test]
57+
fn multiply_with_one_argument_returns_identity() {
58+
let multiply = MultiplyFn {};
59+
let args = vec![Rc::new(Value::I32(5))];
60+
assert_eq!(Value::I32(5), multiply.invoke(args));
61+
}
62+
63+
#[test]
64+
fn multiply_with_two_argument_returns_product() {
65+
let multiply = MultiplyFn {};
66+
let args = vec![Rc::new(Value::I32(5)), Rc::new(Value::I32(6))];
67+
assert_eq!(Value::I32(30), multiply.invoke(args));
68+
}
69+
70+
}
71+
}

src/rust_core/_subtract_.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{Value, ToValue};
3+
use std::rc::Rc;
4+
5+
use crate::error_message;
6+
7+
/// (- x y & xys)
8+
///
9+
#[derive(Debug, Clone)]
10+
pub struct SubtractFn {}
11+
impl ToValue for SubtractFn {
12+
fn to_value(&self) -> Value {
13+
Value::IFn(Rc::new(self.clone()))
14+
}
15+
}
16+
impl IFn for SubtractFn {
17+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
18+
match args.len() {
19+
0 => { error_message::zero_arg_count(args.len()) },
20+
1 => {
21+
let val = args.get(0).unwrap().to_value();
22+
match val {
23+
Value::I32(a_) => Value::I32(-a_),
24+
Value::F64(f_) => Value::F64(-f_),
25+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
26+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
27+
val.type_tag()
28+
))
29+
}
30+
},
31+
_ => {
32+
let mut args_iterator = args.into_iter();
33+
let first_arg = args_iterator.next().unwrap();
34+
args_iterator.fold(first_arg.to_value(), |a, b| match a {
35+
Value::I32(a_) => match *b {
36+
Value::I32(b_) => Value::I32(a_ - b_),
37+
Value::F64(b_) => Value::F64(a_ as f64 - b_),
38+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
39+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
40+
b.type_tag()
41+
)),
42+
},
43+
Value::F64(a_) => match *b {
44+
Value::I32(b_) => Value::F64(a_ - b_ as f64),
45+
Value::F64(b_) => Value::F64(a_ - b_),
46+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
47+
"Type mismatch; Expecting: (i32 | i64 | f32 | f64), Found: {}",
48+
b.type_tag()
49+
)),
50+
},
51+
_ => Value::Condition(format!( // TODO: what error message should be returned regarding using typetags?
52+
"Type mismatch: Expecting: (i32 | i64 | f32 | f64), Found: {}",
53+
a.type_tag()
54+
)),
55+
})
56+
}
57+
}
58+
}
59+
}
60+
61+
#[cfg(test)]
62+
mod tests {
63+
mod subtract_tests {
64+
use crate::rust_core::_subtract_::SubtractFn;
65+
use crate::ifn::IFn;
66+
use crate::value::Value;
67+
use std::rc::Rc;
68+
69+
#[test]
70+
fn subtract_without_arguments_returns_one() {
71+
let subtract = SubtractFn {};
72+
let args = vec![];
73+
assert_eq!(Value::Condition(String::from("Wrong number of arguments given to function (Given: 0)")),
74+
subtract.invoke(args));
75+
}
76+
77+
#[test]
78+
fn subtract_with_one_positive_argument_returns_negated_value() {
79+
let subtract = SubtractFn {};
80+
let args = vec![Rc::new(Value::I32(5))];
81+
assert_eq!(Value::I32(-5), subtract.invoke(args));
82+
}
83+
84+
#[test]
85+
fn subtract_with_one_negative_argument_returns_positive_value() {
86+
let subtract = SubtractFn {};
87+
let args = vec![Rc::new(Value::I32(-5))];
88+
assert_eq!(Value::I32(5), subtract.invoke(args));
89+
}
90+
91+
#[test]
92+
fn subtract_with_two_argument_returns_negative_difference() {
93+
let subtract = SubtractFn {};
94+
let args = vec![Rc::new(Value::I32(5)), Rc::new(Value::I32(6))];
95+
assert_eq!(Value::I32(-1), subtract.invoke(args));
96+
}
97+
98+
#[test]
99+
fn subtract_with_two_argument_returns_positive_difference() {
100+
let subtract = SubtractFn {};
101+
let args = vec![Rc::new(Value::I32(6)), Rc::new(Value::I32(5))];
102+
assert_eq!(Value::I32(1), subtract.invoke(args));
103+
}
104+
105+
#[test]
106+
fn subtract_with_multiple_arguments_returns_difference_case1() {
107+
let subtract = SubtractFn {};
108+
let args = vec![
109+
Rc::new(Value::I32(-3)),
110+
Rc::new(Value::I32(7)),
111+
Rc::new(Value::I32(7)),
112+
Rc::new(Value::I32(4))];
113+
assert_eq!(Value::I32(-21), subtract.invoke(args));
114+
}
115+
116+
#[test]
117+
fn subtract_with_multiple_arguments_returns_difference_case2() {
118+
let subtract = SubtractFn {};
119+
let args = vec![
120+
Rc::new(Value::I32(-3)),
121+
Rc::new(Value::I32(7)),
122+
Rc::new(Value::I32(-7)),
123+
Rc::new(Value::I32(-4))];
124+
assert_eq!(Value::I32(1), subtract.invoke(args));
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)