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}