Merge pull request #652 from HigherOrderCO/629-monadic-operations-inside-match-nested-inside-with-have-unexpected-behaviour

#629 Monadic operations inside match nested inside with have unexpected behaviour
This commit is contained in:
imaqtkatt 2024-08-02 15:39:57 +00:00 committed by GitHub
commit c426087d09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 160 additions and 138 deletions

View File

@ -27,6 +27,7 @@ and this project does not currently adhere to a particular versioning scheme.
- Change tuple syntax to not require parentheses in some cases. ([#554][gh-554])
- Improve error messages in branching statements. ([#464][gh-464])
- Change branches to support ending with ask statements. ([#629][gh-629])
## [0.2.36] - 2024-07-04
@ -410,6 +411,7 @@ and this project does not currently adhere to a particular versioning scheme.
[gh-620]: https://github.com/HigherOrderCO/Bend/issues/620
[gh-621]: https://github.com/HigherOrderCO/Bend/issues/621
[gh-623]: https://github.com/HigherOrderCO/Bend/issues/623
[gh-629]: https://github.com/HigherOrderCO/Bend/issues/629
[gh-642]: https://github.com/HigherOrderCO/Bend/issues/642
[gh-643]: https://github.com/HigherOrderCO/Bend/issues/643
[Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD

View File

@ -37,7 +37,9 @@ impl Stmt {
}
}
Stmt::Ask { pat: _, val, nxt } => {
nxt.gen_map_get(id);
if let Some(nxt) = nxt {
nxt.gen_map_get(id);
}
let substitutions = val.substitute_map_gets(id);
if !substitutions.is_empty() {
*self = gen_get(self, substitutions);

View File

@ -174,7 +174,7 @@ pub enum Stmt {
Ask {
pat: AssignPattern,
val: Box<Expr>,
nxt: Box<Stmt>,
nxt: Option<Box<Stmt>>,
},
// "return" {expr} ";"?
Return {

View File

@ -28,7 +28,9 @@ impl Stmt {
}
Stmt::Ask { val, nxt, .. } => {
val.order_kwargs(book, use_map)?;
nxt.order_kwargs(book, use_map)?;
if let Some(nxt) = nxt {
nxt.order_kwargs(book, use_map)?;
}
}
Stmt::InPlace { val, nxt, .. } => {
val.order_kwargs(book, use_map)?;

View File

@ -496,10 +496,15 @@ impl<'a> PyParser<'a> {
let val = self.parse_expr(true, true)?;
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
self.consume_indent_exactly(*indent)?;
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::Ask { pat, val: Box::new(val), nxt: Box::new(nxt) };
return Ok((stmt, nxt_indent));
let nxt_indent = self.advance_newlines()?;
if nxt_indent == *indent {
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::Ask { pat, val: Box::new(val), nxt: Some(Box::new(nxt)) };
return Ok((stmt, nxt_indent));
} else {
let stmt = Stmt::Ask { pat, val: Box::new(val), nxt: None };
return Ok((stmt, nxt_indent));
}
}
// In-place

View File

@ -61,9 +61,25 @@ impl AssignPattern {
}
}
#[derive(Debug)]
enum StmtToFun {
Return(fun::Term),
Assign(fun::Pattern, fun::Term),
Assign(bool, fun::Pattern, fun::Term),
}
fn take(t: Stmt) -> Result<(bool, Option<fun::Pattern>, fun::Term), String> {
match t.into_fun()? {
StmtToFun::Return(ret) => Ok((false, None, ret)),
StmtToFun::Assign(x, pat, val) => Ok((x, Some(pat), val)),
}
}
fn wrap(nxt: Option<fun::Pattern>, term: fun::Term, ask: bool) -> StmtToFun {
if let Some(pat) = nxt {
StmtToFun::Assign(ask, pat, term)
} else {
StmtToFun::Return(term)
}
}
impl Stmt {
@ -72,10 +88,7 @@ impl Stmt {
// TODO: When we have an error with an assignment, we should show the offending assignment (eg. "{pat} = ...").
let stmt_to_fun = match self {
Stmt::Assign { pat: AssignPattern::MapSet(map, key), val, nxt: Some(nxt) } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Let {
pat: Box::new(fun::Pattern::Var(Some(map.clone()))),
val: Box::new(fun::Term::call(
@ -84,11 +97,7 @@ impl Stmt {
)),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Assign { pat: AssignPattern::MapSet(..), val: _, nxt: None } => {
return Err("Branch ends with map assignment.".to_string());
@ -96,28 +105,17 @@ impl Stmt {
Stmt::Assign { pat, val, nxt: Some(nxt) } => {
let pat = pat.into_fun();
let val = val.to_fun();
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Let { pat: Box::new(pat), val: Box::new(val), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Assign { pat, val, nxt: None } => {
let pat = pat.into_fun();
let val = val.to_fun();
StmtToFun::Assign(pat, val)
StmtToFun::Assign(false, pat, val)
}
Stmt::InPlace { op, pat, val, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
// if it is a mapper operation
if let InPlaceOp::Map = op {
let term = match &*pat {
@ -138,11 +136,7 @@ impl Stmt {
}
};
if let Some(pat) = nxt_pat {
return Ok(StmtToFun::Assign(pat, term));
} else {
return Ok(StmtToFun::Return(term));
}
return Ok(wrap(nxt_pat, term, ask));
}
// otherwise
@ -157,11 +151,7 @@ impl Stmt {
}),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
AssignPattern::MapSet(map, key) => {
let temp = Name::new("%0");
@ -177,19 +167,17 @@ impl Stmt {
val: Box::new(map_term),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
_ => unreachable!(),
}
}
Stmt::If { cond, then, otherwise, nxt } => {
let (pat, then, else_) = match (then.into_fun()?, otherwise.into_fun()?) {
(StmtToFun::Return(t), StmtToFun::Return(e)) => (None, t, e),
(StmtToFun::Assign(tp, t), StmtToFun::Assign(ep, e)) if tp == ep => (Some(tp), t, e),
let (ask, pat, then, else_) = match (then.into_fun()?, otherwise.into_fun()?) {
(StmtToFun::Return(t), StmtToFun::Return(e)) => (false, None, t, e),
(StmtToFun::Assign(ask, tp, t), StmtToFun::Assign(ask_, ep, e)) if tp == ep => {
(ask && ask_, Some(tp), t, e)
}
(StmtToFun::Assign(..), StmtToFun::Assign(..)) => {
return Err("'if' branches end with different assignments.".to_string());
}
@ -213,26 +201,20 @@ impl Stmt {
pred: Some(Name::new("%pred-1")),
arms,
};
wrap_nxt_assign_stmt(term, nxt, pat)?
wrap_nxt_assign_stmt(term, nxt, pat, ask)?
}
Stmt::Match { arg, bnd, with_bnd, with_arg, arms, nxt } => {
let arg = arg.to_fun();
let mut fun_arms = vec![];
let mut arms = arms.into_iter();
let fst = arms.next().unwrap();
let (fst_pat, fst_rgt) = match fst.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (fst_ask, fst_pat, fst_rgt) = take(fst.rgt)?;
let with_arg = with_arg.into_iter().map(Expr::to_fun).collect();
fun_arms.push((fst.lft, vec![], fst_rgt));
for arm in arms {
let (arm_pat, arm_rgt) = match arm.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (arm_ask, arm_pat, arm_rgt) = take(arm.rgt)?;
match (&arm_pat, &fst_pat) {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => {
return Err("'match' arms end with different assignments.".to_string());
}
(Some(_), None) => {
@ -246,26 +228,20 @@ impl Stmt {
}
}
let term = fun::Term::Mat { arg: Box::new(arg), bnd, with_bnd, with_arg, arms: fun_arms };
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
wrap_nxt_assign_stmt(term, nxt, fst_pat, fst_ask)?
}
Stmt::Switch { arg, bnd, with_bnd, with_arg, arms, nxt } => {
let arg = arg.to_fun();
let mut fun_arms = vec![];
let mut arms = arms.into_iter();
let fst = arms.next().unwrap();
let (fst_pat, fst) = match fst.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (fst_ask, fst_pat, fst) = take(fst)?;
let with_arg = with_arg.into_iter().map(Expr::to_fun).collect();
fun_arms.push(fst);
for arm in arms {
let (arm_pat, arm) = match arm.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (arm_ask, arm_pat, arm) = take(arm)?;
match (&arm_pat, &fst_pat) {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => {
return Err("'switch' arms end with different assignments.".to_string());
}
(Some(_), None) => {
@ -280,26 +256,20 @@ impl Stmt {
}
let pred = Some(Name::new(format!("{}-{}", bnd.clone().unwrap(), fun_arms.len() - 1)));
let term = fun::Term::Swt { arg: Box::new(arg), bnd, with_bnd, with_arg, pred, arms: fun_arms };
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
wrap_nxt_assign_stmt(term, nxt, fst_pat, fst_ask)?
}
Stmt::Fold { arg, bnd, with_bnd, with_arg, arms, nxt } => {
let arg = arg.to_fun();
let mut fun_arms = vec![];
let mut arms = arms.into_iter();
let fst = arms.next().unwrap();
let (fst_pat, fst_rgt) = match fst.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (fst_ask, fst_pat, fst_rgt) = take(fst.rgt)?;
fun_arms.push((fst.lft, vec![], fst_rgt));
let with_arg = with_arg.into_iter().map(Expr::to_fun).collect();
for arm in arms {
let (arm_pat, arm_rgt) = match arm.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (arm_ask, arm_pat, arm_rgt) = take(arm.rgt)?;
match (&arm_pat, &fst_pat) {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => {
return Err("'fold' arms end with different assignments.".to_string());
}
(Some(_), None) => {
@ -313,14 +283,16 @@ impl Stmt {
}
}
let term = fun::Term::Fold { arg: Box::new(arg), bnd, with_bnd, with_arg, arms: fun_arms };
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
wrap_nxt_assign_stmt(term, nxt, fst_pat, fst_ask)?
}
Stmt::Bend { bnd, arg, cond, step, base, nxt } => {
let arg = arg.into_iter().map(Expr::to_fun).collect();
let cond = cond.to_fun();
let (pat, step, base) = match (step.into_fun()?, base.into_fun()?) {
(StmtToFun::Return(s), StmtToFun::Return(b)) => (None, s, b),
(StmtToFun::Assign(sp, s), StmtToFun::Assign(bp, b)) if sp == bp => (Some(sp), s, b),
let (ask, pat, step, base) = match (step.into_fun()?, base.into_fun()?) {
(StmtToFun::Return(s), StmtToFun::Return(b)) => (false, None, s, b),
(StmtToFun::Assign(aa, sp, s), StmtToFun::Assign(ba, bp, b)) if sp == bp => {
(aa && ba, Some(sp), s, b)
}
(StmtToFun::Assign(..), StmtToFun::Assign(..)) => {
return Err("'bend' branches end with different assignments.".to_string());
}
@ -337,66 +309,40 @@ impl Stmt {
};
let term =
fun::Term::Bend { bnd, arg, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) };
wrap_nxt_assign_stmt(term, nxt, pat)?
wrap_nxt_assign_stmt(term, nxt, pat, ask)?
}
Stmt::With { typ, bod, nxt } => {
let (pat, bod) = match bod.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, pat, bod) = take(*bod)?;
let term = fun::Term::With { typ, bod: Box::new(bod) };
wrap_nxt_assign_stmt(term, nxt, pat)?
wrap_nxt_assign_stmt(term, nxt, pat, ask)?
}
Stmt::Ask { pat, val, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
Stmt::Ask { pat, val, nxt: Some(nxt) } => {
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term =
fun::Term::Ask { pat: Box::new(pat.into_fun()), val: Box::new(val.to_fun()), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Ask { pat, val, nxt: None } => {
let pat = pat.into_fun();
let val = val.to_fun();
StmtToFun::Assign(true, pat, val)
}
Stmt::Open { typ, var, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Open { typ, var, bod: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Use { nam, val, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Use { nam: Some(nam), val: Box::new(val.to_fun()), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Return { term } => StmtToFun::Return(term.to_fun()),
Stmt::LocalDef { def, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let def = def.to_fun()?;
let term = fun::Term::Def { def, nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Err => unreachable!(),
};
@ -508,24 +454,22 @@ fn wrap_nxt_assign_stmt(
term: fun::Term,
nxt: Option<Box<Stmt>>,
pat: Option<fun::Pattern>,
ask: bool,
) -> Result<StmtToFun, String> {
if let Some(nxt) = nxt {
if let Some(pat) = pat {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let term = fun::Term::Let { pat: Box::new(pat), val: Box::new(term), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
Ok(StmtToFun::Assign(pat, term))
let (ask_nxt, nxt_pat, nxt) = take(*nxt)?;
let term = if ask {
fun::Term::Ask { pat: Box::new(pat), val: Box::new(term), nxt: Box::new(nxt) }
} else {
Ok(StmtToFun::Return(term))
}
fun::Term::Let { pat: Box::new(pat), val: Box::new(term), nxt: Box::new(nxt) }
};
Ok(wrap(nxt_pat, term, ask_nxt))
} else {
Err("Statement ends with return but is not at end of function.".to_string())
}
} else if let Some(pat) = pat {
Ok(StmtToFun::Assign(pat, term))
Ok(StmtToFun::Assign(ask, pat, term))
} else {
Ok(StmtToFun::Return(term))
}

View File

@ -431,7 +431,9 @@ impl Def for imp::Definition {
}
}
Stmt::Ask { nxt, .. } => {
rename_with_type(nxt, types);
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Return { .. } => {}
Stmt::Open { nxt, .. } => {

View File

@ -0,0 +1,12 @@
type Bool:
T
F
def main:
with IO:
match _ = Bool/T:
case Bool/T:
x <- wrap(0)
case Bool/F:
x = wrap(0)
return wrap(x)

View File

@ -0,0 +1,12 @@
type Bool:
T
F
def main:
with IO:
match _ = Bool/T:
case Bool/T:
x <- wrap(0)
case Bool/F:
x <- wrap(0)
return wrap(x)

View File

@ -0,0 +1,6 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/mismatched_ask_statements.bend
---
Errors:
In function 'main': 'match' arms end with different assignments.

View File

@ -0,0 +1,35 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/desugar_file/ask_branch.bend
---
(undefer) = λa (a λb b)
(IO/MAGIC) = (13683217, 16719857)
(IO/wrap) = λa (IO/Done IO/MAGIC a)
(IO/bind) = λa λb (a IO/bind__C2 b)
(main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc λd (c d) IO/wrap))
(IO/Done) = λa λb λc (c IO/Done/tag a b)
(IO/Call) = λa λb λc λd λe (e IO/Call/tag a b c d)
(Bool/T) = λa (a Bool/T/tag)
(Bool/F) = λa (a Bool/F/tag)
(IO/Done/tag) = 0
(IO/Call/tag) = 1
(Bool/T/tag) = 0
(Bool/F/tag) = 1
(IO/bind__C0) = λ* λa λb (undefer b a)
(IO/bind__C1) = λ* λ* λa λb λc λd (IO/Call IO/MAGIC a b λe (IO/bind (c e) d))
(IO/bind__C2) = λa switch a { 0: IO/bind__C0; _: IO/bind__C1; }