Skip to content

Commit ffdaa13

Browse files
committed
Added Protocols (from Rust's perspective at least) and used an Iterable Protocol
to implement map
1 parent ad2f9c4 commit ffdaa13

File tree

4 files changed

+169
-10
lines changed

4 files changed

+169
-10
lines changed

src/environment.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ impl Environment {
7777
let concat_fn = rust_core::ConcatFn {};
7878
let print_string_fn = rust_core::PrintStringFn {};
7979
let assoc_fn = rust_core::AssocFn {};
80-
let get_fn = rust_core::GetFn {};
80+
let get_fn = rust_core::GetFn {};
81+
let map_fn = rust_core::MapFn {};
8182

8283
// Hardcoded fns
8384
let lexical_eval_fn = Value::LexicalEvalFn {};
@@ -87,10 +88,10 @@ impl Environment {
8788
let def_macro = Value::DefMacro {};
8889
let fn_macro = Value::FnMacro {};
8990
let defmacro_macro = Value::DefmacroMacro {};
90-
let if_macro = Value::IfMacro {};
91+
let if_macro = Value::IfMacro {};
9192
let environment = Rc::new(Environment::new_main_environment());
92-
93-
let load_file_fn = rust_core::LoadFileFn::new(Rc::clone(&environment));
93+
94+
let load_file_fn = rust_core::LoadFileFn::new(Rc::clone(&environment));
9495
let eval_fn = rust_core::EvalFn::new(Rc::clone(&environment));
9596

9697
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
@@ -105,22 +106,24 @@ impl Environment {
105106
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
106107
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
107108
environment.insert(Symbol::intern("str"), str_fn.to_rc_value());
109+
environment.insert(Symbol::intern("map"), map_fn.to_rc_value());
110+
108111
environment.insert(Symbol::intern("quote"), quote_macro.to_rc_value());
109112
environment.insert(Symbol::intern("do-fn*"), do_fn.to_rc_value());
110113
environment.insert(Symbol::intern("do"), do_macro.to_rc_value());
111114
environment.insert(Symbol::intern("def"), def_macro.to_rc_value());
112115
environment.insert(Symbol::intern("fn"), fn_macro.to_rc_value());
113-
environment.insert(Symbol::intern("if"), if_macro.to_rc_value());
116+
environment.insert(Symbol::intern("if"), if_macro.to_rc_value());
114117
environment.insert(Symbol::intern("defmacro"), defmacro_macro.to_rc_value());
115118
environment.insert(Symbol::intern("eval"), eval_fn.to_rc_value());
116119
environment.insert(
117120
Symbol::intern("lexical-eval"),
118121
lexical_eval_fn.to_rc_value(),
119122
);
120-
environment.insert(Symbol::intern("load-file"), load_file_fn.to_rc_value());
123+
environment.insert(Symbol::intern("load-file"), load_file_fn.to_rc_value());
121124
environment.insert(Symbol::intern("nth"), nth_fn.to_rc_value());
122-
environment.insert(Symbol::intern("assoc"), assoc_fn.to_rc_value());
123-
environment.insert(Symbol::intern("get"), get_fn.to_rc_value());
125+
environment.insert(Symbol::intern("assoc"), assoc_fn.to_rc_value());
126+
environment.insert(Symbol::intern("get"), get_fn.to_rc_value());
124127
environment.insert(Symbol::intern("concat"), concat_fn.to_rc_value());
125128
environment.insert(
126129
Symbol::intern("print-string"),

src/iterable.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::protocol::Protocol;
2+
use crate::value::Value;
3+
use crate::value::ToValue;
4+
use crate::persistent_list_map::PersistentListMapIter;
5+
use crate::persistent_vector::PersistentVectorIter;
6+
use crate::persistent_list::PersistentListIter;
7+
use crate::persistent_list_map::ToPersistentListMapIter;
8+
use crate::persistent_vector::ToPersistentVectorIter;
9+
use crate::persistent_list::ToPersistentListIter;
10+
use crate::persistent_vector::ToPersistentVector;
11+
use std::rc::Rc;
12+
use std::iter::FromIterator;
13+
14+
//
15+
// This Protocol lives inside of Clojure RS
16+
//
17+
#[derive(Debug, Clone)]
18+
pub struct Iterable {
19+
value: Rc<Value>
20+
}
21+
impl Protocol for Iterable {
22+
fn try_as_protocol(val: &Rc<Value>) -> Option<Self> {
23+
match &**val {
24+
Value::PersistentList(_) =>
25+
Some(Iterable { value: Rc::clone(val)}),
26+
Value::PersistentVector(_) =>
27+
Some(Iterable { value: Rc::clone(val)}),
28+
Value::PersistentListMap(_) =>
29+
Some(Iterable { value: Rc::clone(val)}),
30+
_ =>
31+
None
32+
}
33+
}
34+
fn try_unwrap(&self) -> Option<Rc<Value>> {
35+
match &*self.value {
36+
Value::PersistentList(_) => Some(Rc::clone(&self.value)),
37+
Value::PersistentVector(_) => Some(Rc::clone(&self.value)),
38+
Value::PersistentListMap(_) => Some(Rc::clone(&self.value)),
39+
_ => None
40+
}
41+
}
42+
}
43+
pub enum IterableIter {
44+
PersistentList(PersistentListIter),
45+
PersistentVector(PersistentVectorIter),
46+
PersistentListMap(PersistentListMapIter),
47+
}
48+
impl Iterator for IterableIter {
49+
type Item = Rc<Value>;
50+
fn next(&mut self) -> Option<Self::Item> {
51+
match self {
52+
IterableIter::PersistentList(plist_giter) => plist_giter.next(),
53+
IterableIter::PersistentVector(pvector_iter) => pvector_iter.next(),
54+
IterableIter::PersistentListMap(plist_map_iter) => {
55+
let maybe_map_entry = plist_map_iter.next();
56+
if let Some(map_entry) = maybe_map_entry {
57+
// In Clojure: [key val]
58+
return Some(vec![map_entry.key.clone(), map_entry.val.clone()].into_vector().to_rc_value());
59+
}
60+
None
61+
}
62+
}
63+
}
64+
}
65+
impl Iterable {
66+
pub fn iter(&self) -> IterableIter
67+
{
68+
match &*self.value {
69+
Value::PersistentList(plist) => IterableIter::PersistentList(Rc::new(plist.clone()).iter()),
70+
Value::PersistentVector(pvector) => IterableIter::PersistentVector(Rc::new(pvector.clone()).iter()),
71+
Value::PersistentListMap(pmap) => IterableIter::PersistentListMap(Rc::new(pmap.clone()).iter()),
72+
// We are ok panicking in this case because an invariant on the type is the assumption
73+
// that we only have an Iterable if we were able to convert
74+
_ => panic!("Called Iterable iter on non-iterable")
75+
}
76+
77+
}
78+
}

src/protocol.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use crate::value::Value;
2+
use std::rc::Rc;
3+
4+
// @TODO should we just exclusively call them protocols as
5+
// we've been doing (deciding that Clojure without the host
6+
// has Protocols as its natural abstraction for interfaces)
7+
// or should interfaces, as an abstraction, exist (and perhaps
8+
// be used by Protocols even?)
9+
/// A type that implements Protocol is one that has a pseudo downcasting of
10+
///
11+
/// value.as::<ISeq>()
12+
///
13+
/// Where ISeq would implement Protocol
14+
///
15+
/// Then you can call functions on this ISeq, if you got one back, to forward
16+
/// functions to the Value inside, and unwrap() it to get your Value back when
17+
/// you're done
18+
pub trait Protocol: Sized {
19+
fn try_as_protocol(val: &Rc<Value>) -> Option<Self>; // where Self:Sized;
20+
/// Panics if your Value isn't an instance of Protocol
21+
fn as_protocol(val: &Rc<Value>) -> Self {
22+
Self::try_as_protocol(val).unwrap()
23+
}
24+
fn try_unwrap(&self) -> Option<Rc<Value>>;
25+
/// Panics if your Value isn't an instance of Protocol
26+
fn unwrap(&self) -> Rc<Value>{
27+
self.try_unwrap().unwrap()
28+
}
29+
}
30+
pub trait ProtocolCastable {
31+
fn try_as_protocol<T: Protocol>(&self) -> Option<T>;
32+
fn as_protocol<T: Protocol>(&self) -> T;
33+
}
34+
impl ProtocolCastable for Rc<Value>{
35+
fn try_as_protocol<T: Protocol>(&self) -> Option<T> {
36+
T::try_as_protocol(self)
37+
}
38+
fn as_protocol<T: Protocol>(&self) -> T {
39+
T::as_protocol(self)
40+
}
41+
}

src/rust_core.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ use crate::persistent_list::{
1111
use crate::persistent_list_map::IPersistentMap;
1212
use crate::persistent_vector::{PersistentVector, ToPersistentVectorIter};
1313
use crate::symbol::Symbol;
14+
use crate::keyword::Keyword;
1415
use crate::type_tag::TypeTag;
1516
use crate::value::{Evaluable, ToValue};
17+
use crate::repl::Repl;
18+
use crate::error_message;
19+
use crate::util::IsEven;
1620

1721
use itertools::Itertools;
18-
use crate::error_message;
1922

20-
use crate::util::IsEven;
23+
use crate::protocol::ProtocolCastable;
24+
use crate::protocol::Protocol;
25+
use crate::iterable::Iterable;
2126

2227
// This module will hold core function and macro primitives that aren't special cases
2328
// (like the quote macro, or let), and can't be implemented in clojure itself
@@ -355,3 +360,35 @@ impl IFn for LoadFileFn {
355360
}
356361
}
357362
}
363+
364+
// This is a tide me over rust wrapper, as map is implemented in lower level primitives
365+
// in pure Clojure
366+
// // That being said, I have not decided as to whether or not there is value to having both
367+
#[derive(Debug, Clone)]
368+
pub struct MapFn {}
369+
impl ToValue for MapFn {
370+
fn to_value(&self) -> Value {
371+
Value::IFn(Rc::new(self.clone()))
372+
}
373+
}
374+
impl IFn for MapFn {
375+
fn invoke(&self, args: Vec<Rc<Value>>) -> Value {
376+
if args.is_empty() {
377+
return error_message::wrong_arg_count(1, args.len());
378+
}
379+
if let Value::IFn(ifn) = &**args.get(0).unwrap() {
380+
if let Some(iterable) = args.get(1).unwrap().try_as_protocol::<Iterable>() {
381+
return iterable.iter().map(|rc_val| {
382+
Rc::new(ifn.invoke(vec![rc_val]))
383+
}).collect::<PersistentList>().to_value();
384+
}
385+
}
386+
Value::Condition(format!(
387+
"Type mismatch; Expected instance of {}, Recieved type {}",
388+
TypeTag::IFn,
389+
args.len()
390+
))
391+
392+
}
393+
}
394+

0 commit comments

Comments
 (0)