typr-core 0.5.7

Core type checking and transpilation logic for TypR - a typed superset of R
Documentation
use crate::components::context::Context;
use crate::components::r#type::type_system::TypeSystem;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt::Debug;
use std::ops::Add;
use std::sync::Arc;

/// `memory`/`root` hold the whole-program subtype registry (grows with every
/// distinct type seen — stdlib signatures included), and used to be
/// deep-cloned on every `Graph::clone()` (in turn triggered by every
/// `Context::clone()`). `Arc`-wrapping makes that clone O(1); mutators
/// (`add_type`, `cache_subtype`) recover an owned value via
/// `Arc::unwrap_or_clone`, which only actually copies when another live
/// `Arc` still shares the allocation.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound = "T: Serialize + for<'a> Deserialize<'a>")]
pub struct Graph<T: TypeSystem> {
    memory: Arc<HashSet<T>>,
    root: Arc<Node<T>>,
    #[serde(skip)]
    subtype_cache: Arc<HashMap<(T, T), bool>>,
}

impl<T: TypeSystem> Default for Graph<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T: TypeSystem> Graph<T> {
    pub fn new() -> Self {
        Graph {
            memory: Arc::new(HashSet::new()),
            root: Arc::new(Node::new()),
            subtype_cache: Arc::new(HashMap::new()),
        }
    }

    /// Vérifie si le résultat de sous-typage est en cache
    pub fn check_subtype_cache(&self, t1: &T, t2: &T) -> Option<bool> {
        self.subtype_cache.get(&(t1.clone(), t2.clone())).copied()
    }

    /// Enregistre un résultat de sous-typage dans le cache
    pub fn cache_subtype(self, t1: T, t2: T, result: bool) -> Self {
        let mut subtype_cache = Arc::unwrap_or_clone(self.subtype_cache);
        subtype_cache.insert((t1, t2), result);
        Graph {
            memory: self.memory,
            root: self.root,
            subtype_cache: Arc::new(subtype_cache),
        }
    }

    pub fn add_type(self, typ: T, context: &Context) -> Self {
        if self.memory.contains(&typ) {
            self
        } else {
            let root = Arc::unwrap_or_clone(self.root);
            let new_root = root.add_type(typ.clone(), context);
            let mut new_memory = Arc::unwrap_or_clone(self.memory);
            new_memory.insert(typ);
            Graph {
                memory: Arc::new(new_memory),
                root: Arc::new(new_root),
                subtype_cache: self.subtype_cache,
            }
        }
    }

    pub fn get_hierarchy(&self) -> String {
        self.root.get_hierarchy()
    }

    /// Deterministic rendering of the graph for fingerprinting: only the
    /// insertion-ordered node tree. `memory` (a `HashSet`) and the subtype
    /// cache iterate in random order and must not reach a fingerprint.
    pub fn structure_debug(&self) -> String {
        format!("{:?}", self.root)
    }

    // Deduplicates while preserving the walk order: the result feeds R class
    // vectors (`struct(c(...))` in types.R), where order drives S3 dispatch
    // and must be stable from one build to the next.
    pub fn get_supertypes(&self, typ: &T, context: &Context) -> Vec<T> {
        self.get_ordered_supertypes(typ, context)
    }

    pub fn get_ordered_supertypes(&self, typ: &T, context: &Context) -> Vec<T> {
        let raw = self.root.get_supertypes(typ, context);
        let mut seen = HashSet::new();
        let mut result = Vec::new();
        for item in raw {
            if seen.insert(item.clone()) {
                result.push(item);
            }
        }
        result
    }

    pub fn add_types(self, typs: &[T], context: &Context) -> Self {
        typs.iter()
            .cloned()
            .fold(self, |acc, x| acc.add_type(x, context))
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound = "T: Serialize + for<'a> Deserialize<'a>")]
pub struct Node<T: TypeSystem> {
    value: T,
    subtypes: Vec<Node<T>>,
}

impl<T: TypeSystem> From<T> for Node<T> {
    fn from(val: T) -> Self {
        Node {
            value: val,
            subtypes: vec![],
        }
    }
}

impl<T: TypeSystem> Default for Node<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T: TypeSystem> Node<T> {
    pub fn new() -> Self {
        Node {
            value: T::default(),
            subtypes: vec![],
        }
    }

    pub fn propagate(self, typ: T, context: &Context) -> Self {
        let graph = Node {
            value: self.value.clone(),
            subtypes: self
                .subtypes
                .iter()
                .cloned()
                .map(|x| x.add_type(typ.clone(), context))
                .collect(),
        };
        if graph == self {
            self.add_subtype(typ)
        } else {
            graph
        }
    }

    pub fn add_subtype(self, typ: T) -> Self {
        Node {
            value: self.value,
            subtypes: self
                .subtypes
                .iter()
                .chain([Node::from(typ)].iter())
                .cloned()
                .collect(),
        }
    }

    pub fn set_subtypes(self, subtypes: Vec<Node<T>>) -> Self {
        Node {
            value: self.value,
            subtypes,
        }
    }

    fn switch_if_reverse_subtype(self, typ: T, context: &Context) -> Self {
        if self.value.is_subtype_raw(&typ, context) {
            Node {
                value: typ,
                subtypes: vec![Node::from(self.value).set_subtypes(self.subtypes)],
            }
        } else {
            self
        }
    }

    pub fn add_type(self, typ: T, context: &Context) -> Self {
        if self.value == typ {
            self
        } else {
            match (
                typ.is_subtype_raw(&self.value, context),
                self.subtypes.len(),
            ) {
                (true, 0) => self.add_subtype(typ),
                (true, _) => self.propagate(typ, context),
                _ => self.switch_if_reverse_subtype(typ, context),
            }
        }
    }

    pub fn get_supertypes(&self, target_type: &T, context: &Context) -> Vec<T> {
        if target_type == &self.value {
            vec![]
        } else if target_type.is_subtype_raw(&self.value, context) {
            self.subtypes
                .iter()
                .flat_map(|x| x.get_supertypes(target_type, context))
                .chain([self.value.clone()].iter().cloned())
                .collect::<Vec<T>>()
        } else {
            vec![]
        }
    }

    pub fn get_hierarchy(&self) -> String {
        self.get_hierarchy_helper(0)
    }

    fn tabulation_from_level(level: i32) -> String {
        (0..level).map(|_| "  ").collect::<Vec<_>>().join("")
    }

    pub fn get_hierarchy_helper(&self, level: i32) -> String {
        let tab = Node::<T>::tabulation_from_level(level);
        let children = self
            .subtypes
            .iter()
            .map(|x| x.get_hierarchy_helper(level + 1))
            .collect::<Vec<_>>()
            .join("\n");
        tab + &self.value.pretty() + "\n" + &children
    }
}

use std::fmt;
impl<T: TypeSystem> fmt::Display for Node<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.get_hierarchy())
    }
}

impl<T: TypeSystem> Add for Graph<T> {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        let context = Context::default(); // Or apply a parameter if necessary
        let merged = other
            .memory
            .iter()
            .cloned()
            .fold(self.clone(), |acc, typ| acc.add_type(typ, &context));
        // Fusionner les caches de sous-typage
        let mut new_cache = Arc::unwrap_or_clone(self.subtype_cache);
        new_cache.extend(other.subtype_cache.iter().map(|(k, v)| (k.clone(), *v)));
        Graph {
            subtype_cache: Arc::new(new_cache),
            ..merged
        }
    }
}