aboutsummaryrefslogtreecommitdiffstats
path: root/src/smtlib2/smtlib2_solver.ml
blob: a5ac845bfae6f96963915b97be2a15d3afa50754 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
(**************************************************************************)
(*                                                                        *)
(*     SMTCoq                                                             *)
(*     Copyright (C) 2011 - 2022                                          *)
(*                                                                        *)
(*     See file "AUTHORS" for the list of authors                         *)
(*                                                                        *)
(*   This file is distributed under the terms of the CeCILL-C licence     *)
(*                                                                        *)
(**************************************************************************)


open Format
  
type result = Sat | Unsat

type t = {
  pid : int;
  stdin : Unix.file_descr;
  stdout : Unix.file_descr;
  stderr : Unix.file_descr;
  lexbuf : Lexing.lexbuf;
}


let create cmd =
  let executable = cmd.(0) in

  (* Create pipes for input, output and error output *)
  let stdin_in, stdin_out = Unix.pipe () in
  let stdout_in, stdout_out = Unix.pipe () in 
  let stderr_in, stderr_out = Unix.pipe () in 

  (* Create solver process *)
  let pid = 
    Unix.create_process 
      executable 
      cmd 
      stdin_in
      stdout_out
      stderr_out
  in

  (* Close our end of the pipe which has been duplicated by the
     process *)
  Unix.close stdin_in;
  Unix.close stdout_out; 
  Unix.close stderr_out; 

  (* Get an output channel to read from solver's stdout *)
  let stdout_ch = Unix.in_channel_of_descr stdout_in in

  let lexbuf = Lexing.from_channel stdout_ch in

  (* Create the solver instance *)
  { pid;
    stdin = stdin_out; stdout = stdout_in; stderr = stderr_in; lexbuf }


let kill s =
  try
    Unix.close s.stdin;
    Unix.close s.stdout;
    Unix.close s.stderr;
    Unix.kill s.pid Sys.sigkill;
  with _ -> ()


let read_response { lexbuf } = 
  SExprParser.sexp SExprLexer.main lexbuf 


let error s sexp =
  kill s;
  CoqInterface.error (asprintf "Solver error: %a." SExpr.print sexp)


let read_success s = 
  match SExprParser.sexp SExprLexer.main s.lexbuf with
  | SExpr.Atom "success" -> ()
  | r -> error s r


(* let no_response _ = () *)


let read_check_result s = 
  match SExprParser.sexp SExprLexer.main s.lexbuf with
  | SExpr.Atom "sat" -> Sat
  | SExpr.Atom "unsat" -> Unsat
  | SExpr.Atom "unknown" -> CoqInterface.error ("Solver returned uknown.")
  | r -> error s r


let send_command s cmd read =
  eprintf "%s@." cmd;
  (* let err_p1 = Unix.((fstat s.stderr).st_size) in *)
  try
    let in_ch = Unix.out_channel_of_descr s.stdin in
    let fmt = formatter_of_out_channel in_ch in
    pp_print_string fmt cmd;
    pp_print_newline fmt ();
    read s
  with e ->
    (* After the exception, the file descriptor is invalid *)
    (* let err_p2 = Unix.((fstat s.stderr).st_size) in
     * let len = err_p2 - err_p1 in
     * (\* Was something written to stderr? *\)
     * if len <> 0 then begin
     *   let buf = Bytes.create err_p2 in
     *   Unix.read s.stderr buf 0 err_p2 |> ignore;
     *   let err_msg = Bytes.sub_string buf err_p1 len in
     *   CoqInterface.error ("Solver error: "^err_msg);
     * end
     * else (kill s; raise e) *)
    kill s; raise e


let set_option s name b =
  send_command s
    (asprintf "(set-option :%s %b)" name b)
    read_success


let set_logic s l =
  send_command s
    (sprintf "(set-logic %s)" l)
    read_success


let declare_sort s name arity =
  send_command s
    (asprintf "(declare-sort %s %d)" name arity)
    read_success


let declare_fun s name args ret =
  send_command s
    (asprintf "(declare-fun %s (%a) %s)"
       name
       (fun fmt -> List.iter (fprintf fmt "%s ")) args
       ret)
    read_success
  

let assume s f =
  send_command s
    (sprintf "(assert %s)" f)
    read_success


let check_sat s =
  send_command s "(check-sat)" read_check_result


let get_proof s process_proof =
  send_command s "(get-proof)" (fun s -> process_proof s.lexbuf)


let get_model s =
  send_command s "(get-model)" read_response


let quit s =
  try
    send_command s "(exit)" read_success;
  with Unix.Unix_error _ -> ();
  kill s