use crate::ast::Sexp;
use crate::domain::TataraDomain;
use crate::error::{LispError, Result};
use crate::macro_expand::Expander;
use crate::reader::read;
#[derive(Debug, Clone)]
pub struct NamedDefinition<T> {
pub name: String,
pub spec: T,
}
pub type Definition<T> = NamedDefinition<T>;
pub fn compile_typed<T: TataraDomain>(src: &str) -> Result<Vec<T>> {
let forms = read(src)?;
let mut exp = Expander::new();
let expanded = exp.expand_program(forms)?;
let mut out = Vec::new();
for form in &expanded {
if let Some(list) = form.as_list() {
if list.first().and_then(|s| s.as_symbol()) == Some(T::KEYWORD) {
out.push(T::compile_from_args(&list[1..])?);
}
}
}
Ok(out)
}
pub fn compile_named<T: TataraDomain>(src: &str) -> Result<Vec<NamedDefinition<T>>> {
compile_named_from_forms::<T>(read(src)?)
}
pub fn compile_named_from_forms<T: TataraDomain>(
forms: Vec<Sexp>,
) -> Result<Vec<NamedDefinition<T>>> {
let mut exp = Expander::new();
let expanded = exp.expand_program(forms)?;
let mut out = Vec::new();
for form in &expanded {
let Some(list) = form.as_list() else { continue };
if list.first().and_then(|s| s.as_symbol()) != Some(T::KEYWORD) {
continue;
}
if list.len() < 2 {
return Err(LispError::Compile {
form: T::KEYWORD.to_string(),
message: format!("expected ({} NAME …)", T::KEYWORD),
});
}
let name = list[1]
.as_symbol_or_string()
.ok_or_else(|| LispError::Compile {
form: T::KEYWORD.to_string(),
message: "positional NAME must be a symbol or string".into(),
})?
.to_string();
let spec = T::compile_from_args(&list[2..])?;
out.push(NamedDefinition { name, spec });
}
Ok(out)
}