smtv2.ml 11.4 KB
Newer Older
Andrei Paskevich's avatar
Andrei Paskevich committed
1
2
3
(********************************************************************)
(*                                                                  *)
(*  The Why3 Verification Platform   /   The Why3 Development Team  *)
Jean-Christophe Filliâtre's avatar
Jean-Christophe Filliâtre committed
4
(*  Copyright 2010-2014   --   INRIA - CNRS - Paris-Sud University  *)
Andrei Paskevich's avatar
Andrei Paskevich committed
5
6
7
8
9
10
(*                                                                  *)
(*  This software is distributed under the terms of the GNU Lesser  *)
(*  General Public License version 2.1, with the special exception  *)
(*  on linking described in file LICENSE.                           *)
(*                                                                  *)
(********************************************************************)
11
12
13
14
15
16
17
18
19
20
21

(** SMT v1 printer with some extensions *)

open Format
open Pp
open Ident
open Ty
open Term
open Decl
open Printer

22
(** SMTLIB tokens taken from CVC4: src/parser/smt2/Smt2.g *)
23
24
25
26
27
28
29
30
31
let ident_printer =
  let bls = (*["and";" benchmark";" distinct";"exists";"false";"flet";"forall";
     "if then else";"iff";"implies";"ite";"let";"logic";"not";"or";
     "sat";"theory";"true";"unknown";"unsat";"xor";
     "assumption";"axioms";"defintion";"extensions";"formula";
     "funs";"extrafuns";"extrasorts";"extrapreds";"language";
     "notes";"preds";"sorts";"status";"theory";"Int";"Real";"Bool";
     "Array";"U";"select";"store"]*)
    (** smtlib2 V2 p71 *)
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    [(** Base SMT-LIB tokens *)
      "assert"; "check-sat"; "declare-fun"; "declare-sort"; "define-fun";
      "define-sort"; "get-value"; "get-assignment"; "get-assertions";
      "get-proof"; "get-unsat-core"; "exit"; "ite"; "let"; "!"; "_";
      "set-logic"; "set-info"; "get-info"; "set-option"; "get-option";
      "push"; "pop"; "as";

      (** extended commands *)
      "declare-datatypes"; "get-model"; "echo"; "assert-rewrite";
      "assert-reduction"; "assert-propagation"; "declare-sorts";
      "declare-funs"; "declare-preds"; "define"; "declare-const";
      "simplify";

      (** attributes *)

      (** operators, including theory symbols *)
      "and"; "distinct"; "exists"; "forall"; "is_int"; "not"; "or"; "select";
      "store"; "to_int"; "to_real"; "xor";

      "div"; "mod";

      "concat"; "bvnot"; "bvand"; "bvor"; "bvneg"; "bvadd"; "bvmul"; "bvudiv";
      "bvurem"; "bvshl"; "bvlshr"; "bvult"; "bvnand"; "bvnor"; "bvxor";
      "bvcomp"; "bvsub"; "bvsdiv"; "bvsrem"; "bvsmod"; "bvashr"; "bvule";
56
      "bvugt"; "bvuge"; "bvslt"; "bvsle"; "bvsgt"; "bvsge"; "rotate_left"; "rotate_right";
57

58
59
      "cos"; "sin"; "tan"; "atan"; "pi";

60
61
      (** Other stuff that Why3 seems to need *)
      "DECIMAL"; "NUMERAL"; "par"; "STRING";
62
      "unsat";"sat";
63
64
65
      "Bool"; "true"; "false";
      "Array";"const";
      "abs";
Clément Fumex's avatar
Clément Fumex committed
66
67
68
69
      "BitVec"; "extract"; "bv2nat"; "nat2bv";

      (** From Z3 *)
      "map"
Andrei Paskevich's avatar
Andrei Paskevich committed
70
      ]
71
72
73
74
75
76
77
78
  in
  let san = sanitizer char_to_alpha char_to_alnumus in
  create_ident_printer bls ~sanitizer:san

let print_ident fmt id =
  fprintf fmt "%s" (id_unique ident_printer id)

