ronantakizawa/codereview-bench huggingface dataset:
Repository: HigherOrderCO/Bend
File: src/term/check/unbound_vars.rs (Rust)

Code before:
use crate::term::{DefinitionBook, Name, Term};
use hvmc::run::Val;
use std::collections::HashMap;

impl DefinitionBook {
  pub fn check_unbound_vars(&self) -> anyhow::Result<()> {
    for (_, def) in &self.defs {
      def.body.check_unbound_vars()?;
    }
    Ok(())
  }
}

impl Term {
  /// Checks that all variables are bound.
  /// Precondition: References have been resolved.
  pub fn check_unbound_vars(&self) -> anyhow::Result<()> {
    let mut globals = HashMap::new();
    check_uses(self, &mut HashMap::new(), &mut globals)?;

    // Check global vars
    for (nam, (declared, used)) in globals.into_iter() {
      if used && !declared {
        return Err(anyhow::anyhow!("Unbound unscoped variable '${nam}'"));
      }
    }
    Ok(())
  }
}

/// Scope has the number of times a name was declared in the current scope
/// Globals has how many times a global var name was declared and used.

Review comment (suggestion):
You can do `for def in self.defs.values()` to iterate here. Same goes for the other identical `for`s.

Code after:
use crate::term::{DefinitionBook, Name, Term};
use hvmc::run::Val;
use std::collections::HashMap;

impl DefinitionBook {
  pub fn check_unbound_vars(&self) -> anyhow::Result<()> {
    for def in self.defs.values() {
      def.body.check_unbound_vars()?;
    }
    Ok(())
  }
}

impl Term {
  /// Checks that all variables are bound.
  /// Precondition: References have been resolved.
  pub fn check_unbound_vars(&self) -> anyhow::Result<()> {
    let mut globals = HashMap::new();
    check_uses(self, &mut HashMap::new(), &mut globals)?;

    // Check global vars
    for (nam, (declared, used)) in globals.into_iter() {
      if used && !declared {
        return Err(anyhow::anyhow!("Unbound unscoped variable '${nam}'"));
      }
    }
    Ok(())
  }
}

/// Scope has the number of times a name was declared in the current scope
/// Globals has how many times a global var name was declared and used.
Repository: HigherOrderCO/Bend
File: src/term/net_to_term.rs (Rust)

Code before:
        fun.fix_names(id_counter, book);
        arg.fix_names(id_counter, book);
      }
      Term::Match { cond, zero, succ } => {
        cond.fix_names(id_counter, book);
        zero.fix_names(id_counter, book);
        succ.fix_names(id_counter, book);
      }
      Term::Dup { fst, snd, val, nxt } => {
        val.fix_names(id_counter, book);
        fix_name(fst, id_counter, nxt);
        fix_name(snd, id_counter, nxt);
        nxt.fix_names(id_counter, book);
      }
      Term::Sup { fst, snd } => {
        fst.fix_names(id_counter, book);
        snd.fix_names(id_counter, book);
      }
      Term::Era => {}
      Term::Num { .. } => {}
      Term::Opx { op: _, fst, snd } => {
        fst.fix_names(id_counter, book);
        snd.fix_names(id_counter, book);
      }
      Term::Let { .. } => unreachable!(),
      Term::Tup { .. } => todo!(),
    }
  }
}


Review comment (bug):
This needs to be done, should be identical to Sup one. I don't understand how it's not crashing by leaving this as a todo!(). If it's actualyl unreadcheable then put that instead. Also, maybe have a look if this is disabled for some reason?

