Skip to content

Commit 5572ade

Browse files
committed
Add proper api to environment for changing namespace, inserting
symbols (relative to namespaces), and getting symbols
1 parent fb2196a commit 5572ade

File tree

1 file changed

+291
-13
lines changed

1 file changed

+291
-13
lines changed

src/environment.rs

Lines changed: 291 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,42 @@ use std::rc::Rc;
1515
/// See Environment for overall purpose
1616
#[derive(Debug, Clone)]
1717
pub struct EnvironmentVal {
18-
curr_ns: Namespace,
18+
//@TODO is it worth just making this a mutable reference (to an
19+
// immutable value), and referencing the current symbol at any
20+
// point in time? Is implementing that sort of speedup in general
21+
// significant
22+
curr_ns_sym: RefCell<Symbol>,
1923
namespaces: Namespaces,
2024
}
2125
impl EnvironmentVal {
26+
fn change_namespace(&self,name: Symbol){
27+
self.curr_ns_sym.replace(name);
28+
}
29+
fn insert_into_namespace(&self,namespace: &Symbol, sym: Symbol, val: Rc<Value>) {
30+
self.namespaces.insert_into_namespace(namespace,sym,val);
31+
}
32+
fn insert_into_current_namespace(&self,sym: Symbol, val: Rc<Value>){
33+
self.namespaces.insert_into_namespace(&*self.curr_ns_sym.borrow(),sym,val);
34+
}
35+
fn get_from_namespace(&self,namespace: &Symbol,sym: &Symbol) -> Rc<Value>
36+
{
37+
self.namespaces.get(namespace,sym)
38+
}
39+
fn get_current_namespace(&self) -> Symbol {
40+
self.curr_ns_sym.borrow().clone()
41+
}
42+
// @TODO as mentioned, we've been working with a memory model where values exist
43+
// in our system once-ish and we reference them all over with Rc<..>
44+
// Look into possibly working this into that (if its even significant);
2245
/// Default main environment
2346
fn new_main_val() -> EnvironmentVal {
47+
let curr_ns_sym = Symbol::intern("user");
48+
let curr_ns = Namespace::from_sym(curr_ns_sym.clone());
49+
let namespaces = Namespaces(RefCell::new(HashMap::new()));
50+
namespaces.insert(curr_ns_sym.clone(),curr_ns);
2451
EnvironmentVal {
25-
curr_ns: Namespace::new(Symbol::intern("user"), RefCell::new(HashMap::new())),
26-
namespaces: Namespaces(RefCell::new(HashMap::new())),
52+
curr_ns_sym: RefCell::new(curr_ns_sym),
53+
namespaces
2754
}
2855
}
2956
}
@@ -42,30 +69,121 @@ pub enum Environment {
4269
}
4370
use Environment::*;
4471
impl Environment {
72+
pub fn change_namespace(&self,symbol: Symbol) {
73+
let symbol = symbol.unqualified();
74+
75+
match self.get_main_environment() {
76+
MainEnvironment(EnvironmentVal { curr_ns_sym, ..}) => {
77+
curr_ns_sym.replace(symbol);
78+
},
79+
LocalEnvironment(..) => {
80+
panic!(
81+
"get_main_environment() returns LocalEnvironment,\
82+
but by definition should only return MainEnvironment"
83+
)
84+
}
85+
}
86+
}
87+
// @TODO consider 'get_current_..' for consistency?
88+
// @TODO consider 'current_namespace_sym'? after all, its not the namespace itself
89+
pub fn get_current_namespace(&self) -> Symbol {
90+
match self.get_main_environment() {
91+
MainEnvironment(EnvironmentVal { curr_ns_sym, ..}) =>
92+
curr_ns_sym.borrow().clone(),
93+
LocalEnvironment(..) => {
94+
panic!(
95+
"In get_current_namespace_name(): get_main_environment() returns LocalEnvironment,\
96+
but by definition should only return MainEnvironment"
97+
)
98+
}
99+
}
100+
}
101+
// Note; since we're now dealing with curr_ns as a refcell, we're
102+
// returning a String instead of a &str, as I suspect a &str could
103+
// risk becoming invalid as curr_ns changes
104+
pub fn get_current_namespace_name(&self) -> String {
105+
self.get_current_namespace().name.clone()
106+
}
107+
45108
pub fn new_main_environment() -> Environment {
46109
MainEnvironment(EnvironmentVal::new_main_val())
47110
}
48111
pub fn new_local_environment(outer_environment: Rc<Environment>) -> Environment {
49112
LocalEnvironment(outer_environment, RefCell::new(HashMap::new()))
50113
}
114+
/// Insert a binding into an arbitrary namespace
115+
fn insert_into_namespace(&self,namespace: &Symbol, sym: Symbol, val: Rc<Value>) {
116+
match self.get_main_environment() {
117+
MainEnvironment(env_val) => env_val.insert_into_namespace(namespace,sym,val),
118+
LocalEnvironment(..) => {
119+
panic!(
120+
"get_main_environment() returns LocalEnvironment,\
121+
but by definition should only return MainEnvironment"
122+
)
123+
}
124+
}
125+
}
126+
pub fn insert_into_current_namespace(&self,sym: Symbol, val: Rc<Value>){
127+
match self.get_main_environment() {
128+
MainEnvironment(env_val) => env_val.insert_into_current_namespace(sym,val),
129+
LocalEnvironment(..) => {
130+
panic!(
131+
"get_main_environment() returns LocalEnvironment,\
132+
but by definition should only return MainEnvironment"
133+
)
134+
}
135+
}
136+
}
137+
/// Insert into the environment around you; the local bindings,
138+
/// or the current namespace, if this is top level
139+
/// For instance,
140+
/// ```clojure
141+
/// (def a 1) ;; => main_environment.insert(a,1)
142+
/// (let [a 1] ..) ;; => local_environment.insert(a,1)
51143
pub fn insert(&self, sym: Symbol, val: Rc<Value>) {
52144
match self {
53-
MainEnvironment(EnvironmentVal { curr_ns, .. }) => {
54-
curr_ns.insert(sym, val);
145+
MainEnvironment(_) => { self.insert_into_current_namespace(sym, val);
55146
}
56147
LocalEnvironment(_, mappings) => {
57148
mappings.borrow_mut().insert(sym, val);
58149
}
59150
}
60151
}
152+
fn get_main_environment(&self) -> &Self {
153+
match self {
154+
MainEnvironment(_) => self,
155+
LocalEnvironment(parent_env, ..) => parent_env.get_main_environment()
156+
}
157+
}
158+
159+
// @TODO figure out convention for 'ns' vs 'namespace'
160+
/// Get closest value "around" us; try our local environment, then
161+
/// try our main environment (unless its namespace qualified)
61162
pub fn get(&self, sym: &Symbol) -> Rc<Value> {
62163
match self {
63-
MainEnvironment(EnvironmentVal { curr_ns, .. }) => curr_ns.get(sym),
64-
65-
LocalEnvironment(parent_env, mappings) => match mappings.borrow().get(sym) {
66-
Some(val) => Rc::clone(val),
67-
None => parent_env.get(sym),
68-
},
164+
MainEnvironment(env_val) => {
165+
// If we've recieved a qualified symbol like
166+
// clojure.core/+
167+
if sym.ns != "" {
168+
// Use that namespace
169+
env_val.get_from_namespace(&Symbol::intern(&sym.ns),sym)
170+
}
171+
else {
172+
env_val.get_from_namespace(
173+
&env_val.get_current_namespace(),
174+
&Symbol::intern(&sym.name)
175+
)
176+
}
177+
},
178+
LocalEnvironment(parent_env, mappings) => {
179+
if sym.ns != "" {
180+
return self.get_main_environment().get(sym);
181+
}
182+
match mappings.borrow().get(sym) {
183+
Some(val) => Rc::clone(val),
184+
None => parent_env.get(sym),
185+
}
186+
}
69187
}
70188
}
71189
pub fn clojure_core_environment() -> Rc<Environment> {
@@ -94,7 +212,13 @@ impl Environment {
94212
let defmacro_macro = Value::DefmacroMacro {};
95213
let environment = Rc::new(Environment::new_main_environment());
96214

97-
let eval_fn = rust_core::EvalFn::new(Rc::clone(&environment));
215+
let eval_fn = rust_core::EvalFn::new(Rc::clone(&environment));
216+
let ns_macro = rust_core::NsMacro::new(Rc::clone(&environment));
217+
218+
// @TODO after we merge this with all the other commits we have,
219+
// just change all the `insert`s here to use insert_in_namespace
220+
// I prefer explicity and the non-dependence-on-environmental-factors
221+
environment.change_namespace(Symbol::intern("clojure.core"));
98222

99223
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
100224
environment.insert(Symbol::intern("-"), subtract_fn.to_rc_value());
@@ -114,7 +238,10 @@ impl Environment {
114238
thread_sleep_fn.to_rc_value(),
115239
);
116240

117-
environment.insert(Symbol::intern("System_nanotime"), nanotime_fn.to_rc_value());
241+
environment.insert(
242+
Symbol::intern("System_nanotime"),
243+
nanotime_fn.to_rc_value()
244+
);
118245

119246
environment.insert(Symbol::intern("+"), add_fn.to_rc_value());
120247
environment.insert(Symbol::intern("let"), let_macro.to_rc_value());
@@ -125,6 +252,7 @@ impl Environment {
125252
environment.insert(Symbol::intern("def"), def_macro.to_rc_value());
126253
environment.insert(Symbol::intern("fn"), fn_macro.to_rc_value());
127254
environment.insert(Symbol::intern("defmacro"), defmacro_macro.to_rc_value());
255+
environment.insert(Symbol::intern("ns"), ns_macro.to_rc_value());
128256
environment.insert(Symbol::intern("eval"), eval_fn.to_rc_value());
129257
environment.insert(
130258
Symbol::intern("lexical-eval"),
@@ -144,6 +272,156 @@ impl Environment {
144272
// @TODO its time for a RT (runtime), which environment seems to be becoming
145273
let _ = Repl::new(Rc::clone(&environment)).try_eval_file("./src/clojure/core.clj");
146274

275+
environment.change_namespace(Symbol::intern("user"));
276+
147277
environment
148278
}
149279
}
280+
281+
282+
#[cfg(test)]
283+
mod tests {
284+
mod environment_val_tests {
285+
use crate::environment::Environment;
286+
use crate::environment::Environment::*;
287+
use crate::environment::EnvironmentVal;
288+
use crate::symbol::Symbol;
289+
use crate::value::{ToValue,Value};
290+
use crate::ifn::IFn;
291+
use crate::rust_core;
292+
use std::rc::Rc;
293+
294+
//////////////////////////////////////////////////////////////////////////////////////////////////////
295+
//
296+
// pub fn get_current_namespace(&self) -> Symbol {
297+
//
298+
//////////////////////////////////////////////////////////////////////////////////////////////////////
299+
300+
#[test]
301+
fn test_get_current_namespace() {
302+
let env_val = EnvironmentVal::new_main_val();
303+
304+
assert_eq!(Symbol::intern("user"),env_val.get_current_namespace());
305+
306+
env_val.change_namespace(Symbol::intern("core"));
307+
assert_eq!(Symbol::intern("core"),env_val.get_current_namespace());
308+
309+
env_val.change_namespace(Symbol::intern_with_ns("not-ns","ns"));
310+
assert_eq!(Symbol::intern("ns"),env_val.get_current_namespace());
311+
312+
// @TODO add case for local environment
313+
}
314+
315+
////////////////////////////////////////////////////////////////////////////////////////////////////
316+
//
317+
// fn get_from_namespace(&self,namespace: &Symbol,sym: &Symbol) -> Rc<Value>
318+
//
319+
////////////////////////////////////////////////////////////////////////////////////////////////////
320+
321+
#[test]
322+
fn test_get_from_namespace() {
323+
let env_val = EnvironmentVal::new_main_val();
324+
325+
env_val.insert_into_namespace(
326+
&Symbol::intern("core"),Symbol::intern("+"),Rc::new(Value::Nil)
327+
);
328+
env_val.insert_into_namespace(
329+
&Symbol::intern_with_ns("dragon","core"),
330+
Symbol::intern("+2"),
331+
Rc::new(Value::Nil)
332+
);
333+
env_val.insert_into_namespace(
334+
&Symbol::intern_with_ns("dragon","core"),
335+
Symbol::intern_with_ns("override","+3"),
336+
Rc::new(Value::Nil)
337+
);
338+
339+
assert_eq!(Rc::new(Value::Nil),
340+
env_val.get_from_namespace(
341+
&Symbol::intern("core"),
342+
&Symbol::intern("+")
343+
));
344+
345+
assert_eq!(Rc::new(Value::Nil),
346+
env_val.get_from_namespace(
347+
&Symbol::intern("core"),
348+
&Symbol::intern("+2")
349+
));
350+
351+
assert_eq!(Rc::new(Value::Nil),
352+
env_val.get_from_namespace(
353+
&Symbol::intern("override"),
354+
&Symbol::intern("+3")
355+
));
356+
357+
}
358+
////////////////////////////////////////////////////////////////////////////////////////////////////
359+
// get_from_namespace
360+
////////////////////////////////////////////////////////////////////////////////////////////////////
361+
362+
}
363+
mod environment_tests {
364+
use crate::environment::Environment;
365+
use crate::environment::Environment::*;
366+
use crate::environment::EnvironmentVal;
367+
use crate::symbol::Symbol;
368+
use crate::value::{ToValue,Value};
369+
use crate::ifn::IFn;
370+
use crate::rust_core;
371+
use std::rc::Rc;
372+
////////////////////////////////////////////////////////////////////////////////////////////////////
373+
//
374+
// pub fn get(&self, sym: &Symbol) -> Rc<Value> {
375+
//
376+
////////////////////////////////////////////////////////////////////////////////////////////////////
377+
#[test]
378+
fn test_get__plus() {
379+
let add_fn = rust_core::AddFn {};
380+
381+
let environment = Rc::new(Environment::new_main_environment());
382+
environment.insert(Symbol::intern("+"),add_fn.to_rc_value());
383+
384+
let plus = environment.get(&Symbol::intern("+"));
385+
386+
assert_eq!(8.to_value(),add_fn.invoke(vec![3_i32.to_rc_value(),5_i32.to_rc_value()]));
387+
388+
if let Value::IFn(add_ifn) = &*plus {
389+
assert_eq!(8.to_value(),add_ifn.invoke(vec![3_i32.to_rc_value(),5_i32.to_rc_value()]));
390+
return;
391+
}
392+
panic!("test_get_plus: plus is: {:#?}",plus);
393+
}
394+
////////////////////////////////////////////////////////////////////////////////////////////////////
395+
//
396+
// pub fn insert(&self, sym: Symbol, val: Rc<Value>) {
397+
//
398+
////////////////////////////////////////////////////////////////////////////////////////////////////
399+
#[test]
400+
fn test_insert__plus() {
401+
let add_fn = rust_core::AddFn {};
402+
403+
let environment = Rc::new(Environment::new_main_environment());
404+
environment.insert(Symbol::intern("+"),add_fn.to_rc_value());
405+
406+
let plus : Rc<Value> = match &*environment {
407+
MainEnvironment(EnvironmentVal { curr_ns_sym, namespaces }) => {
408+
namespaces
409+
.0
410+
.borrow()
411+
.get(&Symbol::intern("user"))
412+
.unwrap()
413+
.get(&Symbol::intern("+"))
414+
},
415+
_ => panic!("new_main_environment() should return Main")
416+
};
417+
418+
assert_eq!(8.to_value(),add_fn.invoke(vec![3_i32.to_rc_value(),5_i32.to_rc_value()]));
419+
420+
if let Value::IFn(add_ifn) = &*plus {
421+
assert_eq!(8.to_value(),add_ifn.invoke(vec![3_i32.to_rc_value(),5_i32.to_rc_value()]));
422+
return;
423+
}
424+
panic!("plus should be IFn, is: {:#?}",plus);
425+
}
426+
}
427+
}

0 commit comments

Comments
 (0)