diff --git a/src/main.rs b/src/main.rs index e50e2c5..7835ce0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -167,10 +167,9 @@ pub fn run(src: &'static str) { pub fn main() { let src = " -let bar = :bar -match :foo with { - :foo -> bar -} +let bar = :foo +fn foo (foo, bar, ...) -> bar +foo (42) "; run(src); // struct_scalpel::print_dissection_info::() diff --git a/src/validator.rs b/src/validator.rs index 9caa326..bf3e6d8 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -37,6 +37,13 @@ pub enum FnInfo { Unknown, } +fn match_arities(arities: &HashSet, num_args: u8) -> bool { + arities.iter().any(|arity| match arity { + Arity::Fixed(n) => *n == num_args, + Arity::Splat(n) => *n <= num_args, + }) +} + #[derive(Debug, PartialEq)] pub struct Validator<'a> { // TODO: add another term here: FnStatus. See Issue #18. @@ -269,9 +276,6 @@ impl<'a> Validator<'a> { } // TODO! - // first check all nodes - // 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 Synthetic(first, second, rest) => { match (&first.0, &second.0) { @@ -290,7 +294,7 @@ impl<'a> Validator<'a> { self.span = *span; self.validate(); } - (Ast::Word(_), Ast::Arguments(_)) => { + (Ast::Word(name), Ast::Arguments(args)) => { let (expr, span) = first.as_ref(); self.ast = expr; self.span = *span; @@ -301,7 +305,14 @@ impl<'a> Validator<'a> { self.span = *span; self.validate(); - //TODO: check arity of call + //TODO: check arities of prelude fns, too + let fn_binding = self.bound(name); + if let Some((_, _, FnInfo::Defined(arities, _))) = fn_binding { + let num_args = args.len(); + if !match_arities(arities, num_args as u8) { + self.err(format!("arity mismatch: no clause in function `{name}` with {num_args} argument(s)")) + } + } } _ => unreachable!(), } @@ -412,6 +423,8 @@ impl<'a> Validator<'a> { } } + // TODO: devise a placeholder binding for recursive functions + let from = self.status.used_bindings.len(); let mut arities = HashSet::new(); @@ -588,8 +601,8 @@ impl<'a> Validator<'a> { self.err("splats in patterns must come last".to_string()); } match splatted.as_ref() { - (Ast::Placeholder, _) => (), - (Ast::Word(name), span) => match self.bound(name) { + (PlaceholderPattern, _) => (), + (Word(name), span) => match self.bound(name) { Some(_) => { self.span = *span; self.err(format!("name `{name}` is already bound"))