use cljrs_builtins::form::form_to_value;
use cljrs_gc::GcPtr;
use cljrs_reader::Form;
use cljrs_reader::form::FormKind;
use cljrs_value::{Keyword, PersistentList, PersistentVector, Symbol, Value};
use std::sync::Arc;
use cljrs_env::env::Env;
use cljrs_env::error::{EvalError, EvalResult};
pub fn bind_pattern(pattern: &Form, val: Value, env: &mut Env) -> EvalResult<()> {
match &pattern.kind {
FormKind::Symbol(s) => {
env.bind(Arc::from(s.as_str()), val);
Ok(())
}
FormKind::Vector(forms) => bind_sequential(forms, &val, env),
FormKind::Map(forms) => bind_associative(forms, &val, env),
_ => Err(EvalError::Runtime(format!(
"unsupported binding pattern: {:?}",
pattern.kind
))),
}
}
pub fn bind_sequential(pattern: &[Form], val: &Value, env: &mut Env) -> EvalResult<()> {
let items = value_to_seq_vec(val);
let mut idx = 0usize;
let mut i = 0usize;
while i < pattern.len() {
let p = &pattern[i];
if matches!(&p.kind, FormKind::Symbol(s) if s == "&") {
i += 1;
let rest_pat = pattern
.get(i)
.ok_or_else(|| EvalError::Runtime("& in destructuring requires a name".into()))?;
let rest_list = if idx < items.len() {
let rest_vals: Vec<Value> = items[idx..].to_vec();
Value::List(GcPtr::new(PersistentList::from_iter(rest_vals)))
} else {
Value::Nil
};
bind_pattern(rest_pat, rest_list, env)?;
i += 1;
if i < pattern.len()
&& let FormKind::Keyword(k) = &pattern[i].kind
&& k == "as"
{
i += 1;
let alias = pattern
.get(i)
.ok_or_else(|| EvalError::Runtime(":as requires a name".into()))?;
bind_pattern(alias, val.clone(), env)?;
}
break;
}
if let FormKind::Keyword(k) = &p.kind
&& k == "as"
{
i += 1;
let alias = pattern
.get(i)
.ok_or_else(|| EvalError::Runtime(":as requires a name".into()))?;
bind_pattern(alias, val.clone(), env)?;
break;
}
let item = items.get(idx).cloned().unwrap_or(Value::Nil);
bind_pattern(p, item, env)?;
idx += 1;
i += 1;
}
Ok(())
}
pub fn value_to_seq_vec(val: &Value) -> Vec<Value> {
match val {
Value::WithMeta(inner, _) => value_to_seq_vec(inner),
Value::Nil => vec![],
Value::LazySeq(ls) => value_to_seq_vec(&ls.get().realize()),
Value::Cons(c) => {
let mut result = vec![c.get().head.clone()];
let mut tail = c.get().tail.clone();
loop {
match tail {
Value::Nil => break,
Value::List(l) => {
result.extend(l.get().iter().cloned());
break;
}
Value::Cons(next_c) => {
result.push(next_c.get().head.clone());
tail = next_c.get().tail.clone();
}
Value::LazySeq(ls) => {
tail = ls.get().realize();
}
_ => break,
}
}
result
}
Value::List(l) => l.get().iter().cloned().collect(),
Value::Vector(v) => v.get().iter().cloned().collect(),
Value::Set(s) => s.iter().cloned().collect(),
Value::Map(m) => {
let mut result = Vec::new();
m.for_each(|k, v| {
result.push(Value::Vector(GcPtr::new(PersistentVector::from_iter([
k.clone(),
v.clone(),
]))));
});
result
}
_ => vec![],
}
}
pub fn bind_associative(pattern: &[Form], val: &Value, env: &mut Env) -> EvalResult<()> {
let mut defaults: std::collections::HashMap<String, Value> = std::collections::HashMap::new();
let mut i = 0;
while i + 1 < pattern.len() {
let k = &pattern[i];
let v = &pattern[i + 1];
if let FormKind::Keyword(kw) = &k.kind
&& kw == "or"
{
if let FormKind::Map(or_forms) = &v.kind {
let mut j = 0;
while j + 1 < or_forms.len() {
if let FormKind::Symbol(sym) = &or_forms[j].kind {
defaults.insert(sym.clone(), form_to_value(&or_forms[j + 1]));
}
j += 2;
}
}
}
i += 2;
}
let get_val = |key: &Value| -> Value {
match val.unwrap_meta() {
Value::Map(m) => m.get(key).unwrap_or(Value::Nil),
_ => Value::Nil,
}
};
let mut i = 0;
while i + 1 < pattern.len() {
let k = &pattern[i];
let v = &pattern[i + 1];
i += 2;
match &k.kind {
FormKind::Keyword(kw) if kw == "keys" => {
if let FormKind::Vector(syms) = &v.kind {
for sym_form in syms {
if let FormKind::Symbol(sym) = &sym_form.kind {
let key = Value::keyword(Keyword::simple(sym.as_str()));
let mut bound_val = get_val(&key);
if matches!(bound_val, Value::Nil)
&& let Some(d) = defaults.get(sym.as_str())
{
bound_val = d.clone();
}
env.bind(Arc::from(sym.as_str()), bound_val);
}
}
}
}
FormKind::Keyword(kw) if kw == "strs" => {
if let FormKind::Vector(syms) = &v.kind {
for sym_form in syms {
if let FormKind::Symbol(sym) = &sym_form.kind {
let key = Value::string(sym.clone());
let mut bound_val = get_val(&key);
if matches!(bound_val, Value::Nil)
&& let Some(d) = defaults.get(sym.as_str())
{
bound_val = d.clone();
}
env.bind(Arc::from(sym.as_str()), bound_val);
}
}
}
}
FormKind::Keyword(kw) if kw == "syms" => {
if let FormKind::Vector(syms) = &v.kind {
for sym_form in syms {
if let FormKind::Symbol(sym) = &sym_form.kind {
let key = Value::symbol(Symbol::simple(sym.as_str()));
let mut bound_val = get_val(&key);
if matches!(bound_val, Value::Nil)
&& let Some(d) = defaults.get(sym.as_str())
{
bound_val = d.clone();
}
env.bind(Arc::from(sym.as_str()), bound_val);
}
}
}
}
FormKind::Keyword(kw) if kw == "as" => {
if let FormKind::Symbol(sym) = &v.kind {
env.bind(Arc::from(sym.as_str()), val.clone());
}
}
FormKind::Keyword(kw) if kw == "or" => {
}
_ => {
let lookup_key = form_to_value(v);
let mut bound_val = get_val(&lookup_key);
if matches!(bound_val, Value::Nil)
&& let FormKind::Symbol(sym) = &k.kind
&& let Some(d) = defaults.get(sym.as_str())
{
bound_val = d.clone();
}
bind_pattern(k, bound_val, env)?;
}
}
}
Ok(())
}