Build a Nat env with Nat, Nat.zero, Nat.succ, Nat.rec, and Nat.sub. Nat.sub is defined as a primitive that the kernel's try_reduce_nat handles, but also has a delta-unfoldable body using Nat.rec (to test reduction order).
()
| 3635 | fn walk_literal_char_list( |
| 3636 | &mut self, |
| 3637 | list: &KExpr<M>, |
| 3638 | out: &mut String, |
| 3639 | ) -> Result<bool, TcError<M>> { |
| 3640 | let mut cur = list.clone(); |
| 3641 | loop { |
| 3642 | let Some(w) = self.whnf_prim_arg(&cur)? else { |
| 3643 | return Ok(false); |
| 3644 | }; |
| 3645 | let (head, args) = collect_app_spine(&w); |
| 3646 | let ExprData::Const(id, _, _) = head.data() else { |
| 3647 | return Ok(false); |
| 3648 | }; |
| 3649 | // List.nil.{u} α |
| 3650 | if id.addr == self.prims.list_nil.addr { |
| 3651 | return Ok(args.len() == 1); |
| 3652 | } |
| 3653 | // List.cons.{u} α head tail |
| 3654 | if id.addr != self.prims.list_cons.addr || args.len() != 3 { |
| 3655 | return Ok(false); |
| 3656 | } |
| 3657 | let elem = match self.char_lit_value(&args[1]) { |
| 3658 | Some(c) => Some(c), |
| 3659 | None => match self.whnf_prim_arg(&args[1])? { |
| 3660 | Some(we) => self.char_lit_value(&we), |
| 3661 | None => None, |
| 3662 | }, |
| 3663 | }; |
| 3664 | let Some(c) = elem else { |
| 3665 | return Ok(false); |
| 3666 | }; |
| 3667 | out.push(c); |
| 3668 | cur = args[2].clone(); |
| 3669 | } |
| 3670 | } |
| 3671 | |
| 3672 | /// Recognize the kernel's native char-value form: `Char.ofNat |
| 3673 | /// <Nat-lit>` where the literal is a valid Unicode scalar value. |
| 3674 | /// (`char::from_u32` rejects exactly the complement of |
| 3675 | /// `Nat.isValidChar`: surrogates and codepoints above `0x10FFFF`.) |
| 3676 | /// Purely syntactic — no reduction, and no spine allocation: this |
| 3677 | /// runs on every main-whnf-loop iteration. |
| 3678 | pub(super) fn char_lit_value(&self, e: &KExpr<M>) -> Option<char> { |
| 3679 | // Exactly `App(Const(Char.ofNat), Nat-lit)` — a nested App head |
| 3680 | // (over-application) or any other shape is not a char value. |
| 3681 | let ExprData::App(f, arg, _) = e.data() else { |
| 3682 | return None; |
| 3683 | }; |
| 3684 | let ExprData::Const(id, _, _) = f.data() else { |
| 3685 | return None; |
| 3686 | }; |
| 3687 | if id.addr != self.prims.char_of_nat.addr { |
| 3688 | return None; |
| 3689 | } |
| 3690 | let ExprData::Nat(n, _, _) = arg.data() else { |
| 3691 | return None; |
| 3692 | }; |
| 3693 | let cp = n.to_u64()?; |
| 3694 | u32::try_from(cp).ok().and_then(char::from_u32) |