Skip to content

Commit

Permalink
clojure.string/join
Browse files Browse the repository at this point in the history
  • Loading branch information
erkkikeranen committed May 11, 2020
1 parent c70e35a commit 5caf240
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 15 deletions.
109 changes: 95 additions & 14 deletions src/clojure_string/join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ use std::rc::Rc;

use crate::error_message;
use crate::type_tag::TypeTag;
use crate::persistent_list::ToPersistentListIter;
use crate::persistent_vector::ToPersistentVectorIter;
use itertools::Itertools;
use crate::persistent_list::PersistentList::Cons;

/// clojure.string/reverse ; reverses a string
/// (defn print-string [string] .. prints single string .. )
/// clojure.string/join ; joins a coll of items together as a string
/// (join
/// [coll]
/// [separator coll])
#[derive(Debug, Clone)]
pub struct JoinFn {}
impl ToValue for JoinFn {
Expand All @@ -16,15 +22,42 @@ impl ToValue for JoinFn {
}
impl IFn for JoinFn {
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
if args.len() == 1 {
match args.get(0).unwrap().to_value() {
Value::Iseq(s) => Value::String(s.chars().rev().collect()),
_a => error_message::type_mismatch(TypeTag::String, &_a.to_value())
if args.len() == 1 || args.len() == 2 {
let separator = if args.len() == 2 {
args.get(0).unwrap().to_string()
} else { String::from("") };
let coll = if args.len() == 1 { args.get(0) } else { args.get(1) };
match coll.unwrap().to_value() {
Value::PersistentList(Cons(head, tail, count)) => {
return if count == 0 {
Value::String(String::from(""))
} else if count == 1 {
Value::String(head.to_string())
} else {
Value::String(
String::from(
head.to_string()) + separator.as_str() +
tail.iter()
.map(|x| x.to_string())
.collect::<Vec<std::string::String>>()
.join(&separator).as_str())
}
},
Value::PersistentVector(pvec) => {
return if pvec.vals.len() == 0 {
Value::String(String::from(""))
} else {
Value::String(String::from(
pvec.vals.iter()
.map(|x| x.to_string())
.collect::<Vec<std::string::String>>()
.join(&separator).as_str()))
}
}
_ => Value::String(String::from(""))
}

} else {
return error_message::wrong_arg_count(2, args.len());

return error_message::wrong_varg_count(&[1,2], args.len());
}
}
}
Expand All @@ -34,15 +67,63 @@ mod tests {
mod reverse_tests {
use crate::value::Value;
use std::rc::Rc;
use crate::clojure_string::reverse::JoinFn;
use crate::clojure_string::join::JoinFn;
use crate::ifn::IFn;
use crate::persistent_list::PersistentList;
use crate::persistent_vector::PersistentVector;

#[test]
fn join_empty_collection_to_empty_string() {
let join = JoinFn {};
let args = vec![Rc::new(Value::PersistentList(vec![].into_iter().collect::<PersistentList>()))];
assert_eq!(Value::String(String::from("")), join.invoke(args));
}

#[test]
fn join_one_item_collection_to_string() {
let join = JoinFn {};
let s = "hello";
let args = vec![Rc::new(Value::PersistentList(
vec![Rc::new(Value::String(String::from(s)))].into_iter().collect::<PersistentList>()))];
assert_eq!(Value::String(String::from("hello")), join.invoke(args));
}

#[test]
fn join_multiple_items_in_collection_to_string() {
let join = JoinFn {};
let s = "hello";
let args = vec![Rc::new(Value::PersistentList(
vec![Rc::new(Value::String(String::from(s))),
Rc::new(Value::I32(5)),
Rc::new(Value::String(String::from(s)))]
.into_iter().collect::<PersistentList>()))];
assert_eq!(Value::String(String::from("hello5hello")), join.invoke(args));
}

#[test]
fn join_multiple_items_in_collection_with_separator_to_string() {
let join = JoinFn {};
let s = "hello";
let args = vec![Rc::new(Value::String(String::from(", "))),
Rc::new(Value::PersistentList(
vec![Rc::new(Value::String(String::from(s))),
Rc::new(Value::I32(5)),
Rc::new(Value::String(String::from(s)))]
.into_iter().collect::<PersistentList>()))];
assert_eq!(Value::String(String::from("hello, 5, hello")), join.invoke(args));
}

#[test]
fn reverse_string() {
let reverse = JoinFn {};
fn join_multiple_items_in_pvec_collection_with_separator_to_string() {
let join = JoinFn {};
let s = "hello";
let args = vec![Rc::new(Value::String(String::from(s)))];
assert_eq!(Value::String(String::from("olleh")), reverse.invoke(args));
let args = vec![Rc::new(Value::String(String::from(", "))),
Rc::new(Value::PersistentVector(
vec![Rc::new(Value::String(String::from(s))),
Rc::new(Value::I32(5)),
Rc::new(Value::String(String::from(s)))]
.into_iter().collect::<PersistentVector>()))];
assert_eq!(Value::String(String::from("hello, 5, hello")), join.invoke(args));
}
}
}
11 changes: 10 additions & 1 deletion src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl Environment {

// clojure.string
let reverse_fn = clojure_string::reverse::ReverseFn {};
let join_fn = clojure_string::join::JoinFn {};

// Hardcoded fns
let lexical_eval_fn = Value::LexicalEvalFn {};
Expand Down Expand Up @@ -251,7 +252,15 @@ impl Environment {
environment.insert(Symbol::intern("rust-slurp"), slurp_fn.to_rc_value());

// clojure.string
environment.insert(Symbol::intern("clojure_string_reverse"), reverse_fn.to_rc_value());
environment.insert_into_namespace(
&Symbol::intern("clojure.string"),
Symbol::intern("clojure_string_"),
reverse_fn.to_rc_value());

environment.insert_into_namespace(
&Symbol::intern("clojure.string"),
Symbol::intern("join"),
join_fn.to_rc_value());

environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
Expand Down

0 comments on commit 5caf240

Please sign in to comment.