tsz_solver/evaluation/evaluate_rules/
template_literal.rs1use crate::relations::subtype::TypeResolver;
6use crate::types::{LiteralValue, TemplateLiteralId, TemplateSpan, TypeData, TypeId};
7
8use super::super::evaluate::TypeEvaluator;
9
10impl<'a, R: TypeResolver> TypeEvaluator<'a, R> {
11 pub fn evaluate_template_literal(&mut self, spans: TemplateLiteralId) -> TypeId {
17 use crate::intern::TEMPLATE_LITERAL_EXPANSION_LIMIT;
18
19 let span_list = self.interner().template_list(spans);
20
21 tracing::trace!(
22 span_count = span_list.len(),
23 "evaluate_template_literal: called with {} spans",
24 span_list.len()
25 );
26
27 let all_text = span_list
29 .iter()
30 .all(|span| matches!(span, TemplateSpan::Text(_)));
31
32 if all_text {
33 tracing::trace!("evaluate_template_literal: all text - concatenating");
34 let mut result = String::new();
36 for span in span_list.iter() {
37 if let TemplateSpan::Text(atom) = span {
38 result.push_str(self.interner().resolve_atom_ref(*atom).as_ref());
39 }
40 }
41 return self.interner().literal_string(&result);
42 }
43
44 let mut evaluated_spans = Vec::with_capacity(span_list.len());
47 let mut total_combinations: usize = 1;
48
49 for span in span_list.iter() {
50 match span {
51 TemplateSpan::Text(_atom) => {
52 evaluated_spans.push(None); }
54 TemplateSpan::Type(type_id) => {
55 let evaluated = self.evaluate(*type_id);
56 let strings = self.extract_literal_strings(evaluated);
57
58 if strings.is_empty() {
59 return self.interner().template_literal(span_list.to_vec());
61 }
62
63 total_combinations = total_combinations.saturating_mul(strings.len());
64 if total_combinations > TEMPLATE_LITERAL_EXPANSION_LIMIT {
65 return self.interner().template_literal(span_list.to_vec());
67 }
68 evaluated_spans.push(Some(strings));
69 }
70 }
71 }
72
73 let mut combinations = vec![String::new()];
75
76 for (i, span) in span_list.iter().enumerate() {
77 match span {
78 TemplateSpan::Text(atom) => {
79 let text = self.interner().resolve_atom_ref(*atom);
80 for combo in &mut combinations {
81 combo.push_str(text.as_ref());
82 }
83 }
84 TemplateSpan::Type(_) => {
85 let string_values = evaluated_spans[i].as_ref().unwrap();
87 let new_size = combinations.len() * string_values.len();
88
89 let mut new_combinations = Vec::with_capacity(new_size);
91 for combo in &combinations {
92 for value in string_values {
93 let mut new_combo = String::with_capacity(combo.len() + value.len());
95 new_combo.push_str(combo);
96 new_combo.push_str(value);
97 new_combinations.push(new_combo);
98 }
99 }
100 combinations = new_combinations;
101 }
102 }
103 }
104
105 if combinations.is_empty() {
107 return TypeId::NEVER;
108 }
109
110 let literal_types: Vec<TypeId> = combinations
111 .iter()
112 .map(|s| self.interner().literal_string(s))
113 .collect();
114
115 if literal_types.len() == 1 {
116 literal_types[0]
117 } else {
118 self.interner().union(literal_types)
119 }
120 }
121
122 const MAX_LITERAL_COUNT_DEPTH: u32 = 50;
124
125 pub fn count_literal_members(&self, type_id: TypeId) -> usize {
128 self.count_literal_members_impl(type_id, 0)
129 }
130
131 fn count_literal_members_impl(&self, type_id: TypeId, depth: u32) -> usize {
133 if depth > Self::MAX_LITERAL_COUNT_DEPTH {
135 return 0; }
137
138 if let Some(TypeData::Union(members)) = self.interner().lookup(type_id) {
139 let members = self.interner().type_list(members);
140 let mut count = 0;
141 for &member in members.iter() {
142 let member_count = self.count_literal_members_impl(member, depth + 1);
143 if member_count == 0 {
144 return 0;
145 }
146 count += member_count;
147 }
148 count
149 } else if let Some(TypeData::Literal(_)) = self.interner().lookup(type_id) {
150 1
151 } else if let Some(TypeData::Enum(_, structural_type)) = self.interner().lookup(type_id) {
152 self.count_literal_members_impl(structural_type, depth + 1)
154 } else if type_id == TypeId::STRING
155 || type_id == TypeId::NUMBER
156 || type_id == TypeId::BOOLEAN
157 || type_id == TypeId::BIGINT
158 {
159 0
161 } else {
162 0
163 }
164 }
165
166 pub fn extract_literal_strings(&self, type_id: TypeId) -> Vec<String> {
170 self.extract_literal_strings_impl(type_id, 0)
171 }
172
173 fn extract_literal_strings_impl(&self, type_id: TypeId, depth: u32) -> Vec<String> {
175 if depth > Self::MAX_LITERAL_COUNT_DEPTH {
177 return Vec::new(); }
179
180 if let Some(TypeData::Union(members)) = self.interner().lookup(type_id) {
181 let members = self.interner().type_list(members);
182 let mut result = Vec::new();
183 for &member in members.iter() {
184 let strings = self.extract_literal_strings_impl(member, depth + 1);
185 if strings.is_empty() {
186 return Vec::new();
188 }
189 result.extend(strings);
190 }
191 result
192 } else if let Some(TypeData::Literal(lit)) = self.interner().lookup(type_id) {
193 match lit {
194 LiteralValue::String(atom) => {
195 vec![self.interner().resolve_atom_ref(atom).to_string()]
196 }
197 LiteralValue::Number(n) => {
198 let n_val = n.0;
201 let abs_val = n_val.abs();
202
203 tracing::trace!(
204 number = n_val,
205 abs_val = abs_val,
206 "extract_literal_strings: converting number to string"
207 );
208
209 if !(1e-6..1e21).contains(&abs_val) {
210 let mut s = format!("{n_val:e}");
212 if s.contains("e") && !s.contains("e-") && !s.contains("e+") {
215 let parts: Vec<&str> = s.split('e').collect();
216 if parts.len() == 2 {
217 s = format!("{}e+{}", parts[0], parts[1]);
218 }
219 }
220 tracing::trace!(result = %s, "extract_literal_strings: scientific notation");
221 vec![s]
222 } else if n_val.fract() == 0.0 && abs_val < 1e15 {
223 let s = format!("{}", n_val as i64);
225 tracing::trace!(result = %s, "extract_literal_strings: integer-like");
226 vec![s]
227 } else {
228 let s = format!("{n_val}");
230 tracing::trace!(result = %s, "extract_literal_strings: fixed-point");
231 vec![s]
232 }
233 }
234 LiteralValue::Boolean(b) => {
235 vec![if b {
236 "true".to_string()
237 } else {
238 "false".to_string()
239 }]
240 }
241 LiteralValue::BigInt(atom) => {
242 vec![self.interner().resolve_atom_ref(atom).to_string()]
244 }
245 }
246 } else if let Some(TypeData::Enum(_, structural_type)) = self.interner().lookup(type_id) {
247 self.extract_literal_strings_impl(structural_type, depth + 1)
250 } else {
251 Vec::new()
253 }
254 }
255}