1use std::{
4 collections::{BTreeSet, hash_map::Entry},
5 fmt,
6 hash::{Hash, Hasher},
7 path::{Path, PathBuf},
8 sync::{
9 LazyLock,
10 atomic::{AtomicUsize, Ordering},
11 },
12};
13
14use parking_lot::{MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard};
15use scheme_rs_macros::{maybe_async, maybe_await};
16
17use rustc_hash::FxHashMap as HashMap;
18
19#[cfg(feature = "async")]
20use futures::future::BoxFuture;
21
22pub use crate::ast::{AllowList, ImportPolicy};
23use crate::{
24 ast::{
25 DefinitionBody, ExportSet, ImportSet, LibraryName, LibrarySpec, ParseContext, Primitive,
26 },
27 cps::Compile,
28 exceptions::Exception,
29 gc::{Gc, Trace},
30 proc::{Application, ContBarrier, Procedure},
31 runtime::Runtime,
32 symbols::Symbol,
33 syntax::{Identifier, Syntax},
34 value::{Cell, Value},
35};
36
37pub(crate) mod error {
38 use crate::exceptions::Message;
39
40 use super::*;
41
42 pub(crate) fn name_bound_multiple_times(name: Symbol) -> Exception {
43 Exception::from(Message::new(format!("`{name}` bound multiple times")))
44 }
45}
46
47#[derive(Clone)]
48pub(crate) enum TopLevelBinding {
49 Global(Global),
50 Keyword(Procedure),
51 Primitive(Primitive),
52}
53
54pub(crate) static TOP_LEVEL_BINDINGS: LazyLock<Mutex<HashMap<Binding, TopLevelBinding>>> =
55 LazyLock::new(|| Mutex::new(HashMap::default()));
56
57#[derive(Trace)]
58pub(crate) struct TopLevelEnvironmentInner {
59 pub(crate) rt: Runtime,
60 pub(crate) kind: TopLevelKind,
61 pub(crate) imports: HashMap<Binding, TopLevelEnvironment>,
62 pub(crate) exports: HashMap<Symbol, Export>,
63 pub(crate) state: LibraryState,
64 pub(crate) scope: Scope,
65}
66
67impl TopLevelEnvironmentInner {
68 pub(crate) fn new(
69 rt: &Runtime,
70 kind: TopLevelKind,
71 imports: HashMap<Binding, TopLevelEnvironment>,
72 exports: HashMap<Symbol, Symbol>,
73 scope: Scope,
74 body: Syntax,
75 ) -> Self {
76 let exports = exports
77 .into_iter()
78 .map(|(name, rename)| {
79 let binding = Identifier::from_symbol(name, scope).bind();
80 let origin = imports.get(&binding).cloned();
81 (rename, Export { binding, origin })
82 })
83 .collect();
84
85 Self {
86 rt: rt.clone(),
87 kind,
88 imports,
89 exports,
90 state: LibraryState::Unexpanded(body),
91 scope,
92 }
93 }
94}
95
96#[derive(Trace, Debug)]
97pub enum TopLevelKind {
98 Repl,
100 Libary {
102 name: LibraryName,
103 path: Option<PathBuf>,
104 },
105 Program { path: PathBuf },
107}
108
109#[derive(Clone, Trace)]
110pub(crate) struct Import {
111 pub(crate) binding: Binding,
113 pub(crate) origin: TopLevelEnvironment,
114}
115
116#[derive(Trace, Clone)]
117pub(crate) struct Export {
118 pub(crate) binding: Binding,
119 pub(crate) origin: Option<TopLevelEnvironment>,
120}
121
122#[derive(Trace, Clone)]
124pub struct TopLevelEnvironment(pub(crate) Gc<RwLock<TopLevelEnvironmentInner>>);
125
126impl PartialEq for TopLevelEnvironment {
127 fn eq(&self, rhs: &Self) -> bool {
128 Gc::ptr_eq(&self.0, &rhs.0)
129 }
130}
131
132impl TopLevelEnvironment {
133 pub fn new_repl(rt: &Runtime) -> Self {
134 let inner = TopLevelEnvironmentInner {
135 rt: rt.clone(),
136 kind: TopLevelKind::Repl,
137 exports: HashMap::default(),
138 imports: HashMap::default(),
139 state: LibraryState::Invoked,
140 scope: Scope::new(),
141 };
142 let repl = Self(Gc::new(RwLock::new(inner)));
143 repl.give_import_primitive();
145 repl
146 }
147
148 pub(crate) fn new_program(rt: &Runtime, path: &Path) -> Self {
149 let inner = TopLevelEnvironmentInner {
150 rt: rt.clone(),
151 kind: TopLevelKind::Program {
152 path: path.to_path_buf(),
153 },
154 exports: HashMap::default(),
155 imports: HashMap::default(),
156 state: LibraryState::Invoked,
157 scope: Scope::new(),
158 };
159 let program = Self(Gc::new(RwLock::new(inner)));
160 program.give_import_primitive();
162 program
163 }
164
165 fn give_import_primitive(&self) {
166 let kw = Symbol::intern("import");
167 let lib_name = [
168 Symbol::intern("rnrs"),
169 Symbol::intern("base"),
170 Symbol::intern("primitives"),
171 ];
172 let rnrs_base_prims = self
173 .0
174 .read()
175 .rt
176 .get_registry()
177 .0
178 .read()
179 .libs
180 .get(lib_name.as_slice())
181 .cloned()
182 .unwrap();
183 let import = rnrs_base_prims.0.read().exports.get(&kw).unwrap().clone();
184 let mut this = self.0.write();
185 this.imports.insert(import.binding, rnrs_base_prims.clone());
186 add_binding(Identifier::from_symbol(kw, this.scope), import.binding);
187 }
188
189 pub(crate) fn scope(&self) -> Scope {
190 self.0.read().scope
191 }
192
193 #[maybe_async]
194 pub fn from_spec(rt: &Runtime, spec: LibrarySpec, path: PathBuf) -> Result<Self, Exception> {
195 maybe_await!(Self::from_spec_with_scope(rt, spec, path, Scope::new()))
196 }
197
198 #[maybe_async]
199 pub(crate) fn from_spec_with_scope(
200 rt: &Runtime,
201 spec: LibrarySpec,
202 path: PathBuf,
203 library_scope: Scope,
204 ) -> Result<Self, Exception> {
205 let registry = rt.get_registry();
206
207 let mut bound_names = HashMap::<Symbol, Binding>::default();
209 let mut imports = HashMap::<Binding, TopLevelEnvironment>::default();
210
211 for lib_import in spec.imports.import_sets.into_iter() {
212 for (name, import) in maybe_await!(registry.import(rt, lib_import))? {
213 if let Some(prev_binding) = bound_names.get(&name)
214 && prev_binding != &import.binding
215 {
216 return Err(error::name_bound_multiple_times(name));
217 }
218 bound_names.insert(name, import.binding);
219 imports.insert(import.binding, import.origin);
220 add_binding(Identifier::from_symbol(name, library_scope), import.binding);
222 }
223 }
224
225 let mut exports = HashMap::default();
226 for export in spec.exports.export_sets.into_iter() {
227 match export {
228 ExportSet::Internal { rename, name } => {
229 let rename = if let Some(rename) = rename {
230 rename
231 } else {
232 name
233 };
234 exports.insert(name, rename);
235 }
236 ExportSet::External(lib_import) => {
237 for lib_import in lib_import.import_sets.into_iter() {
238 for (name, import) in maybe_await!(registry.import(rt, lib_import))? {
239 if let Some(prev_binding) = bound_names.get(&name)
240 && prev_binding != &import.binding
241 {
242 return Err(error::name_bound_multiple_times(name));
243 }
244 bound_names.insert(name, import.binding);
245 imports.insert(import.binding, import.origin);
246 add_binding(
248 Identifier::from_symbol(name, library_scope),
249 import.binding,
250 );
251 exports.insert(name, name);
252 }
253 }
254 }
255 }
256 }
257
258 let mut body = spec.body;
259 body.add_scope(library_scope);
260
261 Ok(Self(Gc::new(RwLock::new(TopLevelEnvironmentInner::new(
262 rt,
263 TopLevelKind::Libary {
264 name: spec.name,
265 path: Some(path),
266 },
267 imports,
268 exports,
269 library_scope,
270 body,
271 )))))
272 }
273
274 pub(crate) fn get_kind(&self) -> MappedRwLockReadGuard<'_, TopLevelKind> {
275 RwLockReadGuard::map(self.0.read(), |inner| &inner.kind)
276 }
277
278 pub(crate) fn get_state(&self) -> MappedRwLockReadGuard<'_, LibraryState> {
279 RwLockReadGuard::map(self.0.read(), |inner| &inner.state)
280 }
281
282 #[maybe_async]
287 pub fn eval(
288 &self,
289 import_policy: impl Into<ImportPolicy>,
290 code: &str,
291 ) -> Result<Vec<Value>, Exception> {
292 let mut sexprs = Syntax::from_str(code, None)?;
293 sexprs.add_scope(self.0.read().scope);
294 let Some([body @ .., end]) = sexprs.as_list() else {
295 return Err(Exception::syntax(sexprs, None));
296 };
297 if !end.is_null() {
298 return Err(Exception::syntax(sexprs, None));
299 }
300 let rt = { self.0.read().rt.clone() };
301 let ctxt = ParseContext::new(&rt, import_policy);
302 let body = maybe_await!(DefinitionBody::parse(
303 &ctxt,
304 body,
305 &Environment::Top(self.clone()),
306 &sexprs
307 ))?;
308 let compiled = maybe_await!(rt.compile_expr(body.compile_top_level()));
309 maybe_await!(Application::new(compiled, Vec::new()).eval(&mut ContBarrier::new()))
310 }
311
312 #[maybe_async]
313 pub fn eval_sexpr(
314 &self,
315 import_policy: impl Into<ImportPolicy>,
316 mut sexpr: Syntax,
317 ) -> Result<Vec<Value>, Exception> {
318 let rt = { self.0.read().rt.clone() };
319 let ctxt = ParseContext::new(&rt, import_policy);
320 sexpr.add_scope(self.0.read().scope);
321 let body = std::slice::from_ref(&sexpr);
322 let body = maybe_await!(DefinitionBody::parse(
323 &ctxt,
324 body,
325 &Environment::Top(self.clone()),
326 &sexpr
327 ))?;
328 let compiled = maybe_await!(rt.compile_expr(body.compile_top_level()));
329 maybe_await!(Application::new(compiled, Vec::new()).eval(&mut ContBarrier::new()))
330 }
331
332 #[maybe_async]
333 pub fn import(&self, import_set: ImportSet) -> Result<(), Exception> {
334 let (rt, registry, scope) = {
335 let this = self.0.read();
336 (this.rt.clone(), this.rt.get_registry(), this.scope)
337 };
338 let imports = maybe_await!(registry.import(&rt, import_set))?;
339 let mut this = self.0.write();
340 for (sym, import) in imports {
341 match this.imports.entry(import.binding) {
342 Entry::Occupied(prev_imported) if *prev_imported.key() != import.binding => {
343 return Err(error::name_bound_multiple_times(sym));
344 }
345 Entry::Vacant(slot) => {
346 add_binding(Identifier::from_symbol(sym, scope), import.binding);
347 slot.insert(import.origin);
348 }
349 _ => (),
350 }
351 }
352 Ok(())
353 }
354
355 #[maybe_async]
356 pub(crate) fn maybe_expand(&self) -> Result<(), Exception> {
357 let body = {
358 let mut this = self.0.write();
359 if let LibraryState::Unexpanded(body) = &mut this.state {
360 body.clone()
362 } else {
363 return Ok(());
364 }
365 };
366 let rt = { self.0.read().rt.clone() };
367 let env = Environment::from(self.clone());
368 let expanded = maybe_await!(DefinitionBody::parse_lib_body(&rt, &body, &env))?;
369 self.0.write().state = LibraryState::Expanded(expanded);
370 Ok(())
371 }
372
373 #[maybe_async]
374 pub(crate) fn maybe_invoke(&self) -> Result<(), Exception> {
375 maybe_await!(self.maybe_expand())?;
376 let defn_body = {
377 let mut this = self.0.write();
378 match std::mem::replace(&mut this.state, LibraryState::Invalid) {
379 LibraryState::Expanded(defn_body) => defn_body,
380 x => {
381 this.state = x;
382 return Ok(());
383 }
384 }
385 };
386 let compiled = defn_body.compile_top_level();
387 let rt = { self.0.read().rt.clone() };
388 let proc = maybe_await!(rt.compile_expr(compiled));
389 let _ = maybe_await!(Application::new(proc, Vec::new()).eval(&mut ContBarrier::new()))?;
390 self.0.write().state = LibraryState::Invoked;
391 Ok(())
392 }
393
394 pub fn is_repl(&self) -> bool {
395 matches!(self.0.read().kind, TopLevelKind::Repl)
396 }
397
398 pub fn def_var(&self, binding: Binding, name: Symbol, value: Value) -> Global {
399 let mutable = self
400 .0
401 .read()
402 .exports
403 .get(&name)
404 .map(|export| export.binding)
405 != Some(binding);
406 let mut top_level_binds = TOP_LEVEL_BINDINGS.lock();
407 match top_level_binds.entry(binding) {
408 Entry::Occupied(mut occup) => match occup.get_mut() {
409 TopLevelBinding::Global(global) => global.clone(),
410 occup => {
411 let global = Global::new(name, Cell::new(value), mutable, self.clone());
412 *occup = TopLevelBinding::Global(global.clone());
413 global
414 }
415 },
416 Entry::Vacant(vacant) => {
417 let global = Global::new(name, Cell::new(value), mutable, self.clone());
418 vacant.insert(TopLevelBinding::Global(global.clone()));
419 global
420 }
421 }
422 }
423
424 #[cfg(not(feature = "async"))]
425 pub fn lookup_var(&self, binding: Binding) -> Result<Option<Global>, Exception> {
426 self.lookup_var_inner(binding)
427 }
428
429 #[cfg(feature = "async")]
430 pub(crate) fn lookup_var(
431 &self,
432 binding: Binding,
433 ) -> BoxFuture<'_, Result<Option<Global>, Exception>> {
434 Box::pin(self.lookup_var_inner(binding))
435 }
436
437 #[maybe_async]
438 pub fn lookup_var_inner(&self, binding: Binding) -> Result<Option<Global>, Exception> {
439 if let Some(TopLevelBinding::Global(global)) =
440 { TOP_LEVEL_BINDINGS.lock().get(&binding).cloned() }
441 {
442 if *self != global.origin {
443 maybe_await!(global.origin.maybe_invoke())?;
444 }
445 Ok(Some(global.clone()))
446 } else {
447 Ok(None)
448 }
449 }
450
451 pub fn def_keyword(&self, binding: Binding, transformer: Procedure) {
452 TOP_LEVEL_BINDINGS
453 .lock()
454 .insert(binding, TopLevelBinding::Keyword(transformer));
455 }
456
457 #[cfg(not(feature = "async"))]
458 pub fn lookup_keyword(&self, binding: Binding) -> Result<Option<Procedure>, Exception> {
459 self.lookup_keyword_inner(binding)
460 }
461
462 #[cfg(feature = "async")]
463 pub(crate) fn lookup_keyword(
464 &self,
465 binding: Binding,
466 ) -> BoxFuture<'_, Result<Option<Procedure>, Exception>> {
467 Box::pin(self.lookup_keyword_inner(binding))
468 }
469
470 #[maybe_async]
471 pub fn lookup_keyword_inner(&self, binding: Binding) -> Result<Option<Procedure>, Exception> {
472 if let Some(origin) = { self.0.read().imports.get(&binding).cloned() } {
473 maybe_await!(origin.maybe_expand())?;
474 maybe_await!(origin.lookup_keyword(binding))
475 } else if let Some(TopLevelBinding::Keyword(kw)) = TOP_LEVEL_BINDINGS.lock().get(&binding) {
476 Ok(Some(kw.clone()))
477 } else {
478 Ok(None)
479 }
480 }
481
482 pub fn lookup_primitive(&self, binding: Binding) -> Option<Primitive> {
483 if let Some(TopLevelBinding::Primitive(primitive)) = TOP_LEVEL_BINDINGS.lock().get(&binding)
484 {
485 Some(*primitive)
486 } else {
487 None
488 }
489 }
490}
491
492impl fmt::Debug for TopLevelEnvironment {
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 write!(f, "%top")
495 }
496}
497
498#[derive(Trace, Debug)]
501pub(crate) enum LibraryState {
502 Invalid,
503 BridgesDefined,
504 Unexpanded(Syntax),
505 Expanded(DefinitionBody),
506 Invoked,
507}
508
509#[derive(Trace, PartialEq, Debug, Clone)]
510enum LocalBinding {
511 Var(Local),
512 Keyword(Procedure),
513 Pattern(Local, usize),
514 Imported(TopLevelEnvironment),
515}
516
517#[derive(Trace, Debug)]
518pub(crate) struct LexicalContour {
519 up: Environment,
520 bindings: Mutex<HashMap<Binding, LocalBinding>>,
521 scope: Scope,
522}
523
524impl LexicalContour {
525 fn def_var(&self, binding: Binding, name: Symbol) -> Local {
526 let local = Local::gensym_with_name(name);
527 self.bindings
528 .lock()
529 .insert(binding, LocalBinding::Var(local));
530 local
531 }
532
533 fn def_keyword(&self, binding: Binding, transformer: Procedure) {
534 self.bindings
535 .lock()
536 .insert(binding, LocalBinding::Keyword(transformer));
537 }
538
539 #[maybe_async]
540 fn lookup_keyword(&self, binding: Binding) -> Result<Option<Procedure>, Exception> {
541 if let Some(bound) = { self.bindings.lock().get(&binding).cloned() } {
542 match bound {
543 LocalBinding::Keyword(transformer) => Ok(Some(transformer.clone())),
544 LocalBinding::Imported(imported) => maybe_await!(imported.lookup_keyword(binding)),
545 _ => Ok(None),
546 }
547 } else {
548 maybe_await!(self.up.lookup_keyword(binding))
549 }
550 }
551
552 #[maybe_async]
553 fn lookup_var(&self, binding: Binding) -> Result<Option<Var>, Exception> {
554 if let Some(bound) = { self.bindings.lock().get(&binding).cloned() } {
555 match bound {
556 LocalBinding::Var(local) => Ok(Some(Var::Local(local))),
557 LocalBinding::Imported(imported) => {
558 Ok(maybe_await!(imported.lookup_var(binding))?.map(Var::Global))
559 }
560 _ => Ok(None),
561 }
562 } else {
563 maybe_await!(self.up.lookup_var(binding))
564 }
565 }
566
567 fn lookup_primitive(&self, binding: Binding) -> Option<Primitive> {
568 if let Some(bound) = self.bindings.lock().get(&binding) {
569 match bound {
570 LocalBinding::Imported(imported) => imported.lookup_primitive(binding),
571 _ => None,
572 }
573 } else {
574 self.up.lookup_primitive(binding)
575 }
576 }
577
578 fn lookup_pattern_variable(&self, binding: Binding) -> Option<(Local, usize)> {
579 if let Some(bound) = self.bindings.lock().get(&binding) {
580 match bound {
581 LocalBinding::Pattern(local, depth) => Some((*local, *depth)),
582 _ => None,
583 }
584 } else {
585 self.up.lookup_pattern_variable(binding)
586 }
587 }
588
589 #[maybe_async]
590 pub fn import(&self, import_set: ImportSet) -> Result<(), Exception> {
591 let (rt, registry) = {
592 let top = self.fetch_top();
593 let top = top.0.read();
594 (top.rt.clone(), top.rt.get_registry())
595 };
596 let imports = maybe_await!(registry.import(&rt, import_set))?;
597 let mut bindings = self.bindings.lock();
598 for (sym, import) in imports {
599 let binding_type = LocalBinding::Imported(import.origin.clone());
600 match bindings.entry(import.binding) {
601 Entry::Occupied(prev_imported) if *prev_imported.key() != import.binding => {
602 return Err(error::name_bound_multiple_times(sym));
603 }
604 Entry::Vacant(slot) => {
605 add_binding(Identifier::from_symbol(sym, self.scope), import.binding);
606 slot.insert(binding_type);
607 }
608 _ => (),
609 }
610 }
611 Ok(())
612 }
613
614 pub fn fetch_top(&self) -> TopLevelEnvironment {
615 self.up.fetch_top()
616 }
617}
618
619#[derive(Clone, Trace, Debug)]
621pub(crate) enum Environment {
622 Top(TopLevelEnvironment),
623 LexicalContour(Gc<LexicalContour>),
624}
625
626impl Environment {
627 pub fn new_lexical_contour(&self, scope: Scope) -> Self {
628 Self::LexicalContour(Gc::new(LexicalContour {
629 up: self.clone(),
630 bindings: Mutex::new(HashMap::default()),
631 scope,
632 }))
633 }
634
635 pub fn new_syntax_case_contour(
636 &self,
637 scope: Scope,
638 expansion: Local,
639 vars: HashMap<Binding, usize>,
640 ) -> Self {
641 Self::LexicalContour(Gc::new(LexicalContour {
642 up: self.clone(),
643 bindings: Mutex::new(
644 vars.into_iter()
645 .map(|(binding, depth)| (binding, LocalBinding::Pattern(expansion, depth)))
646 .collect(),
647 ),
648 scope,
649 }))
650 }
651
652 pub fn def_var(&self, binding: Binding, name: Symbol) -> Var {
653 match self {
654 Self::Top(top) => Var::Global(top.def_var(binding, name, Value::undefined())),
655 Self::LexicalContour(lc) => Var::Local(lc.def_var(binding, name)),
656 }
657 }
658
659 pub fn def_keyword(&self, binding: Binding, transformer: Procedure) {
660 match self {
661 Self::Top(top) => top.def_keyword(binding, transformer),
662 Self::LexicalContour(lc) => lc.def_keyword(binding, transformer),
663 }
664 }
665
666 #[cfg(not(feature = "async"))]
667 pub fn lookup_keyword(&self, binding: Binding) -> Result<Option<Procedure>, Exception> {
668 match self {
669 Self::Top(top) => top.lookup_keyword(binding),
670 Self::LexicalContour(lc) => lc.lookup_keyword(binding),
671 }
672 }
673
674 #[cfg(feature = "async")]
675 pub fn lookup_keyword(
676 &self,
677 binding: Binding,
678 ) -> BoxFuture<'_, Result<Option<Procedure>, Exception>> {
679 Box::pin(async move {
680 match self {
681 Self::Top(top) => top.lookup_keyword(binding).await,
682 Self::LexicalContour(lc) => lc.lookup_keyword(binding).await,
683 }
684 })
685 }
686
687 #[cfg(not(feature = "async"))]
688 pub fn lookup_var(&self, binding: Binding) -> Result<Option<Var>, Exception> {
689 match self {
690 Self::Top(top) => Ok(top.lookup_var(binding)?.map(Var::Global)),
691 Self::LexicalContour(lc) => lc.lookup_var(binding),
692 }
693 }
694
695 #[cfg(feature = "async")]
696 pub fn lookup_var(&self, binding: Binding) -> BoxFuture<'_, Result<Option<Var>, Exception>> {
697 Box::pin(async move {
698 match self {
699 Self::Top(top) => Ok(top.lookup_var(binding).await?.map(Var::Global)),
700 Self::LexicalContour(lc) => lc.lookup_var(binding).await,
701 }
702 })
703 }
704
705 pub fn get_scope_set(&self) -> BTreeSet<Scope> {
706 match self {
707 Self::Top(top) => BTreeSet::from([top.scope()]),
708 Self::LexicalContour(lc) => {
709 let mut up_scopes = lc.up.get_scope_set();
710 up_scopes.insert(lc.scope);
711 up_scopes
712 }
713 }
714 }
715
716 pub fn lookup_primitive(&self, binding: Binding) -> Option<Primitive> {
717 match self {
718 Self::Top(top) => top.lookup_primitive(binding),
719 Self::LexicalContour(lc) => lc.lookup_primitive(binding),
720 }
721 }
722
723 pub fn lookup_pattern_variable(&self, binding: Binding) -> Option<(Local, usize)> {
724 match self {
725 Self::Top(_) => None,
726 Self::LexicalContour(lc) => lc.lookup_pattern_variable(binding),
727 }
728 }
729
730 #[maybe_async]
731 pub fn import(&self, import_set: ImportSet) -> Result<(), Exception> {
732 match self {
733 Self::Top(top) => maybe_await!(top.import(import_set)),
734 Self::LexicalContour(lc) => maybe_await!(lc.import(import_set)),
735 }
736 }
737
738 pub fn fetch_top(&self) -> TopLevelEnvironment {
739 match self {
740 Self::Top(top) => top.clone(),
741 Self::LexicalContour(lc) => lc.fetch_top(),
742 }
743 }
744}
745
746impl From<TopLevelEnvironment> for Environment {
747 fn from(env: TopLevelEnvironment) -> Self {
748 Self::Top(env)
749 }
750}
751
752#[derive(Copy, Clone, Trace)]
754pub struct Local {
755 pub(crate) id: usize,
756 pub(crate) name: Option<Symbol>,
757}
758
759impl Hash for Local {
760 fn hash<H>(&self, state: &mut H)
761 where
762 H: Hasher,
763 {
764 self.id.hash(state);
765 }
766}
767
768impl PartialEq for Local {
769 fn eq(&self, rhs: &Self) -> bool {
770 self.id == rhs.id
771 }
772}
773
774impl Eq for Local {}
775
776impl Local {
777 pub(crate) fn gensym() -> Self {
779 static NEXT_SYM: AtomicUsize = AtomicUsize::new(0);
780 Self {
781 id: NEXT_SYM.fetch_add(1, Ordering::Relaxed),
782 name: None,
783 }
784 }
785
786 pub(crate) fn gensym_with_name(name: Symbol) -> Self {
787 let mut sym = Self::gensym();
788 sym.name = Some(name);
789 sym
790 }
791
792 pub(crate) fn get_func_name(&self) -> String {
793 if let Some(name) = self.name {
794 format!("{name}")
795 } else {
796 format!("f{}", self.id)
797 }
798 }
799}
800
801impl fmt::Display for Local {
802 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
803 if let Some(name) = self.name {
804 write!(f, "{name}")
805 } else {
806 write!(f, "%{}", self.id)
807 }
808 }
809}
810
811impl fmt::Debug for Local {
812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 if let Some(name) = self.name {
814 write!(f, "{name}")
815 } else {
816 write!(f, "%{}", self.id)
817 }
818 }
819}
820
821#[derive(Clone, Trace)]
825pub struct Global {
826 pub(crate) name: Symbol,
827 pub(crate) val: Cell,
828 pub(crate) mutable: bool,
829 pub(crate) origin: TopLevelEnvironment,
830}
831
832impl Global {
833 pub(crate) fn new(name: Symbol, val: Cell, mutable: bool, origin: TopLevelEnvironment) -> Self {
834 Global {
835 name,
836 val,
837 mutable,
838 origin,
839 }
840 }
841
842 pub fn is_mutable(&self) -> bool {
843 self.mutable
844 }
845
846 pub fn read(&self) -> Value {
847 self.val.0.read().clone()
848 }
849
850 pub fn set(&self, new: Value) -> Result<(), Exception> {
851 if !self.mutable {
852 return Err(Exception::error("cannot modify immutable variable"));
853 }
854 *self.val.0.write() = new;
855 Ok(())
856 }
857}
858
859impl fmt::Debug for Global {
860 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861 write!(f, "${}", self.name)
862 }
863}
864
865impl PartialEq for Global {
866 fn eq(&self, rhs: &Self) -> bool {
867 Gc::ptr_eq(&self.val.0, &rhs.val.0)
868 }
870}
871
872impl Eq for Global {}
873
874impl Hash for Global {
875 fn hash<H>(&self, state: &mut H)
876 where
877 H: Hasher,
878 {
879 self.name.hash(state);
880 Gc::as_ptr(&self.val.0).hash(state);
881 }
882}
883
884#[derive(Clone, Trace, Hash, PartialEq, Eq)]
885pub(crate) enum Var {
886 Global(Global),
887 Local(Local),
888}
889
890impl Var {
891 pub fn symbol(&self) -> Option<Symbol> {
892 match self {
893 Var::Global(global) => Some(global.name),
894 Var::Local(local) => local.name,
895 }
896 }
897
898 pub fn as_local(&self) -> Option<Local> {
899 match self {
900 Self::Global(_) => None,
901 Self::Local(local) => Some(*local),
902 }
903 }
904}
905
906impl fmt::Debug for Var {
907 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
908 match self {
909 Self::Global(global) => global.fmt(f),
910 Self::Local(local) => local.fmt(f),
911 }
912 }
913}
914
915#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Trace)]
918pub struct Scope(usize);
919
920impl fmt::Debug for Scope {
921 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
922 write!(f, "%{}", self.0)
923 }
924}
925
926impl Scope {
927 pub fn new() -> Self {
929 static NEXT_SCOPE: AtomicUsize = AtomicUsize::new(0);
930
931 Self(NEXT_SCOPE.fetch_add(1, Ordering::Relaxed))
932 }
933}
934
935impl Default for Scope {
936 fn default() -> Self {
937 Self::new()
938 }
939}
940
941#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Trace)]
942pub struct Binding(usize);
943
944impl Binding {
945 pub fn new() -> Self {
946 static NEXT_LOC: AtomicUsize = AtomicUsize::new(0);
947
948 Self(NEXT_LOC.fetch_add(1, Ordering::Relaxed))
949 }
950}
951
952impl Default for Binding {
953 fn default() -> Self {
954 Self::new()
955 }
956}
957
958impl fmt::Debug for Binding {
959 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
960 write!(f, "!{}", self.0)
961 }
962}
963
964type Bindings = Vec<(BTreeSet<Scope>, Binding)>;
965
966pub(crate) static GLOBAL_BINDING_TABLE: LazyLock<Mutex<HashMap<Symbol, Bindings>>> =
968 LazyLock::new(|| Mutex::new(HashMap::default()));
969
970pub(crate) fn add_binding(id: Identifier, binding: Binding) {
971 GLOBAL_BINDING_TABLE
972 .lock()
973 .entry(id.sym)
974 .or_default()
975 .push((id.scopes, binding));
976}
977
978pub(crate) fn resolve(id: &Identifier) -> Option<Binding> {
979 let candidate_ids = find_all_matching_bindings(id);
980 let max_id = candidate_ids
981 .iter()
982 .max_by(|a, b| a.0.len().cmp(&b.0.len()))?;
983 if is_ambiguous(&max_id.0, &candidate_ids) {
984 return None;
985 }
986 Some(max_id.1)
987}
988
989fn is_ambiguous(max_id: &BTreeSet<Scope>, candidates: &[(BTreeSet<Scope>, Binding)]) -> bool {
990 for candidate in candidates {
991 if !candidate.0.is_subset(max_id) {
992 return true;
993 }
994 }
995 false
996}
997
998fn find_all_matching_bindings(id: &Identifier) -> Vec<(BTreeSet<Scope>, Binding)> {
999 GLOBAL_BINDING_TABLE
1000 .lock()
1001 .get(&id.sym)
1002 .map_or_else(Vec::new, |vec| {
1003 vec.iter()
1004 .filter(|(scopes, _)| scopes.is_subset(&id.scopes))
1005 .cloned()
1006 .collect()
1007 })
1008}
1009
1010#[cfg(all(test, not(feature = "async")))]
1011mod tests {
1012 use super::*;
1013 use crate::ast::ImportSet;
1014 use crate::runtime::Runtime;
1015
1016 #[test]
1017 fn import_policy_allow() {
1018 let rt = Runtime::new();
1019 let env = TopLevelEnvironment::new_repl(&rt);
1020 let result = env.eval(ImportPolicy::Allow, "(import (rnrs)) (abs -5)");
1022 assert!(
1023 result.is_ok(),
1024 "ImportPolicy::Allow should permit imports: {result:?}"
1025 );
1026 }
1027
1028 #[test]
1029 fn import_policy_deny_all() {
1030 let rt = Runtime::new();
1031 let env = TopLevelEnvironment::new_repl(&rt);
1032 let result = env.eval(ImportPolicy::deny_all(), "(import (rnrs))");
1034 assert!(result.is_err(), "deny_all should deny all imports");
1035 }
1036
1037 #[test]
1038 fn import_policy_allowlist_permits_listed() {
1039 let rt = Runtime::new();
1040 let env = TopLevelEnvironment::new_repl(&rt);
1041 let allow_list: AllowList = "((rnrs base))".parse().unwrap();
1043 let policy = ImportPolicy::allow_only(allow_list);
1044 let result = env.eval(policy, "(import (rnrs base)) (abs -5)");
1045 assert!(
1046 result.is_ok(),
1047 "AllowList should permit listed library: {result:?}"
1048 );
1049 }
1050
1051 #[test]
1052 fn import_policy_allowlist_denies_unlisted() {
1053 let rt = Runtime::new();
1054 let env = TopLevelEnvironment::new_repl(&rt);
1055 let allow_list: AllowList = "((rnrs base))".parse().unwrap();
1057 let policy = ImportPolicy::allow_only(allow_list);
1058 let result = env.eval(policy, "(import (rnrs io simple))");
1059 assert!(result.is_err(), "AllowList should deny unlisted library");
1060 }
1061
1062 #[test]
1063 fn allowlist_from_str_multiple_libs() {
1064 let allow_list: AllowList = "((rnrs base) (rnrs io simple))".parse().unwrap();
1065 let rt = Runtime::new();
1066 let env = TopLevelEnvironment::new_repl(&rt);
1067 let policy = ImportPolicy::allow_only(allow_list);
1068 let result = env.eval(policy, "(import (rnrs base) (rnrs io simple)) (abs -5)");
1070 assert!(
1071 result.is_ok(),
1072 "AllowList should permit all listed libraries: {result:?}"
1073 );
1074 }
1075
1076 #[test]
1077 fn allowlist_from_slice() {
1078 let allow_list = AllowList::from_slice(&[&["rnrs", "base"]]);
1079 let rt = Runtime::new();
1080 let env = TopLevelEnvironment::new_repl(&rt);
1081 let policy = ImportPolicy::allow_only(allow_list);
1082 let result = env.eval(policy, "(import (rnrs base)) (abs -5)");
1083 assert!(
1084 result.is_ok(),
1085 "AllowList::from_slice should work: {result:?}"
1086 );
1087 }
1088
1089 #[test]
1090 fn allowlist_add_lib() {
1091 use crate::symbols::Symbol;
1092
1093 let mut allow_list = AllowList::default();
1094 allow_list.add_lib(vec![Symbol::intern("rnrs"), Symbol::intern("base")]);
1095 let rt = Runtime::new();
1096 let env = TopLevelEnvironment::new_repl(&rt);
1097 let policy = ImportPolicy::allow_only(allow_list);
1098 let result = env.eval(policy, "(import (rnrs base)) (abs -5)");
1099 assert!(result.is_ok(), "AllowList::add_lib should work: {result:?}");
1100 }
1101
1102 #[test]
1103 fn import_policy_from_bool() {
1104 let rt = Runtime::new();
1105 let env = TopLevelEnvironment::new_repl(&rt);
1106
1107 let result = env.eval(true, "(import (rnrs)) (abs -5)");
1109 assert!(result.is_ok(), "true should permit imports: {result:?}");
1110
1111 let env2 = TopLevelEnvironment::new_repl(&rt);
1113 let result = env2.eval(false, "(import (rnrs))");
1114 assert!(result.is_err(), "false should deny imports");
1115 }
1116
1117 #[test]
1118 fn import_set_library_name() {
1119 let import_set: ImportSet = "(scheme write)".parse().unwrap();
1121 let name: Vec<String> = import_set
1122 .library_name()
1123 .iter()
1124 .map(|s| s.to_str().to_string())
1125 .collect();
1126 assert_eq!(name, vec!["scheme", "write"]);
1127
1128 let import_set: ImportSet = "(only (scheme base) define)".parse().unwrap();
1130 let name: Vec<String> = import_set
1131 .library_name()
1132 .iter()
1133 .map(|s| s.to_str().to_string())
1134 .collect();
1135 assert_eq!(name, vec!["scheme", "base"]);
1136
1137 let import_set: ImportSet = "(except (scheme write) display)".parse().unwrap();
1139 let name: Vec<String> = import_set
1140 .library_name()
1141 .iter()
1142 .map(|s| s.to_str().to_string())
1143 .collect();
1144 assert_eq!(name, vec!["scheme", "write"]);
1145
1146 let import_set: ImportSet = "(prefix (scheme base) s:)".parse().unwrap();
1148 let name: Vec<String> = import_set
1149 .library_name()
1150 .iter()
1151 .map(|s| s.to_str().to_string())
1152 .collect();
1153 assert_eq!(name, vec!["scheme", "base"]);
1154
1155 let import_set: ImportSet = "(rename (scheme write) (display show))".parse().unwrap();
1157 let name: Vec<String> = import_set
1158 .library_name()
1159 .iter()
1160 .map(|s| s.to_str().to_string())
1161 .collect();
1162 assert_eq!(name, vec!["scheme", "write"]);
1163 }
1164}