Skip to main content

typr_core/components/context/
mod.rs

1pub mod config;
2pub mod graph;
3pub mod vartype;
4
5use crate::components::context::config::Config;
6use crate::components::context::config::Environment;
7use crate::components::context::config::TargetLanguage;
8use crate::components::context::graph::Graph;
9use crate::components::context::unification_map::UnificationMap;
10use crate::components::context::vartype::VarType;
11use crate::components::error_message::help_data::HelpData;
12use crate::components::language::var::Var;
13use crate::components::language::var_function::VarFunction;
14use crate::components::language::Lang;
15use crate::components::r#type::argument_type::ArgumentType;
16use crate::components::r#type::kind::Kind;
17use crate::components::r#type::type_system::TypeSystem;
18use crate::components::r#type::vector_type::ConstructorCategory;
19use crate::components::r#type::Type;
20use crate::processes::type_checking::match_types_to_generic;
21use crate::processes::type_checking::type_comparison::reduce_type;
22use crate::processes::type_checking::unification_map;
23use crate::utils::builder;
24use crate::utils::standard_library::not_in_blacklist;
25use serde::Deserialize;
26use serde::Serialize;
27use std::collections::HashMap;
28use std::collections::HashSet;
29use std::iter::Rev;
30use std::ops::Add;
31use tap::Pipe;
32
33/// True for the auto-generated names given to anonymous record types
34/// (`Record0`, `Record1`, …), as produced by `VarType::push_alias_increment`
35/// (`format!("{}{}", TypeCategory::Record, count)`).
36fn is_anonymous_record_name(name: &str) -> bool {
37    name.strip_prefix("Record")
38        .map(|rest| !rest.is_empty() && rest.chars().all(|c| c.is_ascii_digit()))
39        .unwrap_or(false)
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct Context {
44    pub typing_context: VarType,
45    pub subtypes: Graph<Type>,
46    /// Registry of user-declared `typeconstructor`s: (name, parameter signature, category).
47    #[serde(default)]
48    pub type_constructors: Vec<(String, Vec<Type>, ConstructorCategory)>,
49    /// Constraints mapping a rigid generic variable name to its interface.
50    /// Introduced when an interface type appears in parameter position:
51    /// fn(i: I): R  ⇒  i : A  with A: I  stored here.
52    #[serde(default)]
53    pub interface_constraints: HashMap<String, Type>,
54    /// Counter for generating unique rigid generic variable names.
55    #[serde(default)]
56    pub rigid_counter: u64,
57    /// Flat, whole-program registry of every `type X <- list { ... }` record
58    /// alias declared anywhere, including inside `mod` bodies. Unlike
59    /// `typing_context.aliases`, this is never scoped away at a module
60    /// boundary: R's S3 class system has no module privacy, so transpilation
61    /// needs the full picture to compute structural supertypes for the class
62    /// vector (see `record_field_class` callers in `processes::transpiling`).
63    #[serde(default)]
64    pub record_aliases: Vec<(String, Type)>,
65    /// Named type embedding (`embed field: Type`): provenance of every function
66    /// auto-generated by forwarding/reconstruction, as `(type_name, method_name,
67    /// source_field_name)`. Used to detect a later explicit definition that
68    /// collides with an inherited embedded function (E-EMBED-003).
69    #[serde(default)]
70    pub embedded_methods: Vec<(String, String, String)>,
71    /// RFC-TR-031: lines injected at the top of a `Test { ... }` file so the
72    /// test body can reach `@testable` private members of the enclosing module
73    /// (e.g. `sq <- Math$.test_sq`). Set while transpiling a module body in a
74    /// test build; empty otherwise. Not serialised.
75    #[serde(skip)]
76    pub test_preamble: Vec<String>,
77    /// `Self:{ ... }` (generic_constructor.md): the type bound to the
78    /// enclosing function/method's first parameter, set only while
79    /// type-checking that function's body. `None` everywhere else, which is
80    /// what makes `Self` invalid outside a function body.
81    #[serde(skip)]
82    pub self_type: Option<Type>,
83    config: Config,
84}
85
86/// The canonical sentinel nodes seeded into every subtype `Graph` so the
87/// kind-sigil generic categories materialize as intermediate levels of the
88/// lattice. Because `is_subtype_raw` already gives `RecordN <: %_ <: Generic
89/// <: Any` (a concrete Record is a subtype of a record-kinded `KindedGen`, and
90/// any generic is a subtype of the bare `Generic`), seeding one anchor per
91/// kind makes every monomorphized record/interface/char/bool/number auto-nest
92/// under its `G*` node. These sentinels all answer `has_generic() == true`, so
93/// `get_classes` filters them out of generated R `class = c(...)` vectors —
94/// they are a compile-time organisation of the hierarchy only.
95fn generic_sentinels() -> Vec<Type> {
96    let h = HelpData::default();
97    let name = "_".to_string();
98    vec![
99        Type::Generic(name.clone(), h.clone()),
100        Type::KindedGen(Kind::Record, name.clone(), h.clone()),
101        Type::KindedGen(Kind::Interface, name.clone(), h.clone()),
102        Type::KindedGen(Kind::String, name.clone(), h.clone()),
103        Type::KindedGen(Kind::Boolean, name.clone(), h.clone()),
104        Type::IndexGen(name, h),
105    ]
106}
107
108/// A fresh subtype graph pre-seeded with the kind-sigil generic sentinels
109/// (see [`generic_sentinels`]). Built against `Context::empty()` since the
110/// sentinel ordering only exercises the structural `is_subtype_raw` arms
111/// (`(_, Generic)`, `(_, Any)`, the `KindedGen`/`IndexGen` arms) and needs no
112/// typing context.
113fn seeded_subtype_graph() -> Graph<Type> {
114    Graph::new().add_types(&generic_sentinels(), &Context::empty())
115}
116
117impl Default for Context {
118    fn default() -> Self {
119        let config = Config::default();
120        Context {
121            config: config.clone(),
122            typing_context: VarType::from_config(config),
123            subtypes: seeded_subtype_graph(),
124            type_constructors: Vec::new(),
125            interface_constraints: HashMap::new(),
126            rigid_counter: 0,
127            record_aliases: Vec::new(),
128            embedded_methods: Vec::new(),
129            test_preamble: Vec::new(),
130            self_type: None,
131        }
132    }
133}
134
135impl From<Vec<(Lang, Type)>> for Context {
136    fn from(val: Vec<(Lang, Type)>) -> Self {
137        let val2: Vec<(Var, Type)> = val
138            .iter()
139            .map(|(lan, typ)| (Var::from_language(lan.clone()).unwrap(), typ.clone()))
140            .collect();
141        Context {
142            typing_context: val2.into(),
143            ..Context::default()
144        }
145    }
146}
147
148impl Context {
149    pub fn new(types: Vec<(Var, Type)>) -> Context {
150        Context {
151            typing_context: types.into(),
152            ..Context::default()
153        }
154    }
155
156    pub fn empty() -> Self {
157        Context {
158            config: Config::default(),
159            typing_context: VarType::new(),
160            subtypes: Graph::new(),
161            type_constructors: Vec::new(),
162            interface_constraints: HashMap::new(),
163            rigid_counter: 0,
164            record_aliases: Vec::new(),
165            embedded_methods: Vec::new(),
166            test_preamble: Vec::new(),
167            self_type: None,
168        }
169    }
170
171    pub fn set_config(self, config: Config) -> Self {
172        Self { config, ..self }
173    }
174
175    pub fn set_as_module_context(self) -> Context {
176        Self {
177            config: self.config.set_as_module(),
178            ..self
179        }
180    }
181
182    pub fn set_test_mode(self, val: bool) -> Context {
183        Self {
184            config: self.config.set_test_mode(val),
185            ..self
186        }
187    }
188
189    pub fn get_test_mode(&self) -> bool {
190        self.config.test_mode
191    }
192
193    pub fn set_test_preamble(self, lines: Vec<String>) -> Context {
194        Self {
195            test_preamble: lines,
196            ..self
197        }
198    }
199
200    /// `Self:{ ... }` (generic_constructor.md §4.1): bind/clear the type
201    /// denoted by `Self` for the duration of typing a function body.
202    pub fn set_self_type(self, self_type: Option<Type>) -> Context {
203        Self { self_type, ..self }
204    }
205
206    pub fn set_in_module_body(self) -> Self {
207        Self {
208            config: self.config.set_in_module_body(true),
209            ..self
210        }
211    }
212
213    pub fn is_in_module_body(&self) -> bool {
214        self.config.in_module_body
215    }
216
217    /// Retourne un nouveau Context avec le Graph de sous-typage mis à jour
218    pub fn with_subtypes(self, subtypes: Graph<Type>) -> Self {
219        Self { subtypes, ..self }
220    }
221
222    pub fn get_members(&self) -> Vec<(Var, Type)> {
223        self.typing_context
224            .variables()
225            .chain(self.aliases())
226            .cloned()
227            .collect::<Vec<_>>()
228    }
229
230    pub fn variable_exist(&self, var: Var) -> Option<Var> {
231        self.typing_context.variable_exist(var)
232    }
233
234    pub fn get_type_from_variable(&self, var: &Var) -> Result<Type, String> {
235        let res = self
236            .variables()
237            .flat_map(|(var2, typ)| {
238                let conditions = (var.name == var2.name)
239                    && (var.is_opaque == var2.is_opaque)
240                    && var.related_type.is_subtype(&var2.related_type, self).0;
241                if conditions {
242                    Some(typ.clone())
243                } else {
244                    None
245                }
246            })
247            .reduce(|acc, x| if x.is_subtype(&acc, self).0 { x } else { acc });
248        match res {
249            Some(typ) => Ok(typ),
250            _ => Err(format!(
251                "Didn't find {} in the context: {}",
252                var.get_name(),
253                self.display_typing_context()
254            )),
255        }
256    }
257
258    pub fn get_types_from_name(&self, name: &str) -> Vec<Type> {
259        self.variables()
260            .filter(|(var, _)| var.get_name() == name)
261            .map(|(_, typ)| typ.clone())
262            .collect()
263    }
264
265    pub fn get_type_from_aliases(&self, var: &Var) -> Option<Type> {
266        self.aliases()
267            .flat_map(|(var2, type_)| {
268                let conditions = (var.name == var2.name)
269                    && (var.is_opaque == var2.is_opaque)
270                    && var.related_type.is_subtype(&var2.related_type, self).0;
271                if conditions {
272                    Some(type_.clone())
273                } else {
274                    None
275                }
276            })
277            .next()
278    }
279
280    fn is_matching_alias(&self, var1: &Var, var2: &Var) -> bool {
281        var1.name == var2.name
282    }
283
284    pub fn get_matching_alias_signature(&self, var: &Var) -> Option<(Type, Vec<Type>)> {
285        self.aliases()
286            .find(|(var2, _)| self.is_matching_alias(var, var2))
287            .map(|(var2, target_type)| {
288                if var2.is_opaque() {
289                    (var2.clone().to_alias_type(), vec![])
290                } else if let Type::Params(types, _) = var2.get_type() {
291                    (target_type.clone(), types.clone())
292                } else {
293                    panic!("The related type is not Params([...])");
294                }
295            })
296    }
297
298    pub fn variables(&self) -> Rev<std::vec::IntoIter<&(Var, Type)>> {
299        self.typing_context.variables()
300    }
301
302    pub fn aliases(&self) -> Rev<std::vec::IntoIter<&(Var, Type)>> {
303        self.typing_context.aliases()
304    }
305
306    /// Generate a fresh rigid generic variable name (immutable builder pattern).
307    pub fn fresh_rigid_name(self) -> (String, Self) {
308        let name = format!("__rigid_{}", self.rigid_counter);
309        (
310            name,
311            Self {
312                rigid_counter: self.rigid_counter + 1,
313                ..self
314            },
315        )
316    }
317
318    /// Register a constraint: rigid variable → interface type.
319    pub fn add_interface_constraint(mut self, rigid_name: String, interface: Type) -> Context {
320        self.interface_constraints.insert(rigid_name, interface);
321        self
322    }
323
324    /// Look up the interface constraint for a rigid variable.
325    pub fn get_interface_constraint(&self, name: &str) -> Option<&Type> {
326        self.interface_constraints.get(name)
327    }
328
329    /// Check if a name is a constrained rigid variable.
330    pub fn is_rigid_constrained(&self, name: &str) -> bool {
331        self.interface_constraints.contains_key(name)
332    }
333
334    pub fn push_var_type(self, lang: Var, typ: Type, context: &Context) -> Context {
335        let reduced_type = typ.reduce(context);
336        let types = reduced_type.extract_types();
337        let var_type = self
338            .typing_context
339            .clone()
340            .pipe(|vt| {
341                if reduced_type.is_interface() && lang.is_variable() {
342                    vt.clone()
343                        .push_interface(lang.clone(), reduced_type, typ.clone(), context)
344                } else {
345                    vt.push_var_type(&[(lang.clone(), typ.clone())])
346                }
347            })
348            .push_types(&types);
349        let new_subtypes = self.subtypes.add_types(&types, context);
350        Context {
351            typing_context: var_type,
352            subtypes: new_subtypes,
353            ..self
354        }
355    }
356
357    pub fn replace_or_push_var_type(self, lang: Var, typ: Type, context: &Context) -> Context {
358        let types = typ.reduce(context).extract_types();
359        let var_type = self
360            .typing_context
361            .clone()
362            .replace_or_push_var_type(&[(lang.clone(), typ.clone())])
363            .push_types(&types);
364        let new_subtypes = self.subtypes.add_types(&types, context);
365        Context {
366            typing_context: var_type,
367            subtypes: new_subtypes,
368            ..self
369        }
370    }
371
372    // Remove variables from the context
373    // For removing added variables for evaluating a function's body
374    pub fn remove_vars(self, vars: &[Var]) -> Context {
375        Context {
376            typing_context: self.typing_context.remove_vars(vars),
377            ..self
378        }
379    }
380
381    pub fn push_types(self, types: &[Type]) -> Self {
382        Self {
383            typing_context: self.typing_context.push_types(types),
384            ..self
385        }
386    }
387
388    pub fn get_type_from_existing_variable(&self, var: Var) -> Type {
389        if let Type::UnknownFunction(_) = var.get_type() {
390            var.get_type()
391        } else {
392            self.typing_context
393                .variables()
394                .find(|(v, _)| var.match_with(v, self))
395                .map(|(_, ty)| ty)
396                // Return Any type instead of panicking if variable not found
397                .unwrap_or(&Type::Any(var.get_help_data()))
398                .clone()
399        }
400    }
401
402    pub fn get_true_variable(&self, var: &Var) -> Var {
403        let res = self
404            .typing_context
405            .variables()
406            .find(|(v, _)| var.match_with(v, self))
407            .map(|(v, _)| v);
408        match res {
409            Some(vari) => vari.clone(),
410            _ => {
411                // Return the variable with UnknownFunction type if it's a standard function
412                // Otherwise return with Any type to allow error collection
413                if self.is_an_untyped_function(&var.get_name()) {
414                    var.clone()
415                        .set_type(Type::UnknownFunction(var.get_help_data()))
416                } else {
417                    var.clone().set_type(Type::Any(var.get_help_data()))
418                }
419            }
420        }
421    }
422
423    fn is_a_standard_function(&self, name: &str) -> bool {
424        !self.typing_context.name_exists_outside_of_std(name)
425    }
426
427    pub fn is_an_untyped_function(&self, name: &str) -> bool {
428        self.is_a_standard_function(name)
429    }
430
431    pub fn get_class(&self, t: &Type) -> String {
432        // For a named alias whose underlying type is a record, return the alias name directly.
433        // push_types may have also registered the same record with an auto-generated "Record0"
434        // name; searching aliases by type value would find that first (insertion-order) and
435        // return the wrong name.
436        if let Type::Alias(name, _, false, _) = t {
437            if let Some((_, underlying)) = self.aliases().find(|(v, _)| v.get_name() == *name) {
438                if matches!(underlying, Type::Record(_, _)) {
439                    return "'".to_string() + name + "'";
440                }
441            }
442        }
443        let reduced = t.reduce(self);
444        if matches!(reduced, Type::Any(_)) {
445            if let Type::Alias(name, _, _, _) = t {
446                return "'".to_string() + name + "'";
447            }
448        }
449        self.typing_context.get_class(&reduced)
450    }
451
452    pub fn get_class_unquoted(&self, t: &Type) -> String {
453        // Same rationale as get_class: bypass the record-type alias search for named aliases.
454        if let Type::Alias(name, _, false, _) = t {
455            if let Some((_, underlying)) = self.aliases().find(|(v, _)| v.get_name() == *name) {
456                if matches!(underlying, Type::Record(_, _)) {
457                    return name.clone();
458                }
459            }
460        }
461        let reduced = t.reduce(self);
462        if matches!(reduced, Type::Any(_)) {
463            if let Type::Alias(name, _, _, _) = t {
464                return name.clone();
465            }
466        }
467        self.typing_context.get_class_unquoted(&reduced)
468    }
469
470    pub fn module_aliases(&self) -> Vec<(Var, Type)> {
471        self.variables()
472            .flat_map(|(_, typ)| typ.clone().to_module_type())
473            .flat_map(|module| module.get_aliases())
474            .collect()
475    }
476
477    pub fn get_type_anotations(&self) -> String {
478        self.aliases()
479            .chain(
480                [
481                    (Var::from_name("Integer"), builder::integer_type_default()),
482                    (
483                        Var::from_name("Character"),
484                        builder::character_type_default(),
485                    ),
486                    (Var::from_name("Number"), builder::number_type()),
487                    (Var::from_name("Boolean"), builder::boolean_type()),
488                ]
489                .iter(),
490            )
491            .cloned()
492            .chain(self.module_aliases())
493            .filter(|(_, typ)| typ.clone().to_module_type().is_err())
494            // Records that have a user-given alias are excluded: their `as.X` cast
495            // is emitted by the inline constructor/validator pipeline. Anonymous
496            // records (auto-named `Record0`, `Record1`, …) have no constructor, so
497            // they still need their `as.RecordN` annotation generated here.
498            .filter(|(var, typ)| {
499                !matches!(typ, Type::Record(_, _)) || is_anonymous_record_name(&var.get_name())
500            })
501            // Aliases whose underlying type still mentions an unresolved generic
502            // (e.g. `type Animator<%T> <- %T & list { ... }`) have no single
503            // fixed runtime class — there's no monomorphization, so a static
504            // `as.Animator` cast can't be generated. Without this, get_class
505            // panics trying to render `%T` as an R class name.
506            .filter(|(_, typ)| !typ.has_generic())
507            .map(|(var, typ)| (typ, var.get_name()))
508            .map(|(typ, name)| {
509                let name0 = if ["Integer", "Character", "Boolean", "Number"]
510                    .iter()
511                    .any(|x| name == *x)
512                {
513                    format!("'{}', ", name)
514                } else {
515                    Default::default()
516                };
517                let class_str = self.get_class(&typ);
518                let prefix = if name0.is_empty() && class_str != format!("'{}'", name) {
519                    format!("'{}', ", name)
520                } else {
521                    name0
522                };
523                format!(
524                    "as.{} <- function(x) x |> struct(c({}{}, {}))",
525                    name,
526                    prefix,
527                    class_str,
528                    self.get_classes(&typ).unwrap()
529                )
530            })
531            .collect::<Vec<_>>()
532            .join("\n")
533    }
534
535    pub fn get_type_anotation(&self, t: &Type) -> String {
536        self.typing_context.get_type_anotation(t)
537    }
538
539    pub fn get_type_anotation_no_parentheses(&self, t: &Type) -> String {
540        self.typing_context.get_type_anotation_no_parentheses(t)
541    }
542
543    pub fn get_classes(&self, t: &Type) -> Option<String> {
544        let res = self
545            .subtypes
546            .get_supertypes(t, self)
547            .iter()
548            .filter(|typ| (*typ).clone().to_module_type().is_err())
549            .filter(|typ| !typ.is_empty())
550            // A supertype that still mentions an unresolved generic (e.g. a
551            // record-kinded `%T` picked up structurally from a generic alias
552            // like `Animator<%T> <- %T & list {...}`) has no R class name —
553            // it's a compile-time-only constraint, never render it.
554            .filter(|typ| !typ.has_generic())
555            .map(|typ| self.get_class(typ))
556            .collect::<Vec<_>>()
557            .join(", ");
558        if res.is_empty() {
559            Some("'None'".to_string())
560        } else {
561            Some(res)
562        }
563    }
564
565    pub fn get_functions(&self, var1: Var) -> Vec<(Var, Type)> {
566        self.typing_context
567            .variables()
568            .filter(|(var2, typ)| {
569                let reduced_type1 = var1.get_type().reduce(self);
570                let reduced_type2 = var2.get_type().reduce(self);
571                var1.get_name() == var2.get_name()
572                    && typ.is_function()
573                    && reduced_type1.is_subtype(&reduced_type2, self).0
574            })
575            .cloned()
576            .collect()
577    }
578
579    pub fn get_all_generic_functions(&self) -> Vec<(Var, Type)> {
580        let res = self
581            .typing_context
582            .variables()
583            .filter(|(_, typ)| typ.is_function())
584            .filter(|(var, _)| not_in_blacklist(&var.get_name()))
585            .filter(|(var, _)| !var.get_type().is_any())
586            .collect::<HashSet<_>>();
587        res.iter()
588            .map(|(var, typ)| (var.clone().add_backticks_if_percent(), typ.clone()))
589            .collect()
590    }
591
592    pub fn get_first_matching_function(&self, var1: Var) -> Type {
593        let res = self.typing_context.variables().find(|(var2, typ)| {
594            let reduced_type1 = var1.get_type().reduce(self);
595            let reduced_type2 = var2.get_type().reduce(self);
596            var1.get_name() == var2.get_name()
597                && typ.is_function()
598                && (reduced_type1.is_subtype(&reduced_type2, self).0
599                    || reduced_type1.is_upperrank_of(&reduced_type2))
600        });
601        if let Some(res) = res {
602            res.1.clone()
603        } else {
604            self.typing_context
605                .standard_library()
606                .iter()
607                .find(|(var2, _)| var2.get_name() == var1.get_name())
608                .unwrap_or_else(|| {
609                    panic!(
610                        "Can't find var {} in the context:\n {}",
611                        var1,
612                        self.display_typing_context()
613                    )
614                })
615                .1
616                .clone()
617        }
618    }
619
620    pub fn get_matching_typed_functions(&self, var1: Var) -> Vec<Type> {
621        self.typing_context
622            .variables()
623            .filter(|(var2, typ)| {
624                let reduced_type1 = var1.get_type().reduce(self);
625                let reduced_type2 = var2.get_type().reduce(self);
626                var1.get_name() == var2.get_name()
627                    && typ.is_function()
628                    && (reduced_type1.is_subtype(&reduced_type2, self).0
629                        || reduced_type1.is_upperrank_of(&reduced_type2))
630            })
631            .map(|(_, typ)| typ.clone())
632            .collect::<Vec<_>>()
633    }
634
635    pub fn get_matching_untyped_functions(&self, var: Var) -> Result<Vec<Type>, String> {
636        let name1 = var.get_name();
637        let std_lib = self.typing_context.standard_library();
638        let res = std_lib
639            .iter()
640            .find(|(var2, _)| var2.get_name() == name1)
641            .map(|(_, typ)| typ);
642        match res {
643            Some(val) => Ok(vec![val.clone()]),
644            _ => Err(format!(
645                "Can't find var {} in the context:\n {}",
646                var,
647                self.display_typing_context()
648            )),
649        }
650    }
651
652    pub fn get_matching_functions(&self, var: Var) -> Result<Vec<Type>, String> {
653        let res = self.get_matching_typed_functions(var.clone());
654        if res.is_empty() {
655            self.get_matching_untyped_functions(var)
656        } else {
657            Ok(res)
658        }
659    }
660
661    pub fn get_type_from_class(&self, class: &str) -> Type {
662        self.typing_context.get_type_from_class(class)
663    }
664
665    pub fn add_arg_types(&self, params: &[ArgumentType]) -> Context {
666        let param_types = params
667            .iter()
668            .map(|arg_typ| reduce_type(self, &arg_typ.get_type()).for_var())
669            .map(|typ| match typ.to_owned() {
670                Type::Function(typs, _, _) => {
671                    if !typs.is_empty() {
672                        typs[0].get_type()
673                    } else {
674                        typ
675                    }
676                }
677                t => t,
678            })
679            .collect::<Vec<_>>();
680        params
681            .iter()
682            .zip(param_types.clone())
683            .map(|(arg_typ, par_typ): (&ArgumentType, Type)| {
684                (
685                    Var::from_name(&arg_typ.get_argument_str())
686                        .set_type(reduce_type(self, &par_typ)),
687                    reduce_type(self, &arg_typ.get_type()),
688                )
689            })
690            .fold(self.clone(), |cont: Context, (var, typ): (Var, Type)| {
691                cont.clone().push_var_type(var, typ, &cont)
692            })
693    }
694
695    pub fn set_environment(&self, e: Environment) -> Context {
696        Context {
697            config: self.config.set_environment(e),
698            ..self.clone()
699        }
700    }
701
702    pub fn display_typing_context(&self) -> String {
703        let res = self
704            .variables()
705            .chain(self.aliases())
706            .map(|(var, typ)| format!("{} ==> {}", var, typ))
707            .collect::<Vec<_>>()
708            .join("\n");
709        format!("CONTEXT:\n{}", res)
710    }
711
712    pub fn error(&self, msg: String) -> String {
713        format!("{}{}", msg, self.display_typing_context())
714    }
715
716    pub fn push_alias(self, alias_name: String, typ: Type) -> Self {
717        Context {
718            typing_context: self.typing_context.push_alias(alias_name, typ),
719            ..self
720        }
721    }
722
723    /// Register a user-declared `typeconstructor` in the registry.
724    pub fn push_type_constructor(
725        self,
726        name: String,
727        parameters: Vec<Type>,
728        category: ConstructorCategory,
729    ) -> Self {
730        let mut type_constructors = self.type_constructors.clone();
731        // Last declaration wins: drop any previous entry with the same name.
732        type_constructors.retain(|(n, _, _)| n != &name);
733        type_constructors.push((name, parameters, category));
734        Context {
735            type_constructors,
736            ..self
737        }
738    }
739
740    /// Look up a declared `typeconstructor` by name.
741    pub fn get_type_constructor(
742        &self,
743        name: &str,
744    ) -> Option<&(String, Vec<Type>, ConstructorCategory)> {
745        self.type_constructors.iter().find(|(n, _, _)| n == name)
746    }
747
748    /// Register a `type X <- list { ... }` record alias in the whole-program
749    /// registry, regardless of the current module scope. No-op for non-record
750    /// aliases. Last declaration for a given name wins.
751    pub fn push_record_alias(self, name: String, typ: Type) -> Self {
752        if !matches!(typ, Type::Record(_, _)) {
753            return self;
754        }
755        let mut record_aliases = self.record_aliases.clone();
756        record_aliases.retain(|(n, _)| n != &name);
757        record_aliases.push((name, typ));
758        Context {
759            record_aliases,
760            ..self
761        }
762    }
763
764    /// Merge another context's whole-program record-alias registry into this
765    /// one. Used at module boundaries, where the rest of the inner typing
766    /// context is intentionally discarded for encapsulation but this registry
767    /// must still bubble up (see `Lang::Module` in `processes::type_checking`).
768    pub fn merge_record_aliases(self, other: &Context) -> Self {
769        let mut record_aliases = self.record_aliases.clone();
770        for (name, typ) in &other.record_aliases {
771            if !record_aliases.iter().any(|(n, _)| n == name) {
772                record_aliases.push((name.clone(), typ.clone()));
773            }
774        }
775        Context {
776            record_aliases,
777            ..self
778        }
779    }
780
781    /// Record that `method_name` on `type_name` was auto-generated by named type
782    /// embedding (`embed field: Type`), forwarded from `field_name`. See
783    /// `processes::type_checking::embedding`.
784    pub fn push_embedded_method(
785        self,
786        type_name: String,
787        method_name: String,
788        field_name: String,
789    ) -> Self {
790        let mut embedded_methods = self.embedded_methods.clone();
791        embedded_methods.push((type_name, method_name, field_name));
792        Context {
793            embedded_methods,
794            ..self
795        }
796    }
797
798    /// If `method_name` on `type_name` was inherited via named type embedding,
799    /// return the source field name it was forwarded from.
800    pub fn get_embedded_method(&self, type_name: &str, method_name: &str) -> Option<String> {
801        self.embedded_methods
802            .iter()
803            .find(|(t, m, _)| t == type_name && m == method_name)
804            .map(|(_, _, field)| field.clone())
805    }
806
807    pub fn push_alias2(self, alias_var: Var, typ: Type) -> Self {
808        Context {
809            typing_context: self.typing_context.push_alias2(alias_var, typ),
810            ..self
811        }
812    }
813
814    pub fn in_a_project(&self) -> bool {
815        self.config.environment == Environment::Project
816    }
817
818    pub fn get_unification_map(
819        &self,
820        entered_types: &[Type],
821        param_types: &[Type],
822    ) -> Option<UnificationMap> {
823        let res = entered_types
824            .iter()
825            .zip(param_types.iter())
826            .map(|(val_typ, par_typ)| match_types_to_generic(self, &val_typ.clone(), par_typ))
827            .collect::<Option<Vec<_>>>();
828
829        let val = res
830            .map(|vec| vec.iter().flatten().cloned().collect::<Vec<_>>())
831            .map(UnificationMap::new);
832
833        val
834    }
835
836    fn s3_type_definition(&self, var: &Var, typ: &Type) -> String {
837        let first_part = format!("{} <- function(x) x |> ", var.get_name());
838        match typ {
839            Type::RClass(v, _) => format!(
840                "{} struct(c({}))",
841                first_part,
842                v.iter().cloned().collect::<Vec<_>>().join(", ")
843            ),
844            _ => {
845                let class = if typ.is_primitive() {
846                    format!("'{}'", var.get_name())
847                } else {
848                    self.get_class(typ)
849                };
850                format!("{} struct(c({}))", first_part, class)
851            }
852        }
853    }
854
855    fn get_primitive_type_definition(&self) -> Vec<String> {
856        let primitives = [
857            ("Integer", builder::integer_type_default()),
858            ("Character", builder::character_type_default()),
859            ("Number", builder::number_type()),
860            ("Boolean", builder::boolean_type()),
861        ];
862        let new_context = self.clone().push_types(
863            &primitives
864                .iter()
865                .map(|(_, typ)| typ)
866                .cloned()
867                .collect::<Vec<_>>(),
868        );
869        primitives
870            .iter()
871            .map(|(name, prim)| {
872                (
873                    name,
874                    new_context.get_classes(prim).unwrap(),
875                    new_context.get_class(prim),
876                )
877            })
878            .map(|(name, cls, cl)| {
879                format!("{} <- function(x) x |> struct(c({}, {}))", name, cls, cl)
880            })
881            .collect::<Vec<_>>()
882    }
883
884    pub fn get_related_functions(&self, typ: &Type, functions: &VarFunction) -> Vec<Lang> {
885        let names = self.typing_context.get_related_functions(typ);
886        functions.get_bodies(&names)
887    }
888
889    pub fn get_functions_from_type(&self, typ: &Type) -> Vec<(Var, Type)> {
890        self.variables()
891            .filter(|&(var, typ2)| typ2.is_function() && var.get_type() == *typ)
892            .cloned()
893            .collect()
894    }
895
896    pub fn get_functions_from_name(&self, name: &str) -> Vec<(Var, Type)> {
897        self.variables()
898            .filter(|&(var, typ2)| typ2.is_function() && var.get_name() == name)
899            .cloned()
900            .collect()
901    }
902
903    pub fn get_type_definition(&self, _functions: &VarFunction) -> String {
904        match self.get_target_language() {
905            TargetLanguage::R => self
906                .typing_context
907                .aliases
908                .iter()
909                .map(|(var, typ)| self.s3_type_definition(var, typ))
910                .chain(self.get_primitive_type_definition().iter().cloned())
911                .collect::<Vec<_>>()
912                .join("\n"),
913            TargetLanguage::JS => {
914                todo!();
915            }
916        }
917    }
918
919    pub fn update_variable(self, var: Var) -> Self {
920        Self {
921            typing_context: self.typing_context.update_variable(var),
922            ..self
923        }
924    }
925
926    pub fn set_target_language(self, language: TargetLanguage) -> Self {
927        Self {
928            config: self.config.set_target_language(language),
929            typing_context: self.typing_context.source(language),
930            ..self
931        }
932    }
933
934    pub fn set_default_var_types(self) -> Self {
935        Self {
936            typing_context: self.typing_context.set_default_var_types(),
937            ..self
938        }
939    }
940
941    pub fn get_target_language(&self) -> TargetLanguage {
942        self.config.get_target_language()
943    }
944
945    pub fn set_new_aliase_signature(self, alias: &str, related_type: Type) -> Self {
946        let alias = Var::from_type(alias.parse::<Type>().unwrap()).unwrap();
947        self.clone().push_alias2(alias, related_type)
948    }
949
950    pub fn extract_module_as_vartype(&self, module_name: &str) -> Self {
951        let typ = self
952            .get_type_from_variable(&Var::from_name(module_name))
953            .expect("The module name was not found");
954        let empty_context = Context::default();
955        let new_context = match typ.clone() {
956            Type::Module(args, _) => {
957                args.iter()
958                    .rev()
959                    .map(|arg_type| {
960                        (
961                            Var::try_from(arg_type.0.clone()).unwrap(),
962                            arg_type.1.clone(),
963                        )
964                    }) //TODO: Differenciate between pushing variable and
965                    //aliases
966                    .fold(empty_context.clone(), |acc, (var, typ)| {
967                        acc.clone().push_var_type(var, typ, &acc)
968                    })
969            }
970            _ => panic!("{} is not a module", module_name),
971        };
972        new_context
973            .clone()
974            .push_var_type(Var::from_name(module_name), typ, &new_context)
975    }
976
977    pub fn get_vartype(&self) -> VarType {
978        self.clone().typing_context
979    }
980
981    pub fn get_environment(&self) -> Environment {
982        self.config.environment
983    }
984    pub fn extend_typing_context(self, var_types: VarType) -> Self {
985        Self {
986            typing_context: self.typing_context + var_types,
987            ..self
988        }
989    }
990}
991
992impl Add for Context {
993    type Output = Self;
994
995    fn add(self, other: Self) -> Self::Output {
996        let mut type_constructors = self.type_constructors;
997        for (name, params, cat) in other.type_constructors {
998            type_constructors.retain(|(n, _, _)| n != &name);
999            type_constructors.push((name, params, cat));
1000        }
1001        let mut interface_constraints = self.interface_constraints;
1002        interface_constraints.extend(other.interface_constraints);
1003        let rigid_counter = self.rigid_counter.max(other.rigid_counter);
1004        let mut test_preamble = self.test_preamble;
1005        test_preamble.extend(other.test_preamble);
1006        let mut record_aliases = self.record_aliases;
1007        for entry in other.record_aliases {
1008            if !record_aliases.contains(&entry) {
1009                record_aliases.push(entry);
1010            }
1011        }
1012        let mut embedded_methods = self.embedded_methods;
1013        for entry in other.embedded_methods {
1014            if !embedded_methods.contains(&entry) {
1015                embedded_methods.push(entry);
1016            }
1017        }
1018        Context {
1019            typing_context: self.typing_context + other.typing_context,
1020            subtypes: self.subtypes + other.subtypes,
1021            type_constructors,
1022            interface_constraints,
1023            rigid_counter,
1024            record_aliases,
1025            embedded_methods,
1026            test_preamble,
1027            self_type: None,
1028            config: self.config,
1029        }
1030    }
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035    use super::*;
1036
1037    #[test]
1038    fn test_default_context1() {
1039        let context = Context::default();
1040        assert!(!context.display_typing_context().is_empty());
1041    }
1042
1043    #[test]
1044    fn test_record_nests_under_grecord_sentinel() {
1045        let ctx = Context::default();
1046        let rec = builder::record_type(&[("x".to_string(), builder::integer_type_default())]);
1047        let graph = ctx.subtypes.clone().add_type(rec.clone(), &ctx);
1048        let supers = graph.get_supertypes(&rec, &ctx);
1049        assert!(
1050            supers
1051                .iter()
1052                .any(|t| matches!(t, Type::KindedGen(Kind::Record, _, _))),
1053            "a record must nest under the GRecord (%_) sentinel; supers = {:?}",
1054            supers
1055        );
1056        assert!(
1057            supers.iter().any(|t| matches!(t, Type::Generic(_, _))),
1058            "GRecord must itself sit under the bare Generic sentinel; supers = {:?}",
1059            supers
1060        );
1061    }
1062
1063    #[test]
1064    fn test_generic_sentinels_absent_from_r_classes() {
1065        let ctx = Context::default();
1066        let rec = builder::record_type(&[("x".to_string(), builder::integer_type_default())]);
1067        let graph = ctx.subtypes.clone().add_type(rec.clone(), &ctx);
1068        let ctx = ctx.with_subtypes(graph);
1069        let classes = ctx.get_classes(&rec).unwrap();
1070        assert!(
1071            !classes.contains("GRecord") && !classes.contains('%') && !classes.contains("Generic"),
1072            "generic sentinels must be filtered out of generated R classes, got: {}",
1073            classes
1074        );
1075    }
1076}