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