Code after:
        fun.fix_names(id_counter, book);
        arg.fix_names(id_counter, book);
      }
      Term::Match { cond, zero, succ } => {
        cond.fix_names(id_counter, book);
        zero.fix_names(id_counter, book);
        succ.fix_names(id_counter, book);
      }
      Term::Dup { fst, snd, val, nxt } => {
        val.fix_names(id_counter, book);
        fix_name(fst, id_counter, nxt);
        fix_name(snd, id_counter, nxt);
        nxt.fix_names(id_counter, book);
      }
      Term::Sup { fst, snd } => {
        fst.fix_names(id_counter, book);
        snd.fix_names(id_counter, book);
      }
      Term::Era => {}
      Term::Num { .. } => {}
      Term::Opx { op: _, fst, snd } => {
        fst.fix_names(id_counter, book);
        snd.fix_names(id_counter, book);
      }
      Term::Let { .. } => unreachable!(),
      Term::Tup { fst, snd } => {
        fst.fix_names(id_counter, book);
        snd.fix_names(id_counter, book);
      }
    }
  }
}
Repository: HigherOrderCO/Bend
File: src/term/term_to_net.rs (Rust)

Code before:
use super::{DefId, DefinitionBook, Name, Op, Term};
use crate::net::{INet, NodeId, NodeKind::*, Port, LABEL_MASK, ROOT};
use bimap::BiHashMap;
use hvmc::{
  ast::{name_to_val, val_to_name},
  run::Val,
};
use std::collections::HashMap;

pub fn book_to_compact_nets(
  book: &DefinitionBook,
  main: DefId,
) -> anyhow::Result<(HashMap<String, INet>, BiHashMap<DefId, Val>)> {
  let mut nets = HashMap::new();
  let mut id_map = BiHashMap::new();

  for def in book.defs.values() {
    let net = term_to_compat_net(&def.body)?;
    let name = if def.def_id == main { "main".to_string() } else { def_id_to_name(book, def.def_id, &nets) };

    id_map.insert(def.def_id, name_to_val(&name));
    nets.insert(name, net);
  }

  Ok((nets, id_map))
}

