rsmt2/examples/
simple.rs

1//! A simple example
2//!
3//! This example uses the types defined in this module, they will systematically be imported in the
4//! code samples.
5//!
6//!
7//! We first need to define the expression type, and make it implement the
8//! [`Expr2Smt`](crate::print::Expr2Smt) trait that writes it as an SMT-LIB 2 expression in a
9//! writer.
10//!
11//! ## Print functions
12//!
13//! Since the structure for S-expressions is provided by users, they also need to provide functions
14//! to print it in SMT-LIB 2.
15//!
16//! To use all SMT-LIB 2 commands in a type-safe manner, the library requires printers over
17//!
18//! - sorts: [`Sort2Smt`](crate::print::Sort2Smt) trait, *e.g.* for
19//!   [`Solver::declare_fun`](crate::Solver::declare_fun),
20//! - symbols: [`Sym2Smt`](crate::print::Sym2Smt) trait, *e.g.* for
21//!   [`Solver::declare_fun`](crate::Solver::declare_fun),
22//! - expressions: [`Expr2Smt`](crate::print::Expr2Smt) trait, *e.g.* for
23//!   [`Solver::assert`](crate::Solver::assert).
24//!
25//! All user-provided printing functions take some *information*. That way, users can pass some
26//! information to, say, `assert` that can modify printing. This is typically used when dealing with
27//! transition systems to perform "print-time unrolling". See the
28//! [`examples::print_time`](crate::examples::print_time) module if you're interested; the example
29//! below will not use print-time information.
30//!
31//! ```rust
32//! extern crate rsmt2;
33//!
34//! use rsmt2::print::Expr2Smt;
35//! use rsmt2::SmtRes;
36//! use rsmt2::examples::simple::{ Op, Cst };
37//!
38//! /// An example of expression.
39//! pub enum Expr {
40//!     /// A constant.
41//!     C(Cst),
42//!     /// Variable.
43//!     V(String),
44//!     /// Operator application.
45//!     O( Op, Vec<Expr> ),
46//! }
47//! impl Expr {
48//!     pub fn cst<C: Into<Cst>>(c: C) -> Self {
49//!         Expr::C( c.into() )
50//!     }
51//! }
52//! impl Expr2Smt<()> for Expr {
53//!     fn expr_to_smt2<Writer>(
54//!         & self, w: & mut Writer, _: ()
55//!     ) -> SmtRes<()>
56//!     where Writer: ::std::io::Write {
57//!         let mut stack = vec![ (false, vec![self], false) ];
58//!         while let Some((space, mut to_write, closing_paren)) = stack.pop() {
59//!             if let Some(next) = to_write.pop() {
60//!                 if space {
61//!                     write!(w, " ") ?
62//!                 }
63//!                 // We have something to print, push the rest back.
64//!                 stack.push((space, to_write, closing_paren));
65//!                 match * next {
66//!                     Expr::C(cst) => write!(w, "{}", cst) ?,
67//!                     Expr::V(ref var) => write!(w, "{}", var) ?,
68//!                     Expr::O(op, ref sub_terms) => {
69//!                         write!(w, "({}", op) ?;
70//!                         stack.push((true, sub_terms.iter().rev().collect(), true))
71//!                     },
72//!                 }
73//!             } else {
74//!                 // No more things to write at this level.
75//!                 if closing_paren {
76//!                     write!(w, ")") ?
77//!                 }
78//!             }
79//!         }
80//!         Ok(())
81//!     }
82//! }
83//!
84//! # fn main() {}
85//! ```
86//!
87//! For convenience, all the `...2Smt` traits are implemented for `& str`. This is useful for
88//! testing and maybe *very* simple application. Here, we won't implement
89//! [`Sym2Smt`](crate::print::Sym2Smt) or [`Sort2Smt`](crate::print::Sort2Smt) and rely on `& str`
90//! for symbols and sorts. Using a solver then boils down to creating a [`Solver`](crate::Solver)
91//! which wraps a z3 process and provides most of the SMT-LIB 2.5 commands.
92//!
93//! ```rust
94//! extern crate rsmt2;
95//!
96//! use rsmt2::Solver;
97//! use rsmt2::examples::simple::{ Op, Cst, Expr };
98//! # fn main() {
99//!
100//! let mut solver = Solver::default_z3(()).expect(
101//!     "could not spawn solver kid"
102//! );
103//!
104//! let v_1 = "v_1".to_string();
105//! let v_2 = "v_2".to_string();
106//!
107//! solver.declare_const( & v_1, & "Bool" ).expect(
108//!     "while declaring v_1"
109//! );
110//! solver.declare_const( & v_2, & "Int" ).expect(
111//!     "while declaring v_2"
112//! );
113//!
114//! let expr = Expr::O(
115//!     Op::Disj, vec![
116//!         Expr::O(
117//!             Op::Ge, vec![ Expr::cst(-7), Expr::V( v_2.clone() ) ]
118//!         ),
119//!         Expr::V( v_1.clone() )
120//!     ]
121//! );
122//!
123//! solver.assert( & expr ).expect(
124//!     "while asserting an expression"
125//! );
126//!
127//! if solver.check_sat().expect("during check sat") {
128//!     ()
129//! } else {
130//!     panic!("expected sat, got unsat")
131//! }
132//!
133//! solver.kill().unwrap()
134//! # }
135//! ```
136//!
137//! Note the `unit` parameter that we passed to the `solver` function: `solver(& mut kid, ())`. This
138//! is actually the parser the solver should use when it needs to parse values, symbols, types... In
139//! the example above, we only asked for the satisfiability of the assertions. If we had asked for a
140//! model, the compiler would have complained by saying that our parser `()` does not implement the
141//! right parsing traits.
142//!
143//! ## The parser
144//!
145//! This example will only use [`Solver::get_model`](crate::Solver::get_model), which only requires
146//! [`IdentParser`](crate::parse::IdentParser) and [`ModelParser`](crate::parse::ModelParser). In
147//! most cases, an empty parser `struct` with the right implementations should be enough.
148//!
149//! ```rust
150//! # #[macro_use]
151//! # extern crate error_chain;
152//! extern crate rsmt2;
153//!
154//! use rsmt2::SmtRes;
155//! use rsmt2::parse::{ IdentParser, ModelParser };
156//! use rsmt2::examples::simple::Cst;
157//!
158//! /// Empty parser structure, we will not maintain any context.
159//! #[derive(Clone, Copy)]
160//! pub struct Parser;
161//! impl<'a> IdentParser<String, String, & 'a str> for Parser {
162//!     fn parse_ident(self, input: & 'a str) -> SmtRes<String> {
163//!         Ok( input.to_string() )
164//!     }
165//!     fn parse_type(self, input: & 'a str) -> SmtRes<String> {
166//!         match input {
167//!             "Int" => Ok( "Int".into() ),
168//!             "Bool" => Ok( "Bool".into() ),
169//!             sort => bail!("unexpected sort `{}`", sort),
170//!         }
171//!     }
172//! }
173//! impl<'a> ModelParser<String, String, Cst, & 'a str> for Parser {
174//!     fn parse_value(
175//!         self, input: & 'a str,
176//!         _ident: & String, _signature: & [ (String, String) ], _type: & String,
177//!     ) -> SmtRes<Cst> {
178//!         match input.trim() {
179//!             "true" => Ok( Cst::B(true) ),
180//!             "false" => Ok( Cst::B(false) ),
181//!             int => {
182//!                 use std::str::FromStr;
183//!                 let s = int.trim();
184//!                 if let Ok(res) = isize::from_str(s) {
185//!                     return Ok( Cst::I(res) )
186//!                 } else if s.len() >= 4 {
187//!                     if & s[0 .. 1] == "("
188//!                     && & s[s.len() - 1 ..] == ")" {
189//!                         let s = & s[1 .. s.len() - 1].trim();
190//!                         if & s[0 .. 1] == "-" {
191//!                             let s = & s[1..].trim();
192//!                             if let Ok(res) = isize::from_str(s) {
193//!                                 return Ok( Cst::I(- res) )
194//!                             }
195//!                         }
196//!                     }
197//!                 }
198//!                 bail!("unexpected value `{}`", int)
199//!             },
200//!         }
201//!     }
202//! }
203//! # fn main() {}
204//! ```
205//!
206//! As a side note, it would have been simpler to implement
207//! [`ModelParser`](crate::parse::ModelParser) with a [`& mut SmtParser`](crate::parse::SmtParser),
208//! as it provides the parsers we needed.
209//!
210//! ```rust
211//!
212//! use rsmt2::SmtRes;
213//! use rsmt2::parse::{ SmtParser, IdentParser, ModelParser };
214//! use rsmt2::examples::simple::Cst;
215//!
216//!
217//! #[derive(Clone, Copy)]
218//! struct Parser;
219//! impl<'a, Br> ModelParser<
220//!     String, String, Cst, & 'a mut SmtParser<Br>
221//! > for Parser
222//! where Br: ::std::io::BufRead {
223//!     fn parse_value(
224//!         self, input: & 'a mut SmtParser<Br>,
225//!         _ident: & String, _signature: & [ (String, String) ], _type: & String
226//!     ) -> SmtRes<Cst> {
227//!         use std::str::FromStr;
228//!         if let Some(b) = input.try_bool() ? {
229//!             Ok( Cst::B(b) )
230//!         } else if let Some(int) = input.try_int(
231//!             |int, pos| match isize::from_str(int) {
232//!                 Ok(int) => if pos { Ok(int) } else { Ok(- int) },
233//!                 Err(e) => Err(e),
234//!             }
235//!         ) ? {
236//!             Ok( Cst::I(int) )
237//!         } else {
238//!             input.fail_with("unexpected value")
239//!         }
240//!     }
241//! }
242//! ```
243//!
244//! Anyway, once we pass `Parser` to the solver creation function, and all conditions are met to ask
245//! the solver for a model.
246//!
247//! ```rust
248//! # #[macro_use]
249//! # extern crate error_chain;
250//! extern crate rsmt2;
251//!
252//! use rsmt2::{ SmtRes, Solver };
253//! use rsmt2::examples::simple::{
254//!     Cst, Op, Expr, Parser
255//! };
256//!
257//! # fn main() {
258//!
259//! let mut solver = Solver::default_z3(Parser).expect(
260//!     "could not spawn solver kid"
261//! );
262//!
263//! let v_1 = "v_1".to_string();
264//! let v_2 = "v_2".to_string();
265//!
266//! solver.declare_const( & v_1, & "Bool" ).expect(
267//!     "while declaring v_1"
268//! );
269//! solver.declare_const( & v_2, & "Int" ).expect(
270//!     "while declaring v_2"
271//! );
272//!
273//! let expr = Expr::O(
274//!     Op::Disj, vec![
275//!         Expr::O(
276//!             Op::Ge, vec![ Expr::cst(-7), Expr::V( v_2.clone() ) ]
277//!         ),
278//!         Expr::V( v_1.clone() )
279//!     ]
280//! );
281//!
282//! solver.assert( & expr ).expect(
283//!     "while asserting an expression"
284//! );
285//!
286//! if solver.check_sat().expect("during check sat") {
287//!
288//!     let model = solver.get_model_const().expect(
289//!         "while getting model"
290//!     );
291//!
292//!     let mut okay = false;
293//!     for (ident, typ, value) in model {
294//!         if ident == v_1 {
295//!             assert_eq!( typ, "Bool" );
296//!             match value {
297//!                 Cst::B(true) => okay = true,
298//!                 Cst::B(false) => (),
299//!                 Cst::I(int) => panic!(
300//!                     "value for v_1 is `{}`, expected boolean", int
301//!                 ),
302//!             }
303//!         } else if ident == v_2 {
304//!             assert_eq!( typ, "Int" );
305//!             match value {
306//!                 Cst::I(i) if -7 >= i => okay = true,
307//!                 Cst::I(_) => (),
308//!                 Cst::B(b) => panic!(
309//!                     "value for v_2 is `{}`, expected isize", b
310//!                 ),
311//!             }
312//!         }
313//!     }
314//!
315//!     if ! okay {
316//!         panic!("got sat, but model is spurious")
317//!     }
318//!
319//! } else {
320//!     panic!("expected sat, got unsat")
321//! }
322//!
323//! solver.kill().unwrap()
324//! # }
325//! ```
326
327use crate::{
328    errors::SmtRes,
329    parse::{IdentParser, ModelParser},
330    print::Expr2Smt,
331};
332
333#[cfg(test)]
334use crate::examples::get_solver;
335
336/// Operators. Just implements `Display`, never manipulated directly by the solver.
337#[derive(Copy, Clone)]
338pub enum Op {
339    /// Addition.
340    Add,
341    /// Subtraction.
342    Sub,
343    /// Multiplication.
344    Mul,
345    /// Conjunction.
346    Conj,
347    /// Disjunction.
348    Disj,
349    /// Equality.
350    Eql,
351    /// Greater than or equal to.
352    Ge,
353    /// Greater than.
354    Gt,
355    /// Less than.
356    Lt,
357    /// Less than or equal to.
358    Le,
359}
360impl ::std::fmt::Display for Op {
361    fn fmt(&self, w: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
362        w.write_str(match *self {
363            Op::Add => "+",
364            Op::Sub => "-",
365            Op::Mul => "*",
366            Op::Conj => "and",
367            Op::Disj => "or",
368            Op::Eql => "=",
369            Op::Ge => ">=",
370            Op::Gt => ">",
371            Op::Lt => "<",
372            Op::Le => "<=",
373        })
374    }
375}
376
377/// A constant.
378#[derive(Clone, Copy)]
379pub enum Cst {
380    /// Boolean constant.
381    B(bool),
382    /// Integer constant.
383    I(isize),
384}
385impl ::std::fmt::Display for Cst {
386    fn fmt(&self, w: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
387        match *self {
388            Cst::B(b) => write!(w, "{}", b),
389            Cst::I(i) if i >= 0 => write!(w, "{}", i),
390            Cst::I(i) => write!(w, "(- {})", -i),
391        }
392    }
393}
394impl From<bool> for Cst {
395    fn from(b: bool) -> Self {
396        Cst::B(b)
397    }
398}
399impl From<isize> for Cst {
400    fn from(i: isize) -> Self {
401        Cst::I(i)
402    }
403}
404
405/// An example of expression.
406pub enum Expr {
407    /// A constant.
408    C(Cst),
409    /// Variable.
410    V(String),
411    /// Operator application.
412    O(Op, Vec<Expr>),
413}
414impl Expr {
415    /// Constant constructor.
416    pub fn cst<C: Into<Cst>>(c: C) -> Self {
417        Expr::C(c.into())
418    }
419}
420impl Expr2Smt<()> for Expr {
421    fn expr_to_smt2<Writer>(&self, w: &mut Writer, _: ()) -> SmtRes<()>
422    where
423        Writer: ::std::io::Write,
424    {
425        let mut stack = vec![(false, vec![self], false)];
426        while let Some((space, mut to_write, closing_paren)) = stack.pop() {
427            if let Some(next) = to_write.pop() {
428                if space {
429                    write!(w, " ")?
430                }
431                // We have something to print, push the rest back.
432                stack.push((space, to_write, closing_paren));
433                match *next {
434                    Expr::C(cst) => write!(w, "{}", cst)?,
435                    Expr::V(ref var) => write!(w, "{}", var)?,
436                    Expr::O(op, ref sub_terms) => {
437                        write!(w, "({}", op)?;
438                        stack.push((true, sub_terms.iter().rev().collect(), true))
439                    }
440                }
441            } else if closing_paren {
442                // No more things to write at this level.
443                write!(w, ")")?
444            }
445        }
446        Ok(())
447    }
448}
449
450/// Empty parser structure, we will not maintain any context.
451#[derive(Clone, Copy)]
452pub struct Parser;
453impl<'a> IdentParser<String, String, &'a str> for Parser {
454    fn parse_ident(self, input: &'a str) -> SmtRes<String> {
455        Ok(input.to_string())
456    }
457    fn parse_type(self, input: &'a str) -> SmtRes<String> {
458        match input {
459            "Int" => Ok("Int".into()),
460            "Bool" => Ok("Bool".into()),
461            sort => bail!("unexpected sort `{}`", sort),
462        }
463    }
464}
465impl<'a> ModelParser<String, String, Cst, &'a str> for Parser {
466    fn parse_value(
467        self,
468        input: &'a str,
469        _: &String,
470        _: &[(String, String)],
471        _: &String,
472    ) -> SmtRes<Cst> {
473        match input.trim() {
474            "true" => Ok(Cst::B(true)),
475            "false" => Ok(Cst::B(false)),
476            int => {
477                use std::str::FromStr;
478                let s = int.trim();
479                if let Ok(res) = isize::from_str(s) {
480                    return Ok(Cst::I(res));
481                } else if s.len() >= 4 && &s[0..1] == "(" && &s[s.len() - 1..] == ")" {
482                    let s = &s[1..s.len() - 1].trim();
483                    if &s[0..1] == "-" {
484                        let s = &s[1..].trim();
485                        if let Ok(res) = isize::from_str(s) {
486                            return Ok(Cst::I(-res));
487                        }
488                    }
489                }
490                bail!("unexpected value `{}`", int)
491            }
492        }
493    }
494}
495
496#[test]
497fn run() {
498    let mut solver = get_solver(Parser);
499
500    let v_1 = "v_1".to_string();
501    let v_2 = "v_2".to_string();
502
503    solver
504        .declare_const(&v_1, &"Bool")
505        .expect("while declaring v_1");
506    solver
507        .declare_const(&v_2, &"Int")
508        .expect("while declaring v_2");
509
510    let expr = Expr::O(
511        Op::Disj,
512        vec![
513            Expr::O(Op::Ge, vec![Expr::cst(-7), Expr::V(v_2.clone())]),
514            Expr::V(v_1.clone()),
515        ],
516    );
517
518    solver.assert(&expr).expect("while asserting an expression");
519
520    if solver.check_sat().expect("during check sat") {
521        let model = solver.get_model_const().expect("while getting model");
522
523        let mut okay = false;
524        for (ident, typ, value) in model {
525            if ident == v_1 {
526                assert_eq!(typ, "Bool");
527                match value {
528                    Cst::B(true) => okay = true,
529                    Cst::B(false) => (),
530                    Cst::I(int) => panic!("value for v_1 is `{}`, expected boolean", int),
531                }
532            } else if ident == v_2 {
533                assert_eq!(typ, "Int");
534                match value {
535                    Cst::I(i) if -7 >= i => okay = true,
536                    Cst::I(_) => (),
537                    Cst::B(b) => panic!("value for v_2 is `{}`, expected isize", b),
538                }
539            }
540        }
541
542        if !okay {
543            panic!("got sat, but model is spurious")
544        }
545    } else {
546        panic!("expected sat, got unsat")
547    }
548
549    solver.kill().expect("killing solver")
550}