work on functions, discover recursion problems

This commit is contained in:
Scott Richmond 2024-12-23 22:04:25 -05:00
parent d4af160f80
commit 8a9170b002
6 changed files with 96 additions and 191 deletions

View File

@ -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> { pub fn doc<'src>(f: &Value<'src>) -> Value<'src> {
match f { match f {
Value::Fn(f) => { Value::Fn(f) => {
let name = &f.borrow().name; let name = &f.name;
let doc = &f.borrow().doc; let doc = &f.doc;
if let Some(docstr) = doc { if let Some(docstr) = doc {
Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))) Value::AllocatedString(Rc::new(format!("{name}: {docstr}")))
} else { } else {

View File

@ -22,7 +22,7 @@ pub fn report_panic(err: LErr) {
let Value::Fn(f) = function else { let Value::Fn(f) = function else {
unreachable!() unreachable!()
}; };
let fn_name = f.borrow().name.clone(); let fn_name = f.name.clone();
let i = first_span.start; let i = first_span.start;
let j = second_span.end; let j = second_span.end;
let label = Label::new((entry.input, i..j)) let label = Label::new((entry.input, i..j))

View File

@ -94,14 +94,14 @@ pub fn prelude<'src>() -> (
} }
let prelude_parsed = Box::leak(Box::new(p_ast.unwrap())); 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( let mut v6or = Validator::new(
&prelude_parsed.0, &prelude_parsed.0,
prelude_parsed.1, prelude_parsed.1,
"prelude", "prelude",
prelude, prelude,
&base_pkg, base_pkg,
); );
v6or.validate(); v6or.validate();
@ -110,13 +110,15 @@ pub fn prelude<'src>() -> (
panic!("interal Ludus error: invalid prelude") panic!("interal Ludus error: invalid prelude")
} }
let static_vec = Box::leak(Box::new(vec![]));
let mut base_ctx = Process::<'src> { let mut base_ctx = Process::<'src> {
input: "prelude", input: "prelude",
src: prelude, src: prelude,
locals: base_pkg.clone(), locals: base_pkg.clone(),
ast: &prelude_parsed.0, ast: &prelude_parsed.0,
span: prelude_parsed.1, span: prelude_parsed.1,
prelude: vec![], prelude: static_vec,
fn_info: v6or.fn_info, fn_info: v6or.fn_info,
}; };
@ -164,8 +166,9 @@ pub fn run(src: &'static str) {
let parsed = parse_result.unwrap(); let parsed = parse_result.unwrap();
let (prelude_ctx, mut prelude_fn_info) = prelude(); 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(); v6or.validate();
@ -196,10 +199,13 @@ pub fn run(src: &'static str) {
pub fn main() { pub fn main() {
let src = " let src = "
loop (100000, 1) with { fn sum_to {
(n) -> sum_to (n, 0)
(1, acc) -> acc (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); run(src);
// struct_scalpel::print_dissection_info::<value::Value>() // struct_scalpel::print_dissection_info::<value::Value>()

View File

@ -54,7 +54,7 @@ pub struct Process<'src> {
pub input: &'static str, pub input: &'static str,
pub src: &'static str, pub src: &'static str,
pub locals: Vec<(String, Value<'src>)>, pub locals: Vec<(String, Value<'src>)>,
pub prelude: Vec<(String, Value<'src>)>, pub prelude: &'src Vec<(String, Value<'src>)>,
pub ast: &'src Ast, pub ast: &'src Ast,
pub span: SimpleSpan, pub span: SimpleSpan,
pub fn_info: std::collections::HashMap<*const Ast, FnInfo>, pub fn_info: std::collections::HashMap<*const Ast, FnInfo>,
@ -296,13 +296,15 @@ impl<'src> Process<'src> {
// can't just use the `caller` value b/c borrow checker nonsense // can't just use the `caller` value b/c borrow checker nonsense
let args = Tuple(args); let args = Tuple(args);
let to = self.locals.len(); let to = self.locals.len();
let mut f = f.borrow_mut(); if !f.has_run {
for i in 0..f.enclosing.len() { for i in 0..f.enclosing.len() {
let (name, value) = f.enclosing[i].clone(); let (name, value) = f.enclosing[i].clone();
if !f.has_run && matches!(value, Value::FnDecl(_)) { if !f.has_run && matches!(value, Value::FnDecl(_)) {
let defined = self.resolve(&name); let defined = self.resolve(&name);
match defined { match defined {
Ok(Value::Fn(defined)) => f.enclosing[i] = (name.clone(), Fn(defined)), Ok(Value::Fn(defined)) => {
f.enclosing[i] = (name.clone(), Fn(defined))
}
Ok(Value::FnDecl(_)) => { Ok(Value::FnDecl(_)) => {
return self.panic(format!( return self.panic(format!(
"function `{name}` called before it was defined" "function `{name}` called before it was defined"
@ -315,6 +317,7 @@ impl<'src> Process<'src> {
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 input = self.input;
let src = self.src; let src = self.src;
self.input = f.input; self.input = f.input;

View File

@ -1,5 +1,5 @@
use crate::parser::*; use crate::parser::*;
use crate::spans::Span; use crate::spans::{Span, Spanned};
use crate::value::Value; use crate::value::Value;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -53,9 +53,9 @@ fn match_arities(arities: &HashSet<Arity>, num_args: u8) -> bool {
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Validator<'a, 'src> { pub struct Validator<'a> {
pub locals: Vec<(String, Span, FnInfo)>, 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 input: &'static str,
pub src: &'static str, pub src: &'static str,
pub ast: &'a Ast, pub ast: &'a Ast,
@ -65,14 +65,14 @@ pub struct Validator<'a, 'src> {
status: VStatus, status: VStatus,
} }
impl<'a, 'src: 'a> Validator<'a, 'src> { impl<'a> Validator<'a> {
pub fn new( pub fn new(
ast: &'a Ast, ast: &'a Ast,
span: Span, span: Span,
input: &'static str, input: &'static str,
src: &'static str, src: &'static str,
prelude: &'a Vec<(String, Value<'src>)>, prelude: &'a Vec<(String, Value<'a>)>,
) -> Validator<'a, 'src> { ) -> Validator<'a> {
Validator { Validator {
input, input,
src, src,
@ -109,7 +109,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
fn resolved(&self, name: &str) -> bool { fn resolved(&self, name: &str) -> bool {
self.locals.iter().any(|(bound, ..)| name == bound.as_str()) 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)> { 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<Ast>) {
let (expr, span) = node;
self.ast = expr;
self.span = *span;
self.validate();
}
pub fn validate(&mut self) { pub fn validate(&mut self) {
use Ast::*; use Ast::*;
let root = self.ast; let root = self.ast;
@ -179,18 +186,13 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
} }
let to = self.locals.len(); let to = self.locals.len();
let tailpos = self.status.tail_position; 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.status.tail_position = false;
self.ast = expr; self.visit(line);
self.span = *span;
self.validate();
} }
let (expr, span) = block.last().unwrap();
self.ast = expr;
self.span = *span;
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
self.validate(); self.visit(block.last().unwrap());
let block_bindings = self.locals.split_off(to); 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; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = cond.as_ref(); self.visit(cond.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
// pass through tailpos only to then/else // pass through tailpos only to then/else
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
let (expr, span) = then.as_ref(); self.visit(then.as_ref());
self.ast = expr; self.visit(r#else.as_ref());
self.span = *span;
self.validate();
let (expr, span) = r#else.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
} }
Tuple(members) => { Tuple(members) => {
if members.is_empty() { if members.is_empty() {
@ -230,10 +222,8 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in members { for member in members {
self.ast = expr; self.visit(member);
self.span = *span;
self.validate();
} }
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
@ -244,10 +234,8 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in args { for arg in args {
self.ast = expr; self.visit(arg);
self.span = *span;
self.validate();
} }
self.status.has_placeholder = false; self.status.has_placeholder = false;
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
@ -267,30 +255,21 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in list { for member in list {
self.ast = expr; self.visit(member);
self.span = *span;
self.validate();
} }
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
Pair(_, value) => { Pair(_, value) => self.visit(value.as_ref()),
let (expr, span) = value.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
}
Dict(dict) => { Dict(dict) => {
if dict.is_empty() { if dict.is_empty() {
return; return;
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in dict { for pair in dict {
self.ast = expr; self.visit(pair)
self.span = *span;
self.validate();
} }
self.status.tail_position = tailpos; 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 // check arity against fn info if first term is word and second term is args
Synthetic(first, second, rest) => { Synthetic(first, second, rest) => {
match (&first.0, &second.0) { match (&first.0, &second.0) {
(Ast::Word(_), Ast::Keyword(_)) => { (Ast::Word(_), Ast::Keyword(_)) => self.visit(first.as_ref()),
let (expr, span) = first.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
}
(Ast::Keyword(_), Ast::Arguments(args)) => { (Ast::Keyword(_), Ast::Arguments(args)) => {
if args.len() != 1 { if args.len() != 1 {
self.err("called keywords may only take one argument".to_string()) self.err("called keywords may only take one argument".to_string())
} }
let (expr, span) = second.as_ref(); self.visit(second.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
} }
(Ast::Word(name), Ast::Arguments(args)) => { (Ast::Word(name), Ast::Arguments(args)) => {
let (expr, span) = first.as_ref(); self.visit(first.as_ref());
self.ast = expr; self.visit(second.as_ref());
self.span = *span;
self.validate();
let (expr, span) = second.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
//TODO: check arities of prelude fns, too //TODO: check arities of prelude fns, too
let fn_binding = self.bound(name); let fn_binding = self.bound(name);
@ -337,32 +301,20 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
_ => unreachable!(), _ => unreachable!(),
} }
for term in rest { for term in rest {
let (expr, span) = term; self.visit(term);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
WhenClause(cond, body) => { WhenClause(cond, body) => {
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = cond.as_ref(); self.visit(cond.as_ref());
self.ast = expr; //pass through tail position for when bodies
self.span = *span;
self.validate();
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
let (expr, span) = body.as_ref(); self.visit(body.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
} }
When(clauses) => { When(clauses) => {
for clause in clauses { for clause in clauses {
let (expr, span) = clause; self.visit(clause);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
@ -374,54 +326,30 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
} else { } else {
self.bind(name.to_string()); self.bind(name.to_string());
} }
let (expr, span) = boxed.as_ref(); self.visit(boxed.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
} }
Let(lhs, rhs) => { Let(lhs, rhs) => {
let (expr, span) = rhs.as_ref(); self.visit(rhs.as_ref());
self.ast = expr; self.visit(lhs.as_ref());
self.span = *span;
self.validate();
let (expr, span) = lhs.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
} }
MatchClause(pattern, guard, body) => { MatchClause(pattern, guard, body) => {
let to = self.locals.len(); let to = self.locals.len();
let (patt, span) = pattern.as_ref(); self.visit(pattern.as_ref());
self.ast = patt;
self.span = *span;
self.validate();
if let Some((expr, span)) = guard.as_ref() { if let Some(guard) = guard.as_ref() {
self.ast = expr; self.visit(guard);
self.span = *span;
self.validate();
} }
let (expr, span) = body.as_ref(); self.visit(body.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
self.locals.truncate(to); self.locals.truncate(to);
} }
Match(scrutinee, clauses) => { Match(scrutinee, clauses) => {
let (expr, span) = scrutinee.as_ref(); self.visit(scrutinee.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
for clause in clauses { for clause in clauses {
let (expr, span) = clause; self.visit(clause);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
FnDeclaration(name) => { FnDeclaration(name) => {
@ -488,10 +416,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
Panic(msg) => { Panic(msg) => {
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = msg.as_ref(); self.visit(msg.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
// TODO: fix the tail call here? // 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()); return self.err("do expressions must have at least two terms".to_string());
} }
for term in terms.iter().take(terms.len() - 1) { for term in terms.iter().take(terms.len() - 1) {
let (expr, span) = term; self.visit(term);
self.ast = expr;
self.span = *span;
self.validate();
} }
let last = terms.last().unwrap();
let (expr, span) = terms.last().unwrap(); self.visit(last);
self.ast = expr; if matches!(last.0, Ast::Recur(_)) {
self.span = *span;
if matches!(expr, Ast::Recur(_)) {
self.err("`recur` may not be used in `do` forms".to_string()); self.err("`recur` may not be used in `do` forms".to_string());
} }
self.validate();
} }
Repeat(times, body) => { Repeat(times, body) => {
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = times.as_ref(); self.visit(times.as_ref());
self.ast = expr; self.visit(body.as_ref());
self.span = *span;
self.validate();
let (expr, span) = body.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
} }
Loop(with, body) => { Loop(with, body) => {
let (expr, span) = with.as_ref(); self.visit(with.as_ref());
self.span = *span;
self.ast = expr;
self.validate();
let Ast::Tuple(input) = expr else { let Ast::Tuple(input) = &with.0 else {
unreachable!() unreachable!()
}; };
@ -588,10 +497,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
self.status.tail_position = false; self.status.tail_position = false;
for arg in args { for arg in args {
let (expr, span) = arg; self.visit(arg);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
WordPattern(name) => match self.bound(name) { WordPattern(name) => match self.bound(name) {
@ -654,25 +560,15 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
return; return;
} }
for term in terms.iter().take(terms.len() - 1) { for term in terms.iter().take(terms.len() - 1) {
let (patt, span) = term; self.visit(term);
self.ast = patt;
self.span = *span;
self.validate();
} }
self.status.last_term = true; self.status.last_term = true;
let (patt, span) = terms.last().unwrap(); let last = terms.last().unwrap();
self.ast = patt; self.visit(last);
self.span = *span;
self.validate();
self.status.last_term = false; self.status.last_term = false;
} }
PairPattern(_, patt) => { PairPattern(_, patt) => self.visit(patt.as_ref()),
let (patt, span) = patt.as_ref();
self.ast = patt;
self.span = *span;
self.validate();
}
// terminals can never be invalid // terminals can never be invalid
Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (), Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (),
// terminal patterns can never be invalid // terminal patterns can never be invalid

View File

@ -33,7 +33,7 @@ pub enum Value<'src> {
List(Vector<Self>), List(Vector<Self>),
Dict(HashMap<&'static str, Self>), Dict(HashMap<&'static str, Self>),
Box(&'static str, Rc<RefCell<Self>>), Box(&'static str, Rc<RefCell<Self>>),
Fn(Rc<RefCell<Fn<'src>>>), Fn(Rc<Fn<'src>>),
FnDecl(&'static str), FnDecl(&'static str),
Base(BaseFn<'src>), Base(BaseFn<'src>),
Recur(Vec<Self>), Recur(Vec<Self>),