Skip to content

Commit 21ac5ce

Browse files
committed
Add assoc function so we can finally start modelling a bit with our maps!
1 parent 43579d8 commit 21ac5ce

File tree

7 files changed

+62
-17
lines changed

7 files changed

+62
-17
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ edition = "2018"
1010
dyn-clone = "1.0"
1111
nom = "5.1"
1212
rand = "0.7"
13+
itertools= "0.9"

src/environment.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ impl Environment {
7676
let do_macro = rust_core::DoMacro {};
7777
let concat_fn = rust_core::ConcatFn {};
7878
let print_string_fn = rust_core::PrintStringFn {};
79+
let assoc_fn = rust_core::AssocFn {};
7980
// Hardcoded fns
8081
let lexical_eval_fn = Value::LexicalEvalFn {};
8182
// Hardcoded macros
@@ -111,8 +112,8 @@ impl Environment {
111112
Symbol::intern("lexical-eval"),
112113
lexical_eval_fn.to_rc_value(),
113114
);
114-
115115
environment.insert(Symbol::intern("nth"), nth_fn.to_rc_value());
116+
environment.insert(Symbol::intern("assoc"), assoc_fn.to_rc_value());
116117
environment.insert(Symbol::intern("concat"), concat_fn.to_rc_value());
117118
environment.insert(
118119
Symbol::intern("print-string"),

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[macro_use]
22
extern crate nom;
3+
extern crate itertools;
34

45
mod environment;
56
mod ifn;
@@ -14,6 +15,7 @@ mod repl;
1415
mod rust_core;
1516
mod symbol;
1617
mod type_tag;
18+
mod util;
1719
mod value;
1820

1921
fn main() {

src/reader.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub fn identifier_parser(input: &str) -> IResult<&str, String> {
131131
);
132132

133133
named!(identifier_tail<&str, &str>, take_while!(is_identifier_char));
134-
134+
135135
named!(identifier <&str, String>,
136136
do_parse!(
137137
head: identifier_head >>

src/repl.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ use crate::value::Evaluable;
1111
use crate::value::Value;
1212
use std::rc::Rc;
1313

14-
use nom::Err::Incomplete;
15-
use nom::IResult;
16-
use nom::Needed::Size;
17-
1814
pub struct Repl {
1915
environment: Rc<Environment>,
2016
}

src/rust_core.rs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@ use crate::persistent_list::{
88
PersistentList::{Cons, Empty},
99
ToPersistentList, ToPersistentListIter,
1010
};
11+
use crate::persistent_list_map::IPersistentListMap;
1112
use crate::persistent_vector::{PersistentVector, ToPersistentVectorIter};
13+
use crate::repl::Repl;
1214
use crate::symbol::Symbol;
15+
use crate::type_tag::TypeTag;
1316
use crate::value::{Evaluable, ToValue};
1417

15-
//
16-
// This module will hold the core functions and macros that Clojure will
17-
// hook into; Functions / Macros like "+", "fn*", "let", "cond", etc
18-
//
19-
// This is still experimental, and we may instead undo this all and
20-
// represent fn* and let and the like the same it is done in ClojureJVM,
21-
// where I believe they are defined flat out in the Compiler class
22-
//
23-
// However, even in that case this may stick around to implement basic
24-
// functions like "+" that usually rely on interop
25-
//
18+
use itertools::Itertools;
19+
20+
use crate::util::IsEven;
21+
22+
// This module will hold core function and macro primitives that aren't special cases
23+
// (like the quote macro, or let), and can't be implemented in clojure itself
2624

2725
#[derive(Debug, Clone)]
2826
pub struct StrFn {}
@@ -283,3 +281,39 @@ impl IFn for PrintStringFn {
283281
Value::Nil
284282
}
285283
}
284+
// General assoc fn; however, currently just implemented
285+
// for our one map type, PersistentListMap
286+
#[derive(Debug, Clone)]
287+
pub struct AssocFn {}
288+
impl ToValue for AssocFn {
289+
fn to_value(&self) -> Value {
290+
Value::IFn(Rc::new(self.clone()))
291+
}
292+
}
293+
impl IFn for AssocFn {
294+
fn invoke(&self, args: Vec<&Value>) -> Value {
295+
// We don't want even args, because assoc works like
296+
// (assoc {} :a 1) ;; 3 args
297+
// (assoc {} :a 1 :b 2) ;; 5 args
298+
// (assoc {} :a 1 :b 2 :c 3) ;; 7 args ...
299+
if args.len() < 3 || args.len().is_even() {
300+
return Value::Condition(format!(
301+
"Wrong number of arguments given to function (Given: {}, Expected: 3 | 5 | 7 | ..)",
302+
args.len()
303+
));
304+
}
305+
306+
if let Value::PersistentListMap(pmap) = args.get(0).unwrap() {
307+
let mut retval = pmap.clone();
308+
for (key_value, val_value) in args.into_iter().skip(1).tuples() {
309+
let key = key_value.to_rc_value();
310+
let val = val_value.to_rc_value();
311+
println!("key: {:?}, val: {:?}",key,val);
312+
retval = pmap.assoc(key, val);
313+
}
314+
return Value::PersistentListMap(retval);
315+
}
316+
317+
Value::Nil
318+
}
319+
}

src/util.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//! Just some nifty little utilities, as all projects accumulate
2+
3+
// Just a little sugar around having to write 'num % 2 == 0'
4+
pub trait IsEven {
5+
fn is_even(&self) -> bool;
6+
}
7+
impl IsEven for usize {
8+
fn is_even(&self) -> bool {
9+
*self % 2 == 0
10+
}
11+
}

0 commit comments

Comments
 (0)