use crate::base::*; use crate::parser::*; use crate::spans::*; use crate::validator::FnInfo; use crate::value::Value; use chumsky::prelude::SimpleSpan; use imbl::HashMap; use imbl::Vector; use std::cell::RefCell; use std::rc::Rc; #[derive(Debug, Clone, PartialEq)] pub struct LErr<'src> { pub input: &'static str, pub src: &'static str, pub msg: String, pub span: SimpleSpan, pub trace: Vec>, pub extra: String, } #[derive(Debug, Clone, PartialEq)] pub struct Trace<'src> { pub callee: Spanned, pub caller: Spanned, pub function: Value<'src>, pub arguments: Value<'src>, pub input: &'static str, pub src: &'static str, } impl<'src> LErr<'src> { pub fn new( msg: String, span: SimpleSpan, input: &'static str, src: &'static str, ) -> LErr<'src> { LErr { msg, span, input, src, trace: vec![], extra: "".to_string(), } } } type LResult<'src> = Result, LErr<'src>>; #[derive(Debug)] pub struct Process<'src> { pub input: &'static str, pub src: &'static str, pub locals: Vec<(String, Value<'src>)>, pub prelude: Vec<(String, Value<'src>)>, pub ast: &'src Ast, pub span: SimpleSpan, pub fn_info: std::collections::HashMap<*const Ast, FnInfo>, } impl<'src> Process<'src> { pub fn resolve(&self, word: &String) -> LResult<'src> { let resolved_local = self.locals.iter().rev().find(|(name, _)| word == name); match resolved_local { Some((_, value)) => Ok(value.clone()), None => { let resolved_prelude = self.prelude.iter().rev().find(|(name, _)| word == name); match resolved_prelude { Some((_, value)) => Ok(value.clone()), None => Err(LErr::new( format!("unbound name `{word}`"), self.span, self.input, self.src, )), } } } } pub fn panic(&self, msg: String) -> LResult<'src> { Err(LErr::new(msg, self.span, self.input, self.src)) } pub fn bind(&mut self, word: String, value: &Value<'src>) { self.locals.push((word, value.clone())); } pub fn match_eq(&self, x: T, y: T) -> Option<&Process<'src>> where T: PartialEq, { if x == y { Some(self) } else { None } } pub fn match_pattern(&mut self, patt: &Ast, val: &Value<'src>) -> Option<&Process<'src>> { use Ast::*; match (patt, val) { (NilPattern, Value::Nil) => Some(self), (PlaceholderPattern, _) => Some(self), (NumberPattern(x), Value::Number(y)) => self.match_eq(x, y), (BooleanPattern(x), Value::Boolean(y)) => self.match_eq(x, y), (KeywordPattern(x), Value::Keyword(y)) => self.match_eq(x, y), (StringPattern(x), Value::InternedString(y)) => self.match_eq(x, y), (StringPattern(x), Value::AllocatedString(y)) => self.match_eq(&x.to_string(), y), (InterpolatedPattern(_, StringMatcher(matcher)), Value::InternedString(y)) => { match matcher(y.to_string()) { Some(matches) => { let mut matches = matches .iter() .map(|(word, string)| { ( word.clone(), Value::AllocatedString(Rc::new(string.clone())), ) }) .collect::>(); self.locals.append(&mut matches); Some(self) } None => None, } } (WordPattern(w), val) => { self.bind(w.to_string(), val); Some(self) } (AsPattern(word, type_str), value) => { let ludus_type = r#type(value); let type_kw = Value::Keyword(type_str); if type_kw == ludus_type { self.bind(word.to_string(), value); Some(self) } else { None } } (TuplePattern(x), Value::Tuple(y)) => { let has_splat = x.iter().any(|patt| matches!(patt, (Splattern(_), _))); if x.len() > y.len() || (!has_splat && x.len() != y.len()) { return None; }; let to = self.locals.len(); for i in 0..x.len() { if let 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); self.match_pattern(&patt.0, &list); } else if self.match_pattern(&x[i].0, &y[i]).is_none() { self.locals.truncate(to); return None; } } Some(self) } (ListPattern(x), Value::List(y)) => { let has_splat = x.iter().any(|patt| matches!(patt, (Splattern(_), _))); if x.len() > y.len() || (!has_splat && x.len() != y.len()) { return None; }; let to = self.locals.len(); for (i, (patt, _)) in x.iter().enumerate() { if let Splattern(patt) = &patt { let list = Value::List(y.skip(i)); self.match_pattern(&patt.0, &list); } else if self.match_pattern(patt, y.get(i).unwrap()).is_none() { self.locals.truncate(to); return None; } } Some(self) } // TODO: optimize this on several levels // - [ ] opportunistic mutation // - [ ] get rid of all the pointer indirection in word splats (DictPattern(x), Value::Dict(y)) => { let has_splat = x.iter().any(|patt| matches!(patt, (Splattern(_), _))); if x.len() > y.len() || (!has_splat && x.len() != y.len()) { return None; }; let to = self.locals.len(); let mut matched = vec![]; for (pattern, _) in x { match pattern { PairPattern(key, patt) => { if let Some(val) = y.get(key) { if self.match_pattern(&patt.0, val).is_none() { self.locals.truncate(to); return None; } else { matched.push(key); } } else { return None; }; } Splattern(pattern) => match pattern.0 { WordPattern(w) => { // TODO: find a way to take ownership // this will ALWAYS make structural changes, because of this clone // we want opportunistic mutation if possible let mut unmatched = y.clone(); for key in matched.iter() { unmatched.remove(*key); } self.bind(w.to_string(), &Value::Dict(unmatched)); } PlaceholderPattern => (), _ => unreachable!(), }, _ => unreachable!(), } } Some(self) } _ => None, } } pub fn match_clauses( &mut self, value: &Value<'src>, clauses: &'src [Spanned], ) -> LResult<'src> { { let root = self.ast; let to = self.locals.len(); let mut clauses_iter = clauses.iter(); while let Some((Ast::MatchClause(patt, guard, body), _)) = clauses_iter.next() { if self.match_pattern(&patt.0, value).is_some() { let pass_guard = match guard.as_ref() { None => true, Some(guard_expr) => self.visit(guard_expr)?.bool(), }; if !pass_guard { self.locals.truncate(to); continue; } let result = self.visit(body); self.locals.truncate(to); self.ast = root; return result; } } let patterns = clauses .iter() .map(|clause| { let (Ast::MatchClause(patt, ..), _) = clause else { unreachable!("internal Ludus error") }; let patt = &patt.as_ref().0; patt.to_string() }) .collect::>() .join("\n"); dbg!(&patterns); Err(LErr { input: self.input, src: self.src, msg: "no match".to_string(), span: self.span, trace: vec![], extra: format!("expected {value} to match one of\n{}", patterns), }) } } pub fn apply(&mut self, callee: Value<'src>, caller: Value<'src>) -> LResult<'src> { use Value::*; match (callee, caller) { (Keyword(kw), Dict(dict)) => { if let Some(val) = dict.get(kw) { Ok(val.clone()) } else { Ok(Nil) } } (Dict(dict), Keyword(kw)) => { if let Some(val) = dict.get(kw) { Ok(val.clone()) } else { Ok(Nil) } } (Fn(f), Tuple(args)) => { // can't just use the `caller` value b/c borrow checker nonsense let args = Tuple(args); let to = self.locals.len(); let mut f = f.borrow_mut(); for i in 0..f.enclosing.len() { let (name, value) = f.enclosing[i].clone(); if !f.has_run && matches!(value, Value::FnDecl(_)) { let defined = self.resolve(&name); match defined { Ok(Value::Fn(defined)) => f.enclosing[i] = (name.clone(), Fn(defined)), Ok(Value::FnDecl(_)) => { return self.panic(format!( "function `{name}` called before it was defined" )) } _ => unreachable!("internal Ludus error"), } } self.locals.push(f.enclosing[i].clone()); } f.has_run = true; let input = self.input; let src = self.src; self.input = f.input; self.src = f.src; let result = self.match_clauses(&args, f.body); self.locals.truncate(to); self.input = input; self.src = src; result } // TODO: partially applied functions shnould work! In #15 (Fn(_f), Args(_args)) => todo!(), (_, Keyword(_)) => Ok(Nil), (_, Args(_)) => self.panic("only functions and keywords may be called".to_string()), (Base(f), Tuple(args)) => match f { BaseFn::Nullary(f) => { let num_args = args.len(); if num_args != 0 { self.panic(format!("wrong arity: expected 0 arguments, got {num_args}")) } else { Ok(f()) } } BaseFn::Unary(f) => { let num_args = args.len(); if num_args != 1 { self.panic(format!("wrong arity: expected 1 argument, got {num_args}")) } else { Ok(f(&args[0])) } } BaseFn::Binary(r#fn) => { let num_args = args.len(); if num_args != 2 { self.panic(format!("wrong arity: expected 2 arguments, got {num_args}")) } else { Ok(r#fn(&args[0], &args[1])) } } BaseFn::Ternary(f) => { let num_args = args.len(); if num_args != 3 { self.panic(format!("wrong arity: expected 3 arguments, got {num_args}")) } else { Ok(f(&args[0], &args[1], &args[2])) } } }, _ => unreachable!(), } } pub fn visit(&mut self, node: &'src Spanned) -> LResult<'src> { let (expr, span) = node; self.ast = expr; self.span = *span; self.eval() } pub fn eval(&mut self) -> LResult<'src> { use Ast::*; let (root_node, root_span) = (self.ast, self.span); let result = match root_node { Nil => Ok(Value::Nil), Boolean(b) => Ok(Value::Boolean(*b)), Number(n) => Ok(Value::Number(*n)), Keyword(k) => Ok(Value::Keyword(k)), String(s) => Ok(Value::InternedString(s)), Interpolated(parts) => { let mut interpolated = std::string::String::new(); for part in parts { match &part.0 { StringPart::Data(s) => interpolated.push_str(s.as_str()), StringPart::Word(w) => { let val = self.resolve(w)?; interpolated.push_str(val.interpolate().as_str()) } StringPart::Inline(_) => unreachable!(), } } Ok(Value::AllocatedString(Rc::new(interpolated))) } Block(exprs) => { let to = self.locals.len(); let mut result = Value::Nil; for expr in exprs { result = self.visit(expr)?; } self.locals.truncate(to); Ok(result) } If(cond, if_true, if_false) => { let truthy = self.visit(cond)?; let to_visit = if truthy.bool() { if_true } else { if_false }; self.visit(to_visit) } List(members) => { let mut vect = Vector::new(); for member in members { let member_value = self.visit(member)?; match member.0 { Ast::Splat(_) => match member_value { Value::List(list) => vect.append(list), _ => { return self .panic("only lists may be splatted into lists".to_string()) } }, _ => vect.push_back(member_value), } } Ok(Value::List(vect)) } Tuple(members) => { let mut vect = Vec::new(); for member in members { vect.push(self.visit(member)?); } Ok(Value::Tuple(Rc::new(vect))) } Word(w) | Ast::Splat(w) => { let val = self.resolve(&w.to_string())?; Ok(val) } Let(patt, expr) => { let val = self.visit(expr)?; let result = match self.match_pattern(&patt.0, &val) { Some(_) => Ok(val), None => self.panic("no match".to_string()), }; result } Placeholder => Ok(Value::Placeholder), Arguments(a) => { let mut args = vec![]; for arg in a.iter() { args.push(self.visit(arg)?) } let result = if args.iter().any(|arg| matches!(arg, Value::Placeholder)) { Ok(Value::Args(Rc::new(args))) } else { Ok(Value::Tuple(Rc::new(args))) }; result } Dict(terms) => { let mut dict = HashMap::new(); for term in terms { match term { (Ast::Pair(key, value), _) => { dict.insert(*key, self.visit(value)?); } (Ast::Splat(_), _) => { let resolved = self.visit(term)?; let Value::Dict(to_splat) = resolved else { return self.panic("cannot splat non-dict into dict".to_string()); }; dict = to_splat.union(dict); } _ => unreachable!(), } } Ok(Value::Dict(dict)) } LBox(name, expr) => { let val = self.visit(expr)?; let boxed = Value::Box(name, Rc::new(RefCell::new(val))); self.bind(name.to_string(), &boxed); Ok(boxed) } Synthetic(root, first, rest) => { let root_val = self.visit(root)?; let first_val = self.visit(first)?; let mut result = self.apply(root_val.clone(), first_val.clone()); if let Err(mut err) = result { err.trace.push(Trace { callee: *root.clone(), caller: *first.clone(), function: root_val, arguments: first_val, input: self.input, src: self.src, }); return Err(err); }; let mut prev_node; let mut this_node = first.as_ref(); for term in rest.iter() { prev_node = this_node; this_node = term; let caller = self.visit(term)?; let callee = result.unwrap(); result = self.apply(callee.clone(), caller.clone()); if let Err(mut err) = result { err.trace.push(Trace { callee: prev_node.clone(), caller: this_node.clone(), function: caller, arguments: callee, input: self.input, src: self.src, }); return Err(err); } } result } When(clauses) => { for clause in clauses.iter() { let WhenClause(cond, body) = &clause.0 else { unreachable!() }; if self.visit(cond)?.bool() { return self.visit(body); }; } self.panic("no match".to_string()) } Match(scrutinee, clauses) => { let value = self.visit(scrutinee)?; self.match_clauses(&value, clauses) } Fn(name, clauses, doc) => { let doc = doc.map(|s| s.to_string()); let ptr: *const Ast = root_node; let info = self.fn_info.get(&ptr).unwrap(); let FnInfo::Defined(_, _, enclosing) = info else { unreachable!() }; let enclosing = enclosing .iter() .filter(|binding| binding != name) .map(|binding| (binding.clone(), self.resolve(binding).unwrap().clone())) .collect(); let the_fn = Value::Fn::<'src>(Rc::new(RefCell::new(crate::value::Fn::<'src> { name: name.to_string(), body: clauses, doc, enclosing, has_run: false, input: self.input, src: self.src, }))); let maybe_decl_i = self.locals.iter().position(|(binding, _)| binding == name); match maybe_decl_i { None => self.bind(name.to_string(), &the_fn), Some(i) => { let declared = &self.locals[i].1; match declared { Value::FnDecl(_) => { self.locals[i] = (name.to_string(), the_fn.clone()); } _ => unreachable!("internal Ludus error"), } } } Ok(the_fn) } FnDeclaration(name) => { let decl = Value::FnDecl(name); self.bind(name.to_string(), &decl); Ok(decl) } Panic(msg) => { let msg = self.visit(msg)?; self.panic(format!("{msg}")) } Repeat(times, body) => { let times_num = match self.visit(times) { Ok(Value::Number(n)) => n as usize, _ => return self.panic("`repeat` may only take numbers".to_string()), }; for _ in 0..times_num { self.visit(body)?; } Ok(Value::Nil) } Do(terms) => { let mut result = self.visit(&terms[0])?; for term in terms.iter().skip(1) { let next = self.visit(term)?; let arg = Value::Tuple(Rc::new(vec![result])); result = self.apply(next, arg)?; } Ok(result) } Loop(init, clauses) => { let mut args = self.visit(init)?; loop { let result = self.match_clauses(&args, clauses)?; if let Value::Recur(recur_args) = result { args = Value::Tuple(Rc::new(recur_args)); } else { return Ok(result); } } } Recur(args) => { let mut vect = Vec::new(); for arg in args { vect.push(self.visit(arg)?); } Ok(Value::Recur(vect)) } _ => unreachable!(), }; self.ast = root_node; self.span = root_span; result } }