mago_codex/ttype/
combination.rs1use std::collections::BTreeMap;
2
3use ahash::HashSet;
4use ordered_float::OrderedFloat;
5
6use mago_atom::Atom;
7use mago_atom::AtomMap;
8use mago_atom::AtomSet;
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
17bitflags::bitflags! {
18 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21 pub struct CombinationFlags: u32 {
22 const HAS_OBJECT_TOP_TYPE = 1 << 0;
23 const LIST_ARRAY_SOMETIMES_FILLED = 1 << 1;
24 const LIST_ARRAY_ALWAYS_FILLED = 1 << 2;
25 const KEYED_ARRAY_SOMETIMES_FILLED = 1 << 3;
26 const KEYED_ARRAY_ALWAYS_FILLED = 1 << 4;
27 const HAS_EMPTY_ARRAY = 1 << 5;
28 const HAS_KEYED_ARRAY = 1 << 6;
29 const GENERIC_MIXED = 1 << 7;
30 const HAS_MIXED = 1 << 8;
31 const RESOURCE = 1 << 9;
32 const OPEN_RESOURCE = 1 << 10;
33 const CLOSED_RESOURCE = 1 << 11;
34
35 const FALSY_MIXED_SET = 1 << 12;
37 const FALSY_MIXED_VALUE = 1 << 13;
38 const TRUTHY_MIXED_SET = 1 << 14;
39 const TRUTHY_MIXED_VALUE = 1 << 15;
40 const NONNULL_MIXED_SET = 1 << 16;
41 const NONNULL_MIXED_VALUE = 1 << 17;
42 const MIXED_FROM_LOOP_ISSET_SET = 1 << 18;
43 const MIXED_FROM_LOOP_ISSET_VALUE = 1 << 19;
44 }
45}
46
47impl CombinationFlags {
48 #[inline]
50 #[must_use]
51 pub fn get_tristate(self, set_bit: Self, value_bit: Self) -> Option<bool> {
52 if self.contains(set_bit) { Some(self.contains(value_bit)) } else { None }
53 }
54
55 #[inline]
57 pub fn set_tristate(&mut self, set_bit: Self, value_bit: Self, value: Option<bool>) {
58 match value {
59 None => {
60 self.remove(set_bit);
61 self.remove(value_bit);
62 }
63 Some(false) => {
64 self.insert(set_bit);
65 self.remove(value_bit);
66 }
67 Some(true) => {
68 self.insert(set_bit);
69 self.insert(value_bit);
70 }
71 }
72 }
73
74 #[inline]
75 #[must_use]
76 pub fn falsy_mixed(self) -> Option<bool> {
77 self.get_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE)
78 }
79
80 #[inline]
81 pub fn set_falsy_mixed(&mut self, value: Option<bool>) {
82 self.set_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE, value);
83 }
84
85 #[inline]
86 #[must_use]
87 pub fn truthy_mixed(self) -> Option<bool> {
88 self.get_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE)
89 }
90
91 #[inline]
92 pub fn set_truthy_mixed(&mut self, value: Option<bool>) {
93 self.set_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE, value);
94 }
95
96 #[inline]
97 #[must_use]
98 pub fn nonnull_mixed(self) -> Option<bool> {
99 self.get_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE)
100 }
101
102 #[inline]
103 pub fn set_nonnull_mixed(&mut self, value: Option<bool>) {
104 self.set_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE, value);
105 }
106
107 #[inline]
108 #[must_use]
109 pub fn mixed_from_loop_isset(self) -> Option<bool> {
110 self.get_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE)
111 }
112
113 #[inline]
114 pub fn set_mixed_from_loop_isset(&mut self, value: Option<bool>) {
115 self.set_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE, value);
116 }
117}
118
119#[derive(Debug)]
120pub struct TypeCombination {
121 pub flags: CombinationFlags,
122 pub value_types: AtomMap<TAtomic>,
123 pub enum_names: HashSet<(Atom, Option<Atom>)>,
124 pub object_type_params: AtomMap<(Atom, Vec<TUnion>)>,
125 pub object_static: AtomMap<bool>,
126 pub list_array_counts: Option<HashSet<usize>>,
127 pub keyed_array_entries: BTreeMap<ArrayKey, (bool, TUnion)>,
128 pub list_array_entries: BTreeMap<usize, (bool, TUnion)>,
129 pub keyed_array_parameters: Option<(TUnion, TUnion)>,
130 pub list_array_parameter: Option<TUnion>,
131 pub sealed_arrays: Vec<TArray>,
132 pub integers: Vec<TInteger>,
133 pub literal_strings: AtomSet,
134 pub literal_floats: Vec<OrderedFloat<f64>>,
135 pub class_string_types: AtomMap<TAtomic>,
136 pub derived_types: HashSet<TDerived>,
137}
138
139impl Default for TypeCombination {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145impl TypeCombination {
146 #[must_use]
147 pub fn new() -> Self {
148 let flags = CombinationFlags::LIST_ARRAY_ALWAYS_FILLED | CombinationFlags::KEYED_ARRAY_ALWAYS_FILLED;
149
150 Self {
151 flags,
152 value_types: AtomMap::default(),
153 object_type_params: AtomMap::default(),
154 object_static: AtomMap::default(),
155 list_array_counts: Some(HashSet::default()),
156 keyed_array_entries: BTreeMap::new(),
157 list_array_entries: BTreeMap::new(),
158 keyed_array_parameters: None,
159 list_array_parameter: None,
160 sealed_arrays: Vec::new(),
161 literal_strings: AtomSet::default(),
162 integers: Vec::new(),
163 literal_floats: Vec::new(),
164 class_string_types: AtomMap::default(),
165 enum_names: HashSet::default(),
166 derived_types: HashSet::default(),
167 }
168 }
169
170 #[inline]
171 #[must_use]
172 pub fn is_simple(&self) -> bool {
173 if self.value_types.len() == 1
174 && self.sealed_arrays.is_empty()
175 && !self.flags.contains(CombinationFlags::HAS_KEYED_ARRAY)
176 && !self.flags.contains(CombinationFlags::HAS_EMPTY_ARRAY)
177 && !self.flags.intersects(
178 CombinationFlags::RESOURCE | CombinationFlags::OPEN_RESOURCE | CombinationFlags::CLOSED_RESOURCE,
179 )
180 && self.keyed_array_parameters.is_none()
181 && self.list_array_parameter.is_none()
182 {
183 return self.object_type_params.is_empty()
184 && self.enum_names.is_empty()
185 && self.literal_strings.is_empty()
186 && self.class_string_types.is_empty()
187 && self.integers.is_empty()
188 && self.derived_types.is_empty();
189 }
190
191 false
192 }
193}