rsmt2_zz/
conf.rs

1//! Solver configuration, contains backend-solver-specific info.
2//!
3//! Do **NOT** use wildcards when matching over `SmtStyle`. We want the code to fail to compile
4//! whenever we add a solver. Likewise, do not use `if let` with `SmtStyle`.
5
6use std::fmt;
7
8use crate::{SmtRes, Solver};
9
10use self::SmtStyle::*;
11
12/// A configuration item is either a keyword or unsupported.
13pub type ConfItem = Option<&'static str>;
14
15/// Value of an unsupported configuration item.
16#[inline]
17fn unsupported() -> ConfItem {
18    None
19}
20/// Keyword for a configuration item.
21#[inline]
22fn supported(keyword: &'static str) -> ConfItem {
23    Some(keyword)
24}
25
26/// Solver styles.
27///
28/// - [z3][z3]: full support
29/// - [cvc4][cvc4]: full support in theory, but only partially tested. Note that `get-value` is
30///   known to crash some versions of CVC4.
31/// - [yices 2][yices 2]: full support in theory, but only partially tested. Command `get-model`
32///   will only work on Yices 2 > `2.6.1`, and needs to be activated in [SmtConf][SmtConf] with
33///   [`conf.models()`](struct.SmtConf.html#method.models). To understand why, see
34///   <https://github.com/SRI-CSL/yices2/issues/162>.
35#[derive(Debug, Clone, Copy)]
36#[allow(dead_code)]
37pub enum SmtStyle {
38    /// Z3-style smt solver.
39    Z3,
40    /// CVC4-style smt solver.
41    ///
42    /// **NB**: CVC4 has only been partially tested at this point.
43    CVC4,
44    /// Yices-2-style smt solver.
45    ///
46    /// Yices 2 has only been partially tested at this point.
47    Yices2,
48}
49
50impl SmtStyle {
51    /// Default configuration for a solver style.
52    pub fn default(self) -> SmtConf {
53        let cmd = self.cmd();
54        match self {
55            Z3 => SmtConf {
56                style: self,
57                cmd,
58                options: vec!["-in".into(), "-smt2".into()],
59                models: true,
60                incremental: true,
61                parse_success: false,
62                unsat_cores: false,
63                check_sat_assuming: supported("check-sat-assuming"),
64            },
65            CVC4 => SmtConf {
66                style: self,
67                cmd,
68                options: vec![
69                    "-q".into(),
70                    "--interactive".into(),
71                    "--lang".into(),
72                    "smt2".into(),
73                ],
74                models: false,
75                incremental: false,
76                parse_success: false,
77                unsat_cores: false,
78                check_sat_assuming: unsupported(),
79            },
80            Yices2 => SmtConf {
81                style: self,
82                cmd,
83                options: vec![
84                    "--incremental".into(),
85                ],
86                models: false,
87                incremental: true,
88                parse_success: false,
89                unsat_cores: false,
90                check_sat_assuming: supported("check-sat-assuming"),
91            },
92        }
93    }
94
95    /// A solver style from a string.
96    #[allow(dead_code)]
97    pub fn of_str(s: &str) -> Option<Self> {
98        match s {
99            "z3" | "Z3" => Some(Z3),
100            "cvc4" | "CVC4" => Some(CVC4),
101            "Yices2" | "yices2" | "YICES2" | "Yices 2" | "yices 2" | "YICES 2" => Some(CVC4),
102            _ => None,
103        }
104    }
105    /// Legal string representations of solver styles.
106    #[allow(dead_code)]
107    pub fn str_keys() -> Vec<&'static str> {
108        vec![
109            "z3", "Z3", "cvc4", "CVC4", "Yices2", "yices2", "YICES2", "Yices 2", "yices 2",
110            "YICES 2",
111        ]
112    }
113
114    /// Default command for a solver style.
115    #[cfg(not(windows))]
116    pub fn cmd(self) -> String {
117        match self {
118            Z3 => "z3".to_string(),
119            CVC4 => "cvc4".to_string(),
120            Yices2 => "yices-smt2".to_string(),
121        }
122    }
123    /// Default command for a solver style.
124    #[cfg(windows)]
125    pub fn cmd(self) -> String {
126        match self {
127            Z3 => "z3.exe".to_string(),
128            CVC4 => "cvc4.exe".to_string(),
129            Yices2 => "yices-smt2.exe".to_string(),
130        }
131    }
132}
133
134impl fmt::Display for SmtStyle {
135    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
136        match *self {
137            Z3 => write!(fmt, "z3"),
138            CVC4 => write!(fmt, "cvc4"),
139            Yices2 => write!(fmt, "yices2"),
140        }
141    }
142}
143
144/// Configuration and solver specific info.
145///
146/// - [z3][z3]: full support
147/// - [cvc4][cvc4]: full support in theory, but only partially tested. Note that `get-value` is
148///   known to crash some versions of CVC4.
149/// - [yices 2][yices 2]: full support in theory, but only partially tested. Command `get-model`
150///   will only work on Yices 2 > `2.6.1`, and needs to be activated in [SmtConf][SmtConf] with
151///   [`conf.models()`](struct.SmtConf.html#method.models). To understand why, see
152///   <https://github.com/SRI-CSL/yices2/issues/162>.
153#[derive(Debug, Clone)]
154pub struct SmtConf {
155    /// Solver style.
156    style: SmtStyle,
157    /// Solver command.
158    cmd: String,
159    /// Options to call the solver with.
160    options: Vec<String>,
161    /// Model production.
162    models: bool,
163    /// Incrementality.
164    incremental: bool,
165    /// Parse success.
166    parse_success: bool,
167    /// Triggers unsat-core production.
168    unsat_cores: bool,
169    /// Keyword for check-sat with assumptions.
170    check_sat_assuming: ConfItem,
171}
172
173impl SmtConf {
174    /// Creates a new z3-like solver configuration.
175    ///
176    /// # Examples
177    ///
178    /// ```rust
179    /// # use rsmt2::SmtConf ;
180    /// let conf = SmtConf::z3() ;
181    /// assert! {
182    ///     conf.get_cmd() == "z3" || conf.get_cmd() == "z3.exe"
183    /// }
184    /// ```
185    #[inline]
186    pub fn z3() -> Self {
187        Z3.default()
188    }
189
190    /// Creates a new cvc4-like solver configuration.
191    ///
192    /// # Examples
193    ///
194    /// ```rust
195    /// # use rsmt2::SmtConf ;
196    /// let conf = SmtConf::cvc4() ;
197    /// assert! {
198    ///     conf.get_cmd() == "cvc4" || conf.get_cmd() == "cvc4.exe"
199    /// }
200    /// ```
201    #[inline]
202    pub fn cvc4() -> Self {
203        CVC4.default()
204    }
205
206    /// Creates a new yices-2-like solver configuration.
207    ///
208    /// # Examples
209    ///
210    /// ```rust
211    /// # use rsmt2::SmtConf ;
212    /// let conf = SmtConf::yices_2() ;
213    /// assert! {
214    ///     conf.get_cmd() == "yices-smt2" || conf.get_cmd() == "yices-smt2.exe"
215    /// }
216    /// ```
217    #[inline]
218    pub fn yices_2() -> Self {
219        Yices2.default()
220    }
221
222    /// Spawns the solver.
223    ///
224    /// # Examples
225    ///
226    /// ```rust
227    /// # use rsmt2::SmtConf ;
228    /// let _solver = SmtConf::z3().spawn(()).unwrap() ;
229    /// ```
230    #[inline]
231    pub fn spawn<Parser>(self, parser: Parser) -> SmtRes<Solver<Parser>> {
232        Solver::new(self, parser)
233    }
234
235    /// Concise description of the underlying solver.
236    ///
237    /// # Examples
238    ///
239    /// ```rust
240    /// # use rsmt2::SmtConf ;
241    /// assert_eq! { SmtConf::z3().desc(), "z3" }
242    /// ```
243    #[inline]
244    pub fn desc(&self) -> &str {
245        match self.style {
246            Z3 => "z3",
247            CVC4 => "cvc4",
248            Yices2 => "yices2",
249        }
250    }
251
252    /// Model production flag.
253    pub fn get_models(&self) -> bool {
254        self.models
255    }
256    /// Activates model production.
257    pub fn models(&mut self) {
258        if self.models {
259            return ();
260        } else {
261            self.models = true
262        }
263        match self.style {
264            CVC4 => self.options.push("--produce-models".into()),
265            Yices2 => self.options.push("--smt2-model-format".into()),
266            Z3 => (),
267        }
268    }
269
270    /// Incrementality.
271    pub fn get_incremental(&self) -> bool {
272        self.incremental
273    }
274    /// Activates incrementality (push/pop, check-sat-assuming).
275    pub fn incremental(&mut self) {
276        if self.incremental {
277            return ();
278        } else {
279            self.incremental = true
280        }
281        match self.style {
282            CVC4 | Yices2 => self.options.push("--incremental".into()),
283            Z3 => (),
284        }
285    }
286
287    /// Solver command.
288    ///
289    /// # Examples
290    ///
291    /// ```rust
292    /// # use rsmt2::SmtConf ;
293    /// let conf = SmtConf::z3() ;
294    /// assert! {
295    ///     conf.get_cmd() == "z3" || conf.get_cmd() == "z3.exe"
296    /// }
297    /// ```
298    #[inline]
299    pub fn get_cmd(&self) -> &str {
300        &self.cmd
301    }
302
303    /// Options of the configuration.
304    ///
305    /// # Examples
306    ///
307    /// ```rust
308    /// # use rsmt2::SmtConf ;
309    /// assert_eq! {
310    ///     SmtConf::z3().get_options(), & [ "-in", "-smt2" ]
311    /// }
312    /// ```
313    #[inline]
314    pub fn get_options(&self) -> &[String] {
315        &self.options
316    }
317
318    /// Indicates if print success is active.
319    ///
320    /// # Examples
321    ///
322    /// ```rust
323    /// # use rsmt2::SmtConf ;
324    /// assert! { ! SmtConf::z3().get_print_success() }
325    /// ```
326    #[inline]
327    pub fn get_print_success(&self) -> bool {
328        self.parse_success
329    }
330
331    /// Indicates if unsat cores is active.
332    ///
333    /// # Examples
334    ///
335    /// ```rust
336    /// # use rsmt2::SmtConf ;
337    /// assert! { ! SmtConf::z3().get_unsat_cores() }
338    /// ```
339    #[inline]
340    pub fn get_unsat_cores(&self) -> bool {
341        self.unsat_cores
342    }
343
344    /// Keyword for check-sat with assumptions.
345    ///
346    /// # Examples
347    ///
348    /// ```rust
349    /// # use rsmt2::SmtConf ;
350    /// assert_eq! {
351    ///     SmtConf::z3().get_check_sat_assuming(), Some("check-sat-assuming")
352    /// }
353    /// ```
354    #[inline]
355    pub fn get_check_sat_assuming(&self) -> ConfItem {
356        self.check_sat_assuming.as_ref().map(|s| *s)
357    }
358
359    /// Adds an option to the configuration.
360    ///
361    /// # Examples
362    ///
363    /// ```rust
364    /// # use rsmt2::SmtConf ;
365    /// let mut conf = SmtConf::z3() ;
366    /// conf.option("arith.euclidean_solver=true") ;
367    /// assert_eq! {
368    ///     conf.get_options(),
369    ///     & [ "-in", "-smt2", "arith.euclidean_solver=true" ]
370    /// }
371    /// ```
372    #[inline]
373    pub fn option<S: Into<String>>(&mut self, o: S) -> &mut Self {
374        self.options.push(o.into());
375        self
376    }
377
378    /// Sets the command for the solver.
379    ///
380    /// # Examples
381    ///
382    /// ```rust
383    /// # use rsmt2::SmtConf ;
384    /// let mut conf = SmtConf::z3() ;
385    /// conf.cmd("my_custom_z3_command") ;
386    /// assert_eq! { conf.get_cmd(), "my_custom_z3_command" }
387    /// ```
388    #[inline]
389    pub fn cmd<S: Into<String>>(&mut self, cmd: S) -> &mut Self {
390        let cmd = cmd.into();
391        let mut iter = cmd.split(' ');
392
393        'set_cmd: while let Some(cmd) = iter.next() {
394            if !cmd.is_empty() {
395                self.cmd = cmd.into();
396                break 'set_cmd;
397            }
398        }
399
400        for option in iter {
401            if !option.is_empty() {
402                self.options.push(option.into())
403            }
404        }
405
406        self
407    }
408
409    /// Activates parse sucess.
410    ///
411    /// # Examples
412    ///
413    /// ```rust
414    /// # use rsmt2::SmtConf ;
415    /// let mut conf = SmtConf::z3() ;
416    /// conf.print_success() ;
417    /// assert! { conf.get_print_success() }
418    /// ```
419    #[inline]
420    #[cfg(not(no_parse_success))]
421    pub fn print_success(&mut self) {
422        self.parse_success = true
423    }
424
425    /// Activates unsat-core production.
426    ///
427    /// # Examples
428    ///
429    /// ```rust
430    /// # use rsmt2::SmtConf ;
431    /// let mut conf = SmtConf::z3() ;
432    /// conf.unsat_cores() ;
433    /// assert! { conf.get_unsat_cores() }
434    /// ```
435    #[inline]
436    pub fn unsat_cores(&mut self) {
437        self.unsat_cores = true
438    }
439}
440
441/// ## Solver-specific error-handling
442impl SmtConf {
443    /// Adds information to a `get-value` error message if needed.
444    pub fn enrich_get_values_error(&self, e: crate::errors::Error) -> crate::errors::Error {
445        match self.style {
446            SmtStyle::CVC4 => e.chain_err(|| {
447                "some versions of CVC4 produce errors on `get-value` commands, \
448                 consider using `get-model` instead"
449            }),
450            SmtStyle::Z3 | SmtStyle::Yices2 => e,
451        }
452    }
453}