11//! Not a SAT solver, but a prover. There's a difference.
2+ use thiserror:: Error ;
23
3- //use std::{fmt::Display, time::Duration};
4-
5- use std:: { collections:: VecDeque , fmt:: Display , io:: Write , path:: Path , process:: Command , time:: Duration } ;
4+ use std:: { collections:: VecDeque , env, fmt:: Display , io:: { self , Write } , path:: Path , process:: { self , Command } , time:: Duration } ;
65
76use tempfile:: NamedTempFile ;
87
@@ -17,6 +16,18 @@ use crate::{
1716 util:: { set_solver_timeout, ReasonUnknown } ,
1817} ;
1918
19+ #[ derive( Debug , Error ) ]
20+ pub enum CommandError {
21+ #[ error( "Environment variable error: {0}" ) ]
22+ EnvVarError ( #[ from] env:: VarError ) ,
23+ #[ error( "Process execution failed: {0}" ) ]
24+ ProcessError ( #[ from] io:: Error ) ,
25+ }
26+ pub enum SolverType {
27+ Z3 ,
28+ SWINE ,
29+ }
30+
2031/// The result of a prove query.
2132#[ derive( Debug ) ]
2233pub enum ProveResult < ' ctx > {
@@ -25,40 +36,40 @@ pub enum ProveResult<'ctx> {
2536 Unknown ( ReasonUnknown ) ,
2637}
2738
28- /// Find the swine-z3 file located under the dir directory, and execute swine-z3 on the file located at file_path
29- fn execute_swine ( dir : & Path , file_path : & Path ) {
30- let swine = "swine-z3" ;
31-
32- let find_output = Command :: new ( "find" )
33- . arg ( dir)
34- . arg ( "-name" )
35- . arg ( swine)
36- . output ( ) . unwrap ( ) ;
37-
38- if find_output. status . success ( ) {
39- let stdout = String :: from_utf8_lossy ( & find_output. stdout ) ;
40-
41- for line in stdout. lines ( ) . rev ( ) {
42- let path = Path :: new ( line) ;
43-
44- if path. exists ( ) && path. is_file ( ) {
45- let cmd_output = Command :: new ( path)
46- . arg ( file_path)
47- . output ( ) . unwrap ( ) ;
48-
49- if cmd_output. status . success ( ) {
50- println ! ( "{}" , String :: from_utf8_lossy( & cmd_output. stdout) ) ;
51- break ;
52- } else {
53- eprintln ! ( "Failed to execute swine({}) command with status: {}" , line, cmd_output. status) ;
39+ /// Execute swine-z3 on the file located at file_path
40+ fn execute_swine ( file_path : & Path ) -> Result < SatResult , CommandError > {
41+ match env:: var ( "SWINE" ) {
42+ // Use "export SWINE=<path_for_swine>" to set the path for swine in the SWINE variable.
43+ Ok ( swine) => {
44+ let output = Command :: new ( swine)
45+ . arg ( file_path)
46+ . output ( ) ;
47+
48+ match output {
49+ Ok ( output) => {
50+ let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
51+
52+ if stdout. contains ( "unsat" ) {
53+ Ok ( SatResult :: Unsat )
54+ } else if stdout. contains ( "sat" ) {
55+ Ok ( SatResult :: Sat )
56+ } else {
57+ Ok ( SatResult :: Unknown )
58+ }
59+ }
60+ Err ( e) => {
61+ Err ( CommandError :: ProcessError ( e) )
5462 }
5563 }
5664 }
57- } else {
58- eprintln ! ( "Find command execution failed" ) ;
65+ Err ( e) => {
66+ Err ( CommandError :: EnvVarError ( e) )
67+ }
5968 }
6069}
6170
71+ /// In order to execute the program, it is necessary to remove lines that
72+ /// contain a forall quantifier or the declaration of the exponential function (exp).
6273fn remove_lines_for_swine ( input : & str ) -> String {
6374 let mut output = String :: new ( ) ;
6475 let mut tmp_buffer: VecDeque < char > = VecDeque :: new ( ) ;
@@ -151,42 +162,59 @@ impl<'ctx> Prover<'ctx> {
151162 }
152163
153164 pub fn check_proof ( & mut self ) -> ProveResult < ' ctx > {
154- self . check_proof_assuming ( & [ ] )
165+ self . check_proof_assuming ( & [ ] , SolverType :: SWINE )
155166 }
156167
157168 /// Do the SAT check, but consider a check with no provables to be a
158169 /// [`ProveResult::Proof`].
159- pub fn check_proof_assuming ( & mut self , assumptions : & [ Bool < ' ctx > ] ) -> ProveResult < ' ctx > {
170+ pub fn check_proof_assuming ( & mut self , assumptions : & [ Bool < ' ctx > ] , solver_type : SolverType ) -> ProveResult < ' ctx > {
160171 if self . min_level_with_provables . is_none ( ) {
161172 return ProveResult :: Proof ;
162173 }
163174
164- let mut smtlib = self . get_smtlib ( ) ;
165-
166- smtlib. add_check_sat ( ) ;
167-
168- let smtlib = smtlib. into_string ( ) ;
169- let mut smt_file: NamedTempFile = NamedTempFile :: new ( ) . unwrap ( ) ;
170-
171- smt_file. write_all ( remove_lines_for_swine ( & smtlib) . as_bytes ( ) ) . unwrap ( ) ;
172-
173- let file_path = smt_file. path ( ) ;
174- let start_dir = Path :: new ( "../" ) ;
175+ let res;
175176
176- execute_swine ( start_dir, file_path) ;
177+ match solver_type {
178+ SolverType :: SWINE => {
179+ let mut smtlib = self . get_smtlib ( ) ;
180+ smtlib. add_check_sat ( ) ;
181+ let smtlib = smtlib. into_string ( ) ;
182+ let mut smt_file: NamedTempFile = NamedTempFile :: new ( ) . unwrap ( ) ;
183+ smt_file. write_all ( remove_lines_for_swine ( & smtlib) . as_bytes ( ) ) . unwrap ( ) ;
184+ let file_path = smt_file. path ( ) ;
177185
178- let res = if assumptions. is_empty ( ) {
179- self . solver . check ( )
180- } else {
181- self . solver . check_assumptions ( assumptions)
182- } ;
183- match res {
184- SatResult :: Unsat => ProveResult :: Proof ,
185- SatResult :: Unknown => ProveResult :: Unknown ( self . get_reason_unknown ( ) . unwrap ( ) ) ,
186- SatResult :: Sat => {
187- let model = self . get_model ( ) . unwrap ( ) ;
188- let model = InstrumentedModel :: new ( model) ;
189- ProveResult :: Counterexample ( model)
186+ res = execute_swine ( file_path) . unwrap_or_else ( |e| {
187+ eprintln ! ( "{}" , e) ;
188+ process:: exit ( 1 )
189+ } ) ;
190+ match res {
191+ SatResult :: Unsat => ProveResult :: Proof ,
192+ SatResult :: Unknown => {
193+ // TODO: Determine the correct reason for Unknown
194+ ProveResult :: Unknown ( ReasonUnknown :: Other ( "unknown" . to_string ( ) ) )
195+ } ,
196+ SatResult :: Sat => {
197+ // TODO: Get the model from the output of SWINE
198+ println ! ( "The Result of SWINE: sat" ) ;
199+ process:: exit ( 1 )
200+ }
201+ }
202+ }
203+ SolverType :: Z3 => {
204+ res = if assumptions. is_empty ( ) {
205+ self . solver . check ( )
206+ } else {
207+ self . solver . check_assumptions ( assumptions)
208+ } ;
209+ match res {
210+ SatResult :: Unsat => ProveResult :: Proof ,
211+ SatResult :: Unknown => ProveResult :: Unknown ( self . get_reason_unknown ( ) . unwrap ( ) ) ,
212+ SatResult :: Sat => {
213+ let model = self . get_model ( ) . unwrap ( ) ;
214+ let model = InstrumentedModel :: new ( model) ;
215+ ProveResult :: Counterexample ( model)
216+ }
217+ }
190218 }
191219 }
192220 }
0 commit comments