knave-expand 0.13.2

The reading, scoping and expansion stages of knave
Documentation
use std::collections::BTreeSet;

use super::Expander;
use super::expansion_limit::ExpansionLimit;
use super::failed_definition::DeclaredName;
use super::problems::ExpandProblems;
use crate::core::{BindingId, BindingKind, Declaration, ModulePath, Namespace};
use crate::diagnostic::Diagnostic;
use crate::interpret::{CompileTimeHost, Value};
use crate::modules::{ModuleRecord, ModuleSources};
use crate::reader::{Form, LiteralForms, Symbol};
use crate::scope::{Phase, Resolution, ScopeSet};
use crate::source::{Span, TextPosition};

impl CompileTimeHost for Expander<'_> {
    fn global_value(&mut self, binding: BindingId, span: Span) -> Result<Value, Diagnostic> {
        match self.kind(binding) {
            BindingKind::CodeBuiltin(builtin) => Ok(Value::Builtin(builtin)),
            BindingKind::Global(_) => self.evaluate_global(binding, span),
            _ => Err(ExpandProblems::not_at_compile_time(
                self.tables.bindings.get(binding).name(),
                span,
            )),
        }
    }

    fn definition_symbol(
        &self,
        term: &Symbol,
        name: String,
        span: Span,
    ) -> Result<Symbol, Diagnostic> {
        let binding = self.bound_term(term, span)?;
        Ok(Symbol::with_scopes(name, self.definition_scopes(binding)))
    }

    fn definition_call(&self, term: &Symbol, span: Span) -> Result<Option<Form>, Diagnostic> {
        let binding = self.bound_term(term, span)?;
        Ok(self.defining_call(binding))
    }

    fn type_definition_call(&self, term: &Symbol) -> Option<Form> {
        let binding = self.namespace_binding(term, Namespace::Type)?;
        self.defining_call(binding)
    }

    fn expand_call(&mut self, call: Form) -> Result<Option<Form>, Diagnostic> {
        let Some(binding) = self.macro_head(&call) else {
            return Ok(None);
        };
        match self.discarded_expansion(binding, call) {
            Ok(expansion) => Ok(Some(expansion)),
            Err(failure) if ExpansionLimit::stopped(&failure) => Err(failure),
            Err(_) => Ok(None),
        }
    }

    fn type_definition_symbol(&self, term: &Symbol, name: String) -> Symbol {
        let scopes = self
            .namespace_binding(term, Namespace::Type)
            .map_or_else(ScopeSet::default, |binding| self.definition_scopes(binding));
        Symbol::with_scopes(name, scopes)
    }

    fn set_macro_step_limit(&mut self, limit: u64) {
        self.form_budgets.current().steps().set_limit(limit);
    }

    fn add_module_note(&mut self, key: &str, note: Form) {
        let module = self
            .current_module
            .expect("macro code runs while a module expands");
        self.tables.module_notes.add(module, key, note);
    }

    fn module_notes(&self, context: &Symbol, key: &str) -> Vec<Form> {
        self.modules
            .with_body_scopes(context.scopes())
            .map_or_else(Vec::new, |module| self.tables.module_notes.of(module, key))
    }

    fn declare_definition(&mut self, name: Form, declaration: Declaration) {
        let introduction = *self
            .introductions
            .last()
            .expect("macro code runs inside a macro call");
        self.declared_definitions
            .push(DeclaredName::new(name, declaration, introduction));
    }

    fn form_location(&self, form: &Form) -> String {
        let span = form.span();
        let file = self.sources.file(span.source());
        let position = TextPosition::of_offset(file.text(), span.range().start);
        format!(
            "{}:{position}",
            self.module_sources.relative_name(file.name())
        )
    }

    fn in_tasks_directory(&self, form: &Form) -> bool {
        let file = self.sources.file(form.span().source());
        self.module_sources.holds_task(file.name())
    }

    fn read_literal_form(
        &self,
        literal: Span,
        value: &str,
        character: usize,
    ) -> Option<(Form, usize)> {
        LiteralForms::new(self.sources.file(literal.source()), literal.range(), value)
            .read_at(character)
    }

    fn project_modules(&self) -> Vec<Symbol> {
        self.modules
            .records()
            .filter(|record| ModuleSources::is_written(record.path()))
            .filter(|record| !self.is_library_module(record.path()))
            .map(ModuleRecord::symbol)
            .collect()
    }

    fn program_modules(&self) -> Vec<Symbol> {
        self.modules
            .records()
            .filter(|record| ModuleSources::is_written(record.path()))
            .map(ModuleRecord::symbol)
            .collect()
    }

    fn module_exports(&self, path: &Symbol, span: Span) -> Result<Vec<Symbol>, Diagnostic> {
        let record = self.loaded_module(path, span)?;
        let names: BTreeSet<&str> = record.exports().map(|(name, _, _)| &**name).collect();
        Ok(names
            .into_iter()
            .map(|name| Symbol::with_scopes(name, record.scopes().set()))
            .collect())
    }

    fn module_definitions(&self, path: &Symbol, span: Span) -> Result<Vec<Symbol>, Diagnostic> {
        let record = self.loaded_module(path, span)?;
        let module = self.modules.find(record.path());
        let names: BTreeSet<&str> = self
            .tables
            .bindings
            .ids()
            .filter(|binding| !self.tables.bindings.is_introduced(*binding))
            .filter(|binding| self.definition_module(*binding) == module)
            .map(|binding| self.tables.bindings.get(binding).name())
            .collect();
        Ok(names
            .into_iter()
            .map(|name| Symbol::with_scopes(name, record.scopes().set()))
            .collect())
    }

    fn current_module(&self) -> Symbol {
        let module = self
            .current_module
            .expect("macro code runs while a module expands");
        self.modules.get(module).symbol()
    }

    fn module_symbol(&self, path: &Symbol, span: Span) -> Result<Symbol, Diagnostic> {
        let record = self.loaded_module(path, span)?;
        if ModuleSources::is_compiler_provided(record.path()) {
            return Err(ExpandProblems::unknown_module(record.path(), span));
        }
        Ok(record.symbol())
    }

    fn require_module(&mut self, path: &Symbol, span: Span) -> Result<Symbol, Diagnostic> {
        let path = ModulePath::new(path.name());
        if !ModuleSources::is_embedded_prelude(&path) || ModuleSources::is_internal(&path) {
            return Err(ExpandProblems::unknown_module(&path, span));
        }
        let module = self.load_module(&path, span)?;
        Ok(self.modules.get(module).symbol())
    }

    fn is_pattern_head(&self, head: &Symbol) -> bool {
        self.names_pattern_head(head)
    }

    fn same_binding(&self, left: &Symbol, right: &Symbol) -> bool {
        let pairs: Vec<(Resolution, Resolution)> = [Namespace::Value, Namespace::Type]
            .into_iter()
            .map(|namespace| {
                (
                    self.tables.table.resolve(left, namespace, Phase::Runtime),
                    self.tables.table.resolve(right, namespace, Phase::Runtime),
                )
            })
            .collect();
        let bound_together = pairs.iter().any(|pair| {
            matches!(pair, (Resolution::Bound(left_binding), Resolution::Bound(right_binding))
                if left_binding == right_binding)
        });
        let unbound_everywhere = pairs
            .iter()
            .all(|pair| matches!(pair, (Resolution::Unbound, Resolution::Unbound)));
        bound_together || (unbound_everywhere && left.name() == right.name())
    }
}

