Compare commits
No commits in common. "eafe7a7fa975827bf7a37210a21f00c467efad9e" and "f5a6facb1ce17a175f9f72dd8de7d0d688409147" have entirely different histories.
eafe7a7fa9
...
f5a6facb1c
12
src/main.rs
12
src/main.rs
|
@ -148,11 +148,11 @@ pub fn run(src: &'static str) {
|
||||||
|
|
||||||
let dummy_prelude = vec![];
|
let dummy_prelude = vec![];
|
||||||
|
|
||||||
let mut v6or = validator::Validator::new(&ast, span, &dummy_prelude);
|
let mut valxor = validator::Validator::new(&ast, span, &dummy_prelude);
|
||||||
|
|
||||||
v6or.validate();
|
// valxor.validate();
|
||||||
|
|
||||||
dbg!(v6or);
|
// dbg!(valxor);
|
||||||
|
|
||||||
let mut ctx = prelude();
|
let mut ctx = prelude();
|
||||||
ctx.ast = *
|
ctx.ast = *
|
||||||
|
@ -167,10 +167,8 @@ pub fn run(src: &'static str) {
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let src = "
|
let src = "
|
||||||
let bar = :bar
|
let #{:a (x, y), :b [1, 2, (a, b)]} = #{:a (1, 2), :b [1, 2, (7, 8)]}
|
||||||
match :foo with {
|
(x, y, a, b)
|
||||||
:foo -> bar
|
|
||||||
}
|
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
// struct_scalpel::print_dissection_info::<value::Value>()
|
// struct_scalpel::print_dissection_info::<value::Value>()
|
||||||
|
|
241
src/validator.rs
241
src/validator.rs
|
@ -101,34 +101,18 @@ impl<'a> Validator<'a> {
|
||||||
self.status.used_bindings.push(name);
|
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) {
|
pub fn validate(&mut self) {
|
||||||
use Ast::*;
|
|
||||||
let root = self.ast;
|
let root = self.ast;
|
||||||
match root {
|
match root {
|
||||||
Error => unreachable!(),
|
Ast::Error => unreachable!(),
|
||||||
Word(name) | Ast::Splat(name) => {
|
Ast::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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Interpolated(parts) => {
|
Ast::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;
|
||||||
|
@ -145,7 +129,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
|
||||||
Block(block) => {
|
Ast::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;
|
||||||
|
@ -176,7 +160,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
|
||||||
If(cond, then, r#else) => {
|
Ast::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;
|
||||||
|
|
||||||
|
@ -197,7 +181,7 @@ impl<'a> Validator<'a> {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Tuple(members) => {
|
Ast::Tuple(members) => {
|
||||||
if members.is_empty() {
|
if members.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -211,7 +195,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
|
||||||
Arguments(args) => {
|
Ast::Arguments(args) => {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -225,7 +209,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;
|
||||||
}
|
}
|
||||||
Placeholder => {
|
Ast::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"
|
||||||
|
@ -234,7 +218,7 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
self.status.has_placeholder = true;
|
self.status.has_placeholder = true;
|
||||||
}
|
}
|
||||||
List(list) => {
|
Ast::List(list) => {
|
||||||
if list.is_empty() {
|
if list.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -248,13 +232,13 @@ impl<'a> Validator<'a> {
|
||||||
|
|
||||||
self.status.tail_position = tailpos;
|
self.status.tail_position = tailpos;
|
||||||
}
|
}
|
||||||
Pair(_, value) => {
|
Ast::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();
|
||||||
}
|
}
|
||||||
Dict(dict) => {
|
Ast::Dict(dict) => {
|
||||||
if dict.is_empty() {
|
if dict.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +257,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
|
||||||
Synthetic(first, second, rest) => {
|
Ast::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();
|
||||||
|
@ -312,32 +296,26 @@ impl<'a> Validator<'a> {
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WhenClause(cond, body) => {
|
Ast::When(clauses) => {
|
||||||
let tailpos = self.status.tail_position;
|
// let tailpos = self.status.tail_position;
|
||||||
self.status.tail_position = false;
|
// for (clause, _) in clauses {
|
||||||
let (expr, span) = cond.as_ref();
|
// self.status.tail_position = false;
|
||||||
self.ast = expr;
|
// let (expr, span) = clause.cond.clone();
|
||||||
self.span = *span;
|
// self.ast = &expr;
|
||||||
self.validate();
|
// self.span = span;
|
||||||
|
// self.validate();
|
||||||
|
|
||||||
self.status.tail_position = tailpos;
|
// self.status.tail_position = tailpos;
|
||||||
let (expr, span) = body.as_ref();
|
// let (expr, span) = clause.body;
|
||||||
self.ast = expr;
|
// self.ast = &expr;
|
||||||
self.span = *span;
|
// self.span = span;
|
||||||
self.validate();
|
// self.validate();
|
||||||
}
|
// }
|
||||||
When(clauses) => {
|
|
||||||
for clause in clauses {
|
|
||||||
let (expr, span) = clause;
|
|
||||||
self.ast = expr;
|
|
||||||
self.span = *span;
|
|
||||||
self.validate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// binding forms
|
// binding forms
|
||||||
// TODO: set up errors to include original binding
|
// TODO: set up errors to include original binding
|
||||||
LBox(name, boxed) => {
|
Ast::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 {
|
||||||
|
@ -348,52 +326,20 @@ impl<'a> Validator<'a> {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Let(lhs, rhs) => {
|
Ast::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();
|
|
||||||
}
|
}
|
||||||
MatchClause(pattern, guard, body) => {
|
Ast::Match(scrutinee, clauses) => {
|
||||||
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();
|
let (expr, span) = scrutinee.as_ref();
|
||||||
self.ast = expr;
|
self.ast = expr;
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
|
}
|
||||||
|
|
||||||
for clause in clauses {
|
Ast::FnDeclaration(name) => {
|
||||||
let (expr, span) = clause;
|
|
||||||
self.ast = expr;
|
|
||||||
self.span = *span;
|
|
||||||
self.validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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() {
|
||||||
|
@ -403,7 +349,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;
|
||||||
}
|
}
|
||||||
Fn(name, clauses, ..) => {
|
Ast::Fn(name, clauses, ..) => {
|
||||||
match self.bound(name) {
|
match self.bound(name) {
|
||||||
Some((_, _, FnInfo::Declared)) => (),
|
Some((_, _, FnInfo::Declared)) => (),
|
||||||
None => (),
|
None => (),
|
||||||
|
@ -413,16 +359,11 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let from = self.status.used_bindings.len();
|
let from = self.status.used_bindings.len();
|
||||||
let mut arities = HashSet::new();
|
let arities = HashSet::new();
|
||||||
|
|
||||||
for clause in clauses {
|
for clause in clauses {
|
||||||
// TODO: validate all parts of clauses
|
// TODO: validate all parts of clauses
|
||||||
let (expr, span) = clause;
|
|
||||||
self.ast = expr;
|
|
||||||
self.span = *span;
|
|
||||||
// add clause arity to arities
|
// add clause arity to arities
|
||||||
arities.insert(self.arity());
|
|
||||||
self.validate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be right
|
// this should be right
|
||||||
|
@ -440,14 +381,10 @@ impl<'a> Validator<'a> {
|
||||||
|
|
||||||
let info = FnInfo::Defined(arities, closed_over);
|
let info = FnInfo::Defined(arities, closed_over);
|
||||||
|
|
||||||
let root_ptr: *const Ast = root;
|
self.define_fn(name.to_string(), info)
|
||||||
|
|
||||||
self.fn_info.insert(root_ptr, info.clone());
|
|
||||||
|
|
||||||
self.define_fn(name.to_string(), info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Panic(msg) => {
|
Ast::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();
|
||||||
|
@ -457,7 +394,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?
|
||||||
Do(terms) => {
|
Ast::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());
|
||||||
}
|
}
|
||||||
|
@ -476,7 +413,7 @@ impl<'a> Validator<'a> {
|
||||||
}
|
}
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Repeat(times, body) => {
|
Ast::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;
|
||||||
|
@ -488,7 +425,7 @@ impl<'a> Validator<'a> {
|
||||||
self.span = *span;
|
self.span = *span;
|
||||||
self.validate();
|
self.validate();
|
||||||
}
|
}
|
||||||
Loop(with, body) => {
|
Ast::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;
|
||||||
|
@ -503,29 +440,12 @@ impl<'a> Validator<'a> {
|
||||||
self.status.in_loop = true;
|
self.status.in_loop = true;
|
||||||
self.status.loop_arity = input.len() as u8;
|
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.in_loop = in_loop;
|
||||||
self.status.loop_arity = arity;
|
self.status.loop_arity = arity;
|
||||||
}
|
}
|
||||||
Recur(args) => {
|
Ast::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;
|
||||||
|
@ -548,86 +468,9 @@ 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, _) => {
|
|
||||||
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
|
// terminals can never be invalid
|
||||||
Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (),
|
Ast::Nil | Ast::Boolean(_) | Ast::Number(_) | Ast::Keyword(_) | Ast::String(_) => (),
|
||||||
// terminal patterns can never be invalid
|
_ => todo!(),
|
||||||
NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..)
|
|
||||||
| KeywordPattern(..) | PlaceholderPattern => (),
|
|
||||||
};
|
};
|
||||||
self.ast = root;
|
self.ast = root;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user