diff --git a/src/errors.rs b/src/errors.rs index a3e374c..22038ed 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,8 +1,6 @@ // use crate::process::{LErr, Trace}; use crate::validator::VErr; -use crate::value::Value; use ariadne::{sources, Color, Label, Report, ReportKind}; -use std::collections::HashSet; // pub fn report_panic(err: LErr) { // let mut srcs = HashSet::new(); @@ -32,7 +30,6 @@ use std::collections::HashSet; // stack.push(label); // srcs.insert((*input, *src)); // } - // Report::build(ReportKind::Error, (err.input, err.span.into_range())) // .with_message(format!("Ludus panicked! {}", err.msg)) // .with_label(Label::new((err.input, err.span.into_range())).with_color(Color::Red)) diff --git a/src/process.rs b/src/process.rs deleted file mode 100644 index 5eb954f..0000000 --- a/src/process.rs +++ /dev/null @@ -1,632 +0,0 @@ -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(_partial_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 - } -}