easy_smt/
context.rs

1use crate::{
2    known_atoms::KnownAtoms,
3    sexpr::{Arena, DisplayExpr, SExpr, SExprData},
4    solver::Solver,
5};
6use std::{ffi, io, mem};
7
8macro_rules! variadic {
9    ($name:ident, $op:ident) => {
10        pub fn $name<I: IntoIterator<Item = SExpr>>(&self, items: I) -> SExpr {
11            let mut iter = items.into_iter().peekable();
12            let first = iter.next().expect("At least one argument is expected");
13
14            if iter.peek().is_some() {
15                let args = std::iter::once(self.atoms.$op)
16                    .chain(std::iter::once(first))
17                    .chain(iter)
18                    .collect();
19                self.list(args)
20            } else {
21                first
22            }
23        }
24    };
25}
26
27macro_rules! unary {
28    ($name:ident, $op:ident) => {
29        pub fn $name(&self, val: SExpr) -> SExpr {
30            self.list(vec![self.atoms.$op, val])
31        }
32    };
33}
34
35macro_rules! binop {
36    ($name:ident, $op:ident) => {
37        pub fn $name(&self, lhs: SExpr, rhs: SExpr) -> SExpr {
38            self.list(vec![self.atoms.$op, lhs, rhs])
39        }
40    };
41}
42
43macro_rules! right_assoc {
44    ($name:ident, $many:ident, $op:ident) => {
45        binop!($name, $op);
46        variadic!($many, $op);
47    };
48}
49
50macro_rules! left_assoc {
51    ($name:ident, $many:ident, $op:ident) => {
52        binop!($name, $op);
53        variadic!($many, $op);
54    };
55}
56
57macro_rules! chainable {
58    ($name:ident, $many:ident, $op:ident) => {
59        binop!($name, $op);
60        variadic!($many, $op);
61    };
62}
63
64macro_rules! pairwise {
65    ($name:ident, $many:ident, $op:ident) => {
66        binop!($name, $op);
67        variadic!($many, $op);
68    };
69}
70
71#[derive(Default)]
72pub struct ContextBuilder {
73    solver: Option<ffi::OsString>,
74    solver_args: Vec<ffi::OsString>,
75    replay_file: Option<Box<dyn io::Write + Send>>,
76}
77
78impl ContextBuilder {
79    /// Construct a new builder with the default configuration.
80    pub fn new() -> Self {
81        Self::default()
82    }
83
84    /// Initialize the builder with Z3 defaults.
85    pub fn with_z3_defaults(&mut self) -> &mut Self {
86        self.solver("z3").solver_args(["-smt2", "-in", "-v:0"])
87    }
88
89    /// Initialize the builder with CVC5 defaults.
90    pub fn with_cvc5_defaults(&mut self) -> &mut Self {
91        self.solver("cvc5").solver_args(["--quiet", "--lang=smt2", "--incremental"])
92    }
93
94    /// Configure the solver that will be used.
95    ///
96    /// You can pass arguments to the underlying solver via the
97    /// [`solver_args`][Self::solver_args] method.
98    ///
99    /// By default, no solver is configured, and any `Context` created will only
100    /// be able to build and display expressions, not assert them or query for
101    /// their satisfiability.
102    pub fn solver<P>(&mut self, program: P) -> &mut Self
103    where
104        P: Into<ffi::OsString>,
105    {
106        self.solver = Some(program.into());
107        self
108    }
109
110    /// Configure the arguments that will be passed to the solver.
111    ///
112    /// By default, no arguments are passed to the solver.
113    pub fn solver_args<A>(&mut self, args: A) -> &mut Self
114    where
115        A: IntoIterator,
116        A::Item: Into<ffi::OsString>,
117    {
118        self.solver_args = args.into_iter().map(|a| a.into()).collect();
119        self
120    }
121
122    /// Clear the solver configuration, if any.
123    ///
124    /// This returns to the default, no-solver configuration, where any
125    /// `Context` created will only be able to build and display expressions,
126    /// not assert them or query for their satisfiability.
127    pub fn without_solver(&mut self) -> &mut Self {
128        self.solver = None;
129        self.solver_args.clear();
130        self
131    }
132
133    /// An optional file (or anything else that is `std::io::Write`-able) where
134    /// all solver queries and commands are tee'd too.
135    ///
136    /// This let's you replay interactions with the solver offline, without
137    /// needing to dynamically rebuild your expressions, queries, or commands.
138    ///
139    /// This is unused if no solver is configured.
140    ///
141    /// By default, there is no replay file configured.
142    pub fn replay_file<W>(&mut self, replay_file: Option<W>) -> &mut Self
143    where
144        W: 'static + io::Write + Send,
145    {
146        self.replay_file = replay_file.map(|w| Box::new(w) as _);
147        self
148    }
149
150    /// Finish configuring the context and build it.
151    pub fn build(&mut self) -> io::Result<Context> {
152        let arena = Arena::new();
153        let atoms = KnownAtoms::new(&arena);
154
155        let solver = if let Some(program) = self.solver.take() {
156            Some(Solver::new(
157                program,
158                mem::take(&mut self.solver_args),
159                self.replay_file
160                    .take()
161                    .unwrap_or_else(|| Box::new(io::sink())),
162            )?)
163        } else {
164            None
165        };
166
167        let mut ctx = Context {
168            solver,
169            arena,
170            atoms,
171        };
172
173        if ctx.solver.is_some() {
174            ctx.set_option(":print-success", ctx.true_())?;
175            ctx.set_option(":produce-models", ctx.true_())?;
176        }
177
178        Ok(ctx)
179    }
180}
181
182/// An SMT-LIB 2 context, usually backed by a solver subprocess.
183///
184/// Created via a [`ContextBuilder`][crate::ContextBuilder].
185pub struct Context {
186    solver: Option<Solver>,
187    arena: Arena,
188    atoms: KnownAtoms,
189}
190
191/// # Solver Commands and Assertions
192///
193/// These methods send a command or assertion to the context's underlying
194/// solver. As such, they involve I/O with a subprocess and are therefore
195/// fallible.
196///
197/// ## Panics
198///
199/// These methods will panic when called if the context was not created with an
200/// underlying solver (via
201/// [`ContextBuilder::solver`][crate::ContextBuilder::solver]).
202impl Context {
203    pub fn set_option<K>(&mut self, name: K, value: SExpr) -> io::Result<()>
204    where
205        K: Into<String> + AsRef<str>,
206    {
207        let solver = self
208            .solver
209            .as_mut()
210            .expect("set_option requires a running solver");
211        solver.ack_command(
212            &self.arena,
213            self.atoms.success,
214            self.arena
215                .list(vec![self.atoms.set_option, self.arena.atom(name), value]),
216        )
217    }
218
219    /// Assert `check-sat-assuming` with the given list of assumptions.
220    pub fn check_assuming(
221        &mut self,
222        props: impl IntoIterator<Item = SExpr>,
223    ) -> io::Result<Response> {
224        let solver = self
225            .solver
226            .as_mut()
227            .expect("check requires a running solver");
228        let args = self.arena.list(props.into_iter().collect());
229        solver.send(
230            &self.arena,
231            self.arena.list(vec![self.atoms.check_sat_assuming, args]),
232        )?;
233        let resp = solver.recv(&self.arena)?;
234        if resp == self.atoms.sat {
235            Ok(Response::Sat)
236        } else if resp == self.atoms.unsat {
237            Ok(Response::Unsat)
238        } else if resp == self.atoms.unknown {
239            Ok(Response::Unknown)
240        } else {
241            Err(io::Error::new(
242                io::ErrorKind::Other,
243                format!("Unexpected result from solver: {}", self.display(resp)),
244            ))
245        }
246    }
247
248    /// Assert `check-sat` for the current context.
249    pub fn check(&mut self) -> io::Result<Response> {
250        let solver = self
251            .solver
252            .as_mut()
253            .expect("check requires a running solver");
254        solver.send(&self.arena, self.arena.list(vec![self.atoms.check_sat]))?;
255        let resp = solver.recv(&self.arena)?;
256        if resp == self.atoms.sat {
257            Ok(Response::Sat)
258        } else if resp == self.atoms.unsat {
259            Ok(Response::Unsat)
260        } else if resp == self.atoms.unknown {
261            Ok(Response::Unknown)
262        } else {
263            Err(io::Error::new(
264                io::ErrorKind::Other,
265                format!("Unexpected result from solver: {}", self.display(resp)),
266            ))
267        }
268    }
269
270    /// Declare a new constant with the provided sort
271    pub fn declare_const<S: Into<String> + AsRef<str>>(
272        &mut self,
273        name: S,
274        sort: SExpr,
275    ) -> io::Result<SExpr> {
276        let name = self.atom(name);
277        let solver = self
278            .solver
279            .as_mut()
280            .expect("declare_const requires a running solver");
281        solver.ack_command(
282            &self.arena,
283            self.atoms.success,
284            self.arena.list(vec![self.atoms.declare_const, name, sort]),
285        )?;
286        Ok(name)
287    }
288
289    pub fn declare_datatype<S: Into<String> + AsRef<str>>(
290        &mut self,
291        name: S,
292        decl: SExpr,
293    ) -> io::Result<SExpr> {
294        let name = self.atom(name);
295        let solver = self
296            .solver
297            .as_mut()
298            .expect("declare_datatype requires a running solver");
299        solver.ack_command(
300            &self.arena,
301            self.atoms.success,
302            self.arena
303                .list(vec![self.atoms.declare_datatype, name, decl]),
304        )?;
305        Ok(name)
306    }
307
308    pub fn declare_datatypes<N, S, D>(&mut self, sorts: S, decls: D) -> io::Result<()>
309    where
310        N: Into<String> + AsRef<str>,
311        S: IntoIterator<Item = (N, i32)>,
312        D: IntoIterator<Item = SExpr>,
313    {
314        let sorts = sorts
315            .into_iter()
316            .map(|(n, i)| self.list(vec![self.atom(n), self.numeral(i)]))
317            .collect();
318        let decls = decls.into_iter().collect();
319
320        let solver = self
321            .solver
322            .as_mut()
323            .expect("declare_datatype requires a running solver");
324        solver.ack_command(
325            &self.arena,
326            self.atoms.success,
327            self.arena.list(vec![
328                self.atoms.declare_datatypes,
329                self.arena.list(sorts),
330                self.arena.list(decls),
331            ]),
332        )
333    }
334
335    /// Declares a new, uninterpreted function symbol.
336    pub fn declare_fun<S: Into<String> + AsRef<str>>(
337        &mut self,
338        name: S,
339        args: Vec<SExpr>,
340        out: SExpr,
341    ) -> io::Result<SExpr> {
342        let name = self.atom(name);
343        let solver = self
344            .solver
345            .as_mut()
346            .expect("declare_fun requires a running solver");
347        solver.ack_command(
348            &self.arena,
349            self.atoms.success,
350            self.arena.list(vec![
351                self.atoms.declare_fun,
352                name,
353                self.arena.list(args),
354                out,
355            ]),
356        )?;
357        Ok(name)
358    }
359
360    /// Defines a new function with a body.
361    pub fn define_fun<S: Into<String> + AsRef<str>>(
362        &mut self,
363        name: S,
364        args: Vec<(S, SExpr)>,
365        out: SExpr,
366        body: SExpr,
367    ) -> io::Result<SExpr> {
368        let name = self.atom(name);
369        let args = args
370            .into_iter()
371            .map(|(n, s)| self.list(vec![self.atom(n), s]))
372            .collect();
373        let solver = self
374            .solver
375            .as_mut()
376            .expect("define_fun requires a running solver");
377        solver.ack_command(
378            &self.arena,
379            self.atoms.success,
380            self.arena.list(vec![
381                self.atoms.define_fun,
382                name,
383                self.arena.list(args),
384                out,
385                body,
386            ]),
387        )?;
388        Ok(name)
389    }
390
391    /// Define a constant with a body with a value.
392    /// This is a convenience wrapper over [Self::define_fun] since constants
393    /// are nullary functions.
394    pub fn define_const<S: Into<String> + AsRef<str>>(
395        &mut self,
396        name: S,
397        out: SExpr,
398        body: SExpr,
399    ) -> io::Result<SExpr> {
400        self.define_fun(name, vec![], out, body)
401    }
402
403    pub fn declare_sort<S: Into<String> + AsRef<str>>(
404        &mut self,
405        symbol: S,
406        arity: i32,
407    ) -> io::Result<SExpr> {
408        let symbol = self.atom(symbol);
409        let arity = self.numeral(arity);
410        let solver = self
411            .solver
412            .as_mut()
413            .expect("declare_sort requires a running solver");
414        solver.ack_command(
415            &self.arena,
416            self.atoms.success,
417            self.arena
418                .list(vec![self.atoms.declare_sort, symbol, arity]),
419        )?;
420        Ok(symbol)
421    }
422
423    pub fn assert(&mut self, expr: SExpr) -> io::Result<()> {
424        let solver = self
425            .solver
426            .as_mut()
427            .expect("assert requires a running solver");
428        solver.ack_command(
429            &self.arena,
430            self.atoms.success,
431            self.arena.list(vec![self.atoms.assert, expr]),
432        )
433    }
434
435    /// Get a model out from the solver. This is only meaningful after a `check-sat` query.
436    pub fn get_model(&mut self) -> io::Result<SExpr> {
437        let solver = self
438            .solver
439            .as_mut()
440            .expect("get_model requires a running solver");
441        solver.send(&self.arena, self.arena.list(vec![self.atoms.get_model]))?;
442        solver.recv(&self.arena)
443    }
444
445    /// Get bindings for values in the model. This is only meaningful after a `check-sat` query.
446    pub fn get_value(&mut self, vals: Vec<SExpr>) -> io::Result<Vec<(SExpr, SExpr)>> {
447        if vals.is_empty() {
448            return Ok(vec![]);
449        }
450
451        let solver = self
452            .solver
453            .as_mut()
454            .expect("get_value requires a running solver");
455        solver.send(
456            &self.arena,
457            self.arena
458                .list(vec![self.atoms.get_value, self.arena.list(vals)]),
459        )?;
460
461        let resp = solver.recv(&self.arena)?;
462        match self.arena.get(resp) {
463            SExprData::List(pairs) => {
464                let mut res = Vec::with_capacity(pairs.len());
465                for expr in pairs {
466                    match self.arena.get(*expr) {
467                        SExprData::List(pair) => {
468                            assert_eq!(2, pair.len());
469                            res.push((pair[0], pair[1]));
470                        }
471                        _ => unreachable!(),
472                    }
473                }
474                Ok(res)
475            }
476
477            _ => Err(io::Error::new(
478                io::ErrorKind::Other,
479                "Failed to parse solver output",
480            )),
481        }
482    }
483
484    /// Returns the names of the formulas involved in a contradiction.
485    pub fn get_unsat_core(&mut self) -> io::Result<SExpr> {
486        let solver = self
487            .solver
488            .as_mut()
489            .expect("get_unsat_core requires a running solver");
490        solver.send(
491            &self.arena,
492            self.arena.list(vec![self.atoms.get_unsat_core]),
493        )?;
494        solver.recv(&self.arena)
495    }
496
497    pub fn set_logic<L: Into<String> + AsRef<str>>(&mut self, logic: L) -> io::Result<()> {
498        let solver = self
499            .solver
500            .as_mut()
501            .expect("set_logic requires a running solver");
502        solver.ack_command(
503            &self.arena,
504            self.atoms.success,
505            self.arena
506                .list(vec![self.atoms.set_logic, self.arena.atom(logic)]),
507        )
508    }
509
510    /// Instruct the solver to exit.
511    pub fn exit(&mut self) -> io::Result<()> {
512        let solver = self
513            .solver
514            .as_mut()
515            .expect("exit requires a running solver");
516        solver.ack_command(
517            &self.arena,
518            self.atoms.success,
519            self.arena.list(vec![self.atoms.exit]),
520        )
521    }
522
523    /// Push a new context frame in the solver. Same as SMTLIB's `push` command.
524    pub fn push(&mut self) -> io::Result<()> {
525        let solver = self
526            .solver
527            .as_mut()
528            .expect("push requires a running solver");
529        solver.ack_command(
530            &self.arena,
531            self.atoms.success,
532            self.arena.list(vec![self.atoms.push]),
533        )
534    }
535
536    pub fn push_many(&mut self, n: usize) -> io::Result<()> {
537        let solver = self
538            .solver
539            .as_mut()
540            .expect("push_many requires a running solver");
541        solver.ack_command(
542            &self.arena,
543            self.atoms.success,
544            self.arena
545                .list(vec![self.atoms.push, self.arena.atom(n.to_string())]),
546        )
547    }
548
549    /// Pop a context frame in the solver. Same as SMTLIB's `pop` command.
550    pub fn pop(&mut self) -> io::Result<()> {
551        let solver = self.solver.as_mut().expect("pop requires a running solver");
552        solver.ack_command(
553            &self.arena,
554            self.atoms.success,
555            self.arena.list(vec![self.atoms.pop]),
556        )
557    }
558
559    pub fn pop_many(&mut self, n: usize) -> io::Result<()> {
560        let solver = self
561            .solver
562            .as_mut()
563            .expect("pop_many requires a running solver");
564        solver.ack_command(
565            &self.arena,
566            self.atoms.success,
567            self.arena
568                .list(vec![self.atoms.pop, self.arena.atom(n.to_string())]),
569        )
570    }
571
572    /// Directly send an expression to the solver.
573    ///
574    /// You are responsible for reading the response, if any, from the solver by
575    /// calling `context.raw_recv()` immediately after sending a message to the
576    /// solver. Failing to call `context.raw_recv()` afterwards if the solver
577    /// sends a response to your message, or calling it if the solver doesn't
578    /// send a response, will lead to general breakage such as dead locks,
579    /// nonsensical query results, and more.
580    ///
581    /// This is a low-level API and should be used sparingly, such as for
582    /// solver-specific commands that this crate does not provide built-in
583    /// support for. When possible, use the higher-level interfaces provided by
584    /// this crate, for example by calling `context.assert(foo)` rather than
585    /// `context.raw_send(context.list([context.atom("assert"), foo]))`.
586    pub fn raw_send(&mut self, cmd: SExpr) -> io::Result<()> {
587        let solver = self
588            .solver
589            .as_mut()
590            .expect("send requires a running solver");
591        solver.send(&self.arena, cmd)
592    }
593
594    /// Directly receive a response from the solver.
595    ///
596    /// This method should only be used immediately after calling
597    /// `context.raw_send(...)`, to receive the solver's response to that sent
598    /// message. Calling this method at other times, failing to call it when the
599    /// solver sends a response, or calling it when the solver does not send a
600    /// response, will lead to general breakage such as dead locks, nonsensical
601    /// query results, and more.
602    ///
603    /// This is a low-level API and should be used sparingly, such as for
604    /// solver-specific commands that this crate does not provide built-in
605    /// support for. When possible, use the higher-level interfaces provided by
606    /// this crate, for example by calling `context.assert(foo)` rather than
607    /// `context.raw_send(context.list([context.atom("assert"), foo]))`.
608    pub fn raw_recv(&mut self) -> io::Result<SExpr> {
609        let solver = self
610            .solver
611            .as_mut()
612            .expect("recv requires a running solver");
613        solver.recv(&self.arena)
614    }
615}
616
617/// # Basic S-Expression Construction and Inspection
618///
619/// These methods provide the foundation of building s-expressions. Even if you
620/// end up using higher-level helper methods most of the time, everything
621/// bottoms out in calls to these methods.
622impl Context {
623    /// Construct a non-list s-expression from the given string.
624    pub fn atom(&self, name: impl Into<String> + AsRef<str>) -> SExpr {
625        self.arena.atom(name)
626    }
627
628    /// Construct a list s-expression from the given elements.
629    pub fn list(&self, list: Vec<SExpr>) -> SExpr {
630        self.arena.list(list)
631    }
632
633    /// Create a numeral s-expression.
634    pub fn numeral(&self, val: impl IntoNumeral) -> SExpr {
635        self.arena.atom(val.to_string())
636    }
637
638    /// Create a decimal s-expression.
639    pub fn decimal(&self, val: impl IntoDecimal) -> SExpr {
640        self.arena.atom(val.to_string())
641    }
642
643    /// Create a binary s-expression of the given bit width.
644    pub fn binary(&self, bit_width: usize, val: impl IntoBinary) -> SExpr {
645        let val = format!("#b{val:0>bit_width$b}");
646        self.arena.atom(val)
647    }
648
649    /// Get a `std::fmt::Display` representation of the given s-expression.
650    ///
651    /// This allows you to print, log, or otherwise format an s-expression
652    /// creating within this context.
653    ///
654    /// You may only pass in `SExpr`s that were created by this
655    /// `Context`. Failure to do so is safe, but may trigger a panic or return
656    /// invalid data.
657    pub fn display(&self, expr: SExpr) -> DisplayExpr {
658        self.arena.display(expr)
659    }
660
661    /// Get the atom or list data for the given s-expression.
662    ///
663    /// This allows you to inspect s-expressions.
664    ///
665    /// You may only pass in `SExpr`s that were created by this
666    /// `Context`. Failure to do so is safe, but may trigger a panic or return
667    /// invalid data.
668    pub fn get(&self, expr: SExpr) -> SExprData {
669        self.arena.get(expr)
670    }
671
672    /// Get the atom data for the given s-expression.
673    ///
674    /// This allows you to inspect s-expressions. If the s-expression is not an
675    /// atom this function returns `None`.
676    ///
677    /// You may only pass in `SExpr`s that were created by this
678    /// `Context`. Failure to do so is safe, but may trigger a panic or return
679    /// invalid data.
680    pub fn get_atom(&self, expr: SExpr) -> Option<&str> {
681        self.arena.get_atom(expr)
682    }
683
684    /// Get the string data for the given s-expression.
685    ///
686    /// This allows you to inspect s-expressions. If the s-expression is not an
687    /// string this function returns `None`.
688    ///
689    /// You may only pass in `SExpr`s that were created by this
690    /// `Context`. Failure to do so is safe, but may trigger a panic or return
691    /// invalid data.
692    pub fn get_str(&self, expr: SExpr) -> Option<&str> {
693        self.arena.get_str(expr)
694    }
695
696    /// Get the list data for the given s-expression.
697    ///
698    /// This allows you to inspect s-expressions.If the s-expression is not an
699    /// list this function returns `None`.
700    ///
701    /// You may only pass in `SExpr`s that were created by this
702    /// `Context`. Failure to do so is safe, but may trigger a panic or return
703    /// invalid data.
704    pub fn get_list(&self, expr: SExpr) -> Option<&[SExpr]> {
705        self.arena.get_list(expr)
706    }
707
708    /// Get the data for the given s-expression as an `u8`.
709    ///
710    /// This allows you to inspect s-expressions. If the s-expression is not an
711    /// cannot be parsed into an `u8` this function returns `None`.
712    ///
713    /// You may only pass in `SExpr`s that were created by this
714    /// `Context`. Failure to do so is safe, but may trigger a panic or return
715    /// invalid data.
716    pub fn get_u8(&self, expr: SExpr) -> Option<u8> {
717        self.arena.get_i(expr)
718    }
719
720    /// Get the data for the given s-expression as an `u16`.
721    ///
722    /// This allows you to inspect s-expressions. If the s-expression is not an
723    /// cannot be parsed into an `u16` this function returns `None`.
724    ///
725    /// You may only pass in `SExpr`s that were created by this
726    /// `Context`. Failure to do so is safe, but may trigger a panic or return
727    /// invalid data.
728    pub fn get_u16(&self, expr: SExpr) -> Option<u16> {
729        self.arena.get_i(expr)
730    }
731
732    /// Get the data for the given s-expression as an `u32`.
733    ///
734    /// This allows you to inspect s-expressions. If the s-expression is not an
735    /// cannot be parsed into an `u32` this function returns `None`.
736    ///
737    /// You may only pass in `SExpr`s that were created by this
738    /// `Context`. Failure to do so is safe, but may trigger a panic or return
739    /// invalid data.
740    pub fn get_u32(&self, expr: SExpr) -> Option<u32> {
741        self.arena.get_i(expr)
742    }
743
744    /// Get the data for the given s-expression as an `u64`.
745    ///
746    /// This allows you to inspect s-expressions. If the s-expression is not an
747    /// cannot be parsed into an `u64` this function returns `None`.
748    ///
749    /// You may only pass in `SExpr`s that were created by this
750    /// `Context`. Failure to do so is safe, but may trigger a panic or return
751    /// invalid data.
752    pub fn get_u64(&self, expr: SExpr) -> Option<u64> {
753        self.arena.get_i(expr)
754    }
755
756    /// Get the data for the given s-expression as an `u128`.
757    ///
758    /// This allows you to inspect s-expressions. If the s-expression is not an
759    /// cannot be parsed into an `u128` this function returns `None`.
760    ///
761    /// You may only pass in `SExpr`s that were created by this
762    /// `Context`. Failure to do so is safe, but may trigger a panic or return
763    /// invalid data.
764    pub fn get_u128(&self, expr: SExpr) -> Option<u128> {
765        self.arena.get_i(expr)
766    }
767
768    /// Get the data for the given s-expression as an `usize`.
769    ///
770    /// This allows you to inspect s-expressions. If the s-expression is not an
771    /// cannot be parsed into an `usize` this function returns `None`.
772    ///
773    /// You may only pass in `SExpr`s that were created by this
774    /// `Context`. Failure to do so is safe, but may trigger a panic or return
775    /// invalid data.
776    pub fn get_usize(&self, expr: SExpr) -> Option<usize> {
777        self.arena.get_i(expr)
778    }
779
780    /// Get the data for the given s-expression as an `i8`.
781    ///
782    /// This allows you to inspect s-expressions. If the s-expression is not an
783    /// cannot be parsed into an `i8` this function returns `None`.
784    ///
785    /// You may only pass in `SExpr`s that were created by this
786    /// `Context`. Failure to do so is safe, but may trigger a panic or return
787    /// invalid data.
788    pub fn get_i8(&self, expr: SExpr) -> Option<i8> {
789        self.arena.get_i(expr)
790    }
791
792    /// Get the data for the given s-expression as an `i16`.
793    ///
794    /// This allows you to inspect s-expressions. If the s-expression is not an
795    /// cannot be parsed into an `i16` this function returns `None`.
796    ///
797    /// You may only pass in `SExpr`s that were created by this
798    /// `Context`. Failure to do so is safe, but may trigger a panic or return
799    /// invalid data.
800    pub fn get_i16(&self, expr: SExpr) -> Option<i16> {
801        self.arena.get_i(expr)
802    }
803
804    /// Get the data for the given s-expression as an `i32`.
805    ///
806    /// This allows you to inspect s-expressions. If the s-expression is not an
807    /// cannot be parsed into an `i32` this function returns `None`.
808    ///
809    /// You may only pass in `SExpr`s that were created by this
810    /// `Context`. Failure to do so is safe, but may trigger a panic or return
811    /// invalid data.
812    pub fn get_i32(&self, expr: SExpr) -> Option<i32> {
813        self.arena.get_i(expr)
814    }
815
816    /// Get the data for the given s-expression as an `i64`.
817    ///
818    /// This allows you to inspect s-expressions. If the s-expression is not an
819    /// cannot be parsed into an `i64` this function returns `None`.
820    ///
821    /// You may only pass in `SExpr`s that were created by this
822    /// `Context`. Failure to do so is safe, but may trigger a panic or return
823    /// invalid data.
824    pub fn get_i64(&self, expr: SExpr) -> Option<i64> {
825        self.arena.get_i(expr)
826    }
827
828    /// Get the data for the given s-expression as an `i128`.
829    ///
830    /// This allows you to inspect s-expressions. If the s-expression is not an
831    /// cannot be parsed into an `i128` this function returns `None`.
832    ///
833    /// You may only pass in `SExpr`s that were created by this
834    /// `Context`. Failure to do so is safe, but may trigger a panic or return
835    /// invalid data.
836    pub fn get_i128(&self, expr: SExpr) -> Option<i128> {
837        self.arena.get_i(expr)
838    }
839
840    /// Get the data for the given s-expression as an `isize`.
841    ///
842    /// This allows you to inspect s-expressions. If the s-expression is not an
843    /// cannot be parsed into an `isize` this function returns `None`.
844    ///
845    /// You may only pass in `SExpr`s that were created by this
846    /// `Context`. Failure to do so is safe, but may trigger a panic or return
847    /// invalid data.
848    pub fn get_isize(&self, expr: SExpr) -> Option<isize> {
849        self.arena.get_i(expr)
850    }
851
852    /// Get the data for the given s-expression as a `f32`.
853    ///
854    /// This allows you to inspect s-expressions. If the s-expression is not an
855    /// cannot be parsed into an `f32` this function returns `None`.
856    ///
857    /// You may only pass in `SExpr`s that were created by this
858    /// `Context`. Failure to do so is safe, but may trigger a panic or return
859    /// invalid data.
860    pub fn get_f32(&self, expr: SExpr) -> Option<f32> {
861        self.arena.get_f(expr)
862    }
863
864    /// Get the data for the given s-expression as a `f64`.
865    ///
866    /// This allows you to inspect s-expressions. If the s-expression is not an
867    /// cannot be parsed into an `f64` this function returns `None`.
868    /// You may only pass in `SExpr`s that were created by this
869    /// `Context`. Failure to do so is safe, but may trigger a panic or return
870    /// invalid data.
871    pub fn get_f64(&self, expr: SExpr) -> Option<f64> {
872        self.arena.get_f(expr)
873    }
874
875    /// Access "known" atoms.
876    ///
877    /// This lets you skip the is-it-already-interned-or-not checks and hash map
878    /// lookups for certain frequently used atoms.
879    pub fn atoms(&self) -> &KnownAtoms {
880        &self.atoms
881    }
882}
883
884/// # Generic and Polymorphic Helpers
885///
886/// Helpers for constructing s-expressions that are not specific to one sort.
887impl Context {
888    pub fn par<N, S, D>(&self, symbols: S, decls: D) -> SExpr
889    where
890        N: Into<String> + AsRef<str>,
891        S: IntoIterator<Item = N>,
892        D: IntoIterator<Item = SExpr>,
893    {
894        self.list(vec![
895            self.atoms.par,
896            self.list(symbols.into_iter().map(|n| self.atom(n)).collect()),
897            self.list(decls.into_iter().collect()),
898        ])
899    }
900
901    pub fn attr(&self, expr: SExpr, name: impl Into<String> + AsRef<str>, val: SExpr) -> SExpr {
902        self.list(vec![self.atoms.bang, expr, self.atom(name), val])
903    }
904
905    pub fn named(&self, name: impl Into<String> + AsRef<str>, expr: SExpr) -> SExpr {
906        self.attr(expr, ":named", self.atom(name))
907    }
908
909    /// A let-declaration with a single binding.
910    pub fn let_<N: Into<String> + AsRef<str>>(&self, name: N, e: SExpr, body: SExpr) -> SExpr {
911        self.list(vec![
912            self.atoms.let_,
913            self.list(vec![self.list(vec![self.atom(name), e])]),
914            body,
915        ])
916    }
917
918    /// A let-declaration of multiple bindings.
919    pub fn let_many<N, I>(&self, bindings: I, body: SExpr) -> SExpr
920    where
921        I: IntoIterator<Item = (N, SExpr)>,
922        N: Into<String> + AsRef<str>,
923    {
924        self.list(vec![
925            self.atoms.let_,
926            self.list(Vec::from_iter(
927                bindings
928                    .into_iter()
929                    .map(|(n, v)| self.list(vec![self.atom(n), v])),
930            )),
931            body,
932        ])
933    }
934
935    /// Universally quantify sorted variables in an expression.
936    pub fn forall<N, I>(&self, vars: I, body: SExpr) -> SExpr
937    where
938        I: IntoIterator<Item = (N, SExpr)>,
939        N: Into<String> + AsRef<str>,
940    {
941        let vars_iter = vars
942            .into_iter()
943            .map(|(n, s)| self.list(vec![self.atom(n), s]));
944        self.list(vec![
945            self.atoms.forall,
946            self.list(vars_iter.collect()),
947            body,
948        ])
949    }
950
951    /// Existentially quantify sorted variables in an expression.
952    pub fn exists<N, I>(&self, vars: I, body: SExpr) -> SExpr
953    where
954        I: IntoIterator<Item = (N, SExpr)>,
955        N: Into<String> + AsRef<str>,
956    {
957        let vars_iter = vars
958            .into_iter()
959            .map(|(n, s)| self.list(vec![self.atom(n), s]));
960        self.list(vec![
961            self.atoms.exists,
962            self.list(vars_iter.collect()),
963            body,
964        ])
965    }
966
967    /// Perform pattern matching on values of an algebraic data type.
968    pub fn match_<I: IntoIterator<Item = (SExpr, SExpr)>>(&self, term: SExpr, arms: I) -> SExpr {
969        let args: Vec<_> = std::iter::once(self.atoms.match_)
970            .chain(std::iter::once(term))
971            .chain(arms.into_iter().map(|(p, v)| self.list(vec![p, v])))
972            .collect();
973        assert!(args.len() >= 3);
974        self.list(args)
975    }
976
977    /// Construct an if-then-else expression.
978    pub fn ite(&self, c: SExpr, t: SExpr, e: SExpr) -> SExpr {
979        self.list(vec![self.atoms.ite, c, t, e])
980    }
981}
982
983/// # Bool Helpers
984///
985/// These methods help you construct s-expressions for various bool operations.
986impl Context {
987    /// The `Bool` sort.
988    pub fn bool_sort(&self) -> SExpr {
989        self.atoms.bool
990    }
991
992    /// The `true` constant.
993    pub fn true_(&self) -> SExpr {
994        self.atoms.t
995    }
996
997    /// The `false` constant.
998    pub fn false_(&self) -> SExpr {
999        self.atoms.f
1000    }
1001
1002    unary!(not, not);
1003    right_assoc!(imp, imp_many, imp);
1004    left_assoc!(and, and_many, and);
1005    left_assoc!(or, or_many, or);
1006    left_assoc!(xor, xor_many, xor);
1007    chainable!(eq, eq_many, eq);
1008    pairwise!(distinct, distinct_many, distinct);
1009}
1010
1011/// # Int Helpers
1012///
1013/// These methods help you construct s-expressions for various int operations.
1014impl Context {
1015    /// The `Int` sort.
1016    pub fn int_sort(&self) -> SExpr {
1017        self.atoms.int
1018    }
1019
1020    unary!(negate, minus);
1021    left_assoc!(sub, sub_many, minus);
1022    left_assoc!(plus, plus_many, plus);
1023    left_assoc!(times, times_many, times);
1024    left_assoc!(div, div_many, div);
1025    left_assoc!(modulo, modulo_many, modulo);
1026    left_assoc!(rem, rem_many, rem);
1027    chainable!(lte, lte_many, lte);
1028    chainable!(lt, lt_many, lt);
1029    chainable!(gt, gt_many, gt);
1030    chainable!(gte, gte_many, gte);
1031}
1032
1033/// # Real Helpers
1034///
1035/// These methods help you construct s-expressions for various real operations.
1036impl Context {
1037    /// The `Real` sort.
1038    pub fn real_sort(&self) -> SExpr {
1039        self.atoms.real
1040    }
1041
1042    left_assoc!(rdiv, rdiv_many, slash);
1043    unary!(conv_to_real, to_real);
1044}
1045
1046/// # Array Helpers
1047///
1048/// These methods help you construct s-expressions for various array operations.
1049impl Context {
1050    /// Construct the array sort with the given index and element types.
1051    pub fn array_sort(&self, index: SExpr, element: SExpr) -> SExpr {
1052        self.list(vec![self.atoms.array, index, element])
1053    }
1054
1055    /// Select the element at the given index in the array.
1056    pub fn select(&self, ary: SExpr, index: SExpr) -> SExpr {
1057        self.list(vec![self.atoms.select, ary, index])
1058    }
1059
1060    /// Store the value into the given index in the array, yielding a new array.
1061    pub fn store(&self, ary: SExpr, index: SExpr, value: SExpr) -> SExpr {
1062        self.list(vec![self.atoms.store, ary, index, value])
1063    }
1064}
1065
1066/// # Bit Vector Helpers
1067///
1068/// These methods help you construct s-expressions for various bit vector
1069/// operations.
1070impl Context {
1071    /// Construct a BitVec sort with the given width.
1072    pub fn bit_vec_sort(&self, width: SExpr) -> SExpr {
1073        self.list(vec![self.atoms.und, self.atoms.bit_vec, width])
1074    }
1075
1076    /// Concatenate two bit vectors.
1077    pub fn concat(&self, lhs: SExpr, rhs: SExpr) -> SExpr {
1078        self.list(vec![self.atoms.concat, lhs, rhs])
1079    }
1080
1081    /// Extract a range from a bit vector.
1082    pub fn extract(&self, i: i32, j: i32, bv: SExpr) -> SExpr {
1083        self.list(vec![
1084            self.list(vec![
1085                self.atoms.und,
1086                self.atoms.extract,
1087                self.numeral(i),
1088                self.numeral(j),
1089            ]),
1090            bv,
1091        ])
1092    }
1093
1094    unary!(bvnot, bvnot);
1095    left_assoc!(bvor, bvor_many, bvor);
1096    left_assoc!(bvand, bvand_many, bvand);
1097    left_assoc!(bvnand, bvnand_many, bvnand);
1098    left_assoc!(bvxor, bvxor_many, bvxor);
1099    left_assoc!(bvxnor, bvxnor_many, bvxnor);
1100    unary!(bvneg, bvneg);
1101    left_assoc!(bvadd, bvadd_many, bvadd);
1102    binop!(bvsub, bvsub);
1103    left_assoc!(bvmul, bvmul_many, bvmul);
1104    binop!(bvudiv, bvudiv);
1105    binop!(bvurem, bvurem);
1106    binop!(bvsdiv, bvsdiv);
1107    binop!(bvsrem, bvsrem);
1108    binop!(bvsmod, bvsmod);
1109    binop!(bvshl, bvshl);
1110    binop!(bvlshr, bvlshr);
1111    binop!(bvashr, bvashr);
1112    binop!(bvule, bvule);
1113    binop!(bvult, bvult);
1114    binop!(bvuge, bvuge);
1115    binop!(bvugt, bvugt);
1116    binop!(bvsle, bvsle);
1117    binop!(bvslt, bvslt);
1118    binop!(bvsge, bvsge);
1119    binop!(bvsgt, bvsgt);
1120    binop!(bvcomp, bvcomp);
1121}
1122
1123#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1124pub enum Response {
1125    Sat,
1126    Unsat,
1127    Unknown,
1128}
1129
1130mod private {
1131    /// A trait implemented by types that can be used to create numerals.
1132    pub trait IntoNumeral: IntoNumeralSealed {}
1133    pub trait IntoNumeralSealed: std::fmt::Display {}
1134
1135    /// A trait implemented by types that can be used to create binaries.
1136    pub trait IntoBinary: IntoBinarySealed {}
1137    pub trait IntoBinarySealed: std::fmt::Binary {}
1138
1139    impl IntoNumeralSealed for i8 {}
1140    impl IntoNumeral for i8 {}
1141    impl IntoBinarySealed for i8 {}
1142    impl IntoBinary for i8 {}
1143    impl IntoNumeralSealed for i16 {}
1144    impl IntoNumeral for i16 {}
1145    impl IntoBinarySealed for i16 {}
1146    impl IntoBinary for i16 {}
1147    impl IntoNumeralSealed for i32 {}
1148    impl IntoNumeral for i32 {}
1149    impl IntoBinarySealed for i32 {}
1150    impl IntoBinary for i32 {}
1151    impl IntoNumeralSealed for i64 {}
1152    impl IntoNumeral for i64 {}
1153    impl IntoBinarySealed for i64 {}
1154    impl IntoBinary for i64 {}
1155    impl IntoNumeralSealed for i128 {}
1156    impl IntoNumeral for i128 {}
1157    impl IntoBinarySealed for i128 {}
1158    impl IntoBinary for i128 {}
1159    impl IntoNumeralSealed for isize {}
1160    impl IntoNumeral for isize {}
1161    impl IntoBinarySealed for isize {}
1162    impl IntoBinary for isize {}
1163    impl IntoNumeralSealed for u8 {}
1164    impl IntoNumeral for u8 {}
1165    impl IntoBinarySealed for u8 {}
1166    impl IntoBinary for u8 {}
1167    impl IntoNumeralSealed for u16 {}
1168    impl IntoNumeral for u16 {}
1169    impl IntoBinarySealed for u16 {}
1170    impl IntoBinary for u16 {}
1171    impl IntoNumeralSealed for u32 {}
1172    impl IntoNumeral for u32 {}
1173    impl IntoBinarySealed for u32 {}
1174    impl IntoBinary for u32 {}
1175    impl IntoNumeralSealed for u64 {}
1176    impl IntoNumeral for u64 {}
1177    impl IntoBinarySealed for u64 {}
1178    impl IntoBinary for u64 {}
1179    impl IntoNumeralSealed for u128 {}
1180    impl IntoNumeral for u128 {}
1181    impl IntoBinarySealed for u128 {}
1182    impl IntoBinary for u128 {}
1183    impl IntoNumeralSealed for usize {}
1184    impl IntoNumeral for usize {}
1185    impl IntoBinarySealed for usize {}
1186    impl IntoBinary for usize {}
1187
1188    /// A trait implemented by types that can be used to create decimals.
1189    pub trait IntoDecimal: IntoDecimalSealed {}
1190    pub trait IntoDecimalSealed: std::fmt::Display {}
1191
1192    impl IntoDecimal for f32 {}
1193    impl IntoDecimalSealed for f32 {}
1194    impl IntoDecimal for f64 {}
1195    impl IntoDecimalSealed for f64 {}
1196}
1197pub use private::{IntoBinary, IntoDecimal, IntoNumeral};
1198
1199#[cfg(test)]
1200mod tests {
1201    use super::*;
1202
1203    macro_rules! check_expr {
1204        ($ctx:expr, $expr:expr, $expected:expr) => {
1205            let actual = $ctx.display($expr).to_string();
1206            assert_eq!(actual, $expected);
1207        };
1208    }
1209
1210    #[test]
1211    fn test_variadic_ops() {
1212        let ctx = ContextBuilder::new().build().unwrap();
1213
1214        // As all variadic operators are generated by the `variadic!` macro above, we really only need
1215        // to pick one to test.
1216        check_expr!(ctx, ctx.imp_many([ctx.true_()]), "true");
1217        check_expr!(
1218            ctx,
1219            ctx.imp_many([ctx.true_(), ctx.false_()]),
1220            "(=> true false)"
1221        );
1222    }
1223
1224    #[cfg(debug_assertions)]
1225    #[test]
1226    #[should_panic]
1227    fn sexpr_with_wrong_context() {
1228        let ctx1 = ContextBuilder::new().build().unwrap();
1229        let ctx2 = ContextBuilder::new().build().unwrap();
1230
1231        let pizza1 = ctx1.atom("pizza");
1232        let _pizza2 = ctx2.atom("pizza");
1233
1234        // This should trigger a panic in debug builds.
1235        let _ = ctx2.get(pizza1);
1236    }
1237}