impl Expander<'_> {
    fn loaded_module(&self, path: &Symbol, span: Span) -> Result<&ModuleRecord, Diagnostic> {
        let path = ModulePath::new(path.name());
        let module = self
            .modules
            .find(&path)
            .ok_or_else(|| ExpandProblems::unknown_module(&path, span))?;
        Ok(self.modules.get(module))
    }

    fn definition_scopes(&self, binding: BindingId) -> ScopeSet {
        if let Some(module) = self.definition_module(binding) {
            return self.modules.get(module).scopes().set();
        }
        match self.kind(binding) {
            BindingKind::KernelForm(_) | BindingKind::KernelType(_) | BindingKind::Boolean(_) => {
                self.prelude_scopes()
            }
            _ => ScopeSet::default(),
        }
    }

    fn prelude_scopes(&self) -> ScopeSet {
        let prelude = self
            .modules
            .find(&ModulePath::new(ModuleSources::PRELUDE))
            .expect("macro code runs after the prelude module is registered");
        self.modules.get(prelude).scopes().set()
    }

    fn bound_term(&self, term: &Symbol, span: Span) -> Result<BindingId, Diagnostic> {
        self.term_binding(term)
            .ok_or_else(|| self.unbound(term, Namespace::Value, span))
    }

    pub(super) fn term_binding(&self, term: &Symbol) -> Option<BindingId> {
        [Namespace::Value, Namespace::Type]
            .into_iter()
            .find_map(|namespace| self.namespace_binding(term, namespace))
    }

    pub(super) fn namespace_binding(
        &self,
        term: &Symbol,
        namespace: Namespace,
    ) -> Option<BindingId> {
        match self.tables.table.resolve(term, namespace, Phase::Runtime) {
            Resolution::Bound(binding) => Some(binding),
            _ => None,
        }
    }
}