type info = {
Clément Fumex's avatar
Clément Fumex committed
79
80
  info_syn        : syntax_map;
  info_converters : converter_map;
81
82
}

83
(** type *)
84
85
let rec print_type info fmt ty = match ty.ty_node with
  | Tyvar _ -> unsupported "smt : you must encode the polymorphism"
86
  | Tyapp (ts, l) ->
87
88
89
90
91
     begin match query_syntax info.info_syn ts.ts_name, l with
      | Some s, _ -> syntax_arguments s (print_type info) fmt l
      | None, [] -> fprintf fmt "%a" print_ident ts.ts_name
      | None, _ -> fprintf fmt "(%a %a)" print_ident ts.ts_name
          (print_list space (print_type info)) l
92
93
     end

94
95
let print_type info fmt ty = try print_type info fmt ty
  with Unsupported s -> raise (UnsupportedType (ty,s))
96
97
98
99
100
101
102
103
104
105
106
107
108

let print_type_value info fmt = function
  | None -> fprintf fmt "Bool"
  | Some ty -> print_type info fmt ty

(** var *)
let forget_var v = forget_id ident_printer v.vs_name

let print_var fmt {vs_name = id} =
  let n = id_unique ident_printer id in
  fprintf fmt "%s" n

let print_typed_var info fmt vs =
109
110
  fprintf fmt "(%a %a)" print_var vs
    (print_type info) vs.vs_ty
111
112
113
114
115
116

let print_var_list info fmt vsl =
  print_list space (print_typed_var info) fmt vsl

(** expr *)
let rec print_term info fmt t = match t.t_node with
117
118
  | Tconst c ->
      let number_format = {
119
          Number.long_int_support = true;
120
          Number.extra_leading_zeros_support = false;
121
122
123
124
125
126
127
128
129
130
          Number.dec_int_support = Number.Number_default;
          Number.hex_int_support = Number.Number_unsupported;
          Number.oct_int_support = Number.Number_unsupported;
          Number.bin_int_support = Number.Number_unsupported;
          Number.def_int_support = Number.Number_unsupported;
          Number.dec_real_support = Number.Number_unsupported;
          Number.hex_real_support = Number.Number_unsupported;
          Number.frac_real_support = Number.Number_custom
            (Number.PrintFracReal ("%s.0", "(* %s.0 %s.0)", "(/ %s.0 %s.0)"));
          Number.def_real_support = Number.Number_unsupported;
131
        } in
132
      Number.print number_format fmt c
133
  | Tvar v -> print_var fmt v
