Skip to content

Commit aee2ec4

Browse files
committed
adds count
clojure.core/count
1 parent d16acb8 commit aee2ec4

File tree

4 files changed

+92
-3
lines changed

4 files changed

+92
-3
lines changed

src/environment.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ impl Environment {
245245
}
246246
}
247247
}
248-
// @TODO refactor to use ^
248+
// @TODO refactor to use ^
249249
// @TODO figure out convention for 'ns' vs 'namespace'
250250
/// Get closest value "around" us; try our local environment, then
251251
/// try our main environment (unless its namespace qualified)
@@ -339,6 +339,7 @@ impl Environment {
339339
let meta_fn = rust_core::MetaFn::new(Rc::clone(&environment));
340340
let with_meta_fn = rust_core::WithMetaFn::new(Rc::clone(&environment));
341341
let var_fn = rust_core::special_form::VarFn::new(Rc::clone(&environment));
342+
let count_fn = rust_core::count::CountFn {};
342343
// @TODO after we merge this with all the other commits we have,
343344
// just change all the `insert`s here to use insert_in_namespace
344345
// I prefer explicity and the non-dependence-on-environmental-factors
@@ -361,6 +362,12 @@ impl Environment {
361362
environment.insert(Symbol::intern("with-meta"), with_meta_fn.to_rc_value());
362363
environment.insert(Symbol::intern("var-fn*"), var_fn.to_rc_value());
363364

365+
environment.insert_into_namespace(
366+
&Symbol::intern("clojure.core"),
367+
Symbol::intern("count"),
368+
count_fn.to_rc_value(),
369+
);
370+
364371
// Thread namespace
365372
environment.insert_into_namespace(
366373
&Symbol::intern("Thread"),
@@ -463,7 +470,6 @@ impl Environment {
463470
split_fn.to_rc_value(),
464471
);
465472

466-
467473
environment.insert(Symbol::intern("quote"), quote_macro.to_rc_value());
468474
environment.insert(Symbol::intern("do-fn*"), do_fn.to_rc_value());
469475
environment.insert(Symbol::intern("do"), do_macro.to_rc_value());

src/rust_core.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub(crate) mod first;
6565
pub use self::first::*;
6666
pub(crate) mod second;
6767
pub use self::second::*;
68+
pub(crate) mod count;
69+
pub use self::count::*;
6870

6971
// input and output
7072
pub(crate) mod system_newline;

src/rust_core/count.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::ifn::IFn;
2+
use crate::value::{ToValue, Value};
3+
use std::rc::Rc;
4+
5+
use crate::error_message;
6+
use crate::iterable::Iterable;
7+
use crate::persistent_list::ToPersistentList;
8+
use crate::protocol::ProtocolCastable;
9+
use crate::type_tag::TypeTag;
10+
11+
/// (count coll)
12+
/// counts items in coll
13+
#[derive(Debug, Clone)]
14+
pub struct CountFn {}
15+
impl ToValue for CountFn {
16+
fn to_value(&self) -> Value {
17+
Value::IFn(Rc::new(self.clone()))
18+
}
19+
}
20+
impl IFn for CountFn {
21+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
22+
if args.len() != 1 {
23+
return error_message::wrong_arg_count(1, args.len());
24+
}
25+
26+
let coll_size = match args.get(0).unwrap().try_as_protocol::<Iterable>() {
27+
Some(iterable) => iterable.iter().count(),
28+
None => match args.get(0).unwrap().to_value() {
29+
Value::Nil => 0,
30+
_unsupported => return error_message::type_mismatch(TypeTag::ISeq, &_unsupported),
31+
},
32+
};
33+
34+
return Value::I32(coll_size as i32);
35+
}
36+
}
37+
38+
#[cfg(test)]
39+
mod tests {
40+
mod count_tests {
41+
use crate::ifn::IFn;
42+
use crate::persistent_vector::PersistentVector;
43+
use crate::rust_core::CountFn;
44+
use crate::value::Value;
45+
use std::rc::Rc;
46+
47+
#[test]
48+
fn count_nil() {
49+
let count = CountFn {};
50+
let args = vec![Rc::new(Value::Nil)];
51+
assert_eq!(Value::I32(0), count.invoke(args));
52+
}
53+
54+
#[test]
55+
fn count_vector() {
56+
let count = CountFn {};
57+
let args = vec![Rc::new(Value::PersistentVector(
58+
vec![
59+
Rc::new(Value::String(String::from("1"))),
60+
Rc::new(Value::String(String::from("2"))),
61+
]
62+
.into_iter()
63+
.collect::<PersistentVector>(),
64+
))];
65+
assert_eq!(Value::I32(2), count.invoke(args));
66+
}
67+
68+
#[test]
69+
fn count_something_unsupported() {
70+
let count = CountFn {};
71+
let args = vec![Rc::new(Value::Boolean(true))];
72+
assert_eq!(
73+
Value::Condition(
74+
"Type mismatch; Expected instance of clojure.lang.ISeq, Recieved type true"
75+
.to_string()
76+
),
77+
count.invoke(args)
78+
);
79+
}
80+
}
81+
}

src/rust_core/refer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::environment::Environment;
22
use crate::error_message;
33
use crate::ifn::IFn;
44
use crate::keyword::Keyword;
5-
use crate::persistent_vector::{ToPersistentVectorIter};
5+
use crate::persistent_vector::ToPersistentVectorIter;
66
use crate::symbol::Symbol;
77
use crate::type_tag::TypeTag;
88
use crate::util::IsOdd;

0 commit comments

Comments
 (0)