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 ) -> Vec<std::option::Option<NonMaxU64>> {
230 let bound_terms = self[self[iidx].match_]
231 .kind
232 .bound_terms(|e| self[e].owner, |t| t);
233 IntoIterator::into_iter(bound_terms)
234 .map(|bt| self.ast_size(bt, cached).unwrap())
235 .collect()
236 }
237
238 pub fn new_quant_pat_vec<T>(&self, f: impl Fn(QuantPat) -> T) -> QuantPatVec<T> {
239 QuantPatVec(
240 self.quantifiers()
241 .keys()
242 .map(|quant| {
243 let mbqi = f(QuantPat { quant, pat: None });
244 let pats = self.patterns(quant).into_iter().flat_map(|p| p.keys());
245 let pats = pats
246 .map(|pat| {
247 f(QuantPat {
248 quant,
249 pat: Some(pat),
250 })
251 })
252 .collect();
253 PatVec { mbqi, pats }
254 })
255 .collect(),
256 )
257 }
258
259 pub(super) fn bound(&self, match_: &MatchKind, qvar: NonMaxU32) -> Option<TermIdx> {
260 match_.bound_term(|e| &self[e], qvar)
261 }
262
263 fn named_proofs(&self) -> &FxHashMap<ProofIdx, Option<ProofIdx>> {
264 &self.terms.named_asserts.named
265 }
266
267 pub fn named_assert_to_variable(&self, pidx: ProofIdx) -> Option<ProofIdx> {
271 self.named_proofs().get(&pidx).and_then(|opt| *opt)
272 }
273
274 pub fn is_named_variable(&self, pidx: ProofIdx) -> bool {
277 self.named_proofs()
278 .get(&pidx)
279 .is_some_and(|opt| opt.is_none())
280 }
281}
282
283impl Index<TermIdx> for Z3Parser {
284 type Output = Term;
285 fn index(&self, idx: TermIdx) -> &Self::Output {
286 &self.terms[idx]
287 }
288}
289impl Index<SynthIdx> for Z3Parser {
290 type Output = AnyTerm;
291 fn index(&self, idx: SynthIdx) -> &Self::Output {
292 self.synth_terms.index(&self.terms, idx)
293 }
294}
295impl Index<ProofIdx> for Z3Parser {
296 type Output = ProofStep;
297 fn index(&self, idx: ProofIdx) -> &Self::Output {
298 &self.terms[idx]
299 }
300}
301impl Index<QuantIdx> for Z3Parser {
302 type Output = Quantifier;
303 fn index(&self, idx: QuantIdx) -> &Self::Output {
304 &self.quantifiers[idx]
305 }
306}
307impl Index<ENodeIdx> for Z3Parser {
308 type Output = ENode;
309 fn index(&self, idx: ENodeIdx) -> &Self::Output {
310 &self.egraph[idx]
311 }
312}
313impl Index<InstIdx> for Z3Parser {
314 type Output = Instantiation;
315 fn index(&self, idx: InstIdx) -> &Self::Output {
316 &self.insts[idx]
317 }
318}
319impl Index<MatchIdx> for Z3Parser {
320 type Output = Match;
321 fn index(&self, idx: MatchIdx) -> &Self::Output {
322 &self.insts[idx]
323 }
324}
325impl Index<EqGivenIdx> for Z3Parser {
326 type Output = EqualityExpl;
327 fn index(&self, idx: EqGivenIdx) -> &Self::Output {
328 &self.egraph.equalities.given[idx]
329 }
330}
331impl Index<EqTransIdx> for Z3Parser {
332 type Output = TransitiveExpl;
333 fn index(&self, idx: EqTransIdx) -> &Self::Output {
334 &self.egraph.equalities.transitive[idx]
335 }
336}
337impl Index<StackIdx> for Z3Parser {
338 type Output = StackFrame;
339 fn index(&self, idx: StackIdx) -> &Self::Output {
340 &self.stack[idx]
341 }
342}
343impl Index<LitIdx> for Z3Parser {
344 type Output = Literal;
345 fn index(&self, idx: LitIdx) -> &Self::Output {
346 &self.lits[idx]
347 }
348}
349impl Index<CdclIdx> for Z3Parser {
350 type Output = Cdcl;
351 fn index(&self, idx: CdclIdx) -> &Self::Output {
352 &self.lits.cdcl[idx]
353 }
354}
355impl Index<IString> for Z3Parser {
356 type Output = str;
357 fn index(&self, idx: IString) -> &Self::Output {
358 &self.strings[*idx]
359 }
360}
361
362#[test]
363fn ast_size() {
364 use rand::seq::SliceRandom;
365 use rand::SeedableRng;
366
367 let correct: TiVec<TermIdx, AstSize> =
368 serde_json::from_str(include_str!("../../../tests/data/ast_size.json")).unwrap();
369 let parser = crate::test_parser();
370 let mut idxs: Vec<_> = parser.terms().keys().collect();
371 for i in 0..10 {
372 let mut rng = rand::rngs::StdRng::seed_from_u64(i);
373 idxs.shuffle(&mut rng);
374 let mut cached = FxHashMap::default();
375 for &idx in &idxs {
376 let size = parser.ast_size(idx, &mut cached);
377 assert_eq!(size, correct[idx], "idx {idx}");
378 }
379 }
380}