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

Code before:
      // This pass must be done before.
      debug_assert!(
        scope.contains_key(nam),
        "Unbound variable {nam}. Expected this check to be already done"
      );
      let var_stack = scope.get(nam).unwrap();
      let crnt_var = *var_stack.last().unwrap();
      let (declare_port, use_port) = vars.get_mut(crnt_var).unwrap();
      debug_assert!(use_port.is_none(), "Variable {nam} used more than once");
      inet.link(up, *declare_port);
      *use_port = Some(up);
      Ok(Some(*declare_port))
    }
    Term::Lnk { nam } => {
      global_vars.entry(nam.clone()).or_default().1 = up;
      Ok(None)
    }
    // core: @def_id
    Term::Ref { def_id } => {
      let node = inet.new_node(Ref { def_id: *def_id });
      inet.link(Port(node, 1), Port(node, 2));
      inet.link(up, Port(node, 0));
      Ok(Some(Port(node, 0)))
    }
    Term::Let { pat: LetPat::Tup(l_nam, r_nam), val, nxt } => {
      let dup = inet.new_node(Dup { lab: u8::try_from(*dups).unwrap() });
      *dups += 1;
      let val = encode_term(inet, val, Port(dup, 0), scope, vars, global_vars, dups)?;
      link_local(inet, Port(dup, 0), val);

      push_scope(l_nam, Port(dup, 1), scope, vars);
      push_scope(r_nam, Port(dup, 2), scope, vars);
      let nxt = encode_term(inet, nxt, up, scope, vars, global_vars, dups)?;
      pop_scope(r_nam, Port(dup, 2), inet, scope);
      pop_scope(l_nam, Port(dup, 1), inet, scope);

      Ok(nxt)
    }
    Term::Let { .. } => unreachable!(), // Removed in earlier poss
    Term::Sup { .. } => unreachable!(), // Not supported in syntax
    Term::Era => unreachable!(),        // Not supported in syntax
    // core: #val
    Term::Num { val } => {
      debug_assert!(*val <= LABEL_MASK);
      let node = inet.new_node(Num { val: *val });
      // This representation only has nodes of arity 2, so we connect the two aux ports that are not used.
      inet.link(Port(node, 1), Port(node, 2));
      Ok(Some(Port(node, 0)))
    }
    // core: & #op ~ <fst <snd ret>>
    Term::Opx { op, fst, snd } => {

Review comment (suggestion):
I think it would be cleaner to have a new `NodeKind::Tup` instead of using Dup, even though they have the same behaviour. 

Code after:
      let fun = encode_term(inet, fun, Port(app, 0), scope, vars, global_vars, dups)?;
      link_local(inet, Port(app, 0), fun);
      let arg = encode_term(inet, arg, Port(app, 1), scope, vars, global_vars, dups)?;
      link_local(inet, Port(app, 1), arg);
      Ok(Some(Port(app, 2)))
    }
    // core: & cond ~ ? (zero succ) ret
    Term::Match { cond, zero, succ } => {
      let if_ = inet.new_node(Mat);

      let cond = encode_term(inet, cond, Port(if_, 0), scope, vars, global_vars, dups)?;
      link_local(inet, Port(if_, 0), cond);

      let sel = inet.new_node(Con);
      inet.link(Port(sel, 0), Port(if_, 1));

      let zero = encode_term(inet, zero, Port(sel, 1), scope, vars, global_vars, dups)?;
      link_local(inet, Port(sel, 1), zero);

      let succ = encode_term(inet, succ, Port(sel, 2), scope, vars, global_vars, dups)?;
      link_local(inet, Port(sel, 2), succ);

      Ok(Some(Port(if_, 2)))
    }
    // A dup becomes a dup node too. Ports:
    // - 0: points to the value projected.
    // - 1: points to the occurrence of the first variable.
    // - 2: points to the occurrence of the second variable.
    // core: & val ~ {lab fst snd} (val not necessarily main port)
    Term::Dup { fst, snd, val, nxt } => {
      let dup = inet.new_node(Dup { lab: u8::try_from(*dups).unwrap() });
      *dups += 1;
      let val = encode_term(inet, val, Port(dup, 0), scope, vars, global_vars, dups)?;
      link_local(inet, Port(dup, 0), val);

      push_scope(fst, Port(dup, 1), scope, vars);
      push_scope(snd, Port(dup, 2), scope, vars);
      let nxt = encode_term(inet, nxt, up, scope, vars, global_vars, dups)?;
      pop_scope(snd, Port(dup, 2), inet, scope);
      pop_scope(fst, Port(dup, 1), inet, scope);

      Ok(nxt)
    }
    Term::Var { nam } => {
      // We assume this variable to be valid, bound and correctly scoped.
      // This pass must be done before.
      debug_assert!(
        scope.contains_key(nam),
        "Unbound variable {nam}. Expected this check to be already done"
      );
      let var_stack = scope.get(nam).unwrap();