diff --git a/src/main.rs b/src/main.rs index c56ad0f..7e1eee6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,28 +55,8 @@ use crate::base::*; pub fn main() { let src = " -fn fancy_add { - () -> 0 - (n as :number) -> n - (x as :number, y as :number) -> add (x, y) -} - -fn fancy_sub { - () -> 0 - (n as :number) -> n - (x as :number, y as :number) -> sub (x, y) -} - -fn fib { - (1) -> 1 - (2) -> 1 - (n) -> fancy_add( - fib (fancy_sub (n, 1)) - fib (fancy_sub (n, 2)) - ) -} - -fib (30) +let (x, y, ...z) = (1, 2, 3) +z "; let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if lex_errs.len() > 0 { diff --git a/src/parser.rs b/src/parser.rs index 5f66052..faf326d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -211,6 +211,7 @@ pub enum Pattern<'src> { Keyword(&'src str), Word(&'src str), As(&'src str, &'src str), + Splattern(Box>), Placeholder, Tuple(Vec>), List(Vec>), @@ -229,6 +230,7 @@ impl<'src> fmt::Display for Pattern<'src> { Pattern::Keyword(k) => write!(f, ":{}", k), Pattern::Word(w) => write!(f, "{}", w), Pattern::As(w, t) => write!(f, "{} as {}", w, t), + Pattern::Splattern(p) => write!(f, "...{}", (*p).0.to_string()), Pattern::Placeholder => write!(f, "_"), Pattern::Tuple(t) => write!( f, @@ -300,8 +302,24 @@ where } .map_with(|a, e| (a, e.span())); + let bare_splat = just(Token::Punctuation("...")).map_with(|_, e| { + ( + Pattern::Splattern(Box::new((Pattern::Placeholder, e.span()))), + e.span(), + ) + }); + + let splattable = word_pattern.clone().or(placeholder_pattern.clone()); + + let patt_splat = just(Token::Punctuation("...")) + .ignore_then(splattable) + .map_with(|x, e| (Pattern::Splattern(Box::new(x)), e.span())); + + let splattern = patt_splat.or(bare_splat); + let tuple_pattern = pattern .clone() + .or(splattern.clone()) .separated_by(separators.clone()) .allow_leading() .allow_trailing() @@ -312,6 +330,7 @@ where let list_pattern = pattern .clone() + .or(splattern.clone()) .separated_by(separators.clone()) .allow_leading() .allow_trailing() diff --git a/src/vm.rs b/src/vm.rs index aa94b2f..658eb4a 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -79,16 +79,32 @@ pub fn match_pattern<'src, 'a>( } // todo: add splats to these match clauses (Pattern::Tuple(x), Value::Tuple(y)) => { - if x.len() != y.len() { + let has_splat = x.iter().any(|patt| { + if let (Pattern::Splattern(_), _) = patt { + true + } else { + false + } + }); + if x.len() > y.len() || (!has_splat && x.len() != y.len()) { return None; }; let to = ctx.len(); for i in 0..x.len() { - if let None = match_pattern(&x[i].0, &y[i], ctx) { - while ctx.len() > to { - ctx.pop(); + if let Pattern::Splattern(patt) = &x[i].0 { + let mut list = Vector::new(); + for i in i..y.len() { + list.push_back(y[i].clone()) + } + let list = Value::List(list); + match_pattern(&(*patt).0, &list, ctx); + } else { + if let None = match_pattern(&x[i].0, &y[i], ctx) { + while ctx.len() > to { + ctx.pop(); + } + return None; } - return None; } } Some(ctx) @@ -260,7 +276,9 @@ pub fn eval<'src, 'a>( let val = if let Some((_, value)) = ctx.iter().rev().find(|(name, _)| w == name) { value.clone() } else { - unreachable!() + return Err(LudusError { + msg: format!("unbound name {w}"), + }); }; Ok(val) }