Skip to main content

mago_codex/ttype/
combination.rs

1use std::collections::BTreeMap;
2
3use foldhash::HashSet;
4use ordered_float::OrderedFloat;
5
6use mago_word::Word;
7use mago_word::WordMap;
8use mago_word::WordSet;
9
10use crate::ttype::atomic::TAtomic;
11use crate::ttype::atomic::array::TArray;
12use crate::ttype::atomic::array::key::ArrayKey;
13use crate::ttype::atomic::derived::TDerived;
14use crate::ttype::atomic::scalar::int::TInteger;
15use crate::ttype::union::TUnion;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18#[repr(transparent)]
19pub struct CombinationFlags(u32);
20
21impl CombinationFlags {
22    pub const HAS_OBJECT_TOP_TYPE: CombinationFlags = CombinationFlags(1 << 0);
23    pub const LIST_ARRAY_ALWAYS_FILLED: CombinationFlags = CombinationFlags(1 << 2);
24    pub const KEYED_ARRAY_SOMETIMES_FILLED: CombinationFlags = CombinationFlags(1 << 3);
25    pub const KEYED_ARRAY_ALWAYS_FILLED: CombinationFlags = CombinationFlags(1 << 4);
26    pub const HAS_EMPTY_ARRAY: CombinationFlags = CombinationFlags(1 << 5);
27    pub const HAS_KEYED_ARRAY: CombinationFlags = CombinationFlags(1 << 6);
28    pub const GENERIC_MIXED: CombinationFlags = CombinationFlags(1 << 7);
29    pub const HAS_MIXED: CombinationFlags = CombinationFlags(1 << 8);
30    pub const RESOURCE: CombinationFlags = CombinationFlags(1 << 9);
31    pub const OPEN_RESOURCE: CombinationFlags = CombinationFlags(1 << 10);
32    pub const CLOSED_RESOURCE: CombinationFlags = CombinationFlags(1 << 11);
33    // Tristate encoding: 2 bits each (None=neither set, Some(false)=SET only, Some(true)=SET+VALUE)
34    const FALSY_MIXED_SET: CombinationFlags = CombinationFlags(1 << 12);
35    const FALSY_MIXED_VALUE: CombinationFlags = CombinationFlags(1 << 13);
36    const TRUTHY_MIXED_SET: CombinationFlags = CombinationFlags(1 << 14);
37    const TRUTHY_MIXED_VALUE: CombinationFlags = CombinationFlags(1 << 15);
38    const NONNULL_MIXED_SET: CombinationFlags = CombinationFlags(1 << 16);
39    const NONNULL_MIXED_VALUE: CombinationFlags = CombinationFlags(1 << 17);
40    const MIXED_FROM_LOOP_ISSET_SET: CombinationFlags = CombinationFlags(1 << 18);
41    const MIXED_FROM_LOOP_ISSET_VALUE: CombinationFlags = CombinationFlags(1 << 19);
42}
43
44impl CombinationFlags {
45    #[inline]
46    pub const fn insert(&mut self, other: CombinationFlags) {
47        self.0 |= other.0;
48    }
49
50    #[inline]
51    pub const fn remove(&mut self, other: CombinationFlags) {
52        self.0 &= !other.0;
53    }
54
55    #[inline]
56    #[must_use]
57    pub const fn contains(self, other: CombinationFlags) -> bool {
58        (self.0 & other.0) == other.0
59    }
60
61    #[inline]
62    #[must_use]
63    pub const fn intersects(self, other: CombinationFlags) -> bool {
64        (self.0 & other.0) != 0
65    }
66
67    /// Get a tristate value (Option<bool>) from two bits.
68    #[inline]
69    #[must_use]
70    pub fn get_tristate(self, set_bit: CombinationFlags, value_bit: CombinationFlags) -> Option<bool> {
71        if self.contains(set_bit) { Some(self.contains(value_bit)) } else { None }
72    }
73
74    /// Set a tristate value (Option<bool>) using two bits.
75    #[inline]
76    pub fn set_tristate(&mut self, set_bit: CombinationFlags, value_bit: CombinationFlags, value: Option<bool>) {
77        match value {
78            None => {
79                self.remove(set_bit);
80                self.remove(value_bit);
81            }
82            Some(false) => {
83                self.insert(set_bit);
84                self.remove(value_bit);
85            }
86            Some(true) => {
87                self.insert(set_bit);
88                self.insert(value_bit);
89            }
90        }
91    }
92
93    #[inline]
94    #[must_use]
95    pub fn falsy_mixed(self) -> Option<bool> {
96        self.get_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE)
97    }
98
99    #[inline]
100    pub fn set_falsy_mixed(&mut self, value: Option<bool>) {
101        self.set_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE, value);
102    }
103
104    #[inline]
105    #[must_use]
106    pub fn truthy_mixed(self) -> Option<bool> {
107        self.get_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE)
108    }
109
110    #[inline]
111    pub fn set_truthy_mixed(&mut self, value: Option<bool>) {
112        self.set_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE, value);
113    }
114
115    #[inline]
116    #[must_use]
117    pub fn nonnull_mixed(self) -> Option<bool> {
118        self.get_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE)
119    }
120
121    #[inline]
122    pub fn set_nonnull_mixed(&mut self, value: Option<bool>) {
123        self.set_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE, value);
124    }
125
126    #[inline]
127    #[must_use]
128    pub fn mixed_from_loop_isset(self) -> Option<bool> {
129        self.get_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE)
130    }
131
132    #[inline]
133    pub fn set_mixed_from_loop_isset(&mut self, value: Option<bool>) {
134        self.set_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE, value);
135    }
136}
137
138#[derive(Debug)]
139pub struct TypeCombination {
140    pub flags: CombinationFlags,
141    pub value_types: WordMap<TAtomic>,
142    pub enum_names: HashSet<(Word, Option<Word>)>,
143    pub object_type_params: WordMap<(Word, Vec<TUnion>)>,
144    pub object_static: WordMap<bool>,
145    pub keyed_array_entries: BTreeMap<ArrayKey, (bool, TUnion)>,
146    pub list_array_entries: BTreeMap<usize, (bool, TUnion)>,
147    pub keyed_array_parameters: Option<(TUnion, TUnion)>,
148    pub list_array_parameter: Option<TUnion>,
149    pub sealed_arrays: Vec<TArray>,
150    pub sealed_keyed_budget_exhausted: bool,
151    pub integers: Vec<TInteger>,
152    pub literal_strings: WordSet,
153    pub literal_floats: Vec<OrderedFloat<f64>>,
154    pub derived_types: HashSet<TDerived>,
155}
156
157impl Default for TypeCombination {
158    fn default() -> Self {
159        Self::new()
160    }
161}
162
163impl TypeCombination {
164    #[must_use]
165    pub fn new() -> Self {
166        let flags = CombinationFlags::LIST_ARRAY_ALWAYS_FILLED | CombinationFlags::KEYED_ARRAY_ALWAYS_FILLED;
167
168        Self {
169            flags,
170            value_types: WordMap::default(),
171            object_type_params: WordMap::default(),
172            object_static: WordMap::default(),
173            keyed_array_entries: BTreeMap::new(),
174            list_array_entries: BTreeMap::new(),
175            keyed_array_parameters: None,
176            list_array_parameter: None,
177            sealed_arrays: Vec::new(),
178            sealed_keyed_budget_exhausted: false,
179            literal_strings: WordSet::default(),
180            integers: Vec::new(),
181            literal_floats: Vec::new(),
182            enum_names: HashSet::default(),
183            derived_types: HashSet::default(),
184        }
185    }
186
187    #[inline]
188    #[must_use]
189    pub fn is_simple(&self) -> bool {
190        if self.value_types.len() == 1
191            && self.sealed_arrays.is_empty()
192            && !self.flags.contains(CombinationFlags::HAS_KEYED_ARRAY)
193            && !self.flags.contains(CombinationFlags::HAS_EMPTY_ARRAY)
194            && !self.flags.intersects(
195                CombinationFlags::RESOURCE | CombinationFlags::OPEN_RESOURCE | CombinationFlags::CLOSED_RESOURCE,
196            )
197            && self.keyed_array_parameters.is_none()
198            && self.list_array_parameter.is_none()
199        {
200            return self.object_type_params.is_empty()
201                && self.enum_names.is_empty()
202                && self.literal_strings.is_empty()
203                && self.literal_floats.is_empty()
204                && self.integers.is_empty()
205                && self.derived_types.is_empty();
206        }
207
208        false
209    }
210}
211
212impl std::ops::BitOr for CombinationFlags {
213    type Output = Self;
214
215    #[inline]
216    fn bitor(self, rhs: Self) -> Self::Output {
217        CombinationFlags(self.0 | rhs.0)
218    }
219}