Skip to content

Commit fb887e8

Browse files
committed
lt, gt, lte, gte with tests
1 parent 54d6e40 commit fb887e8

File tree

8 files changed

+359
-8
lines changed

8 files changed

+359
-8
lines changed

src/environment.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,10 @@ impl Environment {
307307
let ns_macro = rust_core::NsMacro::new(Rc::clone(&environment));
308308
let load_file_fn = rust_core::LoadFileFn::new(Rc::clone(&environment));
309309
let refer_fn = rust_core::ReferFn::new(Rc::clone(&environment));
310+
let lt_fn = rust_core::lt::LtFn {};
311+
let gt_fn = rust_core::gt::GtFn {};
312+
let lte_fn = rust_core::lte::LteFn {};
313+
let gte_fn = rust_core::gte::GteFn {};
310314
// @TODO after we merge this with all the other commits we have,
311315
// just change all the `insert`s here to use insert_in_namespace
312316
// I prefer explicity and the non-dependence-on-environmental-factors
@@ -326,6 +330,30 @@ impl Environment {
326330
environment.insert(Symbol::intern("defmacro"), defmacro_macro.to_rc_value());
327331
environment.insert(Symbol::intern("eval"), eval_fn.to_rc_value());
328332

333+
// Interop to read real clojure.core
334+
environment.insert_into_namespace(
335+
&Symbol::intern("clojure.interop"),
336+
Symbol::intern("lt"),
337+
lt_fn.to_rc_value(),
338+
);
339+
340+
environment.insert_into_namespace(
341+
&Symbol::intern("clojure.interop"),
342+
Symbol::intern("gt"),
343+
gt_fn.to_rc_value(),
344+
);
345+
environment.insert_into_namespace(
346+
&Symbol::intern("clojure.interop"),
347+
Symbol::intern("lte"),
348+
lte_fn.to_rc_value(),
349+
);
350+
351+
environment.insert_into_namespace(
352+
&Symbol::intern("clojure.interop"),
353+
Symbol::intern("gte"),
354+
gte_fn.to_rc_value(),
355+
);
356+
329357
// Thread namespace
330358
environment.insert_into_namespace(
331359
&Symbol::intern("Thread"),

src/reader.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ fn is_period_char(chr: char) -> bool {
148148
///
149149
/// Clojure defines a whitespace as either a comma or an unicode whitespace.
150150
fn is_clojure_whitespace(c: char) -> bool {
151-
c.is_whitespace() || c == ','
151+
c.is_whitespace() || c == ','
152152
}
153153
////////////////////////////////////////////////////////////////////////////////////////////////////
154154
// End predicates
@@ -169,16 +169,16 @@ fn consume_clojure_whitespaces_parser(input: &str) -> IResult<&str, ()> {
169169

170170
named!(whitespace_parser<&str,()>,
171171
value!((),
172-
many0!(alt!(comment_parser |
172+
many0!(alt!(comment_parser |
173173
take_while1!(is_clojure_whitespace))))
174174
);
175175

176176
named!(no_whitespace_parser<&str,()>, value!((),tag!("")));
177177

178-
// @TODO rename / check that all parsers are consistent?
178+
// @TODO rename / check that all parsers are consistent?
179179
named!(parser<&str,()>,
180180
// Because 'whitespace_parser' loops, we cannot include the case where there's no whitespace at all in
181-
// its definition -- nom wouldn't allow it, as it would loop forever consuming no whitespace
181+
// its definition -- nom wouldn't allow it, as it would loop forever consuming no whitespace
182182
// So instead, we eat up all the whitespace first, and then use the no_whitespace_parser as our sort-of
183183
// base-case after
184184
alt!(whitespace_parser | no_whitespace_parser)
@@ -560,12 +560,15 @@ pub fn read<R: BufRead>(reader: &mut R) -> Value {
560560
// loop over and ask for more lines, accumulating them in input_buffer until we can read
561561
loop {
562562
let maybe_line = reader.by_ref().lines().next();
563-
563+
564564
match maybe_line {
565565
Some(Err(e)) => return Value::Condition(format!("Reader error: {}", e)),
566566
// `lines` does not include \n, but \n is part of the whitespace given to the reader
567-
// (and is important for reading comments) so we will push a newline as well
568-
Some(Ok(line)) => { input_buffer.push_str(&line); input_buffer.push_str("\n"); },
567+
// (and is important for reading comments) so we will push a newline as well
568+
Some(Ok(line)) => {
569+
input_buffer.push_str(&line);
570+
input_buffer.push_str("\n");
571+
}
569572
None => {
570573
return Value::Condition(String::from("Tried to read empty stream; unexpected EOF"))
571574
}

src/rust_core.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ pub use self::rand::*;
3535
pub(crate) mod rand_int;
3636
pub use self::rand_int::*;
3737

38+
pub(crate) mod lt;
39+
pub use self::lt::*;
40+
41+
pub(crate) mod lte;
42+
pub use self::lte::*;
43+
44+
pub(crate) mod gt;
45+
pub use self::gt::*;
46+
47+
pub(crate) mod gte;
48+
pub use self::gte::*;
49+
3850
// string
3951
pub(crate) mod str;
4052
pub use self::str::*;

src/rust_core/gt.rs

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

src/rust_core/gte.rs

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

src/rust_core/lt.rs

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

0 commit comments

Comments
 (0)