From eafe7a7fa975827bf7a37210a21f00c467efad9e Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Tue, 10 Dec 2024 23:42:05 -0500 Subject: [PATCH] complete first draft of validator --- src/main.rs | 12 +-- src/validator.rs | 186 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 166 insertions(+), 32 deletions(-) diff --git a/src/main.rs b/src/main.rs index 98a02a2..e50e2c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -148,11 +148,11 @@ pub fn run(src: &'static str) { let dummy_prelude = vec![]; - let mut valxor = validator::Validator::new(&ast, span, &dummy_prelude); + let mut v6or = validator::Validator::new(&ast, span, &dummy_prelude); - // valxor.validate(); + v6or.validate(); - // dbg!(valxor); + dbg!(v6or); let mut ctx = prelude(); ctx.ast = * @@ -167,8 +167,10 @@ pub fn run(src: &'static str) { pub fn main() { let src = " -let #{:a (x, y), :b [1, 2, (a, b)]} = #{:a (1, 2), :b [1, 2, (7, 8)]} -(x, y, a, b) +let bar = :bar +match :foo with { + :foo -> bar +} "; run(src); // struct_scalpel::print_dissection_info::() diff --git a/src/validator.rs b/src/validator.rs index f5cfd9d..9caa326 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -101,6 +101,21 @@ impl<'a> Validator<'a> { self.status.used_bindings.push(name); } + fn arity(&mut self) -> Arity { + let Ast::MatchClause(pattern, ..) = self.ast else { + unreachable!("internal Ludus error") + }; + let (Ast::TuplePattern(members), _) = pattern.as_ref() else { + unreachable!("internal Ludus error"); + }; + let last_member = members.last(); + match last_member { + None => Arity::Fixed(0), + Some((Ast::Splattern(..), _)) => Arity::Splat(members.len() as u8), + Some(_) => Arity::Fixed(members.len() as u8), + } + } + pub fn validate(&mut self) { use Ast::*; let root = self.ast; @@ -297,22 +312,27 @@ impl<'a> Validator<'a> { self.validate(); } } - WhenClause(cond, body) => todo!(), - When(clauses) => { - // let tailpos = self.status.tail_position; - // for (clause, _) in clauses { - // self.status.tail_position = false; - // let (expr, span) = clause.cond.clone(); - // self.ast = &expr; - // self.span = span; - // self.validate(); + 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.status.tail_position = tailpos; - // let (expr, span) = clause.body; - // self.ast = &expr; - // self.span = span; - // self.validate(); - // } + self.status.tail_position = tailpos; + let (expr, span) = body.as_ref(); + self.ast = expr; + self.span = *span; + self.validate(); + } + When(clauses) => { + for clause in clauses { + let (expr, span) = clause; + self.ast = expr; + self.span = *span; + self.validate(); + } } // binding forms @@ -339,14 +359,40 @@ impl<'a> Validator<'a> { self.span = *span; self.validate(); } - MatchClause(pattern, guard, body) => todo!(), + MatchClause(pattern, guard, body) => { + let to = self.locals.len(); + + let (patt, span) = pattern.as_ref(); + self.ast = patt; + self.span = *span; + self.validate(); + + if let Some((expr, span)) = guard.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.locals.truncate(to); + } Match(scrutinee, clauses) => { let (expr, span) = scrutinee.as_ref(); self.ast = expr; self.span = *span; self.validate(); - } + for clause in clauses { + let (expr, span) = clause; + self.ast = expr; + self.span = *span; + self.validate(); + } + } FnDeclaration(name) => { let tailpos = self.status.tail_position; self.status.tail_position = false; @@ -367,11 +413,16 @@ impl<'a> Validator<'a> { } let from = self.status.used_bindings.len(); - let arities = HashSet::new(); + let mut arities = HashSet::new(); for clause in clauses { // TODO: validate all parts of clauses + let (expr, span) = clause; + self.ast = expr; + self.span = *span; // add clause arity to arities + arities.insert(self.arity()); + self.validate(); } // this should be right @@ -389,7 +440,11 @@ impl<'a> Validator<'a> { let info = FnInfo::Defined(arities, closed_over); - self.define_fn(name.to_string(), info) + let root_ptr: *const Ast = root; + + self.fn_info.insert(root_ptr, info.clone()); + + self.define_fn(name.to_string(), info); } Panic(msg) => { @@ -448,7 +503,24 @@ impl<'a> Validator<'a> { self.status.in_loop = true; self.status.loop_arity = input.len() as u8; - // for clause in body {} + for clause in body { + let (expr, span) = clause; + self.ast = expr; + self.span = *span; + match self.arity() { + Arity::Fixed(clause_arity) => { + if clause_arity != arity { + self.err(format!("mismatched arity: expected {arity} arguments in `loop` clause; got {clause_arity}")) + } + } + Arity::Splat(clause_arity) => { + if clause_arity > arity { + self.err(format!("mismathced arity: expected {arity} arguments in `loop` clause; this clause takes {clause_arity} or more")) + } + } + }; + self.validate(); + } self.status.in_loop = in_loop; self.status.loop_arity = arity; @@ -484,13 +556,73 @@ impl<'a> Validator<'a> { self.bind(name.to_string()); } }, - InterpolatedPattern(parts, _) => todo!(), - AsPattern(name, r#type) => todo!(), - Splattern(splatted) => todo!(), - TuplePattern(tuple) => todo!(), - ListPattern(list) => todo!(), - PairPattern(key, patt) => todo!(), - DictPattern(dict) => todo!(), + InterpolatedPattern(parts, _) => { + for (part, span) in parts { + if let StringPart::Word(name) = part { + self.span = *span; + match self.bound(name) { + Some(_) => self.err(format!("name `{name}` is already bound")), + None => self.bind(name.to_string()), + } + } + } + } + AsPattern(name, r#type) => { + match self.bound(name) { + Some((name, _span, _)) => { + self.err(format!("name `{name}` is already bound")); + } + None => { + self.bind(name.to_string()); + } + } + let as_type = *r#type; + match as_type { + "nil" | "bool" | "number" | "keyword" | "string" | "tuple" | "dict" + | "list" | "fn" | "box" => (), + _ => self.err(format!("unknown type `:{as_type}`")), + } + } + Splattern(splatted) => { + if !self.status.last_term { + self.err("splats in patterns must come last".to_string()); + } + match splatted.as_ref() { + (Ast::Placeholder, _) => (), + (Ast::Word(name), span) => match self.bound(name) { + Some(_) => { + self.span = *span; + self.err(format!("name `{name}` is already bound")) + } + None => self.bind(name.to_string()), + }, + _ => unreachable!(), + } + } + TuplePattern(terms) | ListPattern(terms) | DictPattern(terms) => { + if terms.is_empty() { + return; + } + for term in terms.iter().take(terms.len() - 1) { + let (patt, span) = term; + self.ast = patt; + self.span = *span; + self.validate(); + } + + self.status.last_term = true; + let (patt, span) = terms.last().unwrap(); + self.ast = patt; + self.span = *span; + self.validate(); + self.status.last_term = false; + } + PairPattern(_, patt) => { + let (patt, span) = patt.as_ref(); + self.ast = patt; + self.span = *span; + self.validate(); + } // terminals can never be invalid Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (), // terminal patterns can never be invalid