use crate::base::*; use crate::parser::*; use crate::value::*; use imbl::HashMap; use imbl::Vector; use std::cell::RefCell; use std::rc::Rc; #[derive(Clone, Debug)] pub struct LudusError { pub msg: String, } // oy // lifetimes are a mess // I need 'src kind of everywhere // But (maybe) using 'src in eval // for ctx // means I can't borrow it mutably // I guess the question is how to get // the branches for Ast::Block and Ast::If // to work with a mutable borrow of ctx // pub struct Ctx<'src> { // pub locals: Vec<(&'src str, Value<'src>)>, // // pub names: Vec<&'src str>, // // pub values: Vec>, // } // impl<'src> Ctx<'src> { // pub fn resolve(&self, name: &'src str) -> Value { // if let Some((_, val)) = self.locals.iter().rev().find(|(bound, _)| *bound == name) { // val.clone() // } else { // unreachable!() // } // } // pub fn store(&mut self, name: &'src str, value: Value<'src>) { // self.locals.push((name, value)); // } // } pub fn match_eq(x: T, y: T, z: U) -> Option where T: PartialEq, { if x == y { Some(z) } else { None } } pub fn match_pattern<'src, 'a>( patt: &Pattern<'src>, val: &Value<'src>, ctx: &'a mut Vec<(&'src str, Value<'src>)>, ) -> Option<&'a mut Vec<(&'src str, Value<'src>)>> { match (patt, val) { (Pattern::Nil, Value::Nil) => Some(ctx), (Pattern::Placeholder, _) => Some(ctx), (Pattern::Number(x), Value::Number(y)) => match_eq(x, y, ctx), (Pattern::Boolean(x), Value::Boolean(y)) => match_eq(x, y, ctx), (Pattern::Keyword(x), Value::Keyword(y)) => match_eq(x, y, ctx), (Pattern::String(x), Value::String(y)) => match_eq(x, y, ctx), (Pattern::Word(w), val) => { ctx.push((w, val.clone())); Some(ctx) } (Pattern::As(word, type_str), value) => { let ludus_type = value.ludus_type(); let type_kw = Value::Keyword(type_str); if type_kw == ludus_type { ctx.push((word, value.clone())); Some(ctx) } else { None } } // todo: add splats to these match clauses (Pattern::Tuple(x), Value::Tuple(y)) => { let has_splat = x.iter().any(|patt| { if let (Pattern::Splattern(_), _) = patt { true } else { false } }); if x.len() > y.len() || (!has_splat && x.len() != y.len()) { return None; }; let to = ctx.len(); for i in 0..x.len() { if let Pattern::Splattern(patt) = &x[i].0 { let mut list = Vector::new(); for i in i..y.len() { list.push_back(y[i].clone()) } let list = Value::List(list); match_pattern(&(*patt).0, &list, ctx); } else { if let None = match_pattern(&x[i].0, &y[i], ctx) { while ctx.len() > to { ctx.pop(); } return None; } } } Some(ctx) } (Pattern::List(x), Value::List(y)) => { let has_splat = x.iter().any(|patt| { if let (Pattern::Splattern(_), _) = patt { true } else { false } }); if x.len() > y.len() || (!has_splat && x.len() != y.len()) { return None; }; let to = ctx.len(); for i in 0..x.len() { if let Pattern::Splattern(patt) = &x[i].0 { let list = Value::List(y.skip(i)); match_pattern(&(*patt).0, &list, ctx); } else { if let None = match_pattern(&x[i].0, &y.get(i).unwrap(), ctx) { while ctx.len() > to { ctx.pop(); } return None; } } } Some(ctx) } (Pattern::Dict(x), Value::Dict(y)) => { if x.len() != y.len() { return None; }; let to = ctx.len(); for (PairPattern { key, patt }, _) in x { if let Some(val) = y.get(key) { if let None = match_pattern(&patt.0, val, ctx) { while ctx.len() > to { ctx.pop(); return None; } } } else { return None; }; } Some(ctx) } _ => None, } } pub fn match_clauses<'src, 'a>( value: &Value<'src>, clauses: &'src Vec>, ctx: &'a mut Vec<(&'src str, Value<'src>)>, ) -> Result, LudusError> { let to = ctx.len(); for MatchClause { patt, body, .. } in clauses.iter() { if let Some(ctx) = match_pattern(&patt.0, value, ctx) { let res = eval(&body.0, ctx); while ctx.len() > to { ctx.pop(); } return res; } } Err(LudusError { msg: "no match".to_string(), }) } pub fn apply<'src, 'a>( callee: Value<'src>, caller: Value<'src>, ctx: &'a mut Vec<(&'src str, Value<'src>)>, ) -> Result, LudusError> { match (callee, caller) { (Value::Keyword(kw), Value::Dict(dict)) => { if let Some(val) = dict.get(kw) { Ok(val.clone()) } else { Ok(Value::Nil) } } (Value::Dict(dict), Value::Keyword(kw)) => { if let Some(val) = dict.get(kw) { Ok(val.clone()) } else { Ok(Value::Nil) } } (Value::Fn(f), Value::Tuple(args)) => { let args = Value::Tuple(args); match_clauses(&args, f.body, ctx) } (Value::Fn(_f), Value::Args(_args)) => todo!(), (_, Value::Keyword(_)) => Ok(Value::Nil), (_, Value::Args(_)) => Err(LudusError { msg: "you may only call a function".to_string(), }), (Value::Base(f), Value::Tuple(args)) => match f { Base::Unary(_name, f) => { if args.len() != 1 { Err(LudusError { msg: "wrong arity: expected 1 argument".to_string(), }) } else { f(&args[0]) } } Base::Binary(_name, r#fn) => { if args.len() != 2 { Err(LudusError { msg: "wrong arity: expected 2 arguments".to_string(), }) } else { r#fn(&args[0], &args[1]) } } Base::Ternary(_name, f) => { if args.len() != 3 { Err(LudusError { msg: "wrong arity: expected 3 arguments".to_string(), }) } else { f(&args[0], &args[1], &args[2]) } } }, _ => unreachable!(), } } pub fn eval<'src, 'a>( ast: &'src Ast<'src>, ctx: &'a mut Vec<(&'src str, Value<'src>)>, ) -> Result, LudusError> { match ast { Ast::Nil => Ok(Value::Nil), Ast::Boolean(b) => Ok(Value::Boolean(*b)), Ast::Number(n) => Ok(Value::Number(*n)), Ast::Keyword(k) => Ok(Value::Keyword(k)), Ast::String(s) => Ok(Value::String(s)), Ast::Block(exprs) => { let to = ctx.len(); let mut result = Value::Nil; for (expr, _) in exprs { result = eval(expr, ctx)?; } while ctx.len() > to { ctx.pop(); } Ok(result) } Ast::If(cond, if_true, if_false) => { let truthy = eval(&cond.0, ctx)?.bool(); if truthy { eval(&if_true.0, ctx) } else { eval(&if_false.0, ctx) } } Ast::List(members) => { let mut vect = Vector::new(); for member in members { vect.push_back(eval(&member.0, ctx)?) } Ok(Value::List(vect)) } Ast::Tuple(members) => { let mut vect = Vec::new(); for member in members { vect.push(eval(&member.0, ctx)?); } Ok(Value::Tuple(Rc::new(vect))) } Ast::Word(w) => { let val = if let Some((_, value)) = ctx.iter().rev().find(|(name, _)| w == name) { value.clone() } else { return Err(LudusError { msg: format!("unbound name {w}"), }); }; Ok(val) } Ast::Let(patt, expr) => { let val = eval(&expr.0, ctx)?; match match_pattern(&patt.0, &val, ctx) { Some(_) => Ok(val), None => Err(LudusError { msg: "No match".to_string(), }), } } Ast::Placeholder => Ok(Value::Placeholder), Ast::Error => unreachable!(), Ast::Arguments(a) => { let mut args = vec![]; for (arg, _) in a.iter() { let arg = eval(&arg, ctx)?; args.push(arg); } if args.iter().any(|arg| { if let Value::Placeholder = arg { true } else { false } }) { Ok(Value::Args(Rc::new(args))) } else { Ok(Value::Tuple(Rc::new(args))) } } Ast::Dict(pairs) => { let mut dict = HashMap::new(); for Pair { key, value } in pairs { let value = eval(&value.0, ctx)?; dict.insert(*key, value); } Ok(Value::Dict(dict)) } Ast::Box(name, expr) => { let val = eval(&expr.0, ctx)?; let boxed = Value::Box(name, Rc::new(RefCell::new(val))); ctx.push((name, boxed.clone())); Ok(boxed) } Ast::Synthetic(root, first, rest) => { let root = eval(&root.0, ctx)?; let first = eval(&first.0, ctx)?; let mut curr = apply(root, first, ctx)?; for term in rest.iter() { let next = eval(&term.0, ctx)?; curr = apply(curr, next, ctx)?; } Ok(curr) } Ast::When(clauses) => { for clause in clauses.iter() { let WhenClause { cond, body } = &clause.0; if eval(&cond.0, ctx)?.bool() { return eval(&body.0, ctx); }; } return Err(LudusError { msg: "no match".to_string(), }); } Ast::Match(value, clauses) => { let value = eval(&value.0, ctx)?; match_clauses(&value, clauses, ctx) } Ast::Fn(name, clauses) => { let the_fn = Value::Fn::<'src>(Rc::new(Fn::<'src> { name, body: clauses, })); ctx.push((name, the_fn.clone())); Ok(the_fn) } Ast::FnDeclaration(_name) => todo!(), Ast::Panic(msg) => { let msg = eval(&msg.0, ctx)?; Err(LudusError { msg: msg.to_string(), }) } Ast::Repeat(times, body) => { let mut times_num = 0; match eval(×.0, ctx) { Ok(Value::Number(n)) => { times_num = n as usize; } _ => { return Err(LudusError { msg: "repeat may only take numbers".to_string(), }) } } for _ in 0..times_num { eval(&body.0, ctx)?; } Ok(Value::Nil) } Ast::Do(terms) => { let mut result = eval(&terms[0].0, ctx)?; for i in 1..terms.len() { let next = eval(&terms[i].0, ctx)?; let arg = Value::Tuple(Rc::new(vec![result])); result = apply(next, arg, ctx)?; } Ok(result) } // Ast::Loop(_, _) => todo!(), // Ast::Recur(_) => todo!(), } }