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

Code before:
      },
      Rot => self.error(ReadbackError::ReachedRoot),
      Tup => match next.slot() {
        // If we're visiting a port 0, then it is a Tup.
        0 => {
          let fst = self.read_term(self.net.enter_port(Port(node, 1)));
          let snd = self.read_term(self.net.enter_port(Port(node, 2)));
          match snd {
            Term::Lam { tag, bod, .. } if tag == Tag::str() => decode_str(fst, *bod),
            snd => Term::Tup { fst: Box::new(fst), snd: Box::new(snd) },
          }
        }
        // If we're visiting a port 1 or 2, then it is a variable.
        1 | 2 => {
          self.scope.insert(node);
          Term::Var { nam: self.namegen.var_name(next) }
        }
        _ => unreachable!(),
      },
    };

    term
  }
}

fn decode_str(fst: Term, bod: Term) -> Term {
  let Term::Num { val: len } = fst else { unreachable!() };
  let mut s = String::with_capacity(len as usize);
  let mut next = vec![bod];
  while let Some(t) = next.pop() {
    match t {
      Term::Var { .. } => break, // reached the end of the str
      Term::Tup { fst, snd } => {
        let Term::Num { val } = *fst else { unreachable!() };
        let char = char::from_u32(val as u32);
        s.push(char.unwrap());
        next.push(*snd);
      }
      _ => unreachable!(),
    }
  }
  Term::Str { val: s }
}

/// Represents `let (fst, snd) = val` if `tag` is `None`, and `dup#tag fst snd = val` otherwise.
#[derive(Default)]
struct Split {
  tag: Option<Tag>,
  fst: Option<Name>,
  snd: Option<Name>,
  val: Term,

Review comment (bug):
These `unreachable!`s should instead push readback errors (as many of them are likely reachable in debug mode)

Code after:
          Term::Opx { op: Op::from_hvmc_label(opr).unwrap(), fst: Box::new(fst), snd: Box::new(snd) }
        }
        _ => unreachable!(),
      },
      Rot => self.error(ReadbackError::ReachedRoot),
      Tup => match next.slot() {
        // If we're visiting a port 0, then it is a Tup.
        0 => {
          let fst = self.read_term(self.net.enter_port(Port(node, 1)));
          let snd = self.read_term(self.net.enter_port(Port(node, 2)));
          match snd {
            Term::Lam { tag, bod, .. } if tag == Tag::str() => self.decode_str(fst, *bod),
            snd => Term::Tup { fst: Box::new(fst), snd: Box::new(snd) },
          }
        }
        // If we're visiting a port 1 or 2, then it is a variable.
        1 | 2 => {
          self.scope.insert(node);
          Term::Var { nam: self.namegen.var_name(next) }
        }
        _ => unreachable!(),
      },
    };

    term
  }
}

impl<'a> Reader<'a> {
  fn decode_str(&mut self, fst: Term, bod: Term) -> Term {
    let Term::Num { val: len } = fst else {
      return self.error(ReadbackError::InvalidStrLen);
    };
    let mut s = String::with_capacity(len as usize);
    let mut next = vec![bod];
    while let Some(t) = next.pop() {
      match t {
        Term::Var { .. } => break, // reached the end of the str
        Term::Tup { fst, snd } => {
          let Term::Num { val } = *fst else {
            return self.error(ReadbackError::InvalidChar);
          };
          if let Some(c) = char::from_u32(val as u32) {
            s.push(c);
            next.push(*snd);
          } else {
            self.errors.push(ReadbackError::InvalidChar);
          }
        }
        _ => self.errors.push(ReadbackError::InvalidStrTerm),
      }