Clément Fumex's avatar
Clément Fumex committed
134
135
136
137
138
139
140
141
142
143
144
145
146
  | Tapp (ls, tl) ->
    (* let's check if a converter applies *)
    begin try
      match tl with
      | [ { t_node = Tconst _} ] ->
        begin match query_converter info.info_converters ls with
        | None -> raise Exit
        | Some s -> syntax_arguments s (print_term info) fmt tl
        end
      | _ -> raise Exit
    with Exit ->
    (* non converter applies, then ... *)
    match query_syntax info.info_syn ls.ls_name with
147
      | Some s -> syntax_arguments_typed s (print_term info)
148
        (print_type info) t fmt tl
149
150
151
      | None -> begin match tl with (* for cvc3 wich doesn't accept (toto ) *)
          | [] -> fprintf fmt "@[%a@]" print_ident ls.ls_name
          | _ -> fprintf fmt "@[(%a@ %a)@]"
152
              print_ident ls.ls_name (print_list space (print_term info)) tl
153
154
155
        end end
  | Tlet (t1, tb) ->
      let v, t2 = t_open_bound tb in
156
      fprintf fmt "@[(let ((%a %a))@ %a)@]" print_var v
157
158
159
160
161
162
163
164
165
        (print_term info) t1 (print_term info) t2;
      forget_var v
  | Tif (f1,t1,t2) ->
      fprintf fmt "@[(ite %a@ %a@ %a)@]"
        (print_fmla info) f1 (print_term info) t1 (print_term info) t2
  | Tcase _ -> unsupportedTerm t
      "smtv2 : you must eliminate match"
  | Teps _ -> unsupportedTerm t
      "smtv2 : you must eliminate epsilon"
166
  | Tquant _ | Tbinop _ | Tnot _ | Ttrue | Tfalse -> raise (TermExpected t)
167

168
169
and print_fmla info fmt f = match f.t_node with
  | Tapp ({ ls_name = id }, []) ->
170
      print_ident fmt id
171
  | Tapp (ls, tl) -> begin match query_syntax info.info_syn ls.ls_name with
172
      | Some s -> syntax_arguments_typed s (print_term info)
173
        (print_type info) f fmt tl
174
175
176
      | None -> begin match tl with (* for cvc3 wich doesn't accept (toto ) *)
          | [] -> fprintf fmt "%a" print_ident ls.ls_name
          | _ -> fprintf fmt "(%a@ %a)"
177
              print_ident ls.ls_name (print_list space (print_term info)) tl
178
        end end
179
180
181
  | Tquant (q, fq) ->
      let q = match q with Tforall -> "forall" | Texists -> "exists" in
      let vl, tl, f = t_open_quant fq in
182
183
184
      (* TODO trigger dépend des capacités du prover : 2 printers?
      smtwithtriggers/smtstrict *)
      if tl = [] then
185
        fprintf fmt "@[(%s@ (%a)@ %a)@]"
186
187
188
189
          q
          (print_var_list info) vl
          (print_fmla info) f
      else
190
        fprintf fmt "@[(%s@ (%a)@ (! %a %a))@]"
191
192
193
194
195
          q
          (print_var_list info) vl
          (print_fmla info) f
          (print_triggers info) tl;
      List.iter forget_var vl
196
  | Tbinop (Tand, f1, f2) ->
197
      fprintf fmt "@[(and@ %a@ %a)@]" (print_fmla info) f1 (print_fmla info) f2
198
  | Tbinop (Tor, f1, f2) ->
199
      fprintf fmt "@[(or@ %a@ %a)@]" (print_fmla info) f1 (print_fmla info) f2
200
  | Tbinop (Timplies, f1, f2) ->
Andrei Paskevich's avatar
Andrei Paskevich committed
201
      fprintf fmt "@[(=>@ %a@ %a)@]"
202
        (print_fmla info) f1 (print_fmla info) f2
203
  | Tbinop (Tiff, f1, f2) ->
Andrei Paskevich's avatar
Andrei Paskevich committed
204
      fprintf fmt "@[(=@ %a@ %a)@]" (print_fmla info) f1 (print_fmla info) f2
205
  | Tnot f ->
206
      fprintf fmt "@[(not@ %a)@]" (print_fmla info) f
207
  | Ttrue ->
208
      fprintf fmt "true"
209
  | Tfalse ->
210
      fprintf fmt "false"
211
  | Tif (f1, f2, f3) ->
Andrei Paskevich's avatar
Andrei Paskevich committed
212
      fprintf fmt "@[(ite %a@ %a@ %a)@]"
213
        (print_fmla info) f1 (print_fmla info) f2 (print_fmla info) f3
214
  | Tlet (t1, tb) ->
215
      let v, f2 = t_open_bound tb in
216
217
218
      fprintf fmt "@[(let ((%a %a))@ %a)@]" print_var v
        (print_term info) t1 (print_fmla info) f2;
      forget_var v
219
  | Tcase _ -> unsupportedTerm f
220
      "smtv2 : you must eliminate match"
221
  | Tvar _ | Tconst _ | Teps _ -> raise (FmlaExpected f)
222

223
224
and print_expr info fmt =
  TermTF.t_select (print_term info fmt) (print_fmla info fmt)
225

Andrei Paskevich's avatar
Andrei Paskevich committed
226
and print_trigger info fmt e = fprintf fmt "%a" (print_expr info) e
227
228
229
230
231
232
233

and print_triggers info fmt = function
  | [] -> ()
  | a::l -> fprintf fmt ":pattern (%a) %a"
    (print_list space (print_trigger info)) a
    (print_triggers info) l

234
235
let print_type_decl info fmt ts =
  if ts.ts_def <> None then () else
236
  if Mid.mem ts.ts_name info.info_syn then () else
237
238
  fprintf fmt "(declare-sort %a %i)@\n@\n"
    print_ident ts.ts_name (List.length ts.ts_args)
239

240
241
242
243
244
245
let print_param_decl info fmt ls =
  if Mid.mem ls.ls_name info.info_syn then () else
  fprintf fmt "@[<hov 2>(declare-fun %a (%a) %a)@]@\n@\n"
    print_ident ls.ls_name
    (print_list space (print_type info)) ls.ls_args
    (print_type_value info) ls.ls_value
246

247
let print_logic_decl info fmt (ls,def) =
248
  if Mid.mem ls.ls_name info.info_syn then () else
249
  let vsl,expr = Decl.open_ls_defn def in
250
  fprintf fmt "@[<hov 2>(define-fun %a (%a) %a %a)@]@\n@\n"
251
252
253
254
255
    print_ident ls.ls_name
    (print_var_list info) vsl
    (print_type_value info) ls.ls_value
    (print_expr info) expr;
  List.iter forget_var vsl
256
257
258
259
260
261
262

let print_prop_decl info fmt k pr f = match k with
  | Paxiom ->
      fprintf fmt "@[<hov 2>;; %s@\n(assert@ %a)@]@\n@\n"
        pr.pr_name.id_string (* FIXME? collisions *)
        (print_fmla info) f
  | Pgoal ->
263
264
      fprintf fmt "@[(assert@\n";
      fprintf fmt "@[;; %a@]@\n" print_ident pr.pr_name;
265
      (match pr.pr_name.id_loc with
266
267
268
        | None -> ()
        | Some loc -> fprintf fmt " @[;; %a@]@\n"
            Loc.gen_report_position loc);
269
270
271
272
273
      fprintf fmt "  @[(not@ %a))@]@\n" (print_fmla info) f;
      fprintf fmt "@[(check-sat)@]@\n"
  | Plemma| Pskip -> assert false

let print_decl info fmt d = match d.d_node with
274
275
276
277
278
279
  | Dtype ts ->
      print_type_decl info fmt ts
  | Ddata _ -> unsupportedDecl d
      "smtv2 : algebraic type are not supported"
  | Dparam ls ->
      print_param_decl info fmt ls
280
281
282
  | Dlogic dl ->
      print_list nothing (print_logic_decl info) fmt dl
  | Dind _ -> unsupportedDecl d
283
      "smtv2 : inductive definition are not supported"
284
285
286
  | Dprop (k,pr,f) ->
      if Mid.mem pr.pr_name info.info_syn then () else
      print_prop_decl info fmt k pr f
287

288
let print_decls =
Clément Fumex's avatar
Clément Fumex committed
289
290
  let print_decl (sm, cm) fmt d =
    try print_decl {info_syn = sm; info_converters = cm} fmt d; (sm, cm), []
291
292
293
    with Unsupported s -> raise (UnsupportedDecl (d,s)) in
  let print_decl = Printer.sprint_decl print_decl in
  let print_decl task acc = print_decl task.Task.task_decl acc in
Clément Fumex's avatar
Clément Fumex committed
294
295
296
  Discriminate.on_syntax_map (fun sm ->
  Printer.on_converter_map (fun cm ->
      Trans.fold print_decl ((sm, cm),[])))
297

298
let print_task args ?old:_ fmt task =
299
  (* In trans-based p-printing [forget_all] is a no-no *)
300
  (* forget_all ident_printer; *)
301
302
  print_prelude fmt args.prelude;
  print_th_prelude task fmt args.th_prelude;
303
304
305
306
307
308
309
310
  let rec print = function
    | x :: r -> print r; Pp.string fmt x
    | [] -> () in
  print (snd (Trans.apply print_decls task));
  pp_print_flush fmt ()

let () = register_printer "smtv2" print_task
  ~desc:"Printer@ for@ the@ SMTlib@ version@ 2@ format."