cover all Ast branches
This commit is contained in:
parent
f5a6facb1c
commit
9542dcf5e2
|
@ -102,17 +102,18 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&mut self) {
|
pub fn validate(&mut self) {
|
||||||
|
use Ast::*;
|
||||||
let root = self.ast;
|
let root = self.ast;
|
||||||
match root {
|
match root {
|
||||||
Ast::Error => unreachable!(),
|
Error => unreachable!(),
|
||||||
Ast::Word(name) | Ast::Splat(name) => {
|
Word(name) | Ast::Splat(name) => {
|
||||||
if !self.resolved(name) {
|
if !self.resolved(name) {
|
||||||
self.err(format!("unbound name `{name}`"))
|
self.err(format!("unbound name `{name}`"))
|
||||||
} else {
|
} else {
|
||||||
self.use_name(name.to_string())
|
self.use_name(name.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ast::Interpolated(parts) => {
|
Interpolated(parts) => {
|
||||||
for part in parts {
|
for part in parts {
|
||||||
if let (StringPart::Word(name), span) = part {
|
if let (StringPart::Word(name), span) = part {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
|
@ -129,7 +130,7 @@ impl<'a> Validator<'a> {
|
||||||
// pass through tail position validation
|
// pass through tail position validation
|
||||||
// check if there are any declared but undefined functions
|
// check if there are any declared but undefined functions
|
||||||
// pop all the bindings off the local stack
|
// pop all the bindings off the local stack
|
||||||
Ast::Block(block) => {
|
Block(block) => {
|
||||||
if block.is_empty() {
|
if block.is_empty() {
|
||||||
self.err("blocks must have at least one expression".to_string());
|
self.err("blocks must have at least one expression".to_string());
|
||||||
return;
|
return;
|
||||||
|
@ -160,7 +161,7 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
// if in tail position, pass through tail position validation
|
// if in tail position, pass through tail position validation
|
||||||
// no unbound names
|
// no unbound names
|
||||||
Ast::If(cond, then, r#else) => {
|
If(cond, then, r#else) => {
|
||||||
let tailpos = self.status.tail_position;
|
let tailpos = self.status.tail_position;
|
||||||
self.status.tail_position = false;
|
self.status.tail_position = false;
|
||||||
|
|
||||||
|
@ -181,7 +182,7 @@ impl<'a> Validator<'a> {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Ast::Tuple(members) => {
|
Tuple(members) => {
|
||||||
if members.is_empty() {
|
if members.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +196,7 @@ impl<'a> Validator<'a> {
|
||||||
self.status.tail_position = tailpos;
|
self.status.tail_position = tailpos;
|
||||||
}
|
}
|
||||||
// no more than one placeholder
|
// no more than one placeholder
|
||||||
Ast::Arguments(args) => {
|
Arguments(args) => {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +210,7 @@ impl<'a> Validator<'a> {
|
||||||
self.status.has_placeholder = false;
|
self.status.has_placeholder = false;
|
||||||
self.status.tail_position = tailpos;
|
self.status.tail_position = tailpos;
|
||||||
}
|
}
|
||||||
Ast::Placeholder => {
|
Placeholder => {
|
||||||
if self.status.has_placeholder {
|
if self.status.has_placeholder {
|
||||||
self.err(
|
self.err(
|
||||||
"you may only use one placeholder when partially applying functions"
|
"you may only use one placeholder when partially applying functions"
|
||||||
|
@ -218,7 +219,7 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
self.status.has_placeholder = true;
|
self.status.has_placeholder = true;
|
||||||
}
|
}
|
||||||
Ast::List(list) => {
|
List(list) => {
|
||||||
if list.is_empty() {
|
if list.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -232,13 +233,13 @@ impl<'a> Validator<'a> {
|
||||||
|
|
||||||
self.status.tail_position = tailpos;
|
self.status.tail_position = tailpos;
|
||||||
}
|
}
|
||||||
Ast::Pair(_, value) => {
|
Pair(_, value) => {
|
||||||
let (expr, span) = value.as_ref();
|
let (expr, span) = value.as_ref();
|
||||||
self.ast = expr;
|
self.ast = expr;
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Ast::Dict(dict) => {
|
Dict(dict) => {
|
||||||
if dict.is_empty() {
|
if dict.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -257,7 +258,7 @@ impl<'a> Validator<'a> {
|
||||||
// then...
|
// then...
|
||||||
// check arity is 1 if first term is keyword
|
// check arity is 1 if first term is keyword
|
||||||
// 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
|
||||||
Ast::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(_)) => {
|
||||||
let (expr, span) = first.as_ref();
|
let (expr, span) = first.as_ref();
|
||||||
|
@ -296,7 +297,8 @@ impl<'a> Validator<'a> {
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ast::When(clauses) => {
|
WhenClause(cond, body) => todo!(),
|
||||||
|
When(clauses) => {
|
||||||
// let tailpos = self.status.tail_position;
|
// let tailpos = self.status.tail_position;
|
||||||
// for (clause, _) in clauses {
|
// for (clause, _) in clauses {
|
||||||
// self.status.tail_position = false;
|
// self.status.tail_position = false;
|
||||||
|
@ -315,7 +317,7 @@ impl<'a> Validator<'a> {
|
||||||
|
|
||||||
// binding forms
|
// binding forms
|
||||||
// TODO: set up errors to include original binding
|
// TODO: set up errors to include original binding
|
||||||
Ast::LBox(name, boxed) => {
|
LBox(name, boxed) => {
|
||||||
if self.bound(name).is_some() {
|
if self.bound(name).is_some() {
|
||||||
self.err(format!("box name `{name}` is already bound"));
|
self.err(format!("box name `{name}` is already bound"));
|
||||||
} else {
|
} else {
|
||||||
|
@ -326,20 +328,26 @@ impl<'a> Validator<'a> {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Ast::Let(lhs, rhs) => {
|
Let(lhs, rhs) => {
|
||||||
let (expr, span) = rhs.as_ref();
|
let (expr, span) = rhs.as_ref();
|
||||||
self.ast = expr;
|
self.ast = expr;
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
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();
|
let (expr, span) = scrutinee.as_ref();
|
||||||
self.ast = expr;
|
self.ast = expr;
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast::FnDeclaration(name) => {
|
FnDeclaration(name) => {
|
||||||
let tailpos = self.status.tail_position;
|
let tailpos = self.status.tail_position;
|
||||||
self.status.tail_position = false;
|
self.status.tail_position = false;
|
||||||
if self.bound(name).is_some() {
|
if self.bound(name).is_some() {
|
||||||
|
@ -349,7 +357,7 @@ impl<'a> Validator<'a> {
|
||||||
self.declare_fn(name.to_string());
|
self.declare_fn(name.to_string());
|
||||||
self.status.tail_position = tailpos;
|
self.status.tail_position = tailpos;
|
||||||
}
|
}
|
||||||
Ast::Fn(name, clauses, ..) => {
|
Fn(name, clauses, ..) => {
|
||||||
match self.bound(name) {
|
match self.bound(name) {
|
||||||
Some((_, _, FnInfo::Declared)) => (),
|
Some((_, _, FnInfo::Declared)) => (),
|
||||||
None => (),
|
None => (),
|
||||||
|
@ -384,7 +392,7 @@ impl<'a> Validator<'a> {
|
||||||
self.define_fn(name.to_string(), info)
|
self.define_fn(name.to_string(), info)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast::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();
|
let (expr, span) = msg.as_ref();
|
||||||
|
@ -394,7 +402,7 @@ impl<'a> Validator<'a> {
|
||||||
self.status.tail_position = tailpos;
|
self.status.tail_position = tailpos;
|
||||||
}
|
}
|
||||||
// TODO: fix the tail call here?
|
// TODO: fix the tail call here?
|
||||||
Ast::Do(terms) => {
|
Do(terms) => {
|
||||||
if terms.len() < 2 {
|
if terms.len() < 2 {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
@ -413,7 +421,7 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Ast::Repeat(times, body) => {
|
Repeat(times, body) => {
|
||||||
self.status.tail_position = false;
|
self.status.tail_position = false;
|
||||||
let (expr, span) = times.as_ref();
|
let (expr, span) = times.as_ref();
|
||||||
self.ast = expr;
|
self.ast = expr;
|
||||||
|
@ -425,7 +433,7 @@ impl<'a> Validator<'a> {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Ast::Loop(with, body) => {
|
Loop(with, body) => {
|
||||||
let (expr, span) = with.as_ref();
|
let (expr, span) = with.as_ref();
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.ast = expr;
|
self.ast = expr;
|
||||||
|
@ -445,7 +453,7 @@ impl<'a> Validator<'a> {
|
||||||
self.status.in_loop = in_loop;
|
self.status.in_loop = in_loop;
|
||||||
self.status.loop_arity = arity;
|
self.status.loop_arity = arity;
|
||||||
}
|
}
|
||||||
Ast::Recur(args) => {
|
Recur(args) => {
|
||||||
if !self.status.in_loop {
|
if !self.status.in_loop {
|
||||||
self.err("you may only use `recur` in a `loop` form".to_string());
|
self.err("you may only use `recur` in a `loop` form".to_string());
|
||||||
return;
|
return;
|
||||||
|
@ -468,9 +476,26 @@ impl<'a> Validator<'a> {
|
||||||
self.validate();
|
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
|
// terminals can never be invalid
|
||||||
Ast::Nil | Ast::Boolean(_) | Ast::Number(_) | Ast::Keyword(_) | Ast::String(_) => (),
|
Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (),
|
||||||
_ => todo!(),
|
// terminal patterns can never be invalid
|
||||||
|
NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..)
|
||||||
|
| KeywordPattern(..) | PlaceholderPattern => (),
|
||||||
};
|
};
|
||||||
self.ast = root;
|
self.ast = root;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user