pub fn def_id_to_name(book: &DefinitionBook, def_id: DefId, nets: &HashMap<String, INet>) -> String {
  fn truncate(s: &str, max_chars: usize) -> &str {
    match s.char_indices().nth(max_chars) {
      None => s,
      Some((idx, _)) => &s[.. idx],
    }
  }


Review comment (suggestion):
If you could change the name of this function to `book_to_nets` (also it's misspelled as compact, lol)

Code after:
use super::{DefId, DefinitionBook, Name, Op, Term};
use crate::net::{INet, NodeId, NodeKind::*, Port, LABEL_MASK, ROOT};
use hvmc::{
  ast::{name_to_val, val_to_name},
  run::Val,
};
use std::collections::HashMap;

pub fn book_to_nets(
  book: &DefinitionBook,
  main: DefId,
) -> anyhow::Result<(HashMap<String, INet>, HashMap<DefId, Val>)> {
  let mut nets = HashMap::new();
  let mut id_to_hvmc_name = HashMap::new();

  for def in book.defs.values() {
    let net = term_to_compat_net(&def.body)?;
    let name =
      if def.def_id == main { "main".to_string() } else { def_id_to_hvmc_name(book, def.def_id, &nets) };

    id_to_hvmc_name.insert(def.def_id, name_to_val(&name));
    nets.insert(name, net);
  }

  Ok((nets, id_to_hvmc_name))
}

/// Converts rules names to unique names compatible with hvm-core:
///   If the rule is compiler-generated: Convert the DefId value into a new name
///   If not: Truncates the rule name into 4 chars
/// Them checks if the given hashmap already contains the resulted name,
/// if it does, falls back into converting its DefId and succeeding ones until a unique name is found.
fn def_id_to_hvmc_name(book: &DefinitionBook, def_id: DefId, nets: &HashMap<String, INet>) -> String {
  fn truncate(s: &str, max_chars: usize) -> &str {
    match s.char_indices().nth(max_chars) {
Repository: HigherOrderCO/Bend
File: src/term/transform/scott_generation.rs (Rust)

Code before:
        ctrs.clear();

        let def_id = self.def_names.insert(ctr_name.clone());

        let rule = Rule { def_id, pats: vec![], body: lam };
        let rules = vec![rule];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize, ctr_args: &mut Vec<Name>) {
  for n in 0 .. *args_count {
    let nam = Name(format!("a{n}"));
    ctr_args.push(nam);
  }
}

fn make_lam(ctr_args: &Vec<Name>, ctrs: &mut Vec<Name>, ctr_name: &Name) -> Term {
  let args_to_apply = ctr_args.clone();
  ctrs.reverse();

  let mut lam_args = ctrs.clone();
  let mut lam_args_ctrs = ctr_args.clone();
  lam_args_ctrs.reverse();
  lam_args.extend(lam_args_ctrs);

  let app = Term::Var { nam: ctr_name.clone() };

  let app = args_to_apply
    .into_iter()
    .fold(app, |acc, nam| Term::App { fun: Box::new(acc), arg: Box::new(Term::Var { nam }) });

  let lam = app;

  lam_args.into_iter().fold(lam, |acc, arg| Term::Lam { nam: Some(arg), bod: Box::new(acc) })
}

#[cfg(test)]
mod test {
  use crate::term::parser::parse_definition_book;

  #[test]
  // what should we test here?
  fn adt_generation() {
    let code = r"
    data List = (Cons x xs) | Nil
    data Bool = True | False

Review comment (suggestion):
Instead of cloning into another variable, why not iterate, clone each element and then fold? I think the code would be a bit cleaner:
```
let term = Term::Var {...};
let term = ctr_args.iter().cloned().fold(term, ...);
let term = ctrs.iter().rev().cloned().fold(term, ...);
let term = ctr_args.iter().cloned().fold(term, ...);
```

Code after:
use crate::term::{Book, Definition, Name, Rule, Term};

impl Book {
  pub fn generate_scott_adts(&mut self) {
    for adt in self.adts.values() {
      let curr_ctrs = adt.ctrs.clone();

      for (ctr_name, arity) in &curr_ctrs {
        let ctrs = curr_ctrs.keys().cloned().collect();
        let ctr_args = make_args(arity);

        let lam = make_lam(ctr_args, ctrs, ctr_name);

        let def_id = self.def_names.insert(ctr_name.clone());

        let rules = vec![Rule { def_id, pats: vec![], body: lam }];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize) -> Vec<Name> {
  let to_nam = |n| Name(format!("a{n}"));
  (0 .. *args_count).map(to_nam).collect()
}

fn make_lam(ctr_args: Vec<Name>, ctrs: Vec<Name>, ctr_name: &Name) -> Term {
  let ctr = Term::Var { nam: ctr_name.clone() };

  let app = ctr_args
    .iter()
    .cloned()
    .fold(ctr, |acc, nam| Term::App { fun: Box::new(acc), arg: Box::new(Term::Var { nam }) });

  let fold_lam = |acc, arg| Term::Lam { nam: Some(arg), bod: Box::new(acc) };

  let lam = ctrs.iter().rev().cloned().fold(app, fold_lam);

  ctr_args.iter().rev().cloned().fold(lam, fold_lam)
}
Repository: HigherOrderCO/Bend
File: src/term/transform/scott_generation.rs (Rust)

Code before:
use crate::term::{Book, Definition, Name, Rule, Term};

impl Book {
  pub fn generate_scott_adts(&mut self) {
    for adt in self.adts.values() {
      let curr_ctrs = adt.ctrs.clone();

      let mut ctr_args = Vec::new();
      let mut ctrs = Vec::new();

      for (ctr_name, arity) in &curr_ctrs {
        for ctr in curr_ctrs.keys() {
          ctrs.push(ctr.clone());
          if ctr == ctr_name {
            make_args(arity, &mut ctr_args);
          }
        }

        let lam = make_lam(&ctr_args, &mut ctrs, ctr_name);

        ctr_args.clear();
        ctrs.clear();

        let def_id = self.def_names.insert(ctr_name.clone());

        let rule = Rule { def_id, pats: vec![], body: lam };
        let rules = vec![rule];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize, ctr_args: &mut Vec<Name>) {
  for n in 0 .. *args_count {
    let nam = Name(format!("a{n}"));
    ctr_args.push(nam);
  }
}

fn make_lam(ctr_args: &Vec<Name>, ctrs: &mut Vec<Name>, ctr_name: &Name) -> Term {
  let args_to_apply = ctr_args.clone();
  ctrs.reverse();

  let mut lam_args = ctrs.clone();
  let mut lam_args_ctrs = ctr_args.clone();
  lam_args_ctrs.reverse();
  lam_args.extend(lam_args_ctrs);


Review comment (suggestion):
Consider inlining these two variables `rule` and `rules`. They are pretty straight forward and I don't think we gain by separating these lines.

Code after:
use crate::term::{Book, Definition, Name, Rule, Term};

impl Book {
  pub fn generate_scott_adts(&mut self) {
    for adt in self.adts.values() {
      let curr_ctrs = adt.ctrs.clone();

      for (ctr_name, arity) in &curr_ctrs {
        let ctrs = curr_ctrs.keys().cloned().collect();
        let ctr_args = make_args(arity);

        let lam = make_lam(ctr_args, ctrs, ctr_name);

        let def_id = self.def_names.insert(ctr_name.clone());

        let rules = vec![Rule { def_id, pats: vec![], body: lam }];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize) -> Vec<Name> {
  let to_nam = |n| Name(format!("a{n}"));
  (0 .. *args_count).map(to_nam).collect()
}

fn make_lam(ctr_args: Vec<Name>, ctrs: Vec<Name>, ctr_name: &Name) -> Term {
  let ctr = Term::Var { nam: ctr_name.clone() };

  let app = ctr_args
    .iter()
    .cloned()
    .fold(ctr, |acc, nam| Term::App { fun: Box::new(acc), arg: Box::new(Term::Var { nam }) });

  let fold_lam = |acc, arg| Term::Lam { nam: Some(arg), bod: Box::new(acc) };

  let lam = ctrs.iter().rev().cloned().fold(app, fold_lam);

  ctr_args.iter().rev().cloned().fold(lam, fold_lam)
}
Repository: HigherOrderCO/Bend
File: src/term/transform/scott_generation.rs (Rust)

Code before:
            make_args(arity, &mut ctr_args);
          }
        }

        let lam = make_lam(ctr_args, ctrs, ctr_name);

        let def_id = self.def_names.insert(ctr_name.clone());

        let rules = vec![Rule { def_id, pats: vec![], body: lam }];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize, ctr_args: &mut Vec<Name>) {
  for n in 0 .. *args_count {
    let nam = Name(format!("a{n}"));
    ctr_args.push(nam);
  }
}

fn make_lam(ctr_args: Vec<Name>, ctrs: Vec<Name>, ctr_name: &Name) -> Term {
  fn fold_lam(acc: Term, arg: Name) -> Term {
    Term::Lam { nam: Some(arg), bod: Box::new(acc) }
  }

  let ctr = Term::Var { nam: ctr_name.clone() };

  let app = ctr_args
    .iter()
    .cloned()
    .fold(ctr, |acc, nam| Term::App { fun: Box::new(acc), arg: Box::new(Term::Var { nam }) });

  let lam = ctrs.iter().rev().cloned().fold(app, fold_lam);

  ctr_args.iter().rev().cloned().fold(lam, fold_lam)
}

#[cfg(test)]
mod test {
  use crate::term::parser::parse_definition_book;

  #[test]
  // what should we test here?
  fn adt_generation() {
    let code = r"
    data List = (Cons x xs) | Nil
    data Bool = True | False

Review comment (suggestion):
Can this be just a closure before its used?

```
let fold_lam = |acc, arg|  Term::Lam { nam: Some(arg), bod: Box::new(acc) };
let lam = ctrs.iter().rev().cloned().fold(app, fold_lam);
ctr_args.iter().rev().cloned().fold(lam, fold_lam)
```

Code after:

        let rules = vec![Rule { def_id, pats: vec![], body: lam }];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize) -> Vec<Name> {
  let to_nam = |n| Name(format!("a{n}"));
  (0 .. *args_count).map(to_nam).collect()
}

fn make_lam(ctr_args: Vec<Name>, ctrs: Vec<Name>, ctr_name: &Name) -> Term {
  let ctr = Term::Var { nam: ctr_name.clone() };

  let app = ctr_args
    .iter()
    .cloned()
    .fold(ctr, |acc, nam| Term::App { fun: Box::new(acc), arg: Box::new(Term::Var { nam }) });

  let fold_lam = |acc, arg| Term::Lam { nam: Some(arg), bod: Box::new(acc) };

  let lam = ctrs.iter().rev().cloned().fold(app, fold_lam);

  ctr_args.iter().rev().cloned().fold(lam, fold_lam)
}
Repository: HigherOrderCO/Bend
File: src/term/transform/scott_generation.rs (Rust)

Code before:
use crate::term::{Book, Definition, Name, Rule, Term};

impl Book {
  pub fn generate_scott_adts(&mut self) {
    for adt in self.adts.values() {
      let curr_ctrs = adt.ctrs.clone();

      for (ctr_name, arity) in &curr_ctrs {
        let mut ctr_args = Vec::new();
        let ctrs = curr_ctrs.keys().cloned().collect();
        make_args(arity, &mut ctr_args);

        let lam = make_lam(ctr_args, ctrs, ctr_name);

        let def_id = self.def_names.insert(ctr_name.clone());

        let rules = vec![Rule { def_id, pats: vec![], body: lam }];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize, ctr_args: &mut Vec<Name>) {
  for n in 0 .. *args_count {
    let nam = Name(format!("a{n}"));
    ctr_args.push(nam);
  }
}

fn make_lam(ctr_args: Vec<Name>, ctrs: Vec<Name>, ctr_name: &Name) -> Term {
  let ctr = Term::Var { nam: ctr_name.clone() };

  let app = ctr_args

Review comment (suggestion):
You could make this function return `ctr_args` instead for a cleaner interface.

`let ctr_args = make_args(arity);`

Code after:
use crate::term::{Book, Definition, Name, Rule, Term};

impl Book {
  pub fn generate_scott_adts(&mut self) {
    for adt in self.adts.values() {
      let curr_ctrs = adt.ctrs.clone();

      for (ctr_name, arity) in &curr_ctrs {
        let ctrs = curr_ctrs.keys().cloned().collect();
        let ctr_args = make_args(arity);

        let lam = make_lam(ctr_args, ctrs, ctr_name);

        let def_id = self.def_names.insert(ctr_name.clone());

        let rules = vec![Rule { def_id, pats: vec![], body: lam }];
        let def = Definition { def_id, rules };

        self.defs.insert(def_id, def);
      }
    }
  }
}

fn make_args(args_count: &usize) -> Vec<Name> {
  let to_nam = |n| Name(format!("a{n}"));
  (0 .. *args_count).map(to_nam).collect()
}

fn make_lam(ctr_args: Vec<Name>, ctrs: Vec<Name>, ctr_name: &Name) -> Term {
  let ctr = Term::Var { nam: ctr_name.clone() };

  let app = ctr_args
    .iter()
    .cloned()
    .fold(ctr, |acc, nam| Term::App { fun: Box::new(acc), arg: Box::new(Term::Var { nam }) });
Repository: HigherOrderCO/Bend
File: src/term/mod.rs (Rust)

Code before:
  /// The use of a Channel variable.
  Lnk {
    nam: Name,
  },
  Let {
    pat: LetPat,
    val: Box<Term>,
    nxt: Box<Term>,
  },
  App {
    fun: Box<Term>,
    arg: Box<Term>,
  },
  Tup {
    fst: Box<Term>,
    snd: Box<Term>,
  },
  Dup {
    tag: Option<Name>,
    fst: Option<Name>,
    snd: Option<Name>,
    val: Box<Term>,
    nxt: Box<Term>,
  },
  Sup {
    tag: Option<Name>,
    fst: Box<Term>,
    snd: Box<Term>,
  },
  Num {
    val: u32,
  },
  /// A numeric operation between built-in numbers.
  Opx {
    op: Op,
    fst: Box<Term>,
    snd: Box<Term>,
  },
  Match {
    scrutinee: Box<Term>,
    arms: Vec<(RulePat, Term)>,
  },
  Ref {
    def_id: DefId,
  },
  #[default]
  Era,
}

#[derive(Debug, Clone)]
pub enum LetPat {

Review comment (suggestion):
Rethinking this, we should probably make sup tags obligatory.
* If each dup and sup is given an unique label, the user-provided sups will never be annihilated.
* If dup and sup labels can overlap and are generated independently, we get behaviour that depends on other functions and that the user has no control. Depending on arbitrary decisions of the compiler, a sup may interact with dups that have equal or different labels, leading to different results.

So, to me the only sensible option is to force sups to have explicit labels

Code after:
  /// The use of a Channel variable.
  Lnk {
    nam: Name,
  },
  Let {
    pat: LetPat,
    val: Box<Term>,
    nxt: Box<Term>,
  },
  App {
    fun: Box<Term>,
    arg: Box<Term>,
  },
  Tup {
    fst: Box<Term>,
    snd: Box<Term>,
  },
  Dup {
    tag: Option<Name>,
    fst: Option<Name>,
    snd: Option<Name>,
    val: Box<Term>,
    nxt: Box<Term>,
  },
  Sup {
    tag: Name,
    fst: Box<Term>,
    snd: Box<Term>,
  },
  Num {
    val: u32,
  },
  /// A numeric operation between built-in numbers.
  Opx {
    op: Op,
    fst: Box<Term>,
    snd: Box<Term>,
  },
  Match {
    scrutinee: Box<Term>,
    arms: Vec<(RulePat, Term)>,
  },
  Ref {
    def_id: DefId,
  },
  #[default]
  Era,
}

#[derive(Debug, Clone)]
pub enum LetPat {
Repository: HigherOrderCO/Bend
File: src/term/transform/encode_pattern_matching.rs (Rust)

Code before:
use crate::term::{
  check::type_check::{DefinitionTypes, Type},
  Book, DefId, Name, Pattern, Rule, Term,
};

impl Book {
  pub fn encode_pattern_matching_functions(&mut self, def_types: &DefinitionTypes) {
    for def_id in self.defs.keys().copied().collect::<Vec<_>>() {
      let def_type = &def_types[&def_id];

      let is_matching_def = def_type.iter().any(|t| matches!(t, Type::Adt(_) | Type::Tup));
      if is_matching_def {
        make_pattern_matching_def(self, def_id, def_type);
      } else {
        // For functions with only one rule that doesnt pattern match,
        // we just move the variables from arg to body.
        make_non_pattern_matching_def(self, def_id);
      }
    }
  }
}

/// For functions that don't pattern match, just move the arg variables into the body.
fn make_non_pattern_matching_def(book: &mut Book, def_id: DefId) {
  let def = book.defs.get_mut(&def_id).unwrap();
  let rule = def.rules.first_mut().unwrap();
  for pat in rule.pats.iter().rev() {
    let Pattern::Var(var) = pat else { unreachable!() };
    let bod = std::mem::replace(&mut rule.body, Term::Era);
    rule.body = Term::Lam { nam: var.clone(), bod: Box::new(bod) };
  }
  rule.pats = vec![];
}

/// For function that do pattern match,
///  we break them into a tree of small matching functions

Review comment (suggestion):
You can add Type::Num as well, or !matches!(t, Type::Any) since all the other types will always be matching

Code after:
use crate::term::{
  check::type_check::{DefinitionTypes, Type},
  Book, DefId, Name, Pattern, Rule, Term,
};

impl Book {
  pub fn encode_pattern_matching_functions(&mut self, def_types: &DefinitionTypes) {
    for def_id in self.defs.keys().copied().collect::<Vec<_>>() {
      let def_type = &def_types[&def_id];

      let is_matching_def = def_type.iter().any(|t| matches!(t, Type::Adt(_) | Type::Tup | Type::Num));
      if is_matching_def {
        make_pattern_matching_def(self, def_id, def_type);
      } else {
        // For functions with only one rule that doesnt pattern match,
        // we just move the variables from arg to body.
        make_non_pattern_matching_def(self, def_id);
      }
    }
  }
}

/// For functions that don't pattern match, just move the arg variables into the body.
fn make_non_pattern_matching_def(book: &mut Book, def_id: DefId) {
  let def = book.defs.get_mut(&def_id).unwrap();
  let rule = def.rules.first_mut().unwrap();
  for pat in rule.pats.iter().rev() {
    let Pattern::Var(var) = pat else { unreachable!() };
    let bod = std::mem::replace(&mut rule.body, Term::Era);
    rule.body = Term::Lam { nam: var.clone(), bod: Box::new(bod) };
  }
  rule.pats = vec![];
}

/// For function that do pattern match,
///  we break them into a tree of small matching functions
Repository: HigherOrderCO/Bend
File: src/term/parser/lexer.rs (Rust)

Code before:
  Lambda,

  #[token("$")]
  Dollar,

  #[token("let")]
  Let,

  #[token("dup")]
  Dup,

  #[token("match")]
  Match,

  #[token("=")]
  Equals,

  #[regex("[0-9_]+", |lex| from_radix(10, lex).ok(), priority = 2)]
  #[regex("0[xX][0-9a-fA-F_]+", |lex| from_radix(16, lex).ok())]
  #[regex("0[bB][0-1_]+", |lex| from_radix(2, lex).ok())]
  Num(u64),

  #[regex(r#""([^"\\]|\\t|\\u|\\n|\\")*""#, |lex| normalized_string(lex).ok())]
  Str(String),

  #[regex(r#"'[\W|\w]'"#, num_of_char)]
  Char(u64),

  #[token("#")]
  Hash,

  #[token("+")]
  Add,

  #[token("-")]
  Sub,

  #[token("*")]
  Asterisk,

  #[token("/")]
  Div,

  #[token("%")]
  Mod,

  #[token("~")]
  Tilde,

  #[token("&")]
  And,

Review comment (suggestion):
```suggestion
  #[regex(r#"'.'"#, num_of_char)]
```

Code after:
  Lambda,

  #[token("$")]
  Dollar,

  #[token("let")]
  Let,

  #[token("dup")]
  Dup,

  #[token("match")]
  Match,

  #[token("=")]
  Equals,

  #[regex("[0-9_]+", |lex| from_radix(10, lex).ok(), priority = 2)]
  #[regex("0[xX][0-9a-fA-F_]+", |lex| from_radix(16, lex).ok())]
  #[regex("0[bB][0-1_]+", |lex| from_radix(2, lex).ok())]
  Num(u64),

  #[regex(r#""([^"\\]|\\t|\\u|\\n|\\")*""#, |lex| normalized_string(lex).ok())]
  Str(String),

  #[regex(r#"'(.|\\t|\\u[0-9a-fA-F]{4}|\\n|\\')'"#, normalized_char)]
  Char(u64),

  #[token("#")]
  Hash,

  #[token("+")]
  Add,

  #[token("-")]
  Sub,

  #[token("*")]
  Asterisk,

  #[token("/")]
  Div,

  #[token("%")]
  Mod,

  #[token("~")]
  Tilde,

  #[token("&")]
  And,