use super::Expander;
use super::evaluated_forms::EvaluatedForms;
use super::input_terms::InputTerms;
use super::module_notes::NotesCheckpoint;
use super::recorded_inputs::RecordedInputs;
use crate::core::ModulePath;
use crate::source::{SourceId, Span};
#[derive(Debug, PartialEq, Eq)]
pub enum ClassifiedForm {
Expression,
Definition(InputTerms),
}
impl Expander<'_> {
pub fn classify(
mut self,
path: ModulePath,
sources: &[SourceId],
form: Span,
) -> ClassifiedForm {
self.evaluated_forms = EvaluatedForms::new([form]);
self.recorded_inputs = RecordedInputs::new(vec![form]);
let module = self.load_source_module(path, sources);
let record = self.recorded_inputs.of(form);
if self.evaluated_forms.is_definition(form) {
return ClassifiedForm::Definition(InputTerms::binding(record.into_bound()));
}
if !record.is_lone_document(&self.evaluated_forms.left_out_within(form)) {
return ClassifiedForm::Expression;
}
let documented = self
.noted_terms(module, NotesCheckpoint::START)
.named_within(form);
ClassifiedForm::Definition(InputTerms::documenting(record.into_bound(), documented))
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
use crate::core::Namespace;
use crate::modules::ModuleSources;
use crate::source::SourceMap;
struct InputFixture {
sources: SourceMap,
earlier: Vec<SourceId>,
}
impl InputFixture {
const MODULE: &str = "prompt";
fn new(earlier: &[&str]) -> Self {
let mut sources = SourceMap::default();
let earlier = earlier
.iter()
.map(|text| sources.add(Self::MODULE, *text))
.collect();
Self { sources, earlier }
}
fn classify(&mut self, input: &str) -> ClassifiedForm {
let source = self.sources.add(Self::MODULE, input);
let mut module = self.earlier.clone();
module.push(source);
Expander::new(
&mut self.sources,
ModuleSources::new(PathBuf::new(), PathBuf::new()),
)
.classify(
ModulePath::new(Self::MODULE),
&module,
Span::new(source, 0..input.len()),
)
}
fn names(&mut self, input: &str) -> Vec<(String, Namespace)> {
let ClassifiedForm::Definition(terms) = self.classify(input) else {
panic!("`{input}` is a definition");
};
terms
.bound()
.iter()
.map(|bound| (bound.name.to_string(), bound.namespace))
.collect()
}
}
#[test]
fn a_call_is_an_expression() {
let mut fixture = InputFixture::new(&["(defn twice (x) (* 2 x))"]);
assert_eq!(fixture.classify("(twice 3)"), ClassifiedForm::Expression);
}
#[test]
fn a_function_binds_its_name() {
let mut fixture = InputFixture::new(&[]);
assert_eq!(
fixture.names("(defn twice (x) (* 2 x))"),
[("twice".to_string(), Namespace::Value)]
);
}
#[test]
fn a_struct_binds_its_type_and_its_constructor_but_no_introduced_name() {
let mut fixture = InputFixture::new(&[]);
assert_eq!(
fixture.names("(defstruct point :x i64 :y i64)"),
[
("point".to_string(), Namespace::Type),
("->point".to_string(), Namespace::Value)
]
);
}
#[test]
fn a_definition_of_an_earlier_name_still_binds_the_name() {
let mut fixture = InputFixture::new(&["(defn twice (x) (* 2 x))"]);
assert_eq!(
fixture.names("(defn twice (x) (+ x x))"),
[("twice".to_string(), Namespace::Value)]
);
}
#[test]
fn a_definition_binds_no_name_of_an_earlier_form() {
let mut fixture = InputFixture::new(&["(defn twice (x) (* 2 x))"]);
assert_eq!(
fixture.names("(defn thrice (x) (* 3 x))"),
[("thrice".to_string(), Namespace::Value)]
);
}
}