1use core::{fmt, ops::Index};
2
3#[cfg(feature = "mem_dbg")]
4use mem_dbg::{MemDbg, MemSize};
5use nonmax::NonMaxU64;
6use typed_index_collections::TiSlice;
7
8use crate::{
9 items::*, parsers::z3::VersionInfo, FxHashMap, IString, NonMaxU32, StringTable, TiVec,
10};
11
12use super::{
13 cdcl::Literals,
14 egraph::EGraph,
15 inst::{InstData, Insts},
16 inter_line::InterLine,
17 smt2::EventLog,
18 stack::{HasFrame, Stack},
19 synthetic::{AnyTerm, SynthIdx, SynthTerms},
20 terms::Terms,
21 };
23
24#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
27#[derive(Debug)]
28pub struct Z3Parser {
29 pub(crate) version_info: VersionInfo,
30 pub(crate) push_count: u32,
32 pub(crate) terms: Terms,
33 pub(crate) synth_terms: SynthTerms,
34
35 pub(crate) quantifiers: TiVec<QuantIdx, Quantifier>,
36
37 pub(crate) insts: Insts,
38
39 pub(crate) egraph: EGraph,
41 pub(crate) stack: Stack,
42
43 pub lits: Literals,
44
45 pub strings: StringTable,
46 pub events: EventLog,
47 pub(super) comm: InterLine,
48}
49
50pub type AstSize = core::result::Result<Option<NonMaxU64>, TermIdx>;
51
52#[derive(Default)]
53pub struct ParseErrors {
54 pub match_error_count: usize,
55 pub match_count: usize,
56
57 pub trans_error_count: usize,
58 pub trans_count: usize,
59}
60
61impl fmt::Display for ParseErrors {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 if self.match_error_count == 0 && self.trans_error_count == 0 {
64 return Ok(());
65 }
66 write!(f, " Errors:")?;
67 if self.match_error_count != 0 {
68 write!(
69 f,
70 " {} match ({:.1}%)",
71 self.match_error_count,
72 100. * self.match_error_count as f64 / self.match_count as f64
73 )?;
74 }
75 if self.trans_error_count != 0 {
76 write!(
77 f,
78 " {} eq ({:.1}%)",
79 self.trans_error_count,
80 100. * self.trans_error_count as f64 / self.trans_count as f64
81 )?;
82 }
83 Ok(())
84 }
85}
86
87impl Z3Parser {
88 pub fn errors(&self) -> ParseErrors {
89 let mut errors = ParseErrors::default();
90 for match_ in self.insts.matches.iter() {
91 if match_.kind.pattern().is_none() {
92 continue;
93 }
94 debug_assert!(!match_.blamed.is_empty());
95 errors.match_count += 1;
96 if match_.blamed.iter().any(|b| !b.is_complete()) {
97 errors.match_error_count += 1;
98 }
99 }
100 errors.trans_count = self.egraph.equalities.transitive.len();
101 errors.trans_error_count = self
102 .egraph
103 .equalities
104 .transitive
105 .iter()
106 .filter(|eq| eq.error_from().is_some())
107 .count();
108 errors
109 }
110
111 pub fn meaning(&self, tidx: TermIdx) -> Option<&Meaning> {
112 self.terms.meaning(tidx)
113 }
114
115 pub fn from_to(&self, eq: EqTransIdx) -> (ENodeIdx, ENodeIdx) {
116 self.egraph.equalities.from_to(eq)
117 }
118
119 pub fn quant_count_incl_theory_solving(&self) -> (usize, bool) {
120 (self.quantifiers.len(), self.insts.has_theory_solving_inst())
121 }
122
123 pub fn quantifiers(&self) -> &TiSlice<QuantIdx, Quantifier> {
124 &self.quantifiers
125 }
126 pub fn instantiations(&self) -> &TiSlice<InstIdx, Instantiation> {
127 &self.insts.insts
128 }
129 pub fn instantiations_data(&self) -> impl Iterator<Item = InstData<'_>> + '_ {
130 self.insts.instantiations()
131 }
132 pub fn terms(&self) -> &TiSlice<TermIdx, Term> {
133 self.terms.app_terms.terms()
134 }
135 pub fn proofs(&self) -> &TiSlice<ProofIdx, ProofStep> {
136 self.terms.proof_terms.terms()
137 }
138 pub fn cdcls(&self) -> &TiSlice<CdclIdx, Cdcl> {
139 self.lits.cdcl.cdcls()
140 }
141
142 pub fn quantifier_body(&self, qidx: QuantIdx) -> Option<TermIdx> {
143 let children = &self[self[qidx].term].child_ids;
144 children.last().copied()
145 }
146
147 pub fn patterns(&self, q: QuantIdx) -> Option<&TiSlice<PatternIdx, TermIdx>> {
148 let child_ids = &self[self[q].term].child_ids;
149 child_ids
150 .len()
151 .checked_sub(1)
152 .map(|len| &child_ids[..len])
153 .map(TiSlice::from_ref)
154 }
155
156 pub fn get_inst(&self, iidx: InstIdx) -> InstData<'_> {
157 self.insts.get_inst(iidx)
158 }
159
160 pub fn get_instantiation_body(&self, iidx: InstIdx) -> Option<TermIdx> {
161 self.terms.get_instantiation_body(self[iidx].kind)
162 }
163
164 pub fn as_tidx(&self, sidx: SynthIdx) -> Option<TermIdx> {
165 self.synth_terms.as_tidx(sidx)
166 }
167
168 pub fn get_pattern(&self, qpat: QuantPat) -> Option<TermIdx> {
169 qpat.pat.map(|pat| self.patterns(qpat.quant).unwrap()[pat])
170 }
171
172 pub fn get_pattern_term(&self, qpat: QuantPat) -> Option<&Term> {
173 self.get_pattern(qpat).map(|tidx| &self[tidx])
174 }
175
176 pub fn get_frame(&self, idx: impl HasFrame) -> &StackFrame {
177 &self.stack[idx.frame(self)]
178 }
179
180 pub fn proves_false(&self, pidx: ProofIdx) -> bool {
183 let result = &self[self[pidx].result];
184 result.child_ids.is_empty() && result.app_name().is_some_and(|name| &self[name] == "false")
185 }
186
187 pub fn ast_size(&self, tidx: TermIdx, cached: &mut FxHashMap<TermIdx, AstSize>) -> AstSize {
191 fn children(tidx: TermIdx, term: &Term) -> core::result::Result<&[TermIdx], TermIdx> {
192 match term.kind() {
193 TermKind::Var(_) => Err(tidx),
194 TermKind::App(_) => Ok(&*term.child_ids),
195 TermKind::Quant(_) => {
197 let body = term.child_ids.last().map(core::slice::from_ref);
198 Ok(body.unwrap_or_default())
199 }
200 }
201 }
202 *self.terms.app_terms.ast_walk_cached(
203 tidx,
204 (),
205 cached,
206 |tidx, term, ()| (children(tidx, term).ok().unwrap_or_default(), ()),
207 |tidx, term, cached, ()| {
208 let children = children(tidx, term)?;
209 let mut size = NonMaxU64::ONE;
210 for child in children {
211 let Some(child) = cached[child]? else {
212 return Ok(None);
213 };
214 let child = NonMaxU64::new(size.get().saturating_add(child.get()));
215 let Some(child) = child else {
216 return Ok(None);
217 };
218 size = child;
219 }
220 Ok(Some(size))
221 },
222 )
223 }
224
225 pub fn inst_ast_size(
226 &self,
227 iidx: InstIdx,
228 cached: &mut FxHashMap<TermIdx, AstSize>,
229 ) -> std::option::Option<NonMaxU64> {
230 let bound_terms = self[self[iidx].match_]
231 .kind
232 .bound_terms(|e| self[e].owner, |t| t);
233 let mut sum = NonMaxU64::ZERO;
234 for bt in bound_terms {
235 let size = self.ast_size(bt, cached).unwrap();
236 let size = sum.get().saturating_add(size?.get());
237 sum = NonMaxU64::new(size)?;
238 }
239 Some(sum)
240 }
241
242 pub fn new_quant_pat_vec<T>(&self, f: impl Fn(QuantPat) -> T) -> QuantPatVec<T> {
243 QuantPatVec(
244 self.quantifiers()
245 .keys()
246 .map(|quant| {
247 let mbqi = f(QuantPat { quant, pat: None });
248 let pats = self.patterns(quant).into_iter().flat_map(|p| p.keys());
249 let pats = pats
250 .map(|pat| {
251 f(QuantPat {
252 quant,
253 pat: Some(pat),
254 })
255 })
256 .collect();
257 PatVec { mbqi, pats }
258 })
259 .collect(),
260 )
261 }
262
263 pub(super) fn bound(&self, match_: &MatchKind, qvar: NonMaxU32) -> Option<TermIdx> {
264 match_.bound_term(|e| &self[e], qvar)
265 }
266}
267
268impl Index<TermIdx> for Z3Parser {
269 type Output = Term;
270 fn index(&self, idx: TermIdx) -> &Self::Output {
271 &self.terms[idx]
272 }
273}
274impl Index<SynthIdx> for Z3Parser {
275 type Output = AnyTerm;
276 fn index(&self, idx: SynthIdx) -> &Self::Output {
277 self.synth_terms.index(&self.terms, idx)
278 }
279}
280impl Index<ProofIdx> for Z3Parser {
281 type Output = ProofStep;
282 fn index(&self, idx: ProofIdx) -> &Self::Output {
283 &self.terms[idx]
284 }
285}
286impl Index<QuantIdx> for Z3Parser {
287 type Output = Quantifier;
288 fn index(&self, idx: QuantIdx) -> &Self::Output {
289 &self.quantifiers[idx]
290 }
291}
292impl Index<ENodeIdx> for Z3Parser {
293 type Output = ENode;
294 fn index(&self, idx: ENodeIdx) -> &Self::Output {
295 &self.egraph[idx]
296 }
297}
298impl Index<InstIdx> for Z3Parser {
299 type Output = Instantiation;
300 fn index(&self, idx: InstIdx) -> &Self::Output {
301 &self.insts[idx]
302 }
303}
304impl Index<MatchIdx> for Z3Parser {
305 type Output = Match;
306 fn index(&self, idx: MatchIdx) -> &Self::Output {
307 &self.insts[idx]
308 }
309}
310impl Index<EqGivenIdx> for Z3Parser {
311 type Output = EqualityExpl;
312 fn index(&self, idx: EqGivenIdx) -> &Self::Output {
313 &self.egraph.equalities.given[idx]
314 }
315}
316impl Index<EqTransIdx> for Z3Parser {
317 type Output = TransitiveExpl;
318 fn index(&self, idx: EqTransIdx) -> &Self::Output {
319 &self.egraph.equalities.transitive[idx]
320 }
321}
322impl Index<StackIdx> for Z3Parser {
323 type Output = StackFrame;
324 fn index(&self, idx: StackIdx) -> &Self::Output {
325 &self.stack[idx]
326 }
327}
328impl Index<LitIdx> for Z3Parser {
329 type Output = Literal;
330 fn index(&self, idx: LitIdx) -> &Self::Output {
331 &self.lits[idx]
332 }
333}
334impl Index<CdclIdx> for Z3Parser {
335 type Output = Cdcl;
336 fn index(&self, idx: CdclIdx) -> &Self::Output {
337 &self.lits.cdcl[idx]
338 }
339}
340impl Index<IString> for Z3Parser {
341 type Output = str;
342 fn index(&self, idx: IString) -> &Self::Output {
343 &self.strings[*idx]
344 }
345}
346
347#[test]
348fn ast_size() {
349 use rand::seq::SliceRandom;
350 use rand::SeedableRng;
351
352 let correct: TiVec<TermIdx, AstSize> =
353 serde_json::from_str(include_str!("../../../tests/data/ast_size.json")).unwrap();
354 let parser = crate::test_parser();
355 let mut idxs: Vec<_> = parser.terms().keys().collect();
356 for i in 0..10 {
357 let mut rng = rand::rngs::StdRng::seed_from_u64(i);
358 idxs.shuffle(&mut rng);
359 let mut cached = FxHashMap::default();
360 for &idx in &idxs {
361 let size = parser.ast_size(idx, &mut cached);
362 assert_eq!(size, correct[idx], "idx {idx}");
363 }
364 }
365}