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,
}
}
}