1#[cfg(feature = "mem_dbg")]
2use mem_dbg::{MemDbg, MemSize};
3
4use crate::{BoxSlice, Error, FxHashMap, IString, NonMaxU32, Result, StringTable};
5
6use super::{QuantIdx, TermIdx};
7
8#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct Term {
12 pub id: TermId,
13 kind: TermKindInner,
14 pub child_ids: BoxSlice<TermIdx>,
17}
18
19impl Term {
20 #[allow(clippy::no_effect)]
21 pub(crate) const CHECK_SIZE: bool = {
22 #[allow(dead_code)]
23 struct SmallTerm {
24 id: TermId,
25 kind: TermKind,
26 child_ids: BoxSlice<TermIdx>,
27 }
28 let sizeof_eq = core::mem::size_of::<Term>() == core::mem::size_of::<SmallTerm>();
29 [(); 1][!sizeof_eq as usize];
30 true
31 };
32
33 pub fn new<'a>(
34 id: TermId,
35 kind: TermKind,
36 child_ids: BoxSlice<TermIdx>,
37 t: impl Fn(TermIdx) -> &'a Term,
38 ) -> Self {
39 let _ = Self::CHECK_SIZE;
40 let kind = match kind {
41 TermKind::Var(var) => TermKindInner::Var(var),
42 TermKind::App(app) => {
43 fn get_has_var<'a>(
44 children: &[TermIdx],
45 t: impl Fn(TermIdx) -> &'a Term,
46 ) -> Option<bool> {
47 let mut has_var = false;
48 for &c in children.iter() {
49 has_var |= t(c).has_var()?;
50 }
51 Some(has_var)
52 }
53 TermKindInner::App(get_has_var(&child_ids, t), app)
54 }
55 TermKind::Quant(quant) => TermKindInner::Quant(quant),
56 };
57 Self {
58 id,
59 kind,
60 child_ids,
61 }
62 }
63
64 pub fn kind(&self) -> TermKind {
65 use TermKindInner::*;
66 match self.kind {
67 Var(.., var) => TermKind::Var(var),
68 App(.., app) => TermKind::App(app),
69 Quant(.., quant) => TermKind::Quant(quant),
70 }
71 }
72
73 pub fn var_idx(&self) -> Option<NonMaxU32> {
74 self.kind().var()
75 }
76 pub fn app_name(&self) -> Option<IString> {
77 self.kind().app()
78 }
79 pub fn quant_idx(&self) -> Option<QuantIdx> {
80 self.kind().quant()
81 }
82
83 pub fn has_var(&self) -> Option<bool> {
86 match self.kind {
87 TermKindInner::Var(_) => Some(true),
88 TermKindInner::App(has_var, _) => has_var,
89 TermKindInner::Quant(_) => None,
90 }
91 }
92
93 pub fn can_match(&self, other: &Self) -> bool {
97 if self.kind() != other.kind() {
98 return false;
99 }
100 if self.has_var().unwrap_or(true) || other.has_var().unwrap_or(true) {
101 self.child_ids.len() == other.child_ids.len()
102 } else {
103 self.child_ids == other.child_ids
104 }
105 }
106}
107
108#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
109#[cfg_attr(feature = "mem_dbg", copy_type)]
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111#[repr(u8)]
112pub enum TermKindInner {
113 Var(NonMaxU32),
114 App(Option<bool>, IString),
115 Quant(QuantIdx),
116}
117
118#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
119#[cfg_attr(feature = "mem_dbg", copy_type)]
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
121#[repr(u8)]
122pub enum TermKind {
123 Var(NonMaxU32),
124 App(IString),
125 Quant(QuantIdx),
126}
127
128impl TermKind {
129 pub(crate) fn parse_var(value: &str) -> Result<TermKind> {
130 value
131 .parse::<NonMaxU32>()
132 .map(TermKind::Var)
133 .map_err(Error::InvalidVar)
134 }
135 fn var(&self) -> Option<NonMaxU32> {
136 match self {
137 Self::Var(var) => Some(*var),
138 _ => None,
139 }
140 }
141 fn app(&self) -> Option<IString> {
142 match self {
143 Self::App(name) => Some(*name),
144 _ => None,
145 }
146 }
147 fn quant(&self) -> Option<QuantIdx> {
148 match self {
149 Self::Quant(idx) => Some(*idx),
150 _ => None,
151 }
152 }
153}
154
155#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
157#[cfg_attr(feature = "mem_dbg", copy_type)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq)]
160pub struct TermId {
161 pub namespace: Option<IString>,
162 pub id: Option<NonMaxU32>,
163}
164impl TermId {
165 pub fn parse(strings: &mut StringTable, value: &str) -> Result<Self> {
169 let hash_idx = value.bytes().position(|b| b == b'#');
170 let hash_idx = hash_idx.ok_or_else(|| Error::InvalidIdHash(value.to_string()))?;
171 let (namespace, id) = value.split_at(hash_idx);
172 let id = match &id[1..] {
173 "" => None,
174 id => Some(id.parse::<NonMaxU32>().map_err(Error::InvalidIdNumber)?),
175 };
176 let namespace = (!namespace.is_empty()).then(|| IString(strings.get_or_intern(namespace)));
177 Ok(Self { namespace, id })
178 }
179 pub fn order(&self) -> u32 {
180 self.id.map(|id| id.get() + 1).unwrap_or_default()
181 }
182
183 pub fn to_string_components<'a>(
184 &self,
185 strings: &'a StringTable,
186 ) -> (&'a str, Option<NonMaxU32>) {
187 let namespace = self.namespace.map(|ns| &strings[*ns]).unwrap_or_default();
188 (namespace, self.id)
189 }
190 pub fn to_string(&self, strings: &StringTable) -> String {
191 let (namespace, id) = self.to_string_components(strings);
192 let id = id.map(|id| id.to_string()).unwrap_or_default();
193 format!("{namespace}#{id}")
194 }
195}
196
197#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
202#[derive(Debug)]
203pub struct TermIdToIdxMap<K: Copy> {
204 empty_namespace: Vec<Option<K>>,
205 namespace_map: FxHashMap<IString, Vec<Option<K>>>,
206}
207
208impl<K: Copy> Default for TermIdToIdxMap<K> {
209 fn default() -> Self {
210 Self {
211 empty_namespace: Vec::new(),
212 namespace_map: FxHashMap::default(),
213 }
214 }
215}
216
217impl<K: Copy> TermIdToIdxMap<K> {
218 fn get_vec_mut(&mut self, namespace: Option<IString>) -> Result<&mut Vec<Option<K>>> {
219 if let Some(namespace) = namespace {
220 self.namespace_map.try_reserve(1)?;
221 Ok(self.namespace_map.entry(namespace).or_default())
222 } else {
223 Ok(&mut self.empty_namespace)
225 }
226 }
227 pub fn register_term(&mut self, id: TermId, idx: K) -> Result<Option<K>> {
228 let id_idx = id.order() as usize;
229 let vec = self.get_vec_mut(id.namespace)?;
230 if id_idx >= vec.len() {
231 let new_len = id_idx + 1;
232 vec.try_reserve(new_len - vec.len())?;
233 vec.resize(new_len, None);
234 }
235 let old = vec[id_idx].replace(idx);
239 Ok(old)
240 }
241 fn get_vec(&self, namespace: Option<IString>) -> Option<&Vec<Option<K>>> {
242 if let Some(namespace) = namespace {
243 self.namespace_map.get(&namespace)
244 } else {
245 Some(&self.empty_namespace)
246 }
247 }
248 pub fn get_term(&self, id: &TermId) -> Option<K> {
249 self.get_vec(id.namespace)
250 .and_then(|vec| vec.get(id.order() as usize).and_then(|x| x.as_ref()))
251 .copied()
252 }
253}