Skip to content

Commit 735ca1b

Browse files
committed
Merge branch 'feature/coll-operations' [#46]
2 parents 91d74be + d567ad6 commit 735ca1b

File tree

11 files changed

+287
-7
lines changed

11 files changed

+287
-7
lines changed

src/clojure/core.clj

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
(def *flush-on-newline* true)
22
(def *print-readably* true)
33

4+
(defmacro when [test & body]
5+
(list 'if test (concat (list 'do) body)))
6+
47
(def list (fn [& ls] ls))
58

69
(defmacro defn [name args & body]
@@ -25,7 +28,9 @@
2528
(defn prn [& more]
2629
(apply pr more)
2730
(newline)
28-
(flush))
31+
(when *flush-on-newline*
32+
(flush)
33+
nil))
2934

3035
(defn print [& more]
3136
(apply pr more))
@@ -47,3 +52,17 @@
4752

4853
(defn slurp [f & opts]
4954
(rust-slurp f opts))
55+
56+
"basic operations on collections"
57+
58+
(defn rest [x]
59+
(more x))
60+
61+
(defn next [x]
62+
(let [result (rest x)]
63+
(if (= '() result)
64+
nil
65+
result)))
66+
67+
(defn ffirst [x]
68+
(first (first x)))

src/clojure_string/blank_qmark_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl IFn for BlankFn {
4242

4343
#[cfg(test)]
4444
mod tests {
45-
mod reverse_tests {
45+
mod blank_tests {
4646
use crate::clojure_string::blank_qmark_::BlankFn;
4747
use crate::ifn::IFn;
4848
use crate::value::Value;

src/clojure_string/ends_with_qmark_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl IFn for EndsWithFn {
3333

3434
#[cfg(test)]
3535
mod tests {
36-
mod reverse_tests {
36+
mod ends_with_tests {
3737
use crate::clojure_string::ends_with_qmark_::EndsWithFn;
3838
use crate::ifn::IFn;
3939
use crate::value::Value;

src/clojure_string/includes_qmark_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl IFn for IncludesFn {
3333

3434
#[cfg(test)]
3535
mod tests {
36-
mod reverse_tests {
36+
mod includes_tests {
3737
use crate::clojure_string::includes_qmark_::IncludesFn;
3838
use crate::ifn::IFn;
3939
use crate::value::Value;

src/clojure_string/join.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl IFn for JoinFn {
5050

5151
#[cfg(test)]
5252
mod tests {
53-
mod reverse_tests {
53+
mod join_tests {
5454
use crate::clojure_string::join::JoinFn;
5555
use crate::ifn::IFn;
5656
use crate::persistent_list::PersistentList;

src/clojure_string/starts_with_qmark_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl IFn for StartsWithFn {
3333

3434
#[cfg(test)]
3535
mod tests {
36-
mod reverse_tests {
36+
mod starts_with_tests {
3737
use crate::clojure_string::starts_with_qmark_::StartsWithFn;
3838
use crate::ifn::IFn;
3939
use crate::value::Value;

src/environment.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ impl Environment {
194194
let print_string_fn = rust_core::PrintStringFn {};
195195
let read_line_fn = rust_core::ReadLineFn {};
196196
let assoc_fn = rust_core::AssocFn {};
197+
let more_fn = rust_core::MoreFn {};
198+
let first_fn = rust_core::FirstFn {};
199+
let second_fn = rust_core::SecondFn {};
197200

198201
// rust implementations of core functions
199202
let slurp_fn = rust_core::slurp::SlurpFn {};
@@ -380,7 +383,9 @@ impl Environment {
380383
environment.insert(Symbol::intern("assoc"), assoc_fn.to_rc_value());
381384
environment.insert(Symbol::intern("get"), get_fn.to_rc_value());
382385
environment.insert(Symbol::intern("concat"), concat_fn.to_rc_value());
383-
386+
environment.insert(Symbol::intern("more"), more_fn.to_rc_value());
387+
environment.insert(Symbol::intern("first"), first_fn.to_rc_value());
388+
environment.insert(Symbol::intern("second"), second_fn.to_rc_value());
384389
// input and output
385390
environment.insert(
386391
Symbol::intern("system-newline"),

src/rust_core.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ pub use self::get::*;
4747
pub(crate) mod map;
4848
pub use self::map::*;
4949

50+
pub(crate) mod more;
51+
pub use self::more::*;
52+
pub(crate) mod first;
53+
pub use self::first::*;
54+
pub(crate) mod second;
55+
pub use self::second::*;
56+
5057
// input and output
5158
pub(crate) mod system_newline;
5259
pub use self::system_newline::*;

src/rust_core/first.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use crate::error_message;
2+
use crate::ifn::IFn;
3+
use crate::iterable::Iterable;
4+
use crate::persistent_list::PersistentList;
5+
use crate::protocol::{Protocol, ProtocolCastable};
6+
use crate::type_tag::TypeTag;
7+
use crate::value::{ToValue, Value};
8+
use std::rc::Rc;
9+
10+
/// (first x)
11+
/// returns first element or nil
12+
/// TODO: support for strings
13+
#[derive(Debug, Clone)]
14+
pub struct FirstFn {}
15+
impl ToValue for FirstFn {
16+
fn to_value(&self) -> Value {
17+
Value::IFn(Rc::new(self.clone()))
18+
}
19+
}
20+
impl IFn for FirstFn {
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+
match args.get(0).unwrap().try_as_protocol::<Iterable>() {
26+
Some(iterable) => match iterable.iter().next() {
27+
Some(val) => val.to_value(),
28+
_ => Value::Nil,
29+
},
30+
_ => error_message::type_mismatch(TypeTag::ISeq, args.get(0).unwrap()),
31+
}
32+
}
33+
}
34+
35+
#[cfg(test)]
36+
mod tests {
37+
mod first_tests {
38+
use crate::ifn::IFn;
39+
use crate::persistent_list::PersistentList;
40+
use crate::persistent_vector::PersistentVector;
41+
use crate::rust_core::first::FirstFn;
42+
use crate::value::Value;
43+
use std::rc::Rc;
44+
45+
#[test]
46+
fn first_on_empty_iterable() {
47+
let first = FirstFn {};
48+
let args = vec![Rc::new(Value::PersistentList(
49+
vec![].into_iter().collect::<PersistentList>(),
50+
))];
51+
assert_eq!(Value::Nil, first.invoke(args));
52+
}
53+
54+
#[test]
55+
fn first_on_iterable_with_one_value_list() {
56+
let first = FirstFn {};
57+
let args = vec![Rc::new(Value::PersistentList(
58+
vec![Rc::new(Value::Boolean(true))]
59+
.into_iter()
60+
.collect::<PersistentList>(),
61+
))];
62+
assert_eq!(Value::Boolean(true), first.invoke(args));
63+
}
64+
65+
#[test]
66+
#[should_panic]
67+
fn first_on_non_iterable_value() {
68+
let first = FirstFn {};
69+
let args = vec![Rc::new(Value::Nil)];
70+
assert_eq!(Value::Nil, first.invoke(args));
71+
}
72+
}
73+
}

src/rust_core/more.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use crate::error_message;
2+
use crate::ifn::IFn;
3+
use crate::iterable::Iterable;
4+
use crate::persistent_list::PersistentList;
5+
use crate::protocol::{Protocol, ProtocolCastable};
6+
use crate::type_tag::TypeTag;
7+
use crate::value::{ToValue, Value};
8+
use std::rc::Rc;
9+
10+
/// (more x)
11+
/// for rest, returns possibly empty sequence after the first
12+
/// TODO: support for strings
13+
#[derive(Debug, Clone)]
14+
pub struct MoreFn {}
15+
impl ToValue for MoreFn {
16+
fn to_value(&self) -> Value {
17+
Value::IFn(Rc::new(self.clone()))
18+
}
19+
}
20+
impl IFn for MoreFn {
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+
match args.get(0).unwrap().try_as_protocol::<Iterable>() {
26+
Some(iterable) => match iterable.iter().collect::<Vec<Rc<Value>>>().split_first() {
27+
Some((_, more)) => {
28+
Value::PersistentList(more.to_vec().into_iter().collect::<PersistentList>())
29+
}
30+
_ => Value::PersistentList(vec![].into_iter().collect::<PersistentList>()),
31+
},
32+
_ => error_message::type_mismatch(TypeTag::ISeq, args.get(0).unwrap()),
33+
}
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
mod more_tests {
40+
use crate::ifn::IFn;
41+
use crate::persistent_list::PersistentList;
42+
use crate::persistent_vector::PersistentVector;
43+
use crate::rust_core::more::MoreFn;
44+
use crate::value::Value;
45+
use std::rc::Rc;
46+
47+
#[test]
48+
fn more_on_empty_iterable() {
49+
let more = MoreFn {};
50+
let args = vec![Rc::new(Value::PersistentList(
51+
vec![].into_iter().collect::<PersistentList>(),
52+
))];
53+
assert_eq!(
54+
Value::PersistentList(vec![].into_iter().collect::<PersistentList>()),
55+
more.invoke(args)
56+
);
57+
}
58+
59+
#[test]
60+
fn more_on_iterable_with_one_value_list() {
61+
let more = MoreFn {};
62+
let args = vec![Rc::new(Value::PersistentList(
63+
vec![Rc::new(Value::Boolean(true))]
64+
.into_iter()
65+
.collect::<PersistentList>(),
66+
))];
67+
assert_eq!(
68+
Value::PersistentList(vec![].into_iter().collect::<PersistentList>()),
69+
more.invoke(args)
70+
);
71+
}
72+
73+
#[test]
74+
fn more_on_iterable_with_three_value_list() {
75+
let more = MoreFn {};
76+
let args = vec![Rc::new(Value::PersistentList(
77+
vec![
78+
Rc::new(Value::I32(1)),
79+
Rc::new(Value::I32(2)),
80+
Rc::new(Value::I32(3)),
81+
]
82+
.into_iter()
83+
.collect::<PersistentList>(),
84+
))];
85+
assert_eq!(
86+
Value::PersistentList(
87+
vec![Rc::new(Value::I32(2)), Rc::new(Value::I32(3))]
88+
.into_iter()
89+
.collect::<PersistentList>()
90+
),
91+
more.invoke(args)
92+
);
93+
}
94+
95+
#[test]
96+
#[should_panic]
97+
fn more_on_non_iterable_value() {
98+
let more = MoreFn {};
99+
let args = vec![Rc::new(Value::Nil)];
100+
assert_eq!(Value::Nil, more.invoke(args));
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)