From 9542dcf5e2fbf1c4d48c02f630e38dd83d9394a1 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Tue, 10 Dec 2024 22:40:57 -0500 Subject: [PATCH] cover all Ast branches --- src/validator.rs | 75 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/src/validator.rs b/src/validator.rs index b9bd39e..f5cfd9d 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -102,17 +102,18 @@ impl<'a> Validator<'a> { } pub fn validate(&mut self) { + use Ast::*; let root = self.ast; match root { - Ast::Error => unreachable!(), - Ast::Word(name) | Ast::Splat(name) => { + Error => unreachable!(), + Word(name) | Ast::Splat(name) => { if !self.resolved(name) { self.err(format!("unbound name `{name}`")) } else { self.use_name(name.to_string()) } } - Ast::Interpolated(parts) => { + Interpolated(parts) => { for part in parts { if let (StringPart::Word(name), span) = part { self.span = *span; @@ -129,7 +130,7 @@ impl<'a> Validator<'a> { // pass through tail position validation // check if there are any declared but undefined functions // pop all the bindings off the local stack - Ast::Block(block) => { + Block(block) => { if block.is_empty() { self.err("blocks must have at least one expression".to_string()); return; @@ -160,7 +161,7 @@ impl<'a> Validator<'a> { } // if in tail position, pass through tail position validation // no unbound names - Ast::If(cond, then, r#else) => { + If(cond, then, r#else) => { let tailpos = self.status.tail_position; self.status.tail_position = false; @@ -181,7 +182,7 @@ impl<'a> Validator<'a> { self.span = *span; self.validate(); } - Ast::Tuple(members) => { + Tuple(members) => { if members.is_empty() { return; } @@ -195,7 +196,7 @@ impl<'a> Validator<'a> { self.status.tail_position = tailpos; } // no more than one placeholder - Ast::Arguments(args) => { + Arguments(args) => { if args.is_empty() { return; } @@ -209,7 +210,7 @@ impl<'a> Validator<'a> { self.status.has_placeholder = false; self.status.tail_position = tailpos; } - Ast::Placeholder => { + Placeholder => { if self.status.has_placeholder { self.err( "you may only use one placeholder when partially applying functions" @@ -218,7 +219,7 @@ impl<'a> Validator<'a> { } self.status.has_placeholder = true; } - Ast::List(list) => { + List(list) => { if list.is_empty() { return; } @@ -232,13 +233,13 @@ impl<'a> Validator<'a> { self.status.tail_position = tailpos; } - Ast::Pair(_, value) => { + Pair(_, value) => { let (expr, span) = value.as_ref(); self.ast = expr; self.span = *span; self.validate(); } - Ast::Dict(dict) => { + Dict(dict) => { if dict.is_empty() { return; } @@ -257,7 +258,7 @@ impl<'a> Validator<'a> { // then... // check arity is 1 if first term is keyword // check arity against fn info if first term is word and second term is args - Ast::Synthetic(first, second, rest) => { + Synthetic(first, second, rest) => { match (&first.0, &second.0) { (Ast::Word(_), Ast::Keyword(_)) => { let (expr, span) = first.as_ref(); @@ -296,7 +297,8 @@ impl<'a> Validator<'a> { self.validate(); } } - Ast::When(clauses) => { + WhenClause(cond, body) => todo!(), + When(clauses) => { // let tailpos = self.status.tail_position; // for (clause, _) in clauses { // self.status.tail_position = false; @@ -315,7 +317,7 @@ impl<'a> Validator<'a> { // binding forms // TODO: set up errors to include original binding - Ast::LBox(name, boxed) => { + LBox(name, boxed) => { if self.bound(name).is_some() { self.err(format!("box name `{name}` is already bound")); } else { @@ -326,20 +328,26 @@ impl<'a> Validator<'a> { self.span = *span; self.validate(); } - Ast::Let(lhs, rhs) => { + 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(); } - Ast::Match(scrutinee, clauses) => { + MatchClause(pattern, guard, body) => todo!(), + Match(scrutinee, clauses) => { let (expr, span) = scrutinee.as_ref(); self.ast = expr; self.span = *span; self.validate(); } - Ast::FnDeclaration(name) => { + FnDeclaration(name) => { let tailpos = self.status.tail_position; self.status.tail_position = false; if self.bound(name).is_some() { @@ -349,7 +357,7 @@ impl<'a> Validator<'a> { self.declare_fn(name.to_string()); self.status.tail_position = tailpos; } - Ast::Fn(name, clauses, ..) => { + Fn(name, clauses, ..) => { match self.bound(name) { Some((_, _, FnInfo::Declared)) => (), None => (), @@ -384,7 +392,7 @@ impl<'a> Validator<'a> { self.define_fn(name.to_string(), info) } - Ast::Panic(msg) => { + Panic(msg) => { let tailpos = self.status.tail_position; self.status.tail_position = false; let (expr, span) = msg.as_ref(); @@ -394,7 +402,7 @@ impl<'a> Validator<'a> { self.status.tail_position = tailpos; } // TODO: fix the tail call here? - Ast::Do(terms) => { + Do(terms) => { if terms.len() < 2 { return self.err("do expressions must have at least two terms".to_string()); } @@ -413,7 +421,7 @@ impl<'a> Validator<'a> { } self.validate(); } - Ast::Repeat(times, body) => { + Repeat(times, body) => { self.status.tail_position = false; let (expr, span) = times.as_ref(); self.ast = expr; @@ -425,7 +433,7 @@ impl<'a> Validator<'a> { self.span = *span; self.validate(); } - Ast::Loop(with, body) => { + Loop(with, body) => { let (expr, span) = with.as_ref(); self.span = *span; self.ast = expr; @@ -445,7 +453,7 @@ impl<'a> Validator<'a> { self.status.in_loop = in_loop; self.status.loop_arity = arity; } - Ast::Recur(args) => { + Recur(args) => { if !self.status.in_loop { self.err("you may only use `recur` in a `loop` form".to_string()); return; @@ -468,9 +476,26 @@ impl<'a> Validator<'a> { self.validate(); } } + WordPattern(name) => match self.bound(name) { + Some((name, _span, _)) => { + self.err(format!("name `{name}` is already bound")); + } + None => { + 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!(), // terminals can never be invalid - Ast::Nil | Ast::Boolean(_) | Ast::Number(_) | Ast::Keyword(_) | Ast::String(_) => (), - _ => todo!(), + Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (), + // terminal patterns can never be invalid + NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..) + | KeywordPattern(..) | PlaceholderPattern => (), }; self.ast = root; }