Skip to main content

scheme_rs/
env.rs

1//! Scheme lexical environments.
2
3use 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    /// A Repl is a library that does not have a name.
99    Repl,
100    /// A library has a name and an (optional) path.
101    Libary {
102        name: LibraryName,
103        path: Option<PathBuf>,
104    },
105    /// A program has a path
106    Program { path: PathBuf },
107}
108
109#[derive(Clone, Trace)]
110pub(crate) struct Import {
111    /// The original binding of the identifier before being renamed.
112    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/// A top level environment such as a library, program, or REPL.
123#[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        // Repls are given the import keyword, free of charge.
144        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        // Programs are given the import keyword, free of charge.
161        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        // Import libraries:
208        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                // Bind the new identifier in the global symbol table:
221                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                            // Bind the new identifier in the global symbol table:
247                            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    /// Evaluate the scheme expression in the provided environment and return
283    /// the values. The `import_policy` controls which libraries may be imported;
284    /// use `ImportPolicy::Allow` to permit all imports, or provide an allowlist
285    /// of specific libraries.
286    #[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                // std::mem::take(body)
361                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// TODO: Use these states to detect circular dependencies when we do our DFS.
499// Or, alternatively, just handle circular dependencies like Guile does.
500#[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/// A lexical contour
620#[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/// A local variable.
753#[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    /// Create a new temporary value.
778    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// TODO: Do we need to make this pointer eq?
822/// A global variable, i.e. a variable that is present in a top level
823/// environment.
824#[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        /* self.name == rhs.name && */
869    }
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/// A scope uniquely identifies a lexical contour and is used for binding
916/// resolution.
917#[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    /// Generates a new unique scope.
928    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
966// We can probably design this much more intelligently
967pub(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        // ImportPolicy::Allow should permit imports
1021        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        // deny_all should deny all imports
1033        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        // Allow only (rnrs base) via s-expression parsing
1042        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        // Allow only (rnrs base), but try to import (rnrs io simple)
1056        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        // Both libraries should be permitted
1069        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        // true should permit imports (backward compat)
1108        let result = env.eval(true, "(import (rnrs)) (abs -5)");
1109        assert!(result.is_ok(), "true should permit imports: {result:?}");
1110
1111        // false should deny imports (backward compat)
1112        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        // Plain library reference
1120        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        // Wrapped in (only ...)
1129        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        // Wrapped in (except ...)
1138        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        // Wrapped in (prefix ...)
1147        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        // Wrapped in (rename ...)
1156        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}