@@ -201,10 +201,17 @@ pub struct Specials {
201
201
pub is_err : Interned ,
202
202
pub is_ok : Interned ,
203
203
pub ret : Interned ,
204
+ pub ns : Interned ,
205
+ pub with_ns : Interned ,
206
+ pub import : Interned ,
207
+ pub load : Interned ,
208
+ pub comp_time : Interned ,
204
209
205
210
pub rest : Interned ,
206
211
pub optional : Interned ,
207
212
pub scratch : Interned ,
213
+ pub colon : Interned ,
214
+ pub root : Interned ,
208
215
}
209
216
210
217
impl Specials {
@@ -368,7 +375,6 @@ Add a sequence of numbers. (+) will return 0.
368
375
Section: math
369
376
370
377
Example:
371
- (ns-import 'math)
372
378
(test::assert-equal 0 (+))
373
379
(test::assert-equal 5 (+ 5))
374
380
(test::assert-equal 10 (+ 5 5))
@@ -383,7 +389,6 @@ Subtract a sequence of numbers. Requires at least one number (negate if only on
383
389
Section: math
384
390
385
391
Example:
386
- (ns-import 'math)
387
392
(test::assert-error (- 5 "2"))
388
393
(test::assert-equal -5 (- 5))
389
394
(test::assert-equal -5.0 (- 5.0))
@@ -399,7 +404,6 @@ Multiply a sequence of numbers. (*) will return 1.
399
404
Section: math
400
405
401
406
Example:
402
- (ns-import 'math)
403
407
(test::assert-equal 1 (*))
404
408
(test::assert-equal 5 (* 5))
405
409
(test::assert-equal 5 (* 1 5))
@@ -420,7 +424,6 @@ Divide a sequence of numbers. Requires at least two numbers.
420
424
421
425
Section: math
422
426
Example:
423
- (ns-import 'math)
424
427
(test::assert-equal 5 (/ 50 10))
425
428
(test::assert-equal 5 (/ 50.0 10.0))
426
429
(test::assert-equal 0 (/ 1 5))
@@ -690,12 +693,12 @@ Example:
690
693
(loop (idx) (3) (do
691
694
(set! tot (+ tot 1))
692
695
(if (> idx 1) (recur (- idx 1)))))
693
- (assert-equal 3 tot)
696
+ (test:: assert-equal 3 tot)
694
697
(set! tot 0)
695
698
((fn (idx) (do
696
699
(set! tot (+ tot 1))
697
700
(if (> idx 1) (recur (- idx 1)))))5)
698
- (assert-equal 5 tot)" ,
701
+ (test:: assert-equal 5 tot)" ,
699
702
) ,
700
703
this_fn : add_special ( vm, "this-fn" , "" ) ,
701
704
numeq : add_special ( vm, "==" , r#"Usage: (== val0 ... valN)
@@ -1071,10 +1074,111 @@ Example:
1071
1074
(test::assert-true (ok? nil))
1072
1075
"# ) ,
1073
1076
ret : add_special ( vm, "return" , "" ) ,
1077
+ ns : add_special ( vm, "ns" , r#"Usage: (ns SYMBOL)
1078
+
1079
+ Changes to namespace. This is "open-ended" change and is intended for use with
1080
+ the REPL prefer with-ns for scripts.
1081
+ The symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
1082
+ This will cause all globals defined to have namespace:: prepended.
1083
+ This will also clear any existing imports.
1084
+
1085
+ Section: core
1086
+
1087
+ Example:
1088
+ (ns testing)
1089
+ (def x #t)
1090
+ (test::assert-true x)
1091
+ (ns ::)
1092
+ (test::assert-true testing::x)
1093
+ "# ) ,
1094
+ with_ns : add_special ( vm, "with-ns" , r#"Usage: (with-ns SYMBOL sexp+)
1095
+
1096
+ Create a namespace and compile sexp+ within it. Restore the previous namespace when scope ends.
1097
+ THe symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
1098
+ This will cause all globals defined to have namespace:: prepended.
1099
+ This will also clear any existing imports.
1100
+
1101
+ Section: core
1102
+
1103
+ Example:
1104
+ (with-ns test-with-ns
1105
+ (def ttf (fn () '(1 2 3)))
1106
+ (test::assert-equal '(1 2 3) (ttf))
1107
+ (test::assert-equal '(1 2 3) (test-out::ttf)))
1108
+ (test::assert-equal '(1 2 3) (test-out::ttf))
1109
+ "# ) ,
1110
+ import : add_special ( vm, "import" , r#"Usage: (import namespace [:as symbol])
1111
+
1112
+ Will import a namespace. Without an :as then all symbols in the namespace will become available in the current
1113
+ namespace as if local. With [:as symbol] then all namespace symbols become available with symbol:: prepended.
1114
+
1115
+ Section: core
1116
+
1117
+ Example:
1118
+ (ns testing)
1119
+ (def x #t)
1120
+ (test::assert-true x)
1121
+ (ns ::)
1122
+ (test::assert-true testing::x)
1123
+ (import testing)
1124
+ (test::assert-true x)
1125
+ (import testing :as t)
1126
+ (test::assert-true t::x)
1127
+ "# ) ,
1128
+ load : add_special ( vm, "load" , r#"Usage: (load path) -> [last form value]
1129
+
1130
+ Read and eval a file (from path- a string). The load special form executes at compile time.
1131
+ This means it's parameter must resolve at compile time. Most of the time you will want to use
1132
+ this in conjuction with 'with-ns' to namespace the contents.
1133
+ Note: on it's own does nothing with namespaces.
1134
+
1135
+ Section: core
1136
+
1137
+ Example:
1138
+ (comp-time (def test-temp-file (get-temp-file)) nil)
1139
+ (defer (fs-rm test-temp-file))
1140
+ (let (tst-file (fopen test-temp-file :create))
1141
+ (defer (fclose tst-file))
1142
+ (fprn tst-file "(with-ns test-load")
1143
+ (fprn tst-file " (defn test-fn () '(1 2 3)))"))
1144
+ (load test-temp-file) ; put stuff in it's own namespace
1145
+ (test::assert-equal '(1 2 3) (test-load::test-fn))
1146
+
1147
+
1148
+ (with-ns test-out2
1149
+ (comp-time
1150
+ (def test-temp-file (get-temp-file))
1151
+ (let (tst-file (fopen test-temp-file :create))
1152
+ (defer (fclose tst-file))
1153
+ (fprn tst-file "(defn test-fn () '(1 2 3))"))
1154
+ nil)
1155
+ (defer (fs-rm test-temp-file))
1156
+ (load test-temp-file) ; put new stuff in current namespace
1157
+ (test::assert-equal '(1 2 3) (test-fn))
1158
+ (test::assert-equal '(1 2 3) (test-out2::test-fn)))
1159
+ "# ) ,
1160
+ comp_time : add_special ( vm, "comp-time" , r#"Usage: (comp-time sexp+)
1161
+
1162
+ Compile and execute sexp+ at compile time. The result of the final sexp will then be compiled into
1163
+ the current module being compiled (produce nil to avoid this).
1164
+
1165
+ Section: core
1166
+
1167
+ Example:
1168
+ (with-ns test-out
1169
+ (comp-time '(def ttf (fn () '(1 2 3))))
1170
+ (comp-time (def ttf2 (fn () '(1 2 3))) nil)
1171
+ (test::assert-equal '(1 2 3) (ttf))
1172
+ (test::assert-equal '(1 2 3) (test-out::ttf))
1173
+ (test::assert-equal '(1 2 3) (ttf2))
1174
+ (test::assert-equal '(1 2 3) (test-out::ttf2)))
1175
+ "# ) ,
1074
1176
1075
1177
rest : vm. intern_static ( "&" ) ,
1076
1178
optional : vm. intern_static ( "%" ) ,
1077
1179
scratch : vm. intern_static ( "[SCRATCH]" ) ,
1180
+ colon : vm. intern_static ( ":" ) ,
1181
+ root : vm. intern_static ( "ROOT" ) ,
1078
1182
}
1079
1183
}
1080
1184
}
@@ -1157,12 +1261,42 @@ impl CompileState {
1157
1261
}
1158
1262
}
1159
1263
1264
+ /// Data for the current namespace
1265
+ #[ derive( Clone , Debug ) ]
1266
+ pub struct Namespace {
1267
+ name : String ,
1268
+ imports : Vec < ( String , Option < String > ) > ,
1269
+ }
1270
+
1271
+ impl Namespace {
1272
+ pub fn new_with_name ( name : String ) -> Self {
1273
+ Self {
1274
+ name,
1275
+ imports : vec ! [ ] ,
1276
+ }
1277
+ }
1278
+
1279
+ pub fn name ( & self ) -> & str {
1280
+ & self . name
1281
+ }
1282
+ }
1283
+
1284
+ impl Default for Namespace {
1285
+ fn default ( ) -> Self {
1286
+ Self {
1287
+ name : "" . to_string ( ) ,
1288
+ imports : vec ! [ ] ,
1289
+ }
1290
+ }
1291
+ }
1292
+
1160
1293
pub struct CompileEnvironment {
1161
1294
use_line : bool ,
1162
1295
line : u32 ,
1163
1296
specials : Option < Specials > ,
1164
1297
global_map : HashMap < Interned , usize > ,
1165
1298
gensym_idx : usize ,
1299
+ namespace : Namespace ,
1166
1300
}
1167
1301
1168
1302
impl Default for CompileEnvironment {
@@ -1179,6 +1313,10 @@ impl CompileEnvironment {
1179
1313
specials : None ,
1180
1314
global_map : HashMap :: new ( ) ,
1181
1315
gensym_idx : 0 ,
1316
+ namespace : Namespace {
1317
+ name : "" . to_string ( ) ,
1318
+ imports : vec ! [ ] ,
1319
+ } ,
1182
1320
}
1183
1321
}
1184
1322
@@ -1192,8 +1330,22 @@ impl CompileEnvironment {
1192
1330
self . line
1193
1331
}
1194
1332
1195
- pub fn global_defined ( & self , i : Interned ) -> bool {
1196
- self . global_map . contains_key ( & i)
1333
+ pub fn set_namespace ( & mut self , namespace : Namespace ) {
1334
+ self . namespace = namespace;
1335
+ }
1336
+
1337
+ pub fn add_ns_import ( & mut self , ns : String , alias : Option < String > ) {
1338
+ for ( ns_name, ns_alias) in self . namespace . imports . iter_mut ( ) {
1339
+ if ns_name == & ns {
1340
+ * ns_alias = alias;
1341
+ return ;
1342
+ }
1343
+ }
1344
+ self . namespace . imports . push ( ( ns, alias) ) ;
1345
+ }
1346
+
1347
+ pub fn get_namespace ( & self ) -> & Namespace {
1348
+ & self . namespace
1197
1349
}
1198
1350
}
1199
1351
@@ -1306,6 +1458,56 @@ impl SloshVmTrait for SloshVm {
1306
1458
}
1307
1459
1308
1460
fn global_intern_slot ( & self , symbol : Interned ) -> Option < u32 > {
1461
+ fn check_global ( vm : & SloshVm , ns : & str , sym : & str ) -> Option < u32 > {
1462
+ let mut ns = ns. to_string ( ) ;
1463
+ ns. push_str ( "::" ) ;
1464
+ ns. push_str ( sym) ;
1465
+ if let Some ( i) = vm. get_if_interned ( & ns) {
1466
+ if let Some ( global) = vm. env ( ) . global_map . get ( & i) . copied ( ) . map ( |i| i as u32 ) {
1467
+ return Some ( global) ;
1468
+ }
1469
+ }
1470
+ None
1471
+ }
1472
+ fn is_alias ( alias : & str , sym : & str ) -> bool {
1473
+ let mut a_i = alias. chars ( ) ;
1474
+ let mut s_i = sym. chars ( ) . peekable ( ) ;
1475
+ let mut is_alias = true ;
1476
+ while let ( Some ( ach) , Some ( sch) ) = ( a_i. next ( ) , s_i. peek ( ) ) {
1477
+ if ach != * sch {
1478
+ is_alias = false ;
1479
+ break ;
1480
+ }
1481
+ s_i. next ( ) ;
1482
+ }
1483
+ if is_alias {
1484
+ if let ( Some ( ':' ) , Some ( ':' ) ) = ( s_i. next ( ) , s_i. next ( ) ) {
1485
+ return true ;
1486
+ }
1487
+ }
1488
+ false
1489
+ }
1490
+
1491
+ let sym = self . get_interned ( symbol) ;
1492
+ if let Some ( g) = check_global ( self , & self . env ( ) . namespace . name , sym) {
1493
+ return Some ( g) ;
1494
+ }
1495
+ for ( import, alias) in & self . env ( ) . namespace . imports {
1496
+ if let Some ( alias) = alias {
1497
+ if is_alias ( alias, sym) {
1498
+ let s = sym. replacen ( alias, import, 1 ) ;
1499
+ if let Some ( i) = self . get_if_interned ( & s) {
1500
+ if let Some ( global) =
1501
+ self . env ( ) . global_map . get ( & i) . copied ( ) . map ( |i| i as u32 )
1502
+ {
1503
+ return Some ( global) ;
1504
+ }
1505
+ }
1506
+ }
1507
+ } else if let Some ( g) = check_global ( self , import, sym) {
1508
+ return Some ( g) ;
1509
+ }
1510
+ }
1309
1511
self . env ( )
1310
1512
. global_map
1311
1513
. get ( & symbol)
0 commit comments