tuple splatterns now work

This commit is contained in:
Scott Richmond 2024-11-18 13:25:54 -05:00
parent 3957e9c14a
commit 0acad8b312
3 changed files with 45 additions and 28 deletions

View File

@ -55,28 +55,8 @@ use crate::base::*;
pub fn main() { pub fn main() {
let src = " let src = "
fn fancy_add { let (x, y, ...z) = (1, 2, 3)
() -> 0 z
(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 (tokens, lex_errs) = lexer().parse(src).into_output_errors(); let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if lex_errs.len() > 0 { if lex_errs.len() > 0 {

View File

@ -211,6 +211,7 @@ pub enum Pattern<'src> {
Keyword(&'src str), Keyword(&'src str),
Word(&'src str), Word(&'src str),
As(&'src str, &'src str), As(&'src str, &'src str),
Splattern(Box<Spanned<Self>>),
Placeholder, Placeholder,
Tuple(Vec<Spanned<Self>>), Tuple(Vec<Spanned<Self>>),
List(Vec<Spanned<Self>>), List(Vec<Spanned<Self>>),
@ -229,6 +230,7 @@ impl<'src> fmt::Display for Pattern<'src> {
Pattern::Keyword(k) => write!(f, ":{}", k), Pattern::Keyword(k) => write!(f, ":{}", k),
Pattern::Word(w) => write!(f, "{}", w), Pattern::Word(w) => write!(f, "{}", w),
Pattern::As(w, t) => write!(f, "{} as {}", w, t), Pattern::As(w, t) => write!(f, "{} as {}", w, t),
Pattern::Splattern(p) => write!(f, "...{}", (*p).0.to_string()),
Pattern::Placeholder => write!(f, "_"), Pattern::Placeholder => write!(f, "_"),
Pattern::Tuple(t) => write!( Pattern::Tuple(t) => write!(
f, f,
@ -300,8 +302,24 @@ where
} }
.map_with(|a, e| (a, e.span())); .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 let tuple_pattern = pattern
.clone() .clone()
.or(splattern.clone())
.separated_by(separators.clone()) .separated_by(separators.clone())
.allow_leading() .allow_leading()
.allow_trailing() .allow_trailing()
@ -312,6 +330,7 @@ where
let list_pattern = pattern let list_pattern = pattern
.clone() .clone()
.or(splattern.clone())
.separated_by(separators.clone()) .separated_by(separators.clone())
.allow_leading() .allow_leading()
.allow_trailing() .allow_trailing()

View File

@ -79,16 +79,32 @@ pub fn match_pattern<'src, 'a>(
} }
// todo: add splats to these match clauses // todo: add splats to these match clauses
(Pattern::Tuple(x), Value::Tuple(y)) => { (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; return None;
}; };
let to = ctx.len(); let to = ctx.len();
for i in 0..x.len() { for i in 0..x.len() {
if let None = match_pattern(&x[i].0, &y[i], ctx) { if let Pattern::Splattern(patt) = &x[i].0 {
while ctx.len() > to { let mut list = Vector::new();
ctx.pop(); 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) Some(ctx)
@ -260,7 +276,9 @@ pub fn eval<'src, 'a>(
let val = if let Some((_, value)) = ctx.iter().rev().find(|(name, _)| w == name) { let val = if let Some((_, value)) = ctx.iter().rev().find(|(name, _)| w == name) {
value.clone() value.clone()
} else { } else {
unreachable!() return Err(LudusError {
msg: format!("unbound name {w}"),
});
}; };
Ok(val) Ok(val)
} }