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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
open Base
open Sygus
open Parser
open Semantic
open Sexplib
open Lwt_process
module OC = Stdio.Out_channel
module IC = Stdio.In_channel
open Lwt.Syntax
let pp_link frmt target =
Fmt.(styled `Underline (styled (`Fg `Green) string)) frmt ("file://" ^ target)
;;
module type Logger = sig
val error : (Formatter.t -> unit -> unit) -> unit
val debug : (Formatter.t -> unit -> unit) -> unit
val verb : (Formatter.t -> unit -> unit) -> unit
val log_file : string
val verbose : bool
val log_queries : bool
end
module EmptyLog : Logger = struct
let error _ = ()
let debug _ = ()
let verb _ = ()
let log_file = "tmp"
let verbose = false
let log_queries = false
end
module type Statistics = sig
val log_proc_start : int -> unit
val log_solver_start : int -> string -> unit
val log_proc_restart : int -> unit
val log_alive : int -> unit
val log_proc_quit : ?status:int -> int -> unit
val get_elapsed : int -> float
end
module NoStat : Statistics = struct
let log_proc_start _ = ()
let log_solver_start _ _ = ()
let log_proc_restart _ = ()
let log_alive _ = ()
let log_proc_quit ?(status = 0) _ = ignore status
let get_elapsed _ = 0.
end
module type SolverSystemConfig = sig
val cvc_binary_path : unit -> string
val dryadsynth_binary_path : unit -> string
val eusolver_binary_path : unit -> string
val using_cvc5 : unit -> bool
end
type solver_instance =
{ s_name : string
; s_pid : int
; s_input_file : string
; s_output_file : string
; s_process : process_out
}
let online_solvers : (int * solver_instance) list ref = ref []
let mk_tmp_sl prefix = Caml.Filename.temp_file prefix ".sl"
let commands_to_file (commands : program) (filename : string) =
let out_chan = OC.create filename in
let fout = Stdlib.Format.formatter_of_out_channel out_chan in
Fmt.set_utf_8 fout false;
Stdlib.Format.pp_set_margin fout 100;
List.iter commands ~f:(fun c ->
Command.pp_hum fout c;
Fmt.(pf fout "@."));
OC.close out_chan
;;
module SygusSolver (Stats : Statistics) (Log : Logger) (Config : SolverSystemConfig) =
struct
type t =
| CVC
| DryadSynth
| EUSolver
let default_solver = ref CVC
let binary_path = function
| CVC -> Config.cvc_binary_path ()
| DryadSynth -> Config.dryadsynth_binary_path ()
| EUSolver -> Config.eusolver_binary_path ()
;;
let executable_name x =
let using_cvc5 = Config.using_cvc5 () in
match x with
| CVC -> if using_cvc5 then "cvc5" else "cvc4"
| DryadSynth -> Caml.Filename.basename (Config.dryadsynth_binary_path ())
| EUSolver -> Caml.Filename.basename (Config.eusolver_binary_path ())
;;
let sname = function
| CVC -> "CVC-SyGuS"
| DryadSynth -> "DryadSynth"
| EUSolver -> "EUSolver"
;;
let print_options (frmt : Formatter.t) =
Fmt.(list ~sep:sp (fun fmt opt -> pf fmt "--%s" opt) frmt)
;;
let fetch_solution pid filename =
Log.debug Fmt.(fun fmt () -> pf fmt "Fetching solution in %a" pp_link filename);
let r = reponse_of_sexps (Sexp.input_sexps (Stdio.In_channel.create filename)) in
Stats.log_proc_quit pid;
r
;;
let solver_make_cancellable (s : solver_instance) (p : 'a Lwt.t) : unit =
Lwt.on_cancel p (fun () ->
match s.s_process#state with
| Lwt_process.Exited _ -> ()
| Running ->
Stats.log_proc_quit s.s_process#pid;
Log.debug
Fmt.(
fun fmt () ->
pf
fmt
"Terminating solver %s (PID : %i) (log: %a)"
s.s_name
s.s_pid
pp_link
s.s_output_file);
s.s_process#terminate)
;;
let exec_solver
?(solver_kind = !default_solver)
?(options = [])
((inputfile, outputfile) : string * string)
: solver_instance * solver_response option Lwt.t * int Lwt.u
=
let command =
( binary_path solver_kind
, Array.of_list (executable_name solver_kind :: inputfile :: options) )
in
let out_fd =
Unix.openfile outputfile [ Unix.O_RDWR; Unix.O_TRUNC; Unix.O_CREAT ] 0o644
in
let process = open_process_out ~stdout:(`FD_move out_fd) command in
let solver =
{ s_name = sname solver_kind
; s_pid = process#pid
; s_output_file = outputfile
; s_input_file = inputfile
; s_process = process
}
in
Stats.log_solver_start solver.s_pid solver.s_name;
Log.debug
Fmt.(
fun fmt () ->
pf
fmt
"%s (pid : %i) solving %a -> %a"
solver.s_name
solver.s_pid
pp_link
inputfile
pp_link
outputfile);
try
let t, r = Lwt.task () in
( solver
, Lwt.bind t (fun _ ->
let* status = process#status in
match status with
| Unix.WEXITED 0 -> Lwt.return (Some (fetch_solution solver.s_pid outputfile))
| Unix.WEXITED i ->
Log.error
Fmt.(
fun fmt () ->
pf
fmt
"Solver %s (pid : %i) exited with code %i."
solver.s_name
solver.s_pid
i);
Lwt.return None
| Unix.WSIGNALED i ->
Log.error Fmt.(fun fmt () -> pf fmt "Solver signaled with code %i." i);
Lwt.return None
| Unix.WSTOPPED i ->
Log.error Fmt.(fun fmt () -> pf fmt "Solver stopped with code %i." i);
Lwt.return None)
, r )
with
| Sys_error s ->
failwith ("couldn't talk to solver, double-check path. Sys_error " ^ s)
;;
let solve_commands ?(solver_kind = !default_solver) (p : program)
: solver_response option Lwt.t * int Lwt.u
=
let inputfile = mk_tmp_sl "in_" in
let outputfile = mk_tmp_sl "out_" in
commands_to_file p inputfile;
let s, t, r = exec_solver ~solver_kind (inputfile, outputfile) in
solver_make_cancellable s t;
t, r
;;
end