@@ -15,15 +15,42 @@ use std::rc::Rc;
15
15
/// See Environment for overall purpose
16
16
#[ derive( Debug , Clone ) ]
17
17
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 > ,
19
23
namespaces : Namespaces ,
20
24
}
21
25
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);
22
45
/// Default main environment
23
46
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) ;
24
51
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
27
54
}
28
55
}
29
56
}
@@ -42,30 +69,121 @@ pub enum Environment {
42
69
}
43
70
use Environment :: * ;
44
71
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
+
45
108
pub fn new_main_environment ( ) -> Environment {
46
109
MainEnvironment ( EnvironmentVal :: new_main_val ( ) )
47
110
}
48
111
pub fn new_local_environment ( outer_environment : Rc < Environment > ) -> Environment {
49
112
LocalEnvironment ( outer_environment, RefCell :: new ( HashMap :: new ( ) ) )
50
113
}
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)
51
143
pub fn insert ( & self , sym : Symbol , val : Rc < Value > ) {
52
144
match self {
53
- MainEnvironment ( EnvironmentVal { curr_ns, .. } ) => {
54
- curr_ns. insert ( sym, val) ;
145
+ MainEnvironment ( _) => { self . insert_into_current_namespace ( sym, val) ;
55
146
}
56
147
LocalEnvironment ( _, mappings) => {
57
148
mappings. borrow_mut ( ) . insert ( sym, val) ;
58
149
}
59
150
}
60
151
}
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)
61
162
pub fn get ( & self , sym : & Symbol ) -> Rc < Value > {
62
163
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
+ }
69
187
}
70
188
}
71
189
pub fn clojure_core_environment ( ) -> Rc < Environment > {
@@ -94,7 +212,13 @@ impl Environment {
94
212
let defmacro_macro = Value :: DefmacroMacro { } ;
95
213
let environment = Rc :: new ( Environment :: new_main_environment ( ) ) ;
96
214
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" ) ) ;
98
222
99
223
environment. insert ( Symbol :: intern ( "+" ) , add_fn. to_rc_value ( ) ) ;
100
224
environment. insert ( Symbol :: intern ( "-" ) , subtract_fn. to_rc_value ( ) ) ;
@@ -114,7 +238,10 @@ impl Environment {
114
238
thread_sleep_fn. to_rc_value ( ) ,
115
239
) ;
116
240
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
+ ) ;
118
245
119
246
environment. insert ( Symbol :: intern ( "+" ) , add_fn. to_rc_value ( ) ) ;
120
247
environment. insert ( Symbol :: intern ( "let" ) , let_macro. to_rc_value ( ) ) ;
@@ -125,6 +252,7 @@ impl Environment {
125
252
environment. insert ( Symbol :: intern ( "def" ) , def_macro. to_rc_value ( ) ) ;
126
253
environment. insert ( Symbol :: intern ( "fn" ) , fn_macro. to_rc_value ( ) ) ;
127
254
environment. insert ( Symbol :: intern ( "defmacro" ) , defmacro_macro. to_rc_value ( ) ) ;
255
+ environment. insert ( Symbol :: intern ( "ns" ) , ns_macro. to_rc_value ( ) ) ;
128
256
environment. insert ( Symbol :: intern ( "eval" ) , eval_fn. to_rc_value ( ) ) ;
129
257
environment. insert (
130
258
Symbol :: intern ( "lexical-eval" ) ,
@@ -144,6 +272,156 @@ impl Environment {
144
272
// @TODO its time for a RT (runtime), which environment seems to be becoming
145
273
let _ = Repl :: new ( Rc :: clone ( & environment) ) . try_eval_file ( "./src/clojure/core.clj" ) ;
146
274
275
+ environment. change_namespace ( Symbol :: intern ( "user" ) ) ;
276
+
147
277
environment
148
278
}
149
279
}
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