From 8a9170b0024fb0bd91e1ebf016ff5c08d393ad53 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Mon, 23 Dec 2024 22:04:25 -0500 Subject: [PATCH] work on functions, discover recursion problems --- src/base.rs | 4 +- src/errors.rs | 2 +- src/main.rs | 18 ++-- src/process.rs | 35 ++++---- src/validator.rs | 226 +++++++++++++---------------------------------- src/value.rs | 2 +- 6 files changed, 96 insertions(+), 191 deletions(-) diff --git a/src/base.rs b/src/base.rs index 1a40e5c..81e749e 100644 --- a/src/base.rs +++ b/src/base.rs @@ -50,8 +50,8 @@ pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> Value<'src> { pub fn doc<'src>(f: &Value<'src>) -> Value<'src> { match f { Value::Fn(f) => { - let name = &f.borrow().name; - let doc = &f.borrow().doc; + let name = &f.name; + let doc = &f.doc; if let Some(docstr) = doc { Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))) } else { diff --git a/src/errors.rs b/src/errors.rs index 182cd3a..2f489c7 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -22,7 +22,7 @@ pub fn report_panic(err: LErr) { let Value::Fn(f) = function else { unreachable!() }; - let fn_name = f.borrow().name.clone(); + let fn_name = f.name.clone(); let i = first_span.start; let j = second_span.end; let label = Label::new((entry.input, i..j)) diff --git a/src/main.rs b/src/main.rs index b0e9413..100e679 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,14 +94,14 @@ pub fn prelude<'src>() -> ( } let prelude_parsed = Box::leak(Box::new(p_ast.unwrap())); - let base_pkg = base(); + let base_pkg = Box::leak(Box::new(base())); let mut v6or = Validator::new( &prelude_parsed.0, prelude_parsed.1, "prelude", prelude, - &base_pkg, + base_pkg, ); v6or.validate(); @@ -110,13 +110,15 @@ pub fn prelude<'src>() -> ( panic!("interal Ludus error: invalid prelude") } + let static_vec = Box::leak(Box::new(vec![])); + let mut base_ctx = Process::<'src> { input: "prelude", src: prelude, locals: base_pkg.clone(), ast: &prelude_parsed.0, span: prelude_parsed.1, - prelude: vec![], + prelude: static_vec, fn_info: v6or.fn_info, }; @@ -164,8 +166,9 @@ pub fn run(src: &'static str) { let parsed = parse_result.unwrap(); let (prelude_ctx, mut prelude_fn_info) = prelude(); + let prelude_ctx = Box::leak(Box::new(prelude_ctx)); - let mut v6or = Validator::new(&parsed.0, parsed.1, "script", src, &prelude_ctx); + let mut v6or = Validator::new(&parsed.0, parsed.1, "script", src, prelude_ctx); v6or.validate(); @@ -196,10 +199,13 @@ pub fn run(src: &'static str) { pub fn main() { let src = " -loop (100000, 1) with { +fn sum_to { + (n) -> sum_to (n, 0) (1, acc) -> acc - (n, acc) -> recur (dec (n), add (n, acc)) + (n, acc) -> sum_to (dec (n), add (n, acc)) } + +sum_to (10000) "; run(src); // struct_scalpel::print_dissection_info::() diff --git a/src/process.rs b/src/process.rs index 5eb954f..b59d43d 100644 --- a/src/process.rs +++ b/src/process.rs @@ -54,7 +54,7 @@ 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 prelude: &'src Vec<(String, Value<'src>)>, pub ast: &'src Ast, pub span: SimpleSpan, pub fn_info: std::collections::HashMap<*const Ast, FnInfo>, @@ -296,25 +296,28 @@ impl<'src> Process<'src> { // 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" - )) - } + if !f.has_run { + 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"), + _ => unreachable!("internal Ludus error"), + } } + self.locals.push(f.enclosing[i].clone()); } - self.locals.push(f.enclosing[i].clone()); + f.has_run = true; } - f.has_run = true; let input = self.input; let src = self.src; self.input = f.input; diff --git a/src/validator.rs b/src/validator.rs index 4ff9ade..bbf63d0 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -1,5 +1,5 @@ use crate::parser::*; -use crate::spans::Span; +use crate::spans::{Span, Spanned}; use crate::value::Value; use std::collections::{HashMap, HashSet}; @@ -53,9 +53,9 @@ fn match_arities(arities: &HashSet, num_args: u8) -> bool { } #[derive(Debug, PartialEq)] -pub struct Validator<'a, 'src> { +pub struct Validator<'a> { pub locals: Vec<(String, Span, FnInfo)>, - pub prelude: &'a Vec<(String, Value<'src>)>, + pub prelude: &'a Vec<(String, Value<'a>)>, pub input: &'static str, pub src: &'static str, pub ast: &'a Ast, @@ -65,14 +65,14 @@ pub struct Validator<'a, 'src> { status: VStatus, } -impl<'a, 'src: 'a> Validator<'a, 'src> { +impl<'a> Validator<'a> { pub fn new( ast: &'a Ast, span: Span, input: &'static str, src: &'static str, - prelude: &'a Vec<(String, Value<'src>)>, - ) -> Validator<'a, 'src> { + prelude: &'a Vec<(String, Value<'a>)>, + ) -> Validator<'a> { Validator { input, src, @@ -109,7 +109,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { fn resolved(&self, name: &str) -> bool { self.locals.iter().any(|(bound, ..)| name == bound.as_str()) - || self.prelude.iter().any(|(bound, _)| name == bound.as_str()) + || self.prelude.iter().any(|(bound, _)| name == *bound) } fn bound(&self, name: &str) -> Option<&(String, Span, FnInfo)> { @@ -143,6 +143,13 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { } } + fn visit(&mut self, node: &'a Spanned) { + let (expr, span) = node; + self.ast = expr; + self.span = *span; + self.validate(); + } + pub fn validate(&mut self) { use Ast::*; let root = self.ast; @@ -179,18 +186,13 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { } let to = self.locals.len(); let tailpos = self.status.tail_position; - for (expr, span) in block.iter().take(block.len() - 1) { + for line in block.iter().take(block.len() - 1) { self.status.tail_position = false; - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(line); } - let (expr, span) = block.last().unwrap(); - self.ast = expr; - self.span = *span; self.status.tail_position = tailpos; - self.validate(); + self.visit(block.last().unwrap()); let block_bindings = self.locals.split_off(to); @@ -207,22 +209,12 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { let tailpos = self.status.tail_position; self.status.tail_position = false; - let (expr, span) = cond.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(cond.as_ref()); // pass through tailpos only to then/else self.status.tail_position = tailpos; - let (expr, span) = then.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - - let (expr, span) = r#else.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(then.as_ref()); + self.visit(r#else.as_ref()); } Tuple(members) => { if members.is_empty() { @@ -230,10 +222,8 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { } let tailpos = self.status.tail_position; self.status.tail_position = false; - for (expr, span) in members { - self.ast = expr; - self.span = *span; - self.validate(); + for member in members { + self.visit(member); } self.status.tail_position = tailpos; } @@ -244,10 +234,8 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { } let tailpos = self.status.tail_position; self.status.tail_position = false; - for (expr, span) in args { - self.ast = expr; - self.span = *span; - self.validate(); + for arg in args { + self.visit(arg); } self.status.has_placeholder = false; self.status.tail_position = tailpos; @@ -267,30 +255,21 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { } let tailpos = self.status.tail_position; self.status.tail_position = false; - for (expr, span) in list { - self.ast = expr; - self.span = *span; - self.validate(); + for member in list { + self.visit(member); } self.status.tail_position = tailpos; } - Pair(_, value) => { - let (expr, span) = value.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - } + Pair(_, value) => self.visit(value.as_ref()), Dict(dict) => { if dict.is_empty() { return; } let tailpos = self.status.tail_position; self.status.tail_position = false; - for (expr, span) in dict { - self.ast = expr; - self.span = *span; - self.validate(); + for pair in dict { + self.visit(pair) } self.status.tail_position = tailpos; } @@ -299,31 +278,16 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { // check arity against fn info if first term is word and second term is args Synthetic(first, second, rest) => { match (&first.0, &second.0) { - (Ast::Word(_), Ast::Keyword(_)) => { - let (expr, span) = first.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - } + (Ast::Word(_), Ast::Keyword(_)) => self.visit(first.as_ref()), (Ast::Keyword(_), Ast::Arguments(args)) => { if args.len() != 1 { self.err("called keywords may only take one argument".to_string()) } - let (expr, span) = second.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(second.as_ref()); } (Ast::Word(name), Ast::Arguments(args)) => { - let (expr, span) = first.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - - let (expr, span) = second.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(first.as_ref()); + self.visit(second.as_ref()); //TODO: check arities of prelude fns, too let fn_binding = self.bound(name); @@ -337,32 +301,20 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { _ => unreachable!(), } for term in rest { - let (expr, span) = term; - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(term); } } WhenClause(cond, body) => { let tailpos = self.status.tail_position; self.status.tail_position = false; - let (expr, span) = cond.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - + self.visit(cond.as_ref()); + //pass through tail position for when bodies self.status.tail_position = tailpos; - let (expr, span) = body.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(body.as_ref()); } When(clauses) => { for clause in clauses { - let (expr, span) = clause; - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(clause); } } @@ -374,54 +326,30 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { } else { self.bind(name.to_string()); } - let (expr, span) = boxed.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(boxed.as_ref()); } Let(lhs, rhs) => { - let (expr, span) = rhs.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - - let (expr, span) = lhs.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(rhs.as_ref()); + self.visit(lhs.as_ref()); } MatchClause(pattern, guard, body) => { let to = self.locals.len(); - let (patt, span) = pattern.as_ref(); - self.ast = patt; - self.span = *span; - self.validate(); + self.visit(pattern.as_ref()); - if let Some((expr, span)) = guard.as_ref() { - self.ast = expr; - self.span = *span; - self.validate(); + if let Some(guard) = guard.as_ref() { + self.visit(guard); } - let (expr, span) = body.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(body.as_ref()); self.locals.truncate(to); } Match(scrutinee, clauses) => { - let (expr, span) = scrutinee.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(scrutinee.as_ref()); for clause in clauses { - let (expr, span) = clause; - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(clause); } } FnDeclaration(name) => { @@ -488,10 +416,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { Panic(msg) => { let tailpos = self.status.tail_position; self.status.tail_position = false; - let (expr, span) = msg.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(msg.as_ref()); self.status.tail_position = tailpos; } // TODO: fix the tail call here? @@ -500,39 +425,23 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { return self.err("do expressions must have at least two terms".to_string()); } for term in terms.iter().take(terms.len() - 1) { - let (expr, span) = term; - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(term); } - - let (expr, span) = terms.last().unwrap(); - self.ast = expr; - self.span = *span; - if matches!(expr, Ast::Recur(_)) { + let last = terms.last().unwrap(); + self.visit(last); + if matches!(last.0, Ast::Recur(_)) { self.err("`recur` may not be used in `do` forms".to_string()); } - self.validate(); } Repeat(times, body) => { self.status.tail_position = false; - let (expr, span) = times.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); - - let (expr, span) = body.as_ref(); - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(times.as_ref()); + self.visit(body.as_ref()); } Loop(with, body) => { - let (expr, span) = with.as_ref(); - self.span = *span; - self.ast = expr; - self.validate(); + self.visit(with.as_ref()); - let Ast::Tuple(input) = expr else { + let Ast::Tuple(input) = &with.0 else { unreachable!() }; @@ -588,10 +497,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { self.status.tail_position = false; for arg in args { - let (expr, span) = arg; - self.ast = expr; - self.span = *span; - self.validate(); + self.visit(arg); } } WordPattern(name) => match self.bound(name) { @@ -654,25 +560,15 @@ impl<'a, 'src: 'a> Validator<'a, 'src> { return; } for term in terms.iter().take(terms.len() - 1) { - let (patt, span) = term; - self.ast = patt; - self.span = *span; - self.validate(); + self.visit(term); } self.status.last_term = true; - let (patt, span) = terms.last().unwrap(); - self.ast = patt; - self.span = *span; - self.validate(); + let last = terms.last().unwrap(); + self.visit(last); self.status.last_term = false; } - PairPattern(_, patt) => { - let (patt, span) = patt.as_ref(); - self.ast = patt; - self.span = *span; - self.validate(); - } + PairPattern(_, patt) => self.visit(patt.as_ref()), // terminals can never be invalid Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (), // terminal patterns can never be invalid diff --git a/src/value.rs b/src/value.rs index 5b4aec1..2990ba0 100644 --- a/src/value.rs +++ b/src/value.rs @@ -33,7 +33,7 @@ pub enum Value<'src> { List(Vector), Dict(HashMap<&'static str, Self>), Box(&'static str, Rc>), - Fn(Rc>>), + Fn(Rc>), FnDecl(&'static str), Base(BaseFn<'src>), Recur(Vec),