use cljrs_builtins::form::form_to_value;
use cljrs_reader::Form;
use cljrs_reader::form::FormKind;
use cljrs_types::span::Span;
use cljrs_value::{Symbol, Value};
use std::sync::Arc;
use cljrs_env::env::Env;
use cljrs_env::error::{EvalError, EvalResult};
pub fn macroexpand_1(form: &Form, env: &mut Env) -> EvalResult<Form> {
if let FormKind::List(parts) = &form.kind
&& let Some(FormKind::Symbol(s)) = parts.first().map(|f| &f.kind)
&& let Some(macro_fn) = resolve_macro(s, env)
{
let resolved = resolve_auto_kws(form, env)?;
let parts = if let FormKind::List(p) = &resolved.kind {
p
} else {
unreachable!()
};
let form_val = form_to_value(&resolved);
let env_val = {
let (names, vals) = env.all_local_bindings();
let mut m = cljrs_value::MapValue::empty();
for (name, val) in names.iter().zip(vals.iter()) {
m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
}
Value::Map(m)
};
let mut args = vec![form_val, env_val];
args.extend(parts[1..].iter().map(form_to_value));
let expanded = crate::apply::call_cljrs_fn(¯o_fn, &args, env)?;
let dummy = Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
return value_to_form(&expanded, dummy);
}
Ok(form.clone())
}
pub(crate) fn resolve_auto_kws(form: &Form, env: &Env) -> EvalResult<Form> {
let kind = match &form.kind {
FormKind::AutoKeyword(s) => {
let full = env
.globals
.resolve_auto_keyword(&env.current_ns, s)
.map_err(EvalError::Runtime)?;
FormKind::Keyword(full)
}
FormKind::List(items) => FormKind::List(
items
.iter()
.map(|f| resolve_auto_kws(f, env))
.collect::<EvalResult<Vec<_>>>()?,
),
FormKind::Vector(items) => FormKind::Vector(
items
.iter()
.map(|f| resolve_auto_kws(f, env))
.collect::<EvalResult<Vec<_>>>()?,
),
FormKind::Map(items) => FormKind::Map(
items
.iter()
.map(|f| resolve_auto_kws(f, env))
.collect::<EvalResult<Vec<_>>>()?,
),
FormKind::Set(items) => FormKind::Set(
items
.iter()
.map(|f| resolve_auto_kws(f, env))
.collect::<EvalResult<Vec<_>>>()?,
),
FormKind::Quote(inner) => FormKind::Quote(Box::new(resolve_auto_kws(inner, env)?)),
FormKind::SyntaxQuote(inner) => {
FormKind::SyntaxQuote(Box::new(resolve_auto_kws(inner, env)?))
}
FormKind::Unquote(inner) => FormKind::Unquote(Box::new(resolve_auto_kws(inner, env)?)),
FormKind::UnquoteSplice(inner) => {
FormKind::UnquoteSplice(Box::new(resolve_auto_kws(inner, env)?))
}
_ => return Ok(form.clone()),
};
Ok(Form::new(kind, form.span.clone()))
}
pub fn macroexpand(form: &Form, env: &mut Env) -> EvalResult<Form> {
let mut current = form.clone();
loop {
let is_macro_call = matches!(
¤t.kind,
FormKind::List(parts)
if matches!(parts.first().map(|f| &f.kind), Some(FormKind::Symbol(s)) if resolve_macro(s, env).is_some())
);
if !is_macro_call {
return Ok(current);
}
let expanded = macroexpand_1(¤t, env)?;
if expanded == current {
return Ok(current);
}
current = expanded;
}
}
pub fn macroexpand_all(form: &Form, env: &mut Env) -> EvalResult<Form> {
let expanded = macroexpand(form, env)?;
let span = expanded.span.clone();
let kind = match &expanded.kind {
FormKind::List(parts) if !parts.is_empty() => {
let head_name = match &parts[0].kind {
FormKind::Symbol(s) => Some(s.as_str()),
_ => None,
};
match head_name {
Some("quote") => return Ok(expanded),
Some("fn*") => {
let mut new_parts = vec![parts[0].clone()];
if parts.len() > 1 {
if let FormKind::Vector(_) = &parts[1].kind {
new_parts.push(parts[1].clone()); for p in &parts[2..] {
new_parts.push(macroexpand_all(p, env)?);
}
} else {
for arity in &parts[1..] {
if let FormKind::List(arity_parts) = &arity.kind {
let mut new_arity = Vec::new();
if let Some(params) = arity_parts.first() {
new_arity.push(params.clone()); }
for p in arity_parts.iter().skip(1) {
new_arity.push(macroexpand_all(p, env)?);
}
new_parts.push(Form::new(
FormKind::List(new_arity),
arity.span.clone(),
));
} else {
new_parts.push(arity.clone());
}
}
}
}
FormKind::List(new_parts)
}
Some("let*") | Some("loop*") => {
let mut new_parts = vec![parts[0].clone()];
if parts.len() > 1 {
if let FormKind::Vector(bindings) = &parts[1].kind {
let mut new_bindings = Vec::new();
for (i, b) in bindings.iter().enumerate() {
if i % 2 == 0 {
new_bindings.push(b.clone()); } else {
new_bindings.push(macroexpand_all(b, env)?);
}
}
new_parts.push(Form::new(
FormKind::Vector(new_bindings),
parts[1].span.clone(),
));
} else {
new_parts.push(parts[1].clone());
}
for p in &parts[2..] {
new_parts.push(macroexpand_all(p, env)?);
}
}
FormKind::List(new_parts)
}
_ => {
let new_parts = parts
.iter()
.map(|p| macroexpand_all(p, env))
.collect::<EvalResult<Vec<_>>>()?;
FormKind::List(new_parts)
}
}
}
FormKind::Vector(items) => {
let new_items = items
.iter()
.map(|i| macroexpand_all(i, env))
.collect::<EvalResult<Vec<_>>>()?;
FormKind::Vector(new_items)
}
FormKind::Map(items) => {
let new_items = items
.iter()
.map(|i| macroexpand_all(i, env))
.collect::<EvalResult<Vec<_>>>()?;
FormKind::Map(new_items)
}
FormKind::Set(items) => {
let new_items = items
.iter()
.map(|i| macroexpand_all(i, env))
.collect::<EvalResult<Vec<_>>>()?;
FormKind::Set(new_items)
}
_ => return Ok(expanded),
};
Ok(Form::new(kind, span))
}
fn resolve_macro(sym: &str, env: &Env) -> Option<cljrs_value::CljxFn> {
let parsed = Symbol::parse(sym);
let ns: Arc<str> = match parsed.namespace.as_deref() {
Some(ns_part) => env
.globals
.resolve_alias(&env.current_ns, ns_part)
.unwrap_or_else(|| Arc::from(ns_part)),
None => env.current_ns.clone(),
};
let name = parsed.name.as_ref();
let v = env.globals.lookup_in_ns(&ns, name)?;
if let Value::Macro(f) = v {
Some(f.get().clone())
} else {
None
}
}
pub fn value_to_form(val: &Value, span: Span) -> EvalResult<Form> {
let kind = match val {
Value::Nil => FormKind::Nil,
Value::Bool(b) => FormKind::Bool(*b),
Value::Long(n) => FormKind::Int(*n),
Value::Double(f) => FormKind::Float(*f),
Value::Str(s) => FormKind::Str(s.get().clone()),
Value::Char(c) => FormKind::Char(*c),
Value::BigInt(b) => FormKind::BigInt(b.get().to_string()),
Value::BigDecimal(d) => FormKind::BigDecimal(d.get().to_string()),
Value::Ratio(r) => FormKind::Ratio(format!("{}/{}", r.get().numer(), r.get().denom())),
Value::Symbol(s) => FormKind::Symbol(s.get().full_name()),
Value::Keyword(k) => FormKind::Keyword(k.get().full_name()),
Value::List(l) => {
let items = l.get();
let head_sym = items.iter().next().and_then(|v| {
if let Value::Symbol(s) = v {
Some(s.get().name.clone())
} else {
None
}
});
match (head_sym.as_deref(), items.count()) {
(Some("syntax-quote"), 2) => {
let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
FormKind::SyntaxQuote(Box::new(inner))
}
(Some("unquote"), 2) => {
let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
FormKind::Unquote(Box::new(inner))
}
(Some("unquote-splicing"), 2) => {
let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
FormKind::UnquoteSplice(Box::new(inner))
}
_ => {
let forms: Vec<Form> = items
.iter()
.map(|v| value_to_form(v, span.clone()))
.collect::<EvalResult<_>>()?;
FormKind::List(forms)
}
}
}
Value::Vector(v) => {
let forms: Vec<Form> = v
.get()
.iter()
.map(|v| value_to_form(v, span.clone()))
.collect::<EvalResult<_>>()?;
FormKind::Vector(forms)
}
Value::Map(m) => {
let mut forms = Vec::new();
let mut err: Option<EvalError> = None;
let sc = span.clone();
m.for_each(|k, v| {
if err.is_none() {
match (value_to_form(k, sc.clone()), value_to_form(v, sc.clone())) {
(Ok(kf), Ok(vf)) => {
forms.push(kf);
forms.push(vf);
}
(Err(e), _) | (_, Err(e)) => err = Some(e),
}
}
});
if let Some(e) = err {
return Err(e);
}
FormKind::Map(forms)
}
Value::Set(s) => {
let forms: Vec<Form> = s
.iter()
.map(|v| value_to_form(v, span.clone()))
.collect::<EvalResult<_>>()?;
FormKind::Set(forms)
}
Value::LazySeq(ls) => {
return value_to_form(&ls.get().realize(), span);
}
Value::Cons(c) => {
let mut items: Vec<Form> = Vec::new();
let mut cur = Value::Cons(c.clone());
loop {
match cur {
Value::Cons(cell) => {
items.push(value_to_form(&cell.get().head, span.clone())?);
cur = cell.get().tail.clone();
}
Value::LazySeq(ls) => cur = ls.get().realize(),
Value::List(l) => {
for v in l.get().iter() {
items.push(value_to_form(v, span.clone())?);
}
break;
}
Value::Nil => break,
_ => break,
}
}
FormKind::List(items)
}
Value::Uuid(u) => {
let uuid_str = uuid::Uuid::from_u128(*u).to_string();
FormKind::TaggedLiteral(
"uuid".to_string(),
Box::new(Form::new(FormKind::Str(uuid_str), span.clone())),
)
}
Value::WithMeta(inner, _) => {
return value_to_form(inner, span);
}
Value::Pattern(p) => FormKind::Regex(p.get().as_str().to_string()),
other => FormKind::Symbol(format!("#<{}>", other.type_name())),
};
Ok(Form::new(kind, span))
}