bend/fun/transform/
resugar_string.rs1use crate::{
2 fun::{builtins, Name, Num, Pattern, Tag, Term},
3 maybe_grow, AdtEncoding,
4};
5
6impl Term {
7 pub fn resugar_strings(&mut self, adt_encoding: AdtEncoding) {
9 match adt_encoding {
10 AdtEncoding::Scott => self.try_resugar_strings_with(Self::resugar_strings_scott),
11 AdtEncoding::NumScott => self.try_resugar_strings_with(Self::resugar_strings_num_scott),
12 }
13 }
14
15 fn try_resugar_strings_with(&mut self, extract_fn: fn(&Term) -> Option<(char, &Term)>) {
17 maybe_grow(|| {
18 if !self.try_resugar_strings_nil() && !self.try_resugar_strings_cons(extract_fn) {
20 for child in self.children_mut() {
21 child.try_resugar_strings_with(extract_fn);
22 }
23 }
24 })
25 }
26
27 fn try_resugar_strings_nil(&mut self) -> bool {
29 matches!(self, Term::Ref { nam } if nam == builtins::SNIL).then(|| *self = Term::str("")).is_some()
30 }
31
32 fn try_resugar_strings_cons(&mut self, extract_fn: fn(&Term) -> Option<(char, &Term)>) -> bool {
34 self
35 .try_resugar_strings_cons_with(extract_fn)
36 .or_else(|| self.try_resugar_strings_cons_common())
37 .map(|str| *self = Term::str(&str))
38 .is_some()
39 }
40
41 fn try_resugar_strings_cons_with(&self, extract_fn: fn(&Term) -> Option<(char, &Term)>) -> Option<String> {
43 extract_fn(self)
44 .and_then(|(head_char, tail)| Self::build_strings_common(tail, head_char.to_string(), extract_fn))
45 }
46
47 fn try_resugar_strings_cons_common(&self) -> Option<String> {
49 if let Term::App { tag: Tag::Static, fun, arg: tail } = self {
50 if let Term::App { tag: Tag::Static, fun: inner_fun, arg: head } = fun.as_ref() {
51 if let (Term::Ref { nam }, Term::Num { val: Num::U24(head_val) }) =
52 (inner_fun.as_ref(), head.as_ref())
53 {
54 if nam == builtins::SCONS {
55 let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
56 return Self::build_strings_common(tail, head_char.to_string(), Self::extract_strings_common);
57 }
58 }
59 }
60 }
61 None
62 }
63
64 fn build_strings_common(
66 term: &Term,
67 mut s: String,
68 extract_fn: fn(&Term) -> Option<(char, &Term)>,
69 ) -> Option<String> {
70 maybe_grow(|| {
71 let mut current = term;
72 loop {
73 match current {
74 Term::Ref { nam } if nam == builtins::SNIL => return Some(s),
76 _ => {
77 let (head, next) = extract_fn(current).or_else(|| Self::extract_strings_common(current))?;
79 s.push(head);
80 current = next;
81 }
82 }
83 }
84 })
85 }
86
87 fn resugar_strings_scott(term: &Term) -> Option<(char, &Term)> {
90 if let Term::Lam { tag: Tag::Static, pat: outer_pat, bod } = term {
91 if let Pattern::Var(None) = outer_pat.as_ref() {
92 if let Term::Lam { tag: Tag::Static, pat: inner_pat, bod: inner_bod } = bod.as_ref() {
93 if let Pattern::Var(Some(var_lam)) = inner_pat.as_ref() {
94 if let Term::App { tag: Tag::Static, fun, arg: tail } = inner_bod.as_ref() {
95 if let Term::App { tag: Tag::Static, fun: inner_fun, arg: head } = fun.as_ref() {
96 if let (Term::Var { nam: var_app }, Term::Num { val: Num::U24(head_val) }) =
97 (inner_fun.as_ref(), head.as_ref())
98 {
99 if var_lam == var_app {
100 let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
101 return Some((head_char, tail));
102 }
103 }
104 }
105 }
106 }
107 }
108 }
109 }
110 None
111 }
112
113 fn resugar_strings_num_scott(term: &Term) -> Option<(char, &Term)> {
116 if let Term::Lam { tag: Tag::Static, pat, bod } = term {
117 if let Pattern::Var(Some(var_lam)) = pat.as_ref() {
118 if let Term::App { tag: Tag::Static, fun, arg: tail } = bod.as_ref() {
119 if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_ref() {
120 if let Term::App { tag: Tag::Static, fun, arg } = fun.as_ref() {
121 if let (
122 Term::Var { nam: var_app },
123 Term::Ref { nam: Name(ref_nam) },
124 Term::Num { val: Num::U24(head_val) },
125 ) = (fun.as_ref(), arg.as_ref(), head.as_ref())
126 {
127 if var_lam == var_app && ref_nam == builtins::SCONS_TAG_REF {
128 let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
129 return Some((head_char, tail));
130 }
131 }
132 }
133 }
134 }
135 }
136 }
137 None
138 }
139
140 fn extract_strings_common(term: &Term) -> Option<(char, &Term)> {
143 if let Term::App { tag: Tag::Static, fun, arg: tail } = term {
144 if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_ref() {
145 if let (Term::Ref { nam }, Term::Num { val: Num::U24(head_val) }) = (fun.as_ref(), head.as_ref()) {
146 if nam == builtins::SCONS {
147 let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
148 return Some((head_char, tail));
149 }
150 }
151 }
152 }
153 None
154 }
155}