1(* Copyright (c) 2009-2011 Tjark Weber. All rights reserved. *)
2
3(* Translation of HOL terms into SMT-LIB 2 format *)
4
5structure SmtLib = struct
6
7local
8
9  (* FIXME: We translate into a fictitious SMT-LIB logic AUFBVNIRA
10     that comprises arrays (A), uninterpreted functions (UF),
11     bit-vectors (BV), non-linear (N) integer (I) and real arithmetic
12     (RA).  Unfortunately, there is no actual SMT-LIB logic at the
13     moment that contains all these features: AUFNIRA is missing
14     bit-vectors, while QF_AUFBV is missing quantifiers and
15     arithmetic.  See SmtLib_{Theories,Logics}.sml for details.  At
16     present, we make no attempt to identify a less expressive SMT-LIB
17     logic based on the constants that actually appear in the goal. *)
18
19  (* For successful proof reconstruction, it is important that the
20     translation implemented in SmtLib_{Theories,Logics}.sml is an
21     inverse of the translation implemented in this file. *)
22
23  val ERR = Feedback.mk_HOL_ERR "SmtLib"
24  val WARNING = Feedback.HOL_WARNING "SmtLib"
25
26  (* (HOL type, a function that maps the type to its SMT-LIB sort name) *)
27  val builtin_types = List.foldl
28    (fn ((ty, x), net) => TypeNet.insert (net, ty, x)) TypeNet.empty [
29    (Type.bool, Lib.K "Bool"),
30    (intSyntax.int_ty, Lib.K "Int"),
31    (realSyntax.real_ty, Lib.K "Real"),
32    (* bit-vector types *)
33    (wordsSyntax.mk_word_type Type.alpha, fn ty =>
34      "(_ BitVec " ^ Arbnum.toString
35        (fcpSyntax.dest_numeric_type (wordsSyntax.dest_word_type ty)) ^ ")")
36   ]
37
38  val apfst_K = Lib.apfst o Lib.K
39
40  (* returns true iff 'ty' is a word type that is not fixed-width *)
41  fun is_non_numeric_word_type ty =
42    not (fcpSyntax.is_numeric_type (wordsSyntax.dest_word_type ty))
43      handle Feedback.HOL_ERR _ => false
44
45  (* make sure that all word types in 't' are of fixed width; return 's' *)
46  fun apfst_fixed_width s =
47    Lib.apfst (fn t =>
48      let
49        val (domtys, rngty) = boolSyntax.strip_fun (Term.type_of t)
50      in
51        if List.exists is_non_numeric_word_type (rngty :: domtys) then
52          raise ERR ("<builtin_symbols." ^ s ^ ">")
53            "not a fixed-width word type"
54        else
55          s
56      end)
57
58  (* (HOL term, a function that maps a pair (rator, rands) to an
59     SMT-LIB symbol and a list of remaining (still-to-be-encoded)
60     argument terms) *)
61  val builtin_symbols = List.foldl (Lib.uncurry Net.insert) Net.empty [
62    (* Core *)
63    (boolSyntax.T, apfst_K "true"),
64    (boolSyntax.F, apfst_K "false"),
65    (boolSyntax.negation, apfst_K "not"),
66    (boolSyntax.implication, apfst_K "=>"),
67    (boolSyntax.conjunction, apfst_K "and"),
68    (boolSyntax.disjunction, apfst_K "or"),
69    (* (..., "xor"), *)
70    (boolSyntax.equality, apfst_K "="),
71    (* (..., "distinct"), *)
72    (boolSyntax.conditional, apfst_K "ite"),
73    (* Reals_Ints *)
74    (* numerals (excluding 'intSyntax.negate_tm') *)
75    (Term.mk_var ("x", intSyntax.int_ty), Lib.apfst (fn tm =>
76      if intSyntax.is_int_literal tm andalso not (intSyntax.is_negated tm) then
77        let
78          val i = intSyntax.int_of_term tm
79          val s = Arbint.toString (Arbint.abs i)
80          val s = String.substring (s, 0, String.size s - 1)
81        in
82          if Arbint.< (i, Arbint.zero) then
83            "(- " ^ s ^ ")"
84          else
85            s
86        end
87      else
88        raise ERR "<builtin_symbols.x:int>" "not a numeral (negated?)")),
89    (intSyntax.negate_tm, apfst_K "-"),
90    (intSyntax.minus_tm, apfst_K "-"),
91    (intSyntax.plus_tm, apfst_K "+"),
92    (intSyntax.mult_tm, apfst_K "*"),
93    (* (..., "div"), *)
94    (* (..., "mod"), *)
95    (intSyntax.absval_tm, apfst_K "abs"),
96    (intSyntax.leq_tm, apfst_K "<="),
97    (intSyntax.less_tm, apfst_K "<"),
98    (intSyntax.geq_tm, apfst_K ">="),
99    (intSyntax.great_tm, apfst_K ">"),
100    (* decimals (excluding 'realSyntax.negate_tm') *)
101    (Term.mk_var ("x", realSyntax.real_ty), Lib.apfst (fn tm =>
102      if realSyntax.is_real_literal tm andalso not (realSyntax.is_negated tm)
103          then
104        let
105          val i = realSyntax.int_of_term tm
106          val s = Arbint.toString (Arbint.abs i)
107          val s = String.substring (s, 0, String.size s - 1)
108          val s = s ^ ".0"
109        in
110          if Arbint.< (i, Arbint.zero) then
111            "(- " ^ s ^ ")"
112          else
113            s
114        end
115      else
116        raise ERR "<builtin_symbols.x:real>" "not a decimal (negated?)")),
117    (realSyntax.negate_tm, apfst_K "-"),
118    (realSyntax.minus_tm, apfst_K "-"),
119    (realSyntax.plus_tm, apfst_K "+"),
120    (realSyntax.mult_tm, apfst_K "*"),
121    (realSyntax.div_tm, apfst_K "/"),
122    (realSyntax.leq_tm, apfst_K "<="),
123    (realSyntax.less_tm, apfst_K "<"),
124    (realSyntax.geq_tm, apfst_K ">="),
125    (realSyntax.great_tm, apfst_K ">"),
126    (intrealSyntax.real_of_int_tm, apfst_K "to_real"),
127    (intrealSyntax.INT_FLOOR_tm, apfst_K "to_int"),
128    (intrealSyntax.is_int_tm, apfst_K "is_int"),
129    (* bit-vector constants *)
130    (Term.mk_var ("x", wordsSyntax.mk_word_type Type.alpha), Lib.apfst (fn tm =>
131      if wordsSyntax.is_word_literal tm then
132        let
133          val value = wordsSyntax.dest_word_literal tm
134          val width = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of tm)
135        in
136          "(_ bv" ^ Arbnum.toString value ^ " " ^ Arbnum.toString width ^ ")"
137        end
138      else
139        raise ERR "<builtin_symbols.x:'a word>" "not a word literal")),
140    (wordsSyntax.word_concat_tm, fn (t, ts) =>
141      SmtLib_Theories.two_args (fn (w1, w2) =>
142        let
143          (* make sure that the result in HOL has the expected width *)
144          val dim1 = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of w1)
145          val dim2 = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of w2)
146          val rngty = Lib.snd (boolSyntax.strip_fun (Term.type_of t))
147          val rngdim = fcpSyntax.dest_numeric_type
148            (wordsSyntax.dest_word_type rngty)
149          val _ = if rngdim = Arbnum.+ (dim1, dim2) then
150              ()
151            else (
152              if !Library.trace > 0 then
153                WARNING "translate_term" "word_concat: wrong result type"
154              else
155                ();
156              raise ERR "<builtin_symbols.word_concat_tm>" "wrong result type"
157            )
158        in
159          ("concat", ts)
160        end) ts),
161    (wordsSyntax.word_extract_tm, fn (t, ts) =>
162      SmtLib_Theories.three_args (fn (i, j, w) =>
163        let
164          (* make sure that j <= i < dim(w) *)
165          val i = numSyntax.dest_numeral i
166          val j = numSyntax.dest_numeral j
167          val dim = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of w)
168          val _ = if Arbnum.<= (j, i) then
169              ()
170            else (
171              if !Library.trace > 0 then
172                WARNING "translate_term"
173                  "word_extract: second index larger than first"
174              else
175                ();
176              raise ERR "<builtin_symbols.word_extract_tm>"
177                "second index larger than first"
178            )
179          val _ = if Arbnum.< (i, dim) then
180              ()
181            else (
182              if !Library.trace > 0 then
183                WARNING "translate_term" "word_extract: first index too large"
184              else
185                ();
186              raise ERR "<builtin_symbols.word_extract_tm>"
187                "first index too large"
188            )
189          (* make sure that the result in HOL has the expected width *)
190          val rngty = Lib.snd (boolSyntax.strip_fun (Term.type_of t))
191          val rngdim = fcpSyntax.dest_numeric_type
192            (wordsSyntax.dest_word_type rngty)
193          val _ = if rngdim = Arbnum.+ (Arbnum.- (i, j), Arbnum.one) then
194              ()
195            else (
196              if !Library.trace > 0 then
197                WARNING "translate_term" "word_extract: wrong result type"
198              else
199                ();
200              raise ERR "<builtin_symbols.word_extract_tm>" "wrong result type"
201            )
202        in
203          ("(_ extract " ^ Arbnum.toString i ^ " " ^ Arbnum.toString j ^ ")",
204            [w])
205        end) ts),
206    (wordsSyntax.word_1comp_tm, apfst_fixed_width "bvnot"),
207    (wordsSyntax.word_and_tm, apfst_fixed_width "bvand"),
208    (wordsSyntax.word_or_tm, apfst_fixed_width "bvor"),
209    (wordsSyntax.word_nand_tm, apfst_fixed_width "bvnand"),
210    (wordsSyntax.word_nor_tm, apfst_fixed_width "bvnor"),
211    (wordsSyntax.word_xor_tm, apfst_fixed_width "bvxor"),
212    (wordsSyntax.word_xnor_tm, apfst_fixed_width "bvxnor"),
213    (wordsSyntax.word_2comp_tm, apfst_fixed_width "bvneg"),
214    (wordsSyntax.word_compare_tm, apfst_fixed_width "bvcomp"),
215    (wordsSyntax.word_add_tm, apfst_fixed_width "bvadd"),
216    (wordsSyntax.word_sub_tm, apfst_fixed_width "bvsub"),
217    (wordsSyntax.word_mul_tm, apfst_fixed_width "bvmul"),
218    (wordsSyntax.word_quot_tm, apfst_fixed_width "bvsdiv"),
219    (wordsSyntax.word_rem_tm, apfst_fixed_width "bvsrem"),
220    (integer_wordSyntax.word_smod_tm, apfst_fixed_width "bvsmod"),
221    (wordsSyntax.word_div_tm, apfst_fixed_width "bvudiv"),
222    (wordsSyntax.word_mod_tm, apfst_fixed_width "bvurem"),
223    (* shift operations with two bit-vector arguments; the corresponding HOL
224       shift operations that take a numeral as their second argument are not
225       supported in SMT-LIB *)
226    (wordsSyntax.word_lsl_bv_tm, apfst_fixed_width "bvshl"),
227    (wordsSyntax.word_lsr_bv_tm, apfst_fixed_width "bvlshr"),
228    (wordsSyntax.word_asr_bv_tm, apfst_fixed_width "bvashr"),
229    (wordsSyntax.word_replicate_tm, fn (t, ts) =>
230      SmtLib_Theories.two_args (fn (n, w) =>
231        let
232          val n = numSyntax.dest_numeral n
233          (* make sure that the result in HOL has the expected width *)
234          val dim = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of w)
235          val rngty = Lib.snd (boolSyntax.strip_fun (Term.type_of t))
236          val rngdim = fcpSyntax.dest_numeric_type
237            (wordsSyntax.dest_word_type rngty)
238          val _ = if rngdim = Arbnum.* (n, dim) then
239              ()
240            else (
241              if !Library.trace > 0 then
242                WARNING "translate_term" "word_replicate: wrong result type"
243              else
244                ();
245              raise ERR "<builtin_symbols.word_replicate_tm>"
246                "wrong result type"
247            )
248        in
249          ("(_ repeat " ^ Arbnum.toString n ^ ")", [w])
250        end) ts),
251    (wordsSyntax.w2w_tm, fn (t, ts) =>
252      SmtLib_Theories.one_arg (fn w =>
253        let
254          (* make sure that the result in HOL is at least as long as 'w' *)
255          val dim = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of w)
256          val rngty = Lib.snd (boolSyntax.strip_fun (Term.type_of t))
257          val rngdim = fcpSyntax.dest_numeric_type
258            (wordsSyntax.dest_word_type rngty)
259          val _ = if Arbnum.>= (rngdim, dim) then
260              ()
261            else (
262              if !Library.trace > 0 then
263                WARNING "translate_term" "w2w: result type too short"
264              else
265                ();
266              raise ERR "<builtin_symbols.w2w_tm>" "result type too short"
267            )
268          val n = Arbnum.- (rngdim, dim)
269        in
270          ("(_ zero_extend " ^ Arbnum.toString n ^ ")", [w])
271        end) ts),
272    (wordsSyntax.sw2sw_tm, fn (t, ts) =>
273      SmtLib_Theories.one_arg (fn w =>
274        let
275          (* make sure that the result in HOL is at least as long as 'w' *)
276          val dim = fcpSyntax.dest_numeric_type (wordsSyntax.dim_of w)
277          val rngty = Lib.snd (boolSyntax.strip_fun (Term.type_of t))
278          val rngdim = fcpSyntax.dest_numeric_type
279            (wordsSyntax.dest_word_type rngty)
280          val _ = if Arbnum.>= (rngdim, dim) then
281              ()
282            else (
283              if !Library.trace > 0 then
284                WARNING "translate_term" "sw2sw: result type too short"
285              else
286                ();
287              raise ERR "<builtin_symbols.sw2sw_tm>" "result type too short"
288            )
289          val n = Arbnum.- (rngdim, dim)
290        in
291          ("(_ sign_extend " ^ Arbnum.toString n ^ ")", [w])
292        end) ts),
293    (* rotation by a numeral; the corresponding HOL rotation operations that
294       take two bit-vector arguments are not supported in SMT-LIB *)
295    (wordsSyntax.word_rol_tm, fn (t, ts) =>
296      (
297        apfst_fixed_width "rotate_left" (t, ());
298        SmtLib_Theories.two_args (fn (w, n) =>
299          ("(_ rotate_left " ^ Arbnum.toString (numSyntax.dest_numeral n)
300            ^ ")", [w])) ts
301      )),
302    (wordsSyntax.word_ror_tm, fn (t, ts) =>
303      (
304        apfst_fixed_width "rotate_right" (t, ());
305        SmtLib_Theories.two_args (fn (w, n) =>
306          ("(_ rotate_right " ^ Arbnum.toString (numSyntax.dest_numeral n)
307            ^ ")", [w])) ts
308      )),
309    (wordsSyntax.word_lo_tm, apfst_fixed_width "bvult"),
310    (wordsSyntax.word_ls_tm, apfst_fixed_width "bvule"),
311    (wordsSyntax.word_hi_tm, apfst_fixed_width "bvugt"),
312    (wordsSyntax.word_hs_tm, apfst_fixed_width "bvuge"),
313    (wordsSyntax.word_lt_tm, apfst_fixed_width "bvslt"),
314    (wordsSyntax.word_le_tm, apfst_fixed_width "bvsle"),
315    (wordsSyntax.word_gt_tm, apfst_fixed_width "bvsgt"),
316    (wordsSyntax.word_ge_tm, apfst_fixed_width "bvsge")
317  ]
318
319  (* SMT-LIB type and function names are uniformly generated as "tN"
320     and "vN", respectively, where N is a number. Prefixes must be
321     distinct. *)
322
323  val ty_prefix = "t"  (* for types *)
324  val tm_prefix = "v"  (* for terms *)
325  val bv_prefix = "b"  (* for bound variables *)
326
327  (* returns an updated accumulator, a (possibly empty) list of
328     SMT-LIB type declarations, and the SMT-LIB representation of the
329     given type *)
330  fun translate_type (tydict, ty) =
331  let
332    val name = Lib.tryfind (fn (_, f) => f ty)
333        (TypeNet.match (builtin_types, ty)) (* may fail *)
334      handle Feedback.HOL_ERR _ =>
335        Redblackmap.find (tydict, ty)
336  in
337    (tydict, ([], name))
338  end
339  handle Redblackmap.NotFound =>
340    (* uninterpreted types *)
341    let
342      val name = ty_prefix ^ Int.toString (Redblackmap.numItems tydict)
343      val decl = "(declare-sort " ^ name ^ " 0)\n"
344    in
345      if !Library.trace > 0 andalso Type.is_type ty then
346        WARNING "translate_type"
347          ("uninterpreted type " ^ Hol_pp.type_to_string ty)
348      else
349        ();
350      if !Library.trace > 2 then
351        Feedback.HOL_MESG ("HolSmtLib (SmtLib): inventing name '" ^ name ^
352          "' for HOL type '" ^ Hol_pp.type_to_string ty ^ "'")
353      else
354        ();
355      (Redblackmap.insert (tydict, ty, name), ([decl], name))
356    end
357
358  (* SMT-LIB is first-order.  Thus, functions can only be applied to
359     arguments of base type, but not to arguments of function type;
360     all completely applied terms must be of base type.
361
362     Thus, higher-order arguments must be abstracted so that they are
363     of (uninterpreted) base type.  We achieve this by abstracting the
364     offending function type to a fresh base type, and by abstracting
365     the argument's rator to an uninterpreted term that returns the
366     correct (abstracted) type.  Note that the same function/operator
367     may appear both with and without arguments in a HOL formula.
368     'tmdict' maps terms along with the number of their actual
369     arguments to an SMT-LIB representation. *)
370
371  (* returns an updated accumulator, a (possibly empty) list of
372     SMT-LIB (type and term) declarations, and the SMT-LIB
373     representation of the given term *)
374  fun translate_term (acc as (tydict, tmdict), (bounds, tm)) =
375  let
376    fun sexpr x [] = x
377      | sexpr x xs = "(" ^ x ^ " " ^ String.concatWith " " xs ^ ")"
378    fun builtin_symbol (rator, rands) =
379      let
380        val (name, rands) = Lib.tryfind (fn parsefn => parsefn (rator, rands))
381          (Net.match rator builtin_symbols)  (* may fail *)
382        val (acc, declnames) = Lib.foldl_map
383          (fn (a, t) => translate_term (a, (bounds, t))) (acc, rands)
384        val (declss, names) = Lib.split declnames
385      in
386        (acc, (List.concat declss, sexpr name names))
387      end
388    (* creates a mapping from bound variables to their SMT-LIB names; if a
389       variable is already mapped, we return its existing SMT-LIB name *)
390    fun create_bound_name (bounds, v) =
391      (bounds, Redblackmap.find (bounds, v))
392      handle Redblackmap.NotFound =>
393        let
394          val name = bv_prefix ^ Int.toString (Redblackmap.numItems bounds)
395        in
396          (Redblackmap.insert (bounds, v, name), name)
397        end
398    val tm_has_base_type = not (Lib.can Type.dom_rng (Term.type_of tm))
399  in
400    (* binders *)
401    let
402      (* perhaps we should use a table of binders instead *)
403      val (binder, (vars, body)) = if boolSyntax.is_forall tm then
404          ("forall", boolSyntax.strip_forall tm)
405        else if boolSyntax.is_exists tm then
406          ("exists", boolSyntax.strip_exists tm)
407        else
408          raise ERR "translate_term" "not a binder"  (* handled below *)
409      val (bounds, smtvars) = Lib.foldl_map create_bound_name (bounds, vars)
410      val (tydict, vardecltys) = Lib.foldl_map translate_type
411        (tydict, List.map Term.type_of vars)
412      val (vardeclss, vartys) = Lib.split vardecltys
413      val vardecls = List.concat vardeclss
414      val smtvars = ListPair.mapEq (fn (v, ty) => "(" ^ v ^ " " ^ ty ^ ")")
415        (smtvars, vartys)
416      val (acc, (bodydecls, body)) = translate_term
417        ((tydict, tmdict), (bounds, body))
418    in
419      (acc, (vardecls @ bodydecls, "(" ^ binder ^ " (" ^
420        String.concatWith " " smtvars ^ ") " ^ body ^ ")"))
421    end
422    handle Feedback.HOL_ERR _ =>
423
424    (* let binder - somewhat similar to quantifiers, but we only
425       translate one let at a time (so we don't have to worry about
426       semantic differences caused by parallel vs. sequential let) *)
427    let
428      val (M, N) = boolSyntax.dest_let tm
429      val (var, body) = Term.dest_abs M
430      val (acc, (Ndecls, N)) = translate_term (acc, (bounds, N))
431      val (bounds, name) = create_bound_name (bounds, var)
432      val (acc, (bodydecls, body)) = translate_term (acc, (bounds, body))
433    in
434      (acc, (Ndecls @ bodydecls,
435        "(let ((" ^ name ^ " " ^ N ^ ")) " ^ body ^ ")"))
436    end
437    handle Feedback.HOL_ERR _ =>
438
439    (* bound variables may shadow built-in symbols etc. *)
440    (acc, ([], Redblackmap.find (bounds, tm)))
441    handle Redblackmap.NotFound =>
442
443    (* translate the entire term (e.g., for numerals), using the dictionary of
444       built-in symbols; however, only do this if 'tm' has base type *)
445    (if tm_has_base_type then
446      builtin_symbol (tm, [])
447    else
448      raise ERR "translate_term" "not first-order")  (* handled below *)
449    handle Feedback.HOL_ERR _ =>
450
451    (* split the term into rator and rands *)
452    let
453      val (rator, rands) = boolSyntax.strip_comb tm
454    in
455      (* translate the rator as a built-in symbol (applied to its rands); only
456         do this if 'tm' has base type *)
457      (if tm_has_base_type then
458        builtin_symbol (rator, rands)
459      else
460        raise ERR "translate_term" "not first-order")  (* handled below *)
461      handle Feedback.HOL_ERR _ =>
462
463      let
464        val rands_count = List.length rands
465        val (acc, (decls, name)) =
466          (* translate the rator as a previously defined symbol *)
467          (acc, ([], Redblackmap.find (tmdict, (rator, rands_count))))
468          handle Redblackmap.NotFound =>
469
470          (* translate the rator as a new (i.e., uninterpreted) symbol *)
471          let
472            (* translate 'rator' types required for the rator's
473               SMT-LIB declaration *)
474            fun doms_rng acc 0 ty =
475              (List.rev acc, ty)
476              | doms_rng acc n ty =
477              let
478                val (dom, rng) = Type.dom_rng ty
479              in
480                doms_rng (dom :: acc) (n - 1) rng
481              end
482            (* strip only 'rands_count' many 'domtys', leaving the remaining
483               argument types in 'rngty' *)
484            val (domtys, rngty) = doms_rng [] rands_count (Term.type_of rator)
485            val (tydict, domdecltys) = Lib.foldl_map translate_type
486              (tydict, domtys)
487            val (domdeclss, domtys) = Lib.split domdecltys
488            val domdecls = List.concat domdeclss
489            val (tydict, (rngdecls, rngty)) = translate_type (tydict, rngty)
490            (* invent new name for 'rator' *)
491            val name = tm_prefix ^ Int.toString (Redblackmap.numItems tmdict)
492            val _ = if !Library.trace > 0 andalso Term.is_const rator then
493              WARNING "translate_term"
494                ("uninterpreted constant " ^ Hol_pp.term_to_string rator)
495              else
496                ();
497            val _ = if !Library.trace > 2 then
498                Feedback.HOL_MESG ("HolSmtLib (SmtLib): inventing name '" ^
499                  name ^ "' for HOL term '" ^ Hol_pp.term_to_string rator ^
500                  "' (applied to " ^ Int.toString rands_count ^ " argument(s))")
501              else
502                ()
503            val tmdict = Redblackmap.insert (tmdict, (rator, rands_count), name)
504            val decl = "(declare-fun " ^ name ^ " (" ^
505              String.concatWith " " domtys ^ ") " ^ rngty ^ ")\n"
506          in
507            ((tydict, tmdict), (domdecls @ rngdecls @ [decl], name))
508          end
509        (* translate 'rands' *)
510        val (acc, declnames) = Lib.foldl_map
511          (fn (a, t) => translate_term (a, (bounds, t))) (acc, rands)
512        val (declss, names) = Lib.split declnames
513      in
514        (acc, (decls @ List.concat declss, sexpr name names))
515      end
516    end
517  end
518
519  (* Returns a string list representing the input goal in SMT-LIB file
520     format, together with two dictionaries that map types and terms
521     to identifiers used in the SMT-LIB representation.  The goal's
522     conclusion is negated before translation into SMT-LIB format.
523     The integer in the term dictionary gives the number of actual
524     arguments to the term.  (Because SMT-LIB is first-order,
525     partially applied functions are mapped to different SMT-LIB
526     identifiers, depending on the number of actual arguments.) *)
527  fun goal_to_SmtLib_aux (ts, t)
528    : ((Type.hol_type, string) Redblackmap.dict *
529      (Term.term * int, string) Redblackmap.dict) * string list =
530  let
531    val tydict = Redblackmap.mkDict Type.compare
532    val tmdict = Redblackmap.mkDict
533      (Lib.pair_compare (Term.compare, Int.compare))
534    val bounds = Redblackmap.mkDict Term.compare
535    val (acc, smtlibs) = Lib.foldl_map
536      (fn (acc, tm) => translate_term (acc, (bounds, tm)))
537      ((tydict, tmdict), ts @ [boolSyntax.mk_neg t])
538    (* we choose to intertwine declarations and assertions (for no
539       particular reason; an alternative would be to emit all
540       declarations before all assertions) *)
541    val smtlibs = List.foldl
542      (fn ((xs, s), acc) => acc @ xs @ ["(assert " ^ s ^ ")\n"]) [] smtlibs
543  in
544    (acc, [
545      (* "(set-logic AUFBVNIRA)\n", *)
546      "(set-info :source |Automatically generated from HOL4 by SmtLib.goal_to_SmtLib.\n",
547      "Copyright (c) 2011 Tjark Weber. All rights reserved.|)\n",
548      "(set-info :smt-lib-version 2.0)\n"
549    ] @ smtlibs @ [
550      "(check-sat)\n"
551    ])
552  end
553
554in
555
556  val goal_to_SmtLib =
557    Lib.apsnd (fn xs => xs @ ["(exit)\n"]) o goal_to_SmtLib_aux
558
559  val goal_to_SmtLib_with_get_proof =
560    Lib.apsnd (fn xs => xs @ ["(get-proof)\n", "(exit)\n"]) o goal_to_SmtLib_aux
561
562  (* eliminates some HOL terms that are not supported by the SMT-LIB
563     translation *)
564  fun SIMP_TAC simp_let =
565    let
566      open Tactical simpLib
567      val INT_ABS = intLib.ARITH_PROVE
568                      ``!x. ABS (x:int) = if x < 0i then 0i - x else x``
569    in
570      REPEAT Tactic.GEN_TAC THEN
571      (if simp_let then Library.LET_SIMP_TAC else ALL_TAC) THEN
572      SIMP_TAC pureSimps.pure_ss
573        [integerTheory.INT_MIN, integerTheory.INT_MAX, INT_ABS] THEN
574      Library.WORD_SIMP_TAC THEN
575      Library.SET_SIMP_TAC THEN
576      Tactic.BETA_TAC
577    end
578
579end  (* local *)
580
581end
582