@@ -3,32 +3,76 @@ use crate::value::Value;
3
3
use std:: cell:: RefCell ;
4
4
use std:: collections:: HashMap ;
5
5
use std:: rc:: Rc ;
6
+ /// This is a list of namespaces your namespace has completely referred, and the symbols
7
+ /// that your namespace has individually referred
8
+ /// Example:
9
+ /// ```clojure
10
+ /// (ns cats (:require [dogs :refer :all] [chickens :refer [a b c]))`
11
+ /// ```
12
+ /// =>
13
+ /// ```
14
+ /// Refers {
15
+ /// refers: [
16
+ /// Symbol::intern("clojure.core"),
17
+ /// Symbol::intern("dogs")
18
+ /// },
19
+ /// syms: {
20
+ /// Symbol::intern("chickens") : [ Symbol::intern("a"),..,Symbol::intern("c")]
21
+ /// }
22
+ /// }
23
+ /// ``
24
+ #[ derive( Debug , Clone ) ]
25
+ pub struct Refers {
26
+ /// Namespaces that you have completely referred into your namespace;
27
+ /// Basically, `[blah :refer :all]`
28
+ pub namespaces : Vec < Symbol > ,
29
+ /// Symbols that you have individually referred into your namespace from another;
30
+ /// Basically, `[blah :refer [a b c]]`
31
+ pub syms : HashMap < Symbol , Vec < Symbol > >
32
+ }
33
+ impl Default for Refers {
34
+ fn default ( ) -> Self {
35
+ Refers {
36
+ namespaces : vec ! [ Symbol :: intern( "clojure.core" ) ] ,
37
+ syms : HashMap :: new ( )
38
+ }
39
+ }
40
+ }
6
41
7
42
#[ derive( Debug , Clone ) ]
8
43
pub struct Namespace {
9
44
pub name : Symbol ,
10
45
mappings : RefCell < HashMap < Symbol , Rc < Value > > > ,
46
+ pub refers : RefCell < Refers >
11
47
}
12
48
impl Namespace {
13
- pub fn new ( name : & Symbol , mappings : RefCell < HashMap < Symbol , Rc < Value > > > ) -> Namespace {
49
+ fn new ( name : & Symbol , mappings : HashMap < Symbol , Rc < Value > > , refers : Refers ) -> Namespace {
14
50
Namespace {
15
51
name : name. unqualified ( ) ,
16
- mappings,
52
+ mappings : RefCell :: new ( mappings) ,
53
+ refers : RefCell :: new ( refers)
17
54
}
18
55
}
19
56
pub fn from_sym ( name : & Symbol ) -> Namespace {
20
- Namespace :: new ( name, RefCell :: new ( HashMap :: new ( ) ) )
57
+ Namespace :: new ( name, HashMap :: new ( ) , Refers :: default ( ) )
21
58
}
22
59
pub fn insert ( & self , sym : & Symbol , val : Rc < Value > ) {
23
60
self . mappings . borrow_mut ( ) . insert ( sym. unqualified ( ) , val) ;
24
61
}
25
- pub fn get ( & self , sym : & Symbol ) -> Rc < Value > {
62
+ pub fn try_get ( & self , sym : & Symbol ) -> Option < Rc < Value > > {
26
63
match self . mappings . borrow_mut ( ) . get ( & sym. unqualified ( ) ) {
27
- Some ( val) => Rc :: clone ( val) ,
64
+ Some ( val) => Some ( Rc :: clone ( val) ) ,
65
+ None => None
66
+ }
67
+ }
68
+ pub fn get ( & self , sym : & Symbol ) -> Rc < Value > {
69
+ match self . try_get ( sym) {
70
+ Some ( val) => val,
28
71
None => Rc :: new ( Value :: Condition ( format ! ( "1 Undefined symbol {}" , sym. name) ) ) ,
29
72
}
30
73
}
31
74
}
75
+
32
76
#[ derive( Debug , Clone ) ]
33
77
pub struct Namespaces ( pub RefCell < HashMap < Symbol , Namespace > > ) ;
34
78
@@ -84,26 +128,88 @@ impl Namespaces {
84
128
}
85
129
}
86
130
}
87
- /// Get value of sym at namespace
88
- pub fn get ( & self , namespace_sym : & Symbol , sym : & Symbol ) -> Rc < Value > {
131
+ /// Like get, but slightly lower level; returns a None on failure rather than a
132
+ /// Value::Condition. See docs for get
133
+ pub fn try_get ( & self , namespace_sym : & Symbol , sym : & Symbol ) -> Option < Rc < Value > > {
89
134
// When storing / retrieving from namespaces, we want
90
135
// namespace_sym unqualified keys
91
136
let mut namespace_sym = namespace_sym. unqualified ( ) ;
92
-
137
+ // Ie, a scenario like get(.. , 'clojure.core/+) or get(.., 'shortcut/+)
138
+ let mut grabbing_from_namespace_directly = false ;
93
139
// @TODO just make it an Optional<String>
94
140
// If our sym is namespace qualified, use that as our namespace
95
141
if sym. has_ns ( ) {
142
+ grabbing_from_namespace_directly = true ;
96
143
namespace_sym = Symbol :: intern ( & sym. ns ) ;
97
144
}
98
145
99
146
let sym = sym. unqualified ( ) ;
100
147
let namespaces = self . 0 . borrow ( ) ;
101
148
let namespace = namespaces. get ( & namespace_sym) ;
102
-
149
+ //@TODO change to map
103
150
match namespace {
104
- Some ( namespace) => Rc :: clone ( & namespace. get ( & sym) ) ,
151
+ Some ( namespace) => {
152
+ // If we cannot find the symbol, and its not a direct grab from a specific namespace,
153
+ // we should see if we can find it in one of our referred namespaces or symbols
154
+ let val = namespace. try_get ( & sym) ;
155
+ match val {
156
+ Some ( _) => val,
157
+ None => {
158
+ if grabbing_from_namespace_directly {
159
+ return None ;
160
+ }
161
+ let refers = namespace. refers . borrow ( ) ;
162
+ let referred_namespaces = & refers. namespaces ;
163
+ for referred_namespace_sym in referred_namespaces. into_iter ( ) {
164
+ if * referred_namespace_sym == namespace_sym {
165
+ continue ;
166
+ }
167
+ let try_get_sym_from_other_ns = self . try_get ( & referred_namespace_sym, & sym) ;
168
+ if let Some ( val) = & try_get_sym_from_other_ns {
169
+ return try_get_sym_from_other_ns;
170
+ }
171
+ }
172
+ None
173
+ //
174
+ // Big @TODO
175
+ // lookup iterating through hashmap
176
+ //
177
+ // let referred_syms = refers.syms;
178
+ // for referred_sym in referred_syms.into_iter() {
179
+ // }
180
+ }
181
+ }
182
+ } ,
183
+ None => None
184
+ }
185
+ }
186
+ /// Get value of sym in namespace
187
+ /// Note;
188
+ /// ```
189
+ /// get('clojure.core,'+)
190
+ /// ```
191
+ /// Will be asking what '+ means in 'clojure.core, so
192
+ /// this will only return a value if there is a 'clojure.core/+
193
+ /// But
194
+ /// ```
195
+ /// get('clojure.core, 'clojure.other/+)
196
+ /// ```
197
+ /// Will always mean the same thing, no matter what namespace we're in; it will mean
198
+ /// the value '+ belonging to clojure.other, the namespace you're in is irrelevant
199
+ ///
200
+ /// Finally,
201
+ /// ```
202
+ /// get('clojure.core, 'shortcut/+)
203
+ /// ```
204
+ /// Will depend on what shortcut expands to in clojure.core (assuming shortcut is not an actual namespace here)
205
+ ///
206
+ /// As we can see, this is a relatively high level function meant to be getting _whatever_
207
+ /// a user has typed in for a symbol while inside a namespace
208
+ pub fn get ( & self , namespace_sym : & Symbol , sym : & Symbol ) -> Rc < Value > {
209
+ match self . try_get ( namespace_sym, sym) {
210
+ Some ( val) => val,
105
211
// @TODO should this be a condition or nil?
106
- _ => Rc :: new ( Value :: Condition ( format ! ( "Undefined symbol {}" , sym. name) ) ) ,
212
+ None => Rc :: new ( Value :: Condition ( format ! ( "Undefined symbol {}" , sym. name) ) )
107
213
}
108
214
}
109
215
}
@@ -115,15 +221,16 @@ mod tests {
115
221
// a struct
116
222
mod namespace_struct {
117
223
use crate :: namespace:: Namespace ;
224
+ use crate :: namespace:: Refers ;
118
225
use crate :: symbol:: Symbol ;
119
226
use crate :: value:: Value ;
120
227
use std:: cell:: RefCell ;
121
228
use std:: collections:: HashMap ;
122
229
use std:: rc:: Rc ;
123
-
230
+
124
231
#[ test]
125
232
fn new ( ) {
126
- let namespace = Namespace :: new ( & Symbol :: intern ( "a" ) , RefCell :: new ( HashMap :: new ( ) ) ) ;
233
+ let namespace = Namespace :: new ( & Symbol :: intern ( "a" ) , HashMap :: new ( ) , Refers :: default ( ) ) ;
127
234
assert_eq ! ( namespace. name, Symbol :: intern( "a" ) ) ;
128
235
assert ! ( namespace. mappings. borrow( ) . is_empty( ) ) ;
129
236
}
@@ -132,18 +239,20 @@ mod tests {
132
239
fn new_removes_namespace_from_qualified_symbol ( ) {
133
240
let namespace = Namespace :: new (
134
241
& Symbol :: intern_with_ns ( "ns" , "a" ) ,
135
- RefCell :: new ( HashMap :: new ( ) ) ,
242
+ HashMap :: new ( ) ,
243
+ Refers :: default ( )
136
244
) ;
137
245
assert_eq ! ( namespace. name, Symbol :: intern( "a" ) ) ;
138
246
assert ! ( namespace. name != Symbol :: intern_with_ns( "ns" , "a" ) ) ;
139
247
assert ! ( namespace. mappings. borrow( ) . is_empty( ) ) ;
140
248
}
141
249
#[ test]
142
250
fn new_namespace_starts_empty ( ) {
143
- let namespace = Namespace :: new ( & Symbol :: intern ( "a" ) , RefCell :: new ( HashMap :: new ( ) ) ) ;
251
+ let namespace = Namespace :: new ( & Symbol :: intern ( "a" ) , HashMap :: new ( ) , Refers :: default ( ) ) ;
144
252
let namespace2 = Namespace :: new (
145
253
& Symbol :: intern_with_ns ( "ns" , "b" ) ,
146
- RefCell :: new ( HashMap :: new ( ) ) ,
254
+ HashMap :: new ( ) ,
255
+ Refers :: default ( )
147
256
) ;
148
257
assert ! ( namespace. mappings. borrow( ) . is_empty( ) ) ;
149
258
assert ! ( namespace2. mappings. borrow( ) . is_empty( ) ) ;
@@ -419,6 +528,29 @@ mod tests {
419
528
}
420
529
}
421
530
}
531
+ #[ test]
532
+ fn get_from_referred_namespace ( ) {
533
+ let namespaces = Namespaces :: new ( ) ;
534
+ namespaces. insert_into_namespace (
535
+ & Symbol :: intern ( "clojure.core" ) ,
536
+ & Symbol :: intern ( "+" ) ,
537
+ Rc :: new ( Value :: Nil ) ,
538
+ ) ;
539
+
540
+ namespaces. insert_into_namespace (
541
+ & Symbol :: intern ( "user" ) ,
542
+ & Symbol :: intern ( "-" ) ,
543
+ Rc :: new ( Value :: Nil ) ,
544
+ ) ;
545
+
546
+ match & * namespaces. get ( & Symbol :: intern ( "user" ) , & Symbol :: intern ( "+" ) ) {
547
+ Value :: Condition ( _) => {
548
+ panic ! ( "Namespace user failed to grab clojure.core/+ as a referred symbol" ) ;
549
+ }
550
+ _ => { }
551
+ }
552
+
553
+ }
422
554
////////////////////////////////////////////////////////////////////////////////////////////////////
423
555
//
424
556
////////////////////////////////////////////////////////////////////////////////////////////////////
0 commit comments