add guard clauses to match and fn
This commit is contained in:
parent
7a4bf5ff29
commit
56e6712154
14
src/main.rs
14
src/main.rs
|
@ -21,9 +21,9 @@
|
|||
// - [x] with stack mechanics and refcounting
|
||||
// - [ ] with tail-call optimization (nb: this may not be possible w/ a TW-VM)
|
||||
// - [ ] with all the necessary forms for current Ludus
|
||||
// * [ ] guards in match clauses
|
||||
// * [x] guards in match clauses
|
||||
// * [x] `as` patterns
|
||||
// * [ ] splat patterns in tuples, lists, dicts
|
||||
// * [x] splat patterns in tuples, lists, dicts
|
||||
// * [ ] splats in list and dict literals
|
||||
// * [ ] `loop` and `recur`
|
||||
// * [ ] string patterns
|
||||
|
@ -55,8 +55,14 @@ use crate::base::*;
|
|||
|
||||
pub fn main() {
|
||||
let src = "
|
||||
let [x, y, ...z] = [1, 2, 3, 4]
|
||||
z
|
||||
fn t () -> true
|
||||
fn f () -> false
|
||||
|
||||
fn id {
|
||||
(x) if f () -> x
|
||||
(x) -> :whoops
|
||||
}
|
||||
id (:foo)
|
||||
";
|
||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||
if lex_errs.len() > 0 {
|
||||
|
|
|
@ -215,9 +215,8 @@ pub enum Pattern<'src> {
|
|||
Placeholder,
|
||||
Tuple(Vec<Spanned<Self>>),
|
||||
List(Vec<Spanned<Self>>),
|
||||
// is this the right representation for Dicts?
|
||||
// Could/should this also be a Vec?
|
||||
Dict(Vec<Spanned<PairPattern<'src>>>),
|
||||
Pair(&'src str, Box<Spanned<Self>>),
|
||||
Dict(Vec<Spanned<Self>>),
|
||||
}
|
||||
|
||||
impl<'src> fmt::Display for Pattern<'src> {
|
||||
|
@ -257,6 +256,7 @@ impl<'src> fmt::Display for Pattern<'src> {
|
|||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -340,20 +340,18 @@ where
|
|||
|
||||
let pair_pattern = select! {Token::Keyword(k) => k}
|
||||
.then(pattern.clone())
|
||||
.map_with(|(key, patt), e| (PairPattern { key, patt }, e.span()));
|
||||
.map_with(|(key, patt), e| (Pattern::Pair(key, Box::new(patt)), e.span()));
|
||||
|
||||
let shorthand_pattern = select! {Token::Word(w) => w}.map_with(|w, e| {
|
||||
(
|
||||
PairPattern {
|
||||
key: w,
|
||||
patt: ((Pattern::Word(w), e.span())),
|
||||
},
|
||||
Pattern::Pair(w, Box::new((Pattern::Word(w), e.span()))),
|
||||
e.span(),
|
||||
)
|
||||
});
|
||||
|
||||
let dict_pattern = pair_pattern
|
||||
.or(shorthand_pattern)
|
||||
.or(splattern.clone())
|
||||
.separated_by(separators.clone())
|
||||
.allow_leading()
|
||||
.allow_trailing()
|
||||
|
@ -523,6 +521,18 @@ where
|
|||
)
|
||||
.map_with(|clauses, e| (Ast::When(clauses), e.span()));
|
||||
|
||||
let guarded_clause = pattern
|
||||
.clone()
|
||||
.then_ignore(just(Token::Reserved("if")))
|
||||
.then(simple.clone())
|
||||
.then_ignore(just(Token::Punctuation("->")))
|
||||
.then(expr.clone())
|
||||
.map_with(|((patt, guard), body), _| MatchClause {
|
||||
patt,
|
||||
guard: Some(guard),
|
||||
body,
|
||||
});
|
||||
|
||||
let match_clause = pattern
|
||||
.clone()
|
||||
.then_ignore(just(Token::Punctuation("->")))
|
||||
|
@ -539,6 +549,7 @@ where
|
|||
.then(
|
||||
match_clause
|
||||
.clone()
|
||||
.or(guarded_clause)
|
||||
.separated_by(terminators.clone())
|
||||
.allow_leading()
|
||||
.allow_trailing()
|
||||
|
@ -575,6 +586,19 @@ where
|
|||
.then(block.clone())
|
||||
.map_with(|(count, body), e| (Ast::Repeat(Box::new(count), Box::new(body)), e.span()));
|
||||
|
||||
let fn_guarded = tuple_pattern
|
||||
.clone()
|
||||
.then_ignore(just(Token::Reserved("if")))
|
||||
.then(simple.clone())
|
||||
.then_ignore(just(Token::Punctuation("->")))
|
||||
.then(nonbinding.clone())
|
||||
.map_with(|((patt, guard), body), _| MatchClause {
|
||||
patt,
|
||||
body,
|
||||
guard: Some(guard),
|
||||
})
|
||||
.labelled("function clause");
|
||||
|
||||
let fn_clause = tuple_pattern
|
||||
.clone()
|
||||
.then_ignore(just(Token::Punctuation("->")))
|
||||
|
@ -645,7 +669,7 @@ where
|
|||
|
||||
let fn_named = just(Token::Reserved("fn"))
|
||||
.ignore_then(word.clone())
|
||||
.then(fn_clause.clone())
|
||||
.then(fn_clause.clone().or(fn_guarded.clone()))
|
||||
.map_with(|(word, clause), e| {
|
||||
let name = if let Ast::Word(word) = word.0 {
|
||||
word
|
||||
|
@ -660,6 +684,7 @@ where
|
|||
.then(
|
||||
fn_clause
|
||||
.clone()
|
||||
.or(fn_guarded.clone())
|
||||
.separated_by(terminators.clone())
|
||||
.allow_leading()
|
||||
.allow_trailing()
|
||||
|
|
71
src/vm.rs
71
src/vm.rs
|
@ -136,22 +136,55 @@ pub fn match_pattern<'src, 'a>(
|
|||
}
|
||||
Some(ctx)
|
||||
}
|
||||
// TODO: optimize this on several levels
|
||||
// - [ ] opportunistic mutation
|
||||
// - [ ] get rid of all the pointer indirection in word splats
|
||||
(Pattern::Dict(x), Value::Dict(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 (PairPattern { key, patt }, _) in x {
|
||||
if let Some(val) = y.get(key) {
|
||||
if let None = match_pattern(&patt.0, val, ctx) {
|
||||
while ctx.len() > to {
|
||||
ctx.pop();
|
||||
let mut matched = vec![];
|
||||
for i in 0..x.len() {
|
||||
let (pattern, _) = &x[i];
|
||||
match pattern {
|
||||
Pattern::Pair(key, patt) => {
|
||||
if let Some(val) = y.get(key) {
|
||||
if let None = match_pattern(&patt.0, val, ctx) {
|
||||
while ctx.len() > to {
|
||||
ctx.pop();
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
matched.push(key);
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Pattern::Splattern(pattern) => match &(*pattern).0 {
|
||||
Pattern::Word(w) => {
|
||||
// TODO: find a way to take ownership
|
||||
// this will ALWAYS make structural changes, because of this clone
|
||||
// we want opportunistic mutation if possible
|
||||
let mut unmatched = y.clone();
|
||||
for key in matched.iter() {
|
||||
unmatched.remove(*key);
|
||||
}
|
||||
ctx.push((*w, Value::Dict(unmatched)));
|
||||
}
|
||||
Pattern::Placeholder => (),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Some(ctx)
|
||||
}
|
||||
|
@ -165,8 +198,24 @@ pub fn match_clauses<'src, 'a>(
|
|||
ctx: &'a mut Vec<(&'src str, Value<'src>)>,
|
||||
) -> Result<Value<'src>, LudusError> {
|
||||
let to = ctx.len();
|
||||
for MatchClause { patt, body, .. } in clauses.iter() {
|
||||
for MatchClause { patt, body, guard } in clauses.iter() {
|
||||
if let Some(ctx) = match_pattern(&patt.0, value, ctx) {
|
||||
let pass_guard = match guard {
|
||||
None => true,
|
||||
Some((ast, _)) => {
|
||||
let guard_res = eval(&ast, ctx);
|
||||
match &guard_res {
|
||||
Err(_) => return guard_res,
|
||||
Ok(val) => val.bool(),
|
||||
}
|
||||
}
|
||||
};
|
||||
if !pass_guard {
|
||||
while ctx.len() > to {
|
||||
ctx.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let res = eval(&body.0, ctx);
|
||||
while ctx.len() > to {
|
||||
ctx.pop();
|
||||
|
|
Loading…
Reference in New Issue
Block a user