lisette_semantics/pattern_analysis/
witness.rs1use super::NormalizedPattern;
2use syntax::ast::Literal;
3
4pub fn format_witness(pattern: &NormalizedPattern) -> String {
5 match pattern {
6 NormalizedPattern::Wildcard => "_".to_string(),
7
8 NormalizedPattern::Literal(_) => unreachable!("literals cannot be witnesses"),
11
12 NormalizedPattern::Constructor {
13 type_name,
14 tag,
15 args,
16 } => {
17 if type_name.starts_with("Slice") {
18 return format_slice_pattern(pattern);
19 }
20
21 if type_name.starts_with("Tuple") {
22 let formatted_args = args
23 .iter()
24 .map(format_witness)
25 .collect::<Vec<_>>()
26 .join(", ");
27 return format!("({})", formatted_args);
28 }
29
30 let display_tag = strip_module_prefix(tag);
31
32 if display_tag == "__value_enum_unknown__" {
33 return "_".to_string();
34 }
35
36 if args.is_empty() {
37 display_tag
38 } else {
39 let formatted_args = args
40 .iter()
41 .map(format_witness)
42 .collect::<Vec<_>>()
43 .join(", ");
44 format!("{}({})", display_tag, formatted_args)
45 }
46 }
47 }
48}
49
50pub fn format_pattern(pattern: &NormalizedPattern) -> String {
54 match pattern {
55 NormalizedPattern::Wildcard => "_".to_string(),
56
57 NormalizedPattern::Literal(lit) => format_literal(lit),
59
60 NormalizedPattern::Constructor {
61 type_name,
62 tag,
63 args,
64 } => {
65 if type_name.starts_with("Slice") {
66 return format_slice_pattern_for_display(pattern);
67 }
68
69 if type_name.starts_with("Tuple") {
70 let formatted_args = args
71 .iter()
72 .map(format_pattern)
73 .collect::<Vec<_>>()
74 .join(", ");
75 return format!("({})", formatted_args);
76 }
77
78 let display_tag = strip_module_prefix(tag);
79
80 if display_tag == "__value_enum_unknown__" {
81 return "_".to_string();
82 }
83
84 if args.is_empty() {
85 display_tag
86 } else {
87 let formatted_args = args
88 .iter()
89 .map(format_pattern)
90 .collect::<Vec<_>>()
91 .join(", ");
92 format!("{}({})", display_tag, formatted_args)
93 }
94 }
95 }
96}
97
98fn format_literal(lit: &Literal) -> String {
99 match lit {
100 Literal::Integer { text, value } => text.as_ref().unwrap_or(&value.to_string()).clone(),
101 Literal::Float { text, value } => text.as_ref().unwrap_or(&value.to_string()).clone(),
102 Literal::Imaginary(val) => format!("{}i", val),
103 Literal::Boolean(b) => b.to_string(),
104 Literal::String(s) => format!("\"{}\"", s),
105 Literal::Char(c) => format!("'{}'", c),
106 Literal::FormatString(_) => "f\"...\"".to_string(),
107 Literal::Slice(_) => "[...]".to_string(),
108 }
109}
110
111fn strip_module_prefix(tag: &str) -> String {
112 let parts: Vec<&str> = tag.split('.').collect();
113 if parts.len() >= 2 {
114 parts[1..].join(".")
115 } else {
116 tag.to_string()
117 }
118}
119
120fn format_slice_pattern(pattern: &NormalizedPattern) -> String {
121 let mut elements = Vec::new();
122 let mut current = pattern;
123 let mut ends_with_rest = false;
124
125 loop {
126 match current {
127 NormalizedPattern::Constructor { tag, .. } if tag == "EmptySlice" => {
128 break;
129 }
130 NormalizedPattern::Constructor { tag, args, .. } if tag == "NonEmptySlice" => {
131 if args.len() >= 2 {
132 elements.push(format_witness(&args[0]));
133 current = &args[1];
134 } else {
135 break;
136 }
137 }
138 NormalizedPattern::Wildcard => {
139 ends_with_rest = true;
140 break;
141 }
142 _ => {
143 elements.push(format_witness(current));
144 break;
145 }
146 }
147 }
148
149 if ends_with_rest {
150 elements.push("..".to_string());
151 }
152
153 format!("[{}]", elements.join(", "))
154}
155
156fn format_slice_pattern_for_display(pattern: &NormalizedPattern) -> String {
157 let mut elements = Vec::new();
158 let mut current = pattern;
159 let mut ends_with_rest = false;
160
161 loop {
162 match current {
163 NormalizedPattern::Constructor { tag, .. } if tag == "EmptySlice" => {
164 break;
165 }
166 NormalizedPattern::Constructor { tag, args, .. } if tag == "NonEmptySlice" => {
167 if args.len() >= 2 {
168 elements.push(format_pattern(&args[0]));
169 current = &args[1];
170 } else {
171 break;
172 }
173 }
174 NormalizedPattern::Wildcard => {
175 ends_with_rest = true;
176 break;
177 }
178 _ => {
179 elements.push(format_pattern(current));
180 break;
181 }
182 }
183 }
184
185 if ends_with_rest {
186 elements.push("..".to_string());
187 }
188
189 format!("[{}]", elements.join(", "))
190}