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