src/HOL/Boogie/Tools/boogie_loader.ML
changeset 33408 8ae45e87b992
child 33413 a3b002e2cd55
equal deleted inserted replaced
33407:1312e8337ce5 33408:8ae45e87b992
       
     1 (*  Title:      HOL/Boogie/Tools/boogie_loader.ML
       
     2     Author:     Sascha Boehme, TU Muenchen
       
     3 
       
     4 Loading and interpreting Boogie-generated files.
       
     5 *)
       
     6 
       
     7 signature BOOGIE_LOADER =
       
     8 sig
       
     9   val load_b2i: bool -> Path.T -> theory -> theory
       
    10 end
       
    11 
       
    12 structure Boogie_Loader: BOOGIE_LOADER =
       
    13 struct
       
    14 
       
    15 fun log verbose text args thy =
       
    16   if verbose
       
    17   then (Pretty.writeln (Pretty.big_list text (map Pretty.str args)); thy)
       
    18   else thy
       
    19 
       
    20 val isabelle_name =
       
    21   let 
       
    22     fun purge s = if Symbol.is_letdig s then s else
       
    23       (case s of
       
    24         "." => "_o_"
       
    25       | "_" => "_n_"
       
    26       | "$" => "_S_"
       
    27       | "@" => "_G_"
       
    28       | "#" => "_H_"
       
    29       | "^" => "_T_"
       
    30       | _   => ("_" ^ string_of_int (ord s) ^ "_"))
       
    31   in prefix "b_" o translate_string purge end
       
    32 
       
    33 datatype attribute_value = StringValue of string | TermValue of Term.term
       
    34 
       
    35 
       
    36 
       
    37 local
       
    38   fun lookup_type_name thy name arity =
       
    39     let val intern = Sign.intern_type thy name
       
    40     in
       
    41       if Sign.declared_tyname thy intern
       
    42       then
       
    43         if Sign.arity_number thy intern = arity then SOME intern
       
    44         else error ("Boogie: type already declared with different arity: " ^
       
    45           quote name)
       
    46       else NONE
       
    47     end
       
    48 
       
    49   fun declare (name, arity) thy =
       
    50     let val isa_name = isabelle_name name
       
    51     in
       
    52       (case lookup_type_name thy isa_name arity of
       
    53         SOME type_name => ((type_name, false), thy)
       
    54       | NONE =>
       
    55           let
       
    56             val bname = Binding.name isa_name
       
    57             val args = Name.variant_list [] (replicate arity "'")
       
    58             val (T, thy') =
       
    59               ObjectLogic.typedecl (Binding.name isa_name, args, NoSyn) thy
       
    60             val type_name = fst (Term.dest_Type T)
       
    61           in ((type_name, true), thy') end)
       
    62     end
       
    63 
       
    64   fun type_names ((name, _), (new_name, new)) =
       
    65     if new then SOME (new_name ^ " (was " ^ name ^ ")") else NONE
       
    66 in
       
    67 fun declare_types verbose tys =
       
    68   fold_map declare tys #-> (fn tds =>
       
    69   log verbose "Declared types:" (map_filter type_names (tys ~~ tds)) #>
       
    70   rpair (Symtab.make (map fst tys ~~ map fst tds)))
       
    71 end
       
    72 
       
    73 
       
    74 
       
    75 local
       
    76   val quote_mixfix =
       
    77     Symbol.explode #> map
       
    78       (fn "_" => "'_"
       
    79         | "(" => "'("
       
    80         | ")" => "')"
       
    81         | "/" => "'/"
       
    82         | s => s) #>
       
    83     implode
       
    84 
       
    85   fun mk_syntax name i =
       
    86     let
       
    87       val syn = quote_mixfix name
       
    88       val args = concat (separate ",/ " (replicate i "_"))
       
    89     in
       
    90       if i = 0 then Mixfix (syn, [], 1000)
       
    91       else Mixfix (syn ^ "()'(/" ^ args ^ "')", replicate i 0, 1000)
       
    92     end
       
    93 
       
    94   fun process_attributes T =
       
    95     let
       
    96       fun const name = SOME (Const (name, T))
       
    97 
       
    98       fun choose builtin =
       
    99         (case builtin of
       
   100           "bvnot" => const @{const_name z3_bvnot}
       
   101         | "bvand" => const @{const_name z3_bvand}
       
   102         | "bvor" => const @{const_name z3_bvor}
       
   103         | "bvxor" => const @{const_name z3_bvxor}
       
   104         | "bvadd" => const @{const_name z3_bvadd}
       
   105         | "bvneg" => const @{const_name z3_bvneg}
       
   106         | "bvsub" => const @{const_name z3_bvsub}
       
   107         | "bvmul" => const @{const_name z3_bvmul}
       
   108         | "bvudiv" => const @{const_name z3_bvudiv}
       
   109         | "bvurem" => const @{const_name z3_bvurem}
       
   110         | "bvsdiv" => const @{const_name z3_bvsdiv}
       
   111         | "bvsrem" => const @{const_name z3_bvsrem}
       
   112         | "bvshl" => const @{const_name z3_bvshl}
       
   113         | "bvlshr" => const @{const_name z3_bvlshr}
       
   114         | "bvashr" => const @{const_name z3_bvashr}
       
   115         | "bvult" => const @{const_name z3_bvult}
       
   116         | "bvule" => const @{const_name z3_bvule}
       
   117         | "bvugt" => const @{const_name z3_bvugt}
       
   118         | "bvuge" => const @{const_name z3_bvuge}
       
   119         | "bvslt" => const @{const_name z3_bvslt}
       
   120         | "bvsle" => const @{const_name z3_bvsle}
       
   121         | "bvsgt" => const @{const_name z3_bvsgt}
       
   122         | "bvsge" => const @{const_name z3_bvsge}
       
   123         | "sign_extend" => const @{const_name z3_sign_extend}
       
   124         | _ => NONE)
       
   125 
       
   126       fun is_builtin att =
       
   127         (case att of
       
   128           ("bvbuiltin", [StringValue builtin]) => choose builtin
       
   129         | ("bvint", [StringValue "ITE"]) => const @{const_name If}
       
   130         | _ => NONE)
       
   131     in get_first is_builtin end
       
   132 
       
   133   fun lookup_const thy name T =
       
   134     let
       
   135       val intern = Sign.intern_const thy name
       
   136       val is_type_instance = Type.typ_instance o Sign.tsig_of
       
   137     in
       
   138       if Sign.declared_const thy intern
       
   139       then
       
   140         if is_type_instance thy (T, Sign.the_const_type thy intern)
       
   141         then SOME (Const (intern, T))
       
   142         else error ("Boogie: function already declared with different type: " ^
       
   143           quote name)
       
   144       else NONE
       
   145     end
       
   146 
       
   147   fun declare (name, ((Ts, T), atts)) thy =
       
   148     let val isa_name = isabelle_name name and U = Ts ---> T
       
   149     in
       
   150       (case lookup_const thy isa_name U of
       
   151         SOME t => (((name, t), false), thy)
       
   152       | NONE => 
       
   153           (case process_attributes U atts of
       
   154             SOME t => (((name, t), false), thy)
       
   155           | NONE =>
       
   156               thy
       
   157               |> Sign.declare_const ((Binding.name isa_name, U),
       
   158                    mk_syntax name (length Ts))
       
   159               |> apfst (rpair true o pair name)))
       
   160     end
       
   161 
       
   162   fun const_names ((name, _), ((_, t), new)) =
       
   163     if new then SOME (fst (Term.dest_Const t) ^ " (as " ^ name ^ ")") else NONE
       
   164 in
       
   165 fun declare_functions verbose fns =
       
   166   fold_map declare fns #-> (fn fds =>
       
   167   log verbose "Declared constants:" (map_filter const_names (fns ~~ fds)) #>
       
   168   rpair (Symtab.make (map fst fds)))
       
   169 end
       
   170 
       
   171 
       
   172 
       
   173 local
       
   174   fun name_axioms axs =
       
   175     let fun mk_name idx = "axiom_" ^ string_of_int (idx + 1)
       
   176     in map_index (fn (idx, t) => (mk_name idx, HOLogic.mk_Trueprop t)) axs end
       
   177 
       
   178   fun only_new_boogie_axioms thy =
       
   179     let val baxs = map Thm.prop_of (Boogie_Axioms.get (ProofContext.init thy))
       
   180     in filter_out (member (op aconv) baxs o snd) end
       
   181 in
       
   182 fun add_axioms verbose axs thy =
       
   183   let val axs' = only_new_boogie_axioms thy (name_axioms axs)
       
   184   in
       
   185     thy
       
   186     |> PureThy.add_axioms (map (rpair [] o apfst Binding.name) axs')
       
   187     |-> Context.theory_map o fold Boogie_Axioms.add_thm
       
   188     |> log verbose "The following axioms were added:" (map fst axs')
       
   189     |> Context.theory_map (fn context => fold Split_VC_SMT_Rules.add_thm
       
   190          (Boogie_Axioms.get (Context.proof_of context)) context)
       
   191   end
       
   192 end
       
   193 
       
   194 
       
   195 
       
   196 fun add_vcs verbose vcs thy =
       
   197   let
       
   198     val reused = duplicates (op =) (map (fst o fst) vcs)
       
   199     fun rename (n, i) = isabelle_name n ^
       
   200       (if member (op =) reused n then "_" ^ string_of_int i else "")
       
   201 
       
   202     fun decorate (name, t) = (rename name, HOLogic.mk_Trueprop t)
       
   203     val vcs' = map decorate vcs
       
   204   in
       
   205     thy
       
   206     |> Boogie_VCs.set vcs'
       
   207     |> log verbose "The following verification conditions were loaded:"
       
   208          (map fst vcs')
       
   209   end
       
   210 
       
   211 
       
   212 
       
   213 local
       
   214   fun mk_bitT i T =
       
   215     if i = 0
       
   216     then Type (@{type_name "Numeral_Type.bit0"}, [T])
       
   217     else Type (@{type_name "Numeral_Type.bit1"}, [T])
       
   218 
       
   219   fun mk_binT size = 
       
   220     if size = 0 then @{typ "Numeral_Type.num0"}
       
   221     else if size = 1 then @{typ "Numeral_Type.num1"}
       
   222     else let val (q, r) = Integer.div_mod size 2 in mk_bitT r (mk_binT q) end
       
   223 in
       
   224 fun mk_wordT size =
       
   225   if size >= 0 then Type (@{type_name "word"}, [mk_binT size])
       
   226   else raise TYPE ("mk_wordT: " ^ quote (string_of_int size), [], [])
       
   227 end
       
   228 
       
   229 local
       
   230   fun dest_binT T =
       
   231     (case T of
       
   232       Type (@{type_name "Numeral_Type.num0"}, _) => 0
       
   233     | Type (@{type_name "Numeral_Type.num1"}, _) => 1
       
   234     | Type (@{type_name "Numeral_Type.bit0"}, [T]) => 2 * dest_binT T
       
   235     | Type (@{type_name "Numeral_Type.bit1"}, [T]) => 1 + 2 * dest_binT T
       
   236     | _ => raise TYPE ("dest_binT", [T], []))
       
   237 in
       
   238 val dest_wordT = (fn
       
   239     Type (@{type_name "word"}, [T]) => dest_binT T
       
   240   | T => raise TYPE ("dest_wordT", [T], []))
       
   241 end
       
   242 
       
   243 fun mk_arrayT (Ts, T) = Type (@{type_name "fun"}, [HOLogic.mk_tupleT Ts, T])
       
   244 
       
   245 
       
   246 
       
   247 datatype token = Token of string | Newline | EOF
       
   248 
       
   249 fun tokenize path =
       
   250   let
       
   251     fun blank c = (c = #" " orelse c = #"\t" orelse c = #"\n" orelse c = #"\r")
       
   252     fun split line (i, tss) = (i + 1,
       
   253       map (pair i) (map Token (String.tokens blank line) @ [Newline]) :: tss)
       
   254   in apsnd (flat o rev) (File.fold_lines split path (1, [])) end
       
   255 
       
   256 fun stopper i = Scan.stopper (K (i, EOF)) (fn (_, EOF) => true | _ => false)
       
   257 
       
   258 fun scan_err msg [] = "Boogie (at end of input): " ^ msg
       
   259   | scan_err msg ((i, _) :: _) = "Boogie (at line " ^ string_of_int i ^ "): " ^
       
   260       msg
       
   261 
       
   262 fun scan_fail msg = Scan.fail_with (scan_err msg)
       
   263 
       
   264 fun finite scan path =
       
   265   let val (i, ts) = tokenize path
       
   266   in
       
   267     (case Scan.error (Scan.finite (stopper i) scan) ts of
       
   268       (x, []) => x
       
   269     | (_, ts) => error (scan_err "unparsed input" ts))
       
   270   end
       
   271 
       
   272 fun read_int' s = (case read_int (explode s) of (i, []) => SOME i | _ => NONE)
       
   273 
       
   274 fun $$$ s = Scan.one (fn (_, Token s') => s = s' | _ => false)
       
   275 val str = Scan.some (fn (_, Token s) => SOME s | _ => NONE)
       
   276 val num = Scan.some (fn (_, Token s) => read_int' s | _ => NONE)
       
   277 
       
   278 fun scan_line key scan =
       
   279   $$$ key |-- scan --| Scan.one (fn (_, Newline) => true | _ => false)
       
   280 fun scan_line' key = scan_line key (Scan.succeed ())
       
   281 
       
   282 fun scan_count scan i =
       
   283   if i > 0 then scan ::: scan_count scan (i - 1) else Scan.succeed []
       
   284 
       
   285 fun scan_lookup kind tab key =
       
   286   (case Symtab.lookup tab key of
       
   287     SOME value => Scan.succeed value
       
   288   | NONE => scan_fail ("undefined " ^ kind ^ ": " ^ quote key))
       
   289 
       
   290 fun typ tds =
       
   291   let
       
   292     fun tp st =
       
   293      (scan_line' "bool" >> K @{typ bool} ||
       
   294       scan_line' "int" >> K @{typ int} ||
       
   295       scan_line "bv" num >> mk_wordT ||
       
   296       scan_line "type-con" (str -- num) :|-- (fn (name, arity) =>
       
   297         scan_lookup "type constructor" tds name -- scan_count tp arity >>
       
   298         Type) ||
       
   299       scan_line "array" num :|-- (fn arity =>
       
   300         scan_count tp (arity - 1) -- tp >> mk_arrayT) ||
       
   301       scan_fail "illegal type") st
       
   302   in tp end
       
   303 
       
   304 local
       
   305   fun mk_nary _ t [] = t
       
   306     | mk_nary f _ (t :: ts) = fold f ts t
       
   307 
       
   308   fun mk_distinct T ts =
       
   309     Const (@{const_name distinct}, HOLogic.listT T --> @{typ bool}) $ 
       
   310       HOLogic.mk_list T ts
       
   311 
       
   312   fun quant name f = scan_line name (num -- num -- num) >> pair f
       
   313   val quants =
       
   314     quant "forall" HOLogic.all_const ||
       
   315     quant "exists" HOLogic.exists_const ||
       
   316     scan_fail "illegal quantifier kind"
       
   317   fun mk_quant q (n, T) t = q T $ Term.absfree (isabelle_name n, T, t)
       
   318 
       
   319   val patternT = @{typ pattern}
       
   320   fun mk_pattern _ [] = raise TERM ("mk_pattern", [])
       
   321     | mk_pattern n [t] = Const (n, Term.fastype_of t --> patternT) $ t
       
   322     | mk_pattern n (t :: ts) =
       
   323         let val T = patternT --> Term.fastype_of t --> patternT
       
   324         in Const (@{const_name andpat}, T) $ mk_pattern n ts $ t end
       
   325   fun patt n c scan =
       
   326     scan_line n num :|-- scan_count scan >> (mk_pattern c)
       
   327   fun pattern scan =
       
   328     patt "pat" @{const_name pat} scan ||
       
   329     patt "nopat" @{const_name nopat} scan ||
       
   330     scan_fail "illegal pattern kind"
       
   331   fun mk_trigger [] t = t
       
   332     | mk_trigger ps t = @{term trigger} $ HOLogic.mk_list patternT ps $ t
       
   333 
       
   334   val label_kind =
       
   335     $$$ "pos" >> K @{term block_at} ||
       
   336     $$$ "neg" >> K @{term assert_at} ||
       
   337     scan_fail "illegal label kind"
       
   338 
       
   339   fun mk_select (m, k) =
       
   340     let
       
   341       val mT = Term.fastype_of m and kT = Term.fastype_of k
       
   342       val vT = Term.range_type mT
       
   343     in Const (@{const_name boogie_select}, mT --> kT --> vT) $ m $ k end
       
   344 
       
   345   fun mk_store ((m, k), v) =
       
   346     let
       
   347       val mT = Term.fastype_of m and kT = Term.fastype_of k
       
   348       val vT = Term.fastype_of v
       
   349     in
       
   350       Const (@{const_name boogie_store}, mT --> kT --> vT --> mT) $ m $ k $ v
       
   351     end
       
   352   
       
   353   fun mk_extract ((msb, lsb), t) =
       
   354     let
       
   355       val dT = Term.fastype_of t and rT = mk_wordT (msb - lsb)
       
   356       val nT = @{typ nat}
       
   357       val mk_nat_num = HOLogic.mk_number @{typ nat}
       
   358       val m = HOLogic.mk_number @{typ nat}
       
   359     in
       
   360       Const (@{const_name boogie_bv_extract}, [nT, nT, dT] ---> rT) $
       
   361         mk_nat_num msb $ mk_nat_num lsb $ t
       
   362     end
       
   363 
       
   364   fun mk_concat (t1, t2) =
       
   365     let
       
   366       val T1 = Term.fastype_of t1 and T2 = Term.fastype_of t2
       
   367       val U = mk_wordT (dest_wordT T1 + dest_wordT T2)
       
   368     in Const (@{const_name boogie_bv_concat}, [T1, T2] ---> U) $ t1 $ t2 end
       
   369 in
       
   370 fun expr tds fds =
       
   371   let
       
   372     fun binop t (u1, u2) = t $ u1 $ u2
       
   373     fun binexp s f = scan_line' s |-- exp -- exp >> f
       
   374 
       
   375     and exp st =
       
   376      (scan_line' "true" >> K @{term True} ||
       
   377       scan_line' "false" >> K @{term False} ||
       
   378       scan_line' "not" |-- exp >> HOLogic.mk_not ||
       
   379       scan_line "and" num :|-- scan_count exp >> 
       
   380         mk_nary (curry HOLogic.mk_conj) @{term True} ||
       
   381       scan_line "or" num :|-- scan_count exp >>
       
   382         mk_nary (curry HOLogic.mk_disj) @{term False} ||
       
   383       binexp "implies" (binop @{term "op -->"}) ||
       
   384       scan_line "distinct" num :|-- scan_count exp >>
       
   385         (fn [] => @{term True}
       
   386           | ts as (t :: _) => mk_distinct (Term.fastype_of t) ts) ||
       
   387       binexp "=" HOLogic.mk_eq ||
       
   388       scan_line "var" str -- typ tds >> (Free o apfst isabelle_name) ||
       
   389       scan_line "fun" (str -- num) :|-- (fn (name, arity) =>
       
   390         scan_lookup "constant" fds name -- scan_count exp arity >>
       
   391         Term.list_comb) ||
       
   392       quants :|-- (fn (q, ((n, k), i)) =>
       
   393         scan_count (scan_line "var" str -- typ tds) n --
       
   394         scan_count (pattern exp) k --
       
   395         scan_count (attribute tds fds) i --
       
   396         exp >> (fn (((vs, ps), _), t) =>
       
   397           fold_rev (mk_quant q) vs (mk_trigger ps t))) ||
       
   398       scan_line "label" (label_kind -- num -- num) -- exp >>
       
   399         (fn (((l, line), col), t) =>
       
   400           if line = 0 orelse col = 0 then t
       
   401           else l $ Free ("Line_" ^ string_of_int line ^ "_Column_" ^
       
   402             string_of_int col, @{typ bool}) $ t) ||
       
   403       scan_line "int-num" num >> HOLogic.mk_number @{typ int} ||
       
   404       binexp "<" (binop @{term "op < :: int => _"}) ||
       
   405       binexp "<=" (binop @{term "op <= :: int => _"}) ||
       
   406       binexp ">" (binop @{term "op < :: int => _"} o swap) ||
       
   407       binexp ">=" (binop @{term "op <= :: int => _"} o swap) ||
       
   408       binexp "+" (binop @{term "op + :: int => _"}) ||
       
   409       binexp "-" (binop @{term "op + :: int => _"}) ||
       
   410       binexp "*" (binop @{term "op + :: int => _"}) ||
       
   411       binexp "/" (binop @{term boogie_div}) ||
       
   412       binexp "%" (binop @{term boogie_mod}) ||
       
   413       scan_line "select" num :|-- (fn arity =>
       
   414         exp -- (scan_count exp (arity - 1) >> HOLogic.mk_tuple) >>
       
   415           mk_select) ||
       
   416       scan_line "store" num :|-- (fn arity =>
       
   417         exp -- (scan_count exp (arity - 2) >> HOLogic.mk_tuple) -- exp >> 
       
   418           mk_store) ||
       
   419       scan_line "bv-num" (num -- num) >> (fn (size, i) =>
       
   420         HOLogic.mk_number (mk_wordT size) i) ||
       
   421       scan_line "bv-extract" (num -- num) -- exp >> mk_extract ||
       
   422       binexp "bv-concat" mk_concat ||
       
   423       scan_fail "illegal expression") st
       
   424   in exp end
       
   425 
       
   426 and attribute tds fds =
       
   427   let
       
   428     val attr_val = 
       
   429       scan_line' "expr-attr" |-- expr tds fds >> TermValue ||
       
   430       scan_line "string-attr" (Scan.repeat1 str) >>
       
   431         (StringValue o space_implode " ") ||
       
   432       scan_fail "illegal attribute value"
       
   433   in
       
   434     scan_line "attribute" (str -- num) :|-- (fn (name, i) =>
       
   435       scan_count attr_val i >> pair name) ||
       
   436     scan_fail "illegal attribute"
       
   437   end
       
   438 end
       
   439 
       
   440 fun type_decls verbose = Scan.depend (fn thy => 
       
   441   Scan.repeat (scan_line "type-decl" (str -- num -- num) :|-- (fn (ty, i) =>
       
   442     scan_count (attribute Symtab.empty Symtab.empty) i >> K ty)) >>
       
   443     (fn tys => declare_types verbose tys thy))
       
   444 
       
   445 fun fun_decls verbose tds = Scan.depend (fn thy =>
       
   446   Scan.repeat (scan_line "fun-decl" (str -- num -- num) :|--
       
   447     (fn ((name, arity), i) =>
       
   448       scan_count (typ tds) (arity - 1) -- typ tds --
       
   449       scan_count (attribute tds Symtab.empty) i >> pair name)) >>
       
   450     (fn fns => declare_functions verbose fns thy))
       
   451 
       
   452 fun axioms verbose tds fds = Scan.depend (fn thy =>
       
   453   Scan.repeat (scan_line "axiom" num :|-- (fn i =>
       
   454     expr tds fds --| scan_count (attribute tds fds) i)) >>
       
   455     (fn axs => (add_axioms verbose axs thy, ())))
       
   456 
       
   457 fun var_decls tds fds = Scan.depend (fn thy =>
       
   458   Scan.repeat (scan_line "var-decl" (str -- num) :|-- (fn (_, i) =>
       
   459     typ tds -- scan_count (attribute tds fds) i >> K ())) >> K (thy, ()))
       
   460 
       
   461 fun vcs verbose tds fds = Scan.depend (fn thy =>
       
   462   Scan.repeat (scan_line "vc" (str -- num) -- 
       
   463     (expr tds fds >> Sign.cert_term thy)) >>
       
   464     (fn vcs => ((), add_vcs verbose vcs thy)))
       
   465 
       
   466 fun parse verbose thy = Scan.pass thy
       
   467  (type_decls verbose :|-- (fn tds =>
       
   468   fun_decls verbose tds :|-- (fn fds =>
       
   469   axioms verbose tds fds |--
       
   470   var_decls tds fds |--
       
   471   vcs verbose tds fds)))
       
   472 
       
   473 fun load_b2i verbose path thy = finite (parse verbose thy) path
       
   474 
       
   475 end