Skip to main content

typr_core/components/language/
var.rs

1#![allow(
2    dead_code,
3    unused_variables,
4    unused_imports,
5    unreachable_code,
6    unused_assignments
7)]
8use crate::components::context::Context;
9use crate::components::error_message::help_data::HelpData;
10use crate::components::error_message::locatable::Locatable;
11use crate::components::language::Lang;
12use crate::components::r#type::function_type::FunctionType;
13use crate::components::r#type::tchar::Tchar;
14use crate::components::r#type::type_system::TypeSystem;
15use crate::components::r#type::Type;
16use crate::processes::parsing::elements::is_pascal_case;
17use crate::processes::transpiling::translatable::RTranslatable;
18use crate::processes::type_checking::typing;
19use crate::utils::builder;
20use serde::{Deserialize, Serialize};
21use std::fmt;
22
23type Name = String;
24type IsPackageOpaque = bool;
25
26#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, Hash)]
27pub enum Permission {
28    Private,
29    Public,
30}
31
32impl fmt::Display for Permission {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Permission::Private => write!(f, "private"),
36            Permission::Public => write!(f, "public"),
37        }
38    }
39}
40
41impl From<Permission> for bool {
42    fn from(val: Permission) -> Self {
43        matches!(val, Permission::Public)
44    }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct Var {
49    pub name: Name,
50    pub is_opaque: IsPackageOpaque,
51    pub related_type: Type,
52    pub help_data: HelpData,
53}
54
55impl PartialEq for Var {
56    fn eq(&self, other: &Self) -> bool {
57        self.name == other.name
58            && self.is_opaque == other.is_opaque
59            && self.related_type == other.related_type
60    }
61}
62
63impl Eq for Var {}
64
65impl std::hash::Hash for Var {
66    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
67        self.name.hash(state);
68        self.is_opaque.hash(state);
69        self.related_type.hash(state);
70    }
71}
72
73impl Locatable for Var {
74    fn get_help_data(&self) -> HelpData {
75        self.help_data.clone()
76    }
77}
78
79impl Var {
80    pub fn set_type_from_params(self, params: &[Lang], context: &Context) -> Self {
81        let typ = if !params.is_empty() {
82            typing(context, &params[0]).value
83        } else {
84            Default::default()
85        };
86        self.set_type(typ)
87    }
88
89    pub fn add_backticks_if_percent(self) -> Self {
90        let s = self.get_name();
91        let res = if s.starts_with('%') && s.ends_with('%') {
92            format!("`{}`", s)
93        } else {
94            s.to_string()
95        };
96        self.set_name(&res)
97    }
98
99    pub fn alias(name: &str, params: &[Type]) -> Self {
100        Var::from(name).set_type(Type::Params(params.to_vec(), HelpData::default()))
101    }
102
103    pub fn set_var_related_type(&self, types: &[Type], context: &Context) -> Var {
104        if let Some(first_arg) = types.first() {
105            self.clone().set_type(first_arg.clone())
106        } else {
107            self.clone()
108        }
109    }
110
111    fn keep_minimal(liste: Vec<Type>, context: &Context) -> Option<Type> {
112        let mut mins: Vec<Type> = Vec::new();
113
114        for candidat in liste {
115            let mut keep_candidat = true;
116            let mut indices_to_delete = Vec::new();
117
118            for (i, existant) in mins.iter().enumerate() {
119                if candidat.is_subtype(existant, context).0 {
120                    indices_to_delete.push(i);
121                } else if existant.is_subtype(&candidat, context).0 {
122                    keep_candidat = false;
123                    break;
124                }
125            }
126
127            if keep_candidat {
128                for &i in indices_to_delete.iter().rev() {
129                    mins.remove(i);
130                }
131                mins.push(candidat);
132            }
133        }
134        // get smallest type
135        if mins.iter().any(|x| !x.is_interface()) {
136            mins.iter().find(|x| !x.is_interface()).cloned()
137        } else {
138            mins.first().cloned()
139        }
140    }
141
142    pub fn get_functions_from_name(&self, context: &Context) -> Vec<FunctionType> {
143        context
144            .get_functions_from_name(&self.get_name())
145            .iter()
146            .flat_map(|(_, typ)| typ.clone().to_function_type())
147            .collect()
148    }
149
150    pub fn from_language(l: Lang) -> Option<Var> {
151        match l {
152            Lang::Variable {
153                name,
154                is_opaque: muta,
155                related_type: typ,
156                help_data: h,
157            } => Some(Var {
158                name,
159                is_opaque: muta,
160                related_type: typ,
161                help_data: h,
162            }),
163            _ => None,
164        }
165    }
166
167    pub fn from_type(t: Type) -> Option<Var> {
168        match t {
169            Type::Alias(name, concret_types, opacity, h) => {
170                let var = Var::from_name(&name)
171                    .set_type(Type::Params(
172                        concret_types.to_vec(),
173                        concret_types.clone().into(),
174                    ))
175                    .set_help_data(h)
176                    .set_opacity(opacity);
177                Some(var)
178            }
179            Type::Char(val, h) => {
180                let var = Var::from_name(&val.get_val()).set_help_data(h);
181                Some(var)
182            }
183            _ => None,
184        }
185    }
186
187    pub fn from_name(name: &str) -> Self {
188        Var {
189            name: name.to_string(),
190            is_opaque: false,
191            related_type: builder::empty_type(),
192            help_data: HelpData::default(),
193        }
194    }
195
196    pub fn to_language(self) -> Lang {
197        Lang::Variable {
198            name: self.name,
199            is_opaque: self.is_opaque,
200            related_type: self.related_type,
201            help_data: self.help_data,
202        }
203    }
204
205    pub fn set_name(self, s: &str) -> Var {
206        Var {
207            name: s.to_string(),
208            is_opaque: self.is_opaque,
209            related_type: self.related_type,
210            help_data: self.help_data,
211        }
212    }
213
214    pub fn set_type(self, typ: Type) -> Var {
215        let typ = match typ {
216            Type::Function(params, _, h) => {
217                if !params.is_empty() {
218                    params[0].get_type()
219                } else {
220                    Type::Any(h)
221                }
222            }
223            _ => typ,
224        };
225        Var {
226            name: self.name,
227            is_opaque: self.is_opaque,
228            related_type: typ,
229            help_data: self.help_data,
230        }
231    }
232
233    pub fn set_type_raw(self, typ: Type) -> Var {
234        Var {
235            name: self.name,
236            is_opaque: self.is_opaque,
237            related_type: typ,
238            help_data: self.help_data,
239        }
240    }
241
242    pub fn set_opacity(self, opa: bool) -> Var {
243        Var {
244            name: self.name,
245            is_opaque: opa,
246            related_type: self.related_type,
247            help_data: self.help_data,
248        }
249    }
250
251    pub fn get_name(&self) -> String {
252        self.name.to_string()
253    }
254
255    pub fn get_type(&self) -> Type {
256        self.related_type.clone()
257    }
258
259    pub fn get_help_data(&self) -> HelpData {
260        self.help_data.clone()
261    }
262
263    pub fn match_with(&self, var: &Var, context: &Context) -> bool {
264        (self.get_name() == var.get_name())
265            && self.get_type().is_subtype(&var.get_type(), context).0
266    }
267
268    pub fn set_help_data(self, h: HelpData) -> Var {
269        Var {
270            name: self.name,
271            is_opaque: self.is_opaque,
272            related_type: self.related_type,
273            help_data: h,
274        }
275    }
276
277    pub fn is_imported(&self) -> bool {
278        self.is_variable() && self.is_opaque
279    }
280
281    pub fn is_alias(&self) -> bool {
282        matches!(self.get_type(), Type::Params(_, _))
283    }
284
285    pub fn is_variable(&self) -> bool {
286        !self.is_alias()
287    }
288
289    pub fn is_opaque(&self) -> bool {
290        self.is_alias() && self.is_opaque
291    }
292
293    pub fn get_opacity(&self) -> bool {
294        self.is_opaque
295    }
296
297    pub fn to_alias_type(self) -> Type {
298        Type::Alias(
299            self.get_name(),
300            vec![],
301            self.get_opacity(),
302            self.get_help_data(),
303        )
304    }
305
306    pub fn to_alias_lang(self) -> Lang {
307        Lang::Alias {
308            identifier: Box::new(self.clone().to_language()),
309            parameters: vec![],
310            target_type: builder::unknown_function_type(),
311            is_public: false,
312            help_data: self.get_help_data(),
313        }
314    }
315
316    pub fn to_let(self) -> Lang {
317        Lang::Let {
318            variable: Box::new(self.clone().to_language()),
319            r#type: builder::unknown_function_type(),
320            expression: Box::default(),
321            is_public: false,
322            is_testable: false,
323            is_export: false,
324            help_data: self.get_help_data(),
325        }
326    }
327
328    pub fn contains(&self, s: &str) -> bool {
329        self.get_name().contains(s)
330    }
331
332    pub fn replace(self, old: &str, new: &str) -> Self {
333        let res = self.get_name().replace(old, new);
334        self.set_name(&res)
335    }
336
337    pub fn display_type(self, cont: &Context) -> Self {
338        if !self.get_name().contains(".") {
339            let type_str = match self.get_type() {
340                Type::Empty(_) | Type::Any(_) => "".to_string(),
341                ty => {
342                    // Only add the S3 suffix when the variable is a known S3 method
343                    // binding: context must have an entry for this name with a non-Empty
344                    // related_type (set by the parser's let-fn path). Local closures and
345                    // partial-application results are stored with Empty related_type and
346                    // must not receive the suffix even if set_related_type_if_variable
347                    // temporarily gave them a non-Empty type for dispatch resolution.
348                    let is_method = cont
349                        .get_functions_from_name(&self.get_name())
350                        .iter()
351                        .any(|(v, _)| !matches!(v.get_type(), Type::Empty(_)));
352                    if is_method {
353                        ".".to_string() + &cont.get_class(&ty).replace("'", "")
354                    } else {
355                        "".to_string()
356                    }
357                }
358            };
359            let new_name = if self.contains("`") {
360                "`".to_string() + &self.get_name().replace("`", "") + &type_str + "`"
361            } else {
362                self.get_name() + &type_str
363            };
364            self.set_name(&new_name)
365        } else {
366            self
367        }
368    }
369
370    pub fn get_digit(&self, s: &str) -> i8 {
371        self.get_name()[s.len()..].parse::<i8>().unwrap()
372    }
373
374    pub fn add_digit(self, d: i8) -> Self {
375        self.clone().set_name(&(self.get_name() + &d.to_string()))
376    }
377
378    pub fn exist(&self, context: &Context) -> Option<Self> {
379        context.variable_exist(self.clone())
380    }
381}
382
383impl fmt::Display for Var {
384    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385        write!(f, "{}<{}>", self.name, self.related_type)
386    }
387}
388
389impl Default for Var {
390    fn default() -> Self {
391        Var {
392            name: "".to_string(),
393            is_opaque: false,
394            related_type: Type::Empty(HelpData::default()),
395            help_data: HelpData::default(),
396        }
397    }
398}
399
400impl RTranslatable<String> for Var {
401    fn to_r(&self, _: &Context) -> String {
402        self.name.to_string()
403    }
404}
405
406impl TryFrom<Lang> for Var {
407    type Error = ();
408
409    fn try_from(value: Lang) -> Result<Self, Self::Error> {
410        match value {
411            Lang::Variable {
412                name,
413                is_opaque: muta,
414                related_type: typ,
415                help_data: h,
416            } => Ok(Var {
417                name,
418                is_opaque: muta,
419                related_type: typ,
420                help_data: h,
421            }),
422            _ => Err(()),
423        }
424    }
425}
426
427impl TryFrom<Box<Lang>> for Var {
428    type Error = ();
429
430    fn try_from(value: Box<Lang>) -> Result<Self, Self::Error> {
431        Var::try_from((*value).clone())
432    }
433}
434
435impl TryFrom<&Box<Lang>> for Var {
436    type Error = ();
437
438    fn try_from(value: &Box<Lang>) -> Result<Self, Self::Error> {
439        Var::try_from((*value).clone())
440    }
441}
442
443impl From<&str> for Var {
444    fn from(val: &str) -> Self {
445        Var {
446            name: val.to_string(),
447            is_opaque: false,
448            related_type: Type::Empty(HelpData::default()),
449            help_data: HelpData::default(),
450        }
451    }
452}
453
454impl TryFrom<Type> for Var {
455    type Error = String;
456
457    fn try_from(value: Type) -> Result<Self, Self::Error> {
458        match value {
459            Type::Char(tchar, h) => match tchar {
460                Tchar::Val(name) => {
461                    let var = if is_pascal_case(&name) {
462                        Var::from_name(&name)
463                            .set_help_data(h)
464                            .set_type(builder::params_type())
465                    } else {
466                        Var::from_name(&name).set_help_data(h)
467                    };
468                    Ok(var)
469                }
470                _ => todo!(),
471            },
472            _ => Err("From type to Var, not possible".to_string()),
473        }
474    }
475}