Skip to content

Commit a125312

Browse files
authored
Merge branch 'main' into devops/fix-ci
2 parents 2f0ac90 + 8e25d75 commit a125312

File tree

21 files changed

+835
-410
lines changed

21 files changed

+835
-410
lines changed

builtins/src/collections.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ pub fn occurs(environment: &mut SloshVm, haystack: Value, needle: Value) -> VMRe
152152
/// Section: collection
153153
///
154154
/// Example:
155-
/// (assert-true (in? [1 2 3 4 5] 3))
156-
/// (assert-false (in? [1 2 3 4 5] 9))
157-
/// (assert-true (in? (list 1 2 3 4 5) 3))
158-
/// (assert-true (in? '(1 2 3 4 5) 5))
155+
/// (test::assert-true (in? [1 2 3 4 5] 3))
156+
/// (test::assert-false (in? [1 2 3 4 5] 9))
157+
/// (test::assert-true (in? (list 1 2 3 4 5) 3))
158+
/// (test::assert-true (in? '(1 2 3 4 5) 5))
159159
#[sl_sh_fn(fn_name = "in?", takes_env = true)]
160160
pub fn is_in(environment: &mut SloshVm, haystack: Value, needle: Value) -> VMResult<Value> {
161161
let mut stack = vec![];
@@ -254,12 +254,12 @@ pub fn flatten(vm: &mut SloshVm, registers: &[Value]) -> VMResult<Value> {
254254
///
255255
/// Example:
256256
/// (let (tmap [1 2 3 0])
257-
/// (assert-false (empty? tmap))
257+
/// (test::assert-false (empty? tmap))
258258
/// (set! tmap (reverse tmap))
259-
/// (assert-equal 2 (get tmap 2))
260-
/// (assert-equal 1 (get tmap 3))
261-
/// (assert-equal 0 (get tmap 0))
262-
/// (assert-error (reverse "string")))
259+
/// (test::assert-equal 2 (get tmap 2))
260+
/// (test::assert-equal 1 (get tmap 3))
261+
/// (test::assert-equal 0 (get tmap 0))
262+
/// (test::assert-error (reverse "string")))
263263
#[sl_sh_fn(fn_name = "reverse", takes_env = true)]
264264
pub fn reverse(environment: &mut SloshVm, seq: Value) -> VMResult<Value> {
265265
match seq {
@@ -332,10 +332,10 @@ it into one vector of values.
332332
Section: collection
333333
334334
Example:
335-
(assert-equal [1 2 3 1 2 3] (flatten 1 2 3 (list 1 2 3)))
336-
(assert-equal [1 2 3 1 2 3] (flatten 1 2 3 [1 2 3]))
337-
(assert-equal [1 2 3 1 2] (flatten 1 2 3 (list 1 2)))
338-
(assert-equal [1 2 3 1 2 3 1 2] (flatten 1 2 3 (list 1 2 3 (list 1 2))))
335+
(test::assert-equal [1 2 3 1 2 3] (flatten 1 2 3 (list 1 2 3)))
336+
(test::assert-equal [1 2 3 1 2 3] (flatten 1 2 3 [1 2 3]))
337+
(test::assert-equal [1 2 3 1 2] (flatten 1 2 3 (list 1 2)))
338+
(test::assert-equal [1 2 3 1 2 3 1 2] (flatten 1 2 3 (list 1 2 3 (list 1 2))))
339339
",
340340
);
341341
add_builtin(

builtins/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ Remainder from dividing first int by the second.
191191
Section: math
192192
193193
Example:
194-
(ns-import 'math)
195194
(test::assert-equal 0 (rem 50 10))
196195
(test::assert-equal 5 (rem 55 10))
197196
(test::assert-equal 1 (rem 1 2))

builtins/src/print.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub fn fprn(vm: &mut SloshVm, registers: &[Value]) -> VMResult<Value> {
153153
} else {
154154
Err(VMError::new(
155155
"io",
156-
"fpr: require a writable IO object as first parameter",
156+
"fprn: require a writable IO object as first parameter",
157157
))
158158
}
159159
}

compile_state/src/state.rs

Lines changed: 210 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,17 @@ pub struct Specials {
201201
pub is_err: Interned,
202202
pub is_ok: Interned,
203203
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,
204209

205210
pub rest: Interned,
206211
pub optional: Interned,
207212
pub scratch: Interned,
213+
pub colon: Interned,
214+
pub root: Interned,
208215
}
209216

210217
impl Specials {
@@ -368,7 +375,6 @@ Add a sequence of numbers. (+) will return 0.
368375
Section: math
369376
370377
Example:
371-
(ns-import 'math)
372378
(test::assert-equal 0 (+))
373379
(test::assert-equal 5 (+ 5))
374380
(test::assert-equal 10 (+ 5 5))
@@ -383,7 +389,6 @@ Subtract a sequence of numbers. Requires at least one number (negate if only on
383389
Section: math
384390
385391
Example:
386-
(ns-import 'math)
387392
(test::assert-error (- 5 "2"))
388393
(test::assert-equal -5 (- 5))
389394
(test::assert-equal -5.0 (- 5.0))
@@ -399,7 +404,6 @@ Multiply a sequence of numbers. (*) will return 1.
399404
Section: math
400405
401406
Example:
402-
(ns-import 'math)
403407
(test::assert-equal 1 (*))
404408
(test::assert-equal 5 (* 5))
405409
(test::assert-equal 5 (* 1 5))
@@ -420,7 +424,6 @@ Divide a sequence of numbers. Requires at least two numbers.
420424
421425
Section: math
422426
Example:
423-
(ns-import 'math)
424427
(test::assert-equal 5 (/ 50 10))
425428
(test::assert-equal 5 (/ 50.0 10.0))
426429
(test::assert-equal 0 (/ 1 5))
@@ -690,12 +693,12 @@ Example:
690693
(loop (idx) (3) (do
691694
(set! tot (+ tot 1))
692695
(if (> idx 1) (recur (- idx 1)))))
693-
(assert-equal 3 tot)
696+
(test::assert-equal 3 tot)
694697
(set! tot 0)
695698
((fn (idx) (do
696699
(set! tot (+ tot 1))
697700
(if (> idx 1) (recur (- idx 1)))))5)
698-
(assert-equal 5 tot)",
701+
(test::assert-equal 5 tot)",
699702
),
700703
this_fn: add_special(vm, "this-fn", ""),
701704
numeq: add_special(vm, "==", r#"Usage: (== val0 ... valN)
@@ -1071,10 +1074,111 @@ Example:
10711074
(test::assert-true (ok? nil))
10721075
"#),
10731076
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+
"#),
10741176

10751177
rest: vm.intern_static("&"),
10761178
optional: vm.intern_static("%"),
10771179
scratch: vm.intern_static("[SCRATCH]"),
1180+
colon: vm.intern_static(":"),
1181+
root: vm.intern_static("ROOT"),
10781182
}
10791183
}
10801184
}
@@ -1157,12 +1261,42 @@ impl CompileState {
11571261
}
11581262
}
11591263

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+
11601293
pub struct CompileEnvironment {
11611294
use_line: bool,
11621295
line: u32,
11631296
specials: Option<Specials>,
11641297
global_map: HashMap<Interned, usize>,
11651298
gensym_idx: usize,
1299+
namespace: Namespace,
11661300
}
11671301

11681302
impl Default for CompileEnvironment {
@@ -1179,6 +1313,10 @@ impl CompileEnvironment {
11791313
specials: None,
11801314
global_map: HashMap::new(),
11811315
gensym_idx: 0,
1316+
namespace: Namespace {
1317+
name: "".to_string(),
1318+
imports: vec![],
1319+
},
11821320
}
11831321
}
11841322

@@ -1192,8 +1330,22 @@ impl CompileEnvironment {
11921330
self.line
11931331
}
11941332

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
11971349
}
11981350
}
11991351

@@ -1306,6 +1458,56 @@ impl SloshVmTrait for SloshVm {
13061458
}
13071459

13081460
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+
}
13091511
self.env()
13101512
.global_map
13111513
.get(&symbol)

0 commit comments

Comments
 (0)