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 pub fn get_tristate(self, set_bit: Self, value_bit: Self) -> Option<bool> {
51 if self.contains(set_bit) { Some(self.contains(value_bit)) } else { None }
52 }
53
54 #[inline]
56 pub fn set_tristate(&mut self, set_bit: Self, value_bit: Self, value: Option<bool>) {
57 match value {
58 None => {
59 self.remove(set_bit);
60 self.remove(value_bit);
61 }
62 Some(false) => {
63 self.insert(set_bit);
64 self.remove(value_bit);
65 }
66 Some(true) => {
67 self.insert(set_bit);
68 self.insert(value_bit);
69 }
70 }
71 }
72
73 #[inline]
74 pub fn falsy_mixed(self) -> Option<bool> {
75 self.get_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE)
76 }
77
78 #[inline]
79 pub fn set_falsy_mixed(&mut self, value: Option<bool>) {
80 self.set_tristate(Self::FALSY_MIXED_SET, Self::FALSY_MIXED_VALUE, value);
81 }
82
83 #[inline]
84 pub fn truthy_mixed(self) -> Option<bool> {
85 self.get_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE)
86 }
87
88 #[inline]
89 pub fn set_truthy_mixed(&mut self, value: Option<bool>) {
90 self.set_tristate(Self::TRUTHY_MIXED_SET, Self::TRUTHY_MIXED_VALUE, value);
91 }
92
93 #[inline]
94 pub fn nonnull_mixed(self) -> Option<bool> {
95 self.get_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE)
96 }
97
98 #[inline]
99 pub fn set_nonnull_mixed(&mut self, value: Option<bool>) {
100 self.set_tristate(Self::NONNULL_MIXED_SET, Self::NONNULL_MIXED_VALUE, value);
101 }
102
103 #[inline]
104 pub fn mixed_from_loop_isset(self) -> Option<bool> {
105 self.get_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE)
106 }
107
108 #[inline]
109 pub fn set_mixed_from_loop_isset(&mut self, value: Option<bool>) {
110 self.set_tristate(Self::MIXED_FROM_LOOP_ISSET_SET, Self::MIXED_FROM_LOOP_ISSET_VALUE, value);
111 }
112}
113
114#[derive(Debug)]
115pub struct TypeCombination {
116 pub flags: CombinationFlags,
117 pub value_types: AtomMap<TAtomic>,
118 pub enum_names: HashSet<(Atom, Option<Atom>)>,
119 pub object_type_params: AtomMap<(Atom, Vec<TUnion>)>,
120 pub object_static: AtomMap<bool>,
121 pub list_array_counts: Option<HashSet<usize>>,
122 pub keyed_array_entries: BTreeMap<ArrayKey, (bool, TUnion)>,
123 pub list_array_entries: BTreeMap<usize, (bool, TUnion)>,
124 pub keyed_array_parameters: Option<(TUnion, TUnion)>,
125 pub list_array_parameter: Option<TUnion>,
126 pub sealed_arrays: Vec<TArray>,
127 pub integers: Vec<TInteger>,
128 pub literal_strings: AtomSet,
129 pub literal_floats: Vec<OrderedFloat<f64>>,
130 pub class_string_types: AtomMap<TAtomic>,
131 pub derived_types: HashSet<TDerived>,
132}
133
134impl Default for TypeCombination {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140impl TypeCombination {
141 pub fn new() -> Self {
142 let flags = CombinationFlags::LIST_ARRAY_ALWAYS_FILLED | CombinationFlags::KEYED_ARRAY_ALWAYS_FILLED;
143
144 Self {
145 flags,
146 value_types: AtomMap::default(),
147 object_type_params: AtomMap::default(),
148 object_static: AtomMap::default(),
149 list_array_counts: Some(HashSet::default()),
150 keyed_array_entries: BTreeMap::new(),
151 list_array_entries: BTreeMap::new(),
152 keyed_array_parameters: None,
153 list_array_parameter: None,
154 sealed_arrays: Vec::new(),
155 literal_strings: AtomSet::default(),
156 integers: Vec::new(),
157 literal_floats: Vec::new(),
158 class_string_types: AtomMap::default(),
159 enum_names: HashSet::default(),
160 derived_types: HashSet::default(),
161 }
162 }
163
164 #[inline]
165 pub fn is_simple(&self) -> bool {
166 if self.value_types.len() == 1
167 && self.sealed_arrays.is_empty()
168 && !self.flags.contains(CombinationFlags::HAS_KEYED_ARRAY)
169 && !self.flags.contains(CombinationFlags::HAS_EMPTY_ARRAY)
170 && !self.flags.intersects(
171 CombinationFlags::RESOURCE | CombinationFlags::OPEN_RESOURCE | CombinationFlags::CLOSED_RESOURCE,
172 )
173 && self.keyed_array_parameters.is_none()
174 && self.list_array_parameter.is_none()
175 {
176 return self.object_type_params.is_empty()
177 && self.enum_names.is_empty()
178 && self.literal_strings.is_empty()
179 && self.class_string_types.is_empty()
180 && self.integers.is_empty()
181 && self.derived_types.is_empty();
182 }
183
184 false
185 }
186}