1use std::collections::HashMap;
19
20use gruel_error::{CompileError, CompileResult, CompileWarning, ErrorKind, PreviewFeature};
21use gruel_span::Span;
22use lasso::Spur;
23
24use crate::inference::InferType;
25use crate::sema_context::SemaContext;
26use crate::types::{ArrayTypeId, Type};
27
28#[derive(Debug)]
45pub struct FunctionAnalyzer<'a, 'ctx> {
46 pub ctx: &'a SemaContext<'ctx>,
48 string_table: HashMap<String, u32>,
50 strings: Vec<String>,
52 warnings: Vec<CompileWarning>,
54}
55
56#[derive(Debug)]
58pub struct FunctionAnalyzerOutput {
59 pub strings: Vec<String>,
61 pub warnings: Vec<CompileWarning>,
63}
64
65impl<'a, 'ctx> FunctionAnalyzer<'a, 'ctx> {
66 pub fn new(ctx: &'a SemaContext<'ctx>) -> Self {
68 Self {
69 ctx,
70 string_table: HashMap::new(),
71 strings: Vec::new(),
72 warnings: Vec::new(),
73 }
74 }
75
76 pub fn into_output(self) -> FunctionAnalyzerOutput {
78 FunctionAnalyzerOutput {
79 strings: self.strings,
80 warnings: self.warnings,
81 }
82 }
83
84 pub fn add_string(&mut self, content: String) -> u32 {
87 use std::collections::hash_map::Entry;
88 match self.string_table.entry(content) {
89 Entry::Occupied(e) => *e.get(),
90 Entry::Vacant(e) => {
91 let id = self.strings.len() as u32;
92 self.strings.push(e.key().clone());
93 e.insert(id);
94 id
95 }
96 }
97 }
98
99 pub fn add_warning(&mut self, warning: CompileWarning) {
101 self.warnings.push(warning);
102 }
103
104 pub fn get_or_create_array_type(&self, element_type: Type, length: u64) -> ArrayTypeId {
108 self.ctx.get_or_create_array_type(element_type, length)
109 }
110
111 pub fn get_array_type_def(&self, id: ArrayTypeId) -> (Type, u64) {
115 self.ctx.get_array_type_def(id)
116 }
117
118 pub fn pre_create_array_types_from_infer_type(&self, ty: &InferType) {
127 match ty {
128 InferType::Array { element, length } => {
129 self.pre_create_array_types_from_infer_type(element);
131
132 let elem_ty = self.infer_type_to_type(element);
134 if elem_ty != Type::ERROR {
135 self.get_or_create_array_type(elem_ty, *length);
137 }
138 }
139 InferType::Concrete(_)
140 | InferType::Var(_)
141 | InferType::IntLiteral
142 | InferType::FloatLiteral => {
143 }
145 }
146 }
147
148 pub fn infer_type_to_type(&self, ty: &InferType) -> Type {
150 match ty {
151 InferType::Concrete(t) => *t,
152 InferType::Var(_) => Type::ERROR,
153 InferType::IntLiteral => Type::I32,
154 InferType::FloatLiteral => Type::F64,
155 InferType::Array { element, length } => {
156 let elem_ty = self.infer_type_to_type(element);
157 if elem_ty == Type::ERROR {
158 return Type::ERROR;
159 }
160 let id = self.get_or_create_array_type(elem_ty, *length);
162 Type::new_array(id)
163 }
164 }
165 }
166
167 pub fn require_preview(
169 &self,
170 feature: PreviewFeature,
171 what: &str,
172 span: Span,
173 ) -> CompileResult<()> {
174 if self.ctx.preview_features.contains(&feature) {
175 Ok(())
176 } else {
177 Err(CompileError::new(
178 ErrorKind::PreviewFeatureRequired {
179 feature,
180 what: what.to_string(),
181 },
182 span,
183 )
184 .with_help(format!(
185 "use `--preview {}` to enable this feature ({})",
186 feature.name(),
187 feature.adr()
188 )))
189 }
190 }
191
192 pub fn format_type_name(&self, ty: Type) -> String {
195 if let Some(array_id) = ty.as_array() {
196 let (element_type, length) = self.get_array_type_def(array_id);
197 format!("[{}; {}]", self.format_type_name(element_type), length)
198 } else {
199 self.ctx.format_type_name(ty)
200 }
201 }
202
203 pub fn is_type_copy(&self, ty: Type) -> bool {
206 if let Some(array_id) = ty.as_array() {
207 let (element_type, _length) = self.get_array_type_def(array_id);
208 self.is_type_copy(element_type)
209 } else {
210 self.ctx.is_type_copy(ty)
211 }
212 }
213
214 pub fn abi_slot_count(&self, ty: Type) -> u32 {
217 if let Some(array_id) = ty.as_array() {
218 let (element_type, length) = self.get_array_type_def(array_id);
219 let element_slots = self.abi_slot_count(element_type);
220 element_slots * length as u32
221 } else {
222 self.ctx.abi_slot_count(ty)
223 }
224 }
225
226 pub fn resolve_type(&mut self, type_sym: Spur, span: Span) -> CompileResult<Type> {
228 let type_name = self.ctx.interner.resolve(&type_sym);
229
230 match type_name {
232 "i8" => return Ok(Type::I8),
233 "i16" => return Ok(Type::I16),
234 "i32" => return Ok(Type::I32),
235 "i64" => return Ok(Type::I64),
236 "u8" => return Ok(Type::U8),
237 "u16" => return Ok(Type::U16),
238 "u32" => return Ok(Type::U32),
239 "u64" => return Ok(Type::U64),
240 "bool" => return Ok(Type::BOOL),
241 "()" => return Ok(Type::UNIT),
242 "!" => return Ok(Type::NEVER),
243 _ => {}
244 }
245
246 if let Some(struct_id) = self.ctx.get_struct(type_sym) {
247 Ok(Type::new_struct(struct_id))
248 } else if let Some(enum_id) = self.ctx.get_enum(type_sym) {
249 Ok(Type::new_enum(enum_id))
250 } else {
251 if let Some((element_type, length)) = crate::types::parse_array_type_syntax(type_name) {
253 let element_sym = self.ctx.interner.get_or_intern(&element_type);
255 let element_ty = self.resolve_type(element_sym, span)?;
256 let array_type_id = self.get_or_create_array_type(element_ty, length);
258 Ok(Type::new_array(array_type_id))
259 } else if let Some((pointee_type, mutability)) =
260 crate::types::parse_pointer_type_syntax(type_name)
261 {
262 let pointee_sym = self.ctx.interner.get_or_intern(&pointee_type);
264 let pointee_ty = self.resolve_type(pointee_sym, span)?;
265 match mutability {
267 crate::types::PtrMutability::Const => {
268 let ptr_id = self.ctx.get_or_create_ptr_const_type(pointee_ty);
269 Ok(Type::new_ptr_const(ptr_id))
270 }
271 crate::types::PtrMutability::Mut => {
272 let ptr_id = self.ctx.get_or_create_ptr_mut_type(pointee_ty);
273 Ok(Type::new_ptr_mut(ptr_id))
274 }
275 }
276 } else {
277 Err(CompileError::new(
278 ErrorKind::UnknownType(type_name.to_string()),
279 span,
280 ))
281 }
282 }
283 }
284
285 pub fn warnings(&self) -> &[CompileWarning] {
287 &self.warnings
288 }
289
290 pub fn strings(&self) -> &[String] {
292 &self.strings
293 }
294}
295
296#[derive(Debug, Default)]
302pub struct MergedFunctionOutput {
303 pub strings: Vec<String>,
305 pub string_map: HashMap<String, u32>,
307 pub warnings: Vec<CompileWarning>,
309}
310
311impl MergedFunctionOutput {
312 pub fn new() -> Self {
314 Self::default()
315 }
316
317 pub fn merge_function_output(
322 &mut self,
323 output: FunctionAnalyzerOutput,
324 ) -> FunctionOutputRemapping {
325 let mut string_remap = HashMap::new();
326
327 for (idx, content) in output.strings.into_iter().enumerate() {
329 let old_id = idx as u32;
330 let new_id = if let Some(&id) = self.string_map.get(&content) {
331 id
332 } else {
333 let id = self.strings.len() as u32;
334 self.strings.push(content.clone());
335 self.string_map.insert(content, id);
336 id
337 };
338 if old_id != new_id {
339 string_remap.insert(old_id, new_id);
340 }
341 }
342
343 self.warnings.extend(output.warnings);
345
346 FunctionOutputRemapping { string_remap }
347 }
348}
349
350#[derive(Debug, Default)]
352pub struct FunctionOutputRemapping {
353 pub string_remap: HashMap<u32, u32>,
355}
356
357impl FunctionOutputRemapping {
358 pub fn is_empty(&self) -> bool {
360 self.string_remap.is_empty()
361 }
362
363 pub fn remap_string(&self, id: u32) -> u32 {
365 self.string_remap.get(&id).copied().unwrap_or(id)
366 }
367}