Repository: HigherOrderCO/Bend
File: src/term/transform/flatten.rs (Rust)

Code before:
    }
  }
  let new_body = Term::call(Term::Ref { def_id: new_split_def_id }, new_body_args);
  Rule { pats: new_pats, body: new_body }
}

/// Makes one of the new rules, flattening one layer of the original pattern.
fn make_split_rule(old_rule: &Rule, other_rule: &Rule, def_names: &DefNames) -> Rule {
  // (Foo a     (B x P) (C y0 y1)) = F
  // (Foo (A k) (B x Q) y        ) = G
  // -----------------------------
  // (Foo a (B x u) (C y0 y1)) = (Foo.0 a x u y0 y1)
  //   (Foo.0 a     x P y0 y1) = F
  //   (Foo.0 (A k) x Q f0 f1) = G [y <- (C f0 f1)] // f0 and f1 are fresh
  let mut new_pats = Vec::new();
  let mut new_body = other_rule.body.clone();
  let mut var_count = 0;
  for (rule_arg, other_arg) in old_rule.pats.iter().zip(&other_rule.pats) {
    match (rule_arg, other_arg) {
      // We checked before that these two have the same constructor and match together
      (Pattern::Ctr(_, _), Pattern::Ctr(_, other_arg_args)) => {
        for other_field in other_arg_args {
          new_pats.push(other_field.clone());
        }
      }
      (Pattern::Ctr(..), Pattern::Var(None)) => new_pats.push(Pattern::Var(None)),
      (Pattern::Ctr(rule_arg_name, rule_arg_args), Pattern::Var(Some(other_arg))) => {
        let mut new_ctr_args = vec![];
        for _ in 0 .. rule_arg_args.len() {
          let new_nam = make_var_name(&mut var_count);
          new_ctr_args.push(Term::Var { nam: new_nam.clone() });
          new_pats.push(Pattern::Var(Some(new_nam)));
        }
        let rule_arg_def_id = def_names.def_id(rule_arg_name).unwrap();
        let new_ctr = Term::call(Term::Ref { def_id: rule_arg_def_id }, new_ctr_args);
        new_body.subst(other_arg, &new_ctr);
      }
      (Pattern::Num(..), Pattern::Num(..)) => new_pats.push(other_arg.clone()),
      (Pattern::Num(..), Pattern::Var(..)) => {
        // How to do this with this kind of number pattern? Subst with a match?
        todo!();
      }
      (Pattern::Tup(_, _), Pattern::Tup(fst, snd)) => {
        new_pats.push(*fst.clone());
        new_pats.push(*snd.clone());
      }
      (Pattern::Tup(..), Pattern::Var(Some(other_arg))) => {
        let fst_nam = make_var_name(&mut var_count);
        let snd_nam = make_var_name(&mut var_count);
        let fst_arg = Term::Var { nam: fst_nam.clone() };
        let snd_arg = Term::Var { nam: snd_nam.clone() };

Review comment (suggestion):
We need to also implement `(Pattern::Tup(..), Pattern::Var(None))`, `(Pattern::Num(..), Pattern::Var(None))`, etc, right?

And remove the unreachable on the bottom of the match

Code after:
    }
  }
  let new_body = Term::call(Term::Ref { def_id: new_split_def_id }, new_body_args);
  Rule { pats: new_pats, body: new_body }
}

/// Makes one of the new rules, flattening one layer of the original pattern.
fn make_split_rule(old_rule: &Rule, other_rule: &Rule, def_names: &DefNames) -> Rule {
  // (Foo a     (B x P) (C y0 y1)) = F
  // (Foo (A k) (B x Q) y        ) = G
  // -----------------------------
  // (Foo a (B x u) (C y0 y1)) = (Foo.0 a x u y0 y1)
  //   (Foo.0 a     x P y0 y1) = F
  //   (Foo.0 (A k) x Q f0 f1) = G [y <- (C f0 f1)] // f0 and f1 are fresh
  let mut new_pats = Vec::new();
  let mut new_body = other_rule.body.clone();
  let mut var_count = 0;
  for (rule_arg, other_arg) in old_rule.pats.iter().zip(&other_rule.pats) {
    match (rule_arg, other_arg) {
      // We checked before that these two have the same constructor and match together
      (Pattern::Ctr(_, _), Pattern::Ctr(_, other_arg_args)) => {
        for other_field in other_arg_args {
          new_pats.push(other_field.clone());
        }
      }
      (Pattern::Ctr(rule_arg_name, rule_arg_args), Pattern::Var(Some(other_arg))) => {
        let mut new_ctr_args = vec![];
        for _ in 0 .. rule_arg_args.len() {
          let new_nam = make_var_name(&mut var_count);
          new_ctr_args.push(Term::Var { nam: new_nam.clone() });
          new_pats.push(Pattern::Var(Some(new_nam)));
        }
        let rule_arg_def_id = def_names.def_id(rule_arg_name).unwrap();
        let new_ctr = Term::call(Term::Ref { def_id: rule_arg_def_id }, new_ctr_args);
        new_body.subst(other_arg, &new_ctr);
      }
      // Since numbers don't have subpatterns this should be unreachable.
      (Pattern::Num(..), _) => unreachable!(),
      (Pattern::Tup(_, _), Pattern::Tup(fst, snd)) => {
        new_pats.push(*fst.clone());
        new_pats.push(*snd.clone());
      }
      (Pattern::Tup(..), Pattern::Var(Some(other_arg))) => {
        let fst_nam = make_var_name(&mut var_count);
        let snd_nam = make_var_name(&mut var_count);
        let fst_arg = Term::Var { nam: fst_nam.clone() };
        let snd_arg = Term::Var { nam: snd_nam.clone() };
        new_pats.push(Pattern::Var(Some(fst_nam)));
        new_pats.push(Pattern::Var(Some(snd_nam)));
        let new_ctr = Term::Tup { fst: Box::new(fst_arg), snd: Box::new(snd_arg) };
        new_body.subst(other_arg, &new_ctr);