Skip to content

Commit fb2196a

Browse files
committed
Add api to namespaces / namespace for properly inserting / getting
1 parent 37dd57b commit fb2196a

File tree

1 file changed

+203
-1
lines changed

1 file changed

+203
-1
lines changed

src/namespace.rs

Lines changed: 203 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,217 @@ impl Namespace {
1313
pub fn new(name: Symbol, mappings: RefCell<HashMap<Symbol, Rc<Value>>>) -> Namespace {
1414
Namespace { name, mappings }
1515
}
16+
pub fn from_sym(name: Symbol) -> Namespace {
17+
Namespace::new(name,RefCell::new(HashMap::new()))
18+
}
1619
pub fn insert(&self, sym: Symbol, val: Rc<Value>) {
1720
self.mappings.borrow_mut().insert(sym, val);
1821
}
1922
pub fn get(&self, sym: &Symbol) -> Rc<Value> {
2023
match self.mappings.borrow_mut().get(sym) {
2124
Some(val) => Rc::clone(val),
22-
None => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name))),
25+
None => Rc::new(Value::Condition(format!("1 Undefined symbol {}", sym.name))),
2326
}
2427
}
2528
}
2629
#[derive(Debug, Clone)]
2730
pub struct Namespaces(pub RefCell<HashMap<Symbol, Namespace>>);
31+
32+
impl Namespaces {
33+
pub fn new() -> Namespaces {
34+
Namespaces(RefCell::new(HashMap::new()))
35+
}
36+
/// Insert a new namespace of name (sym)
37+
pub fn insert(&self, sym: Symbol, namespace: Namespace) {
38+
// When storing / retrieving from namespaces, we want
39+
// namespace unqualified keys
40+
let sym = sym.unqualified();
41+
self.0.borrow_mut().insert(sym, namespace);
42+
}
43+
pub fn has_namespace(&self, namespace_sym: &Symbol) -> bool{
44+
let namespace_sym = namespace_sym.unqualified();
45+
46+
let namespaces = self.0.borrow();
47+
let namespace = namespaces.get(&namespace_sym);
48+
match namespace {
49+
Some(_) => true,
50+
None => false
51+
}
52+
}
53+
// @TODO Consider writing `sym` as reference here, because we clone it anyways
54+
// Only reason to keep it is `inserts` are pass-by-value here normally,
55+
// since the idea normally is you are literally inserting the keys too
56+
// I'd prefer that consistency, unless we find it has a noticeable
57+
// performance impact
58+
/// Insert a binding (sym = val) *into* namespace (namespace)
59+
pub fn insert_into_namespace(&self, namespace_sym: &Symbol, sym: Symbol, val: Rc<Value>){
60+
let mut namespace_sym = &namespace_sym.unqualified();
61+
// We will only use this if ns isn't ""
62+
let symbol_namespace_sym = Symbol::intern(&sym.ns);
63+
64+
if sym.ns != "" {
65+
namespace_sym = &symbol_namespace_sym;
66+
}
67+
68+
let namespaces = self.0.borrow();
69+
let namespace = namespaces.get(namespace_sym);
70+
match namespace {
71+
Some(namespace) => {
72+
namespace.insert(sym.unqualified(),val);
73+
},
74+
None => {
75+
drop(namespaces);
76+
let namespace = Namespace::from_sym(namespace_sym.clone());
77+
namespace.insert(sym.unqualified(),val);
78+
self.insert(namespace_sym.unqualified(),namespace);
79+
}
80+
}
81+
}
82+
/// Get value of sym at namespace
83+
pub fn get(&self,namespace_sym: &Symbol,sym: &Symbol) -> Rc<Value>
84+
{
85+
// When storing / retrieving from namespaces, we want
86+
// namespace_sym unqualified keys
87+
let mut namespace_sym = namespace_sym.unqualified();
88+
89+
// @TODO just make it an Optional<String>
90+
// If our sym is namespace qualified, use that as our namespace
91+
if sym.ns != "" {
92+
namespace_sym = Symbol::intern(&sym.ns);
93+
}
94+
95+
let sym = sym.unqualified();
96+
let namespaces = self.0.borrow();
97+
let namespace = namespaces.get(&namespace_sym);
98+
99+
match namespace {
100+
Some(namespace) => Rc::clone(&namespace.get(&sym)),
101+
// @TODO should this be a condition or nil?
102+
_ => Rc::new(Value::Condition(format!("Undefined symbol {}", sym.name))),
103+
}
104+
105+
}
106+
}
107+
108+
#[cfg(test)]
109+
mod tests {
110+
111+
mod namespaces_tests {
112+
use crate::namespace::Namespace;
113+
use crate::namespace::Namespaces;
114+
use crate::symbol::Symbol;
115+
use crate::value::Value;
116+
use std::rc::Rc;
117+
118+
////////////////////////////////////////////////////////////////////////////////////////////////////
119+
//
120+
// pub fn get(&self,namespace_sym: &Symbol,sym: &Symbol) -> Rc<Value>
121+
//
122+
////////////////////////////////////////////////////////////////////////////////////////////////////
123+
#[test]
124+
fn test_get_namespace__get_empty_and_fail() {
125+
let namespaces = Namespaces::new();
126+
let clojure_core__plus = Symbol::intern("clojure.core/+");
127+
match &*namespaces.get(&Symbol::intern("clojure.your/+"),&clojure_core__plus) {
128+
Value::Condition(cond) => {},
129+
_ => { panic!("Symbol {} somehow succeeded in {:#?}",clojure_core__plus,namespaces); }
130+
}
131+
}
132+
133+
#[test]
134+
fn test_get_namespace__qualified_symbol_overriding_namespace() {
135+
let namespaces = Namespaces::new();
136+
137+
let clojure_core1__plus_1 = Symbol::intern("clojure.core1/+1");
138+
namespaces.insert_into_namespace(
139+
&Symbol::intern("clojure.core1"),
140+
Symbol::intern("+1"),
141+
Rc::new(Value::Nil)
142+
);
143+
match &*namespaces.get(&Symbol::intern("clojure.your"),&clojure_core1__plus_1) {
144+
Value::Condition(cond) => {
145+
panic!("Symbol {} somehow failed in {:#?}",clojure_core1__plus_1,namespaces);
146+
},
147+
_ => { assert!(true); }
148+
}
149+
}
150+
151+
#[test]
152+
fn test_get_namespace__overwritten_namespace_again() {
153+
let namespaces = Namespaces::new();
154+
155+
let clojure_core__plus = Symbol::intern("clojure.core/+");
156+
namespaces.insert_into_namespace(
157+
&Symbol::intern("clojure.core"),
158+
Symbol::intern("+"),
159+
Rc::new(Value::Nil)
160+
);
161+
// Really means get +/+, but is overwritten to mean get clojure.core/+
162+
match &*namespaces.get(&Symbol::intern("clojure.core/+"),&clojure_core__plus) {
163+
Value::Condition(cond) => { panic!("Symbol {} somehow failed in {:#?}",clojure_core__plus,namespaces); },
164+
_ => { }
165+
}
166+
167+
}
168+
169+
#[test]
170+
fn test_get_namespace__namespace_symbol_and_symbol_separate() {
171+
let namespaces = Namespaces::new();
172+
173+
174+
// add namespace core2/+2
175+
let plus_2 = Symbol::intern("+2");
176+
namespaces.insert_into_namespace(
177+
&Symbol::intern("core2"),
178+
Symbol::intern("+2"),
179+
Rc::new(Value::Nil)
180+
);
181+
// Get intern("core2/+2")
182+
// ----------------------
183+
// Here is the part where namespace symbol and symbol are separate;
184+
// rather than having &plus_2 qualified fully as 'core2/+2'
185+
// ---------------------
186+
// Should succeed
187+
match &*namespaces.get(&Symbol::intern("core2"),&plus_2) {
188+
Value::Condition(cond) => {
189+
panic!("Symbol {} somehow failed in {:#?}",&plus_2,namespaces);
190+
},
191+
_ => { assert!(true); }
192+
}
193+
}
194+
#[test]
195+
fn test_get_namespace__wrong_ns_right_name(){
196+
let namespaces = Namespaces::new();
197+
namespaces.insert_into_namespace(
198+
&Symbol::intern("core2"),
199+
Symbol::intern("+2"),
200+
Rc::new(Value::Nil)
201+
);
202+
203+
let plus_2 = Symbol::intern("+2");
204+
// get intern("core1/+2")
205+
// Should fail
206+
match &*namespaces.get(&Symbol::intern("clojure.core1"),&plus_2) {
207+
Value::Condition(cond) => {
208+
assert!(true);
209+
},
210+
_ => { panic!("Symbol {} somehow failed in {:#?}",&plus_2,namespaces); }
211+
}
212+
213+
// Make sure it normally works
214+
// get intern("core2/+2")
215+
// Should succeed
216+
match &*namespaces.get(&Symbol::intern("core2"),&plus_2) {
217+
Value::Condition(cond) => {
218+
panic!("Symbol {} somehow failed in {:#?}",&plus_2,namespaces);
219+
},
220+
_ => { assert!(true); }
221+
}
222+
}
223+
////////////////////////////////////////////////////////////////////////////////////////////////////
224+
//
225+
////////////////////////////////////////////////////////////////////////////////////////////////////
226+
227+
//let namespaces.insert_into_namespace(&Symbol::intern("clojure.core/+"), , ${3:val: Rc<Value>})
228+
}
229+
}

0 commit comments

Comments
 (0)