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}