src/HOL/Boogie/Tools/boogie_commands.ML
changeset 34181 003333ffa543
parent 34080 a36d80e4e42e
child 35125 acace7e30357
equal deleted inserted replaced
34180:6e82fd81f813 34181:003333ffa543
    12 structure Boogie_Commands: BOOGIE_COMMANDS =
    12 structure Boogie_Commands: BOOGIE_COMMANDS =
    13 struct
    13 struct
    14 
    14 
    15 (* commands *)
    15 (* commands *)
    16 
    16 
    17 fun boogie_load (quiet, base_name) thy =
    17 fun boogie_open (quiet, base_name) thy =
    18   let
    18   let
    19     val path = Path.explode (base_name ^ ".b2i")
    19     val path = Path.explode (base_name ^ ".b2i")
    20     val _ = File.exists path orelse
    20     val _ = File.exists path orelse
    21       error ("Unable to find file " ^ quote (Path.implode path))
    21       error ("Unable to find file " ^ quote (Path.implode path))
    22     val _ = Boogie_VCs.is_closed thy orelse
    22     val _ = Boogie_VCs.is_closed thy orelse
    23       error ("Found the beginning of a new Boogie environment, " ^
    23       error ("Found the beginning of a new Boogie environment, " ^
    24         "but another Boogie environment is still open.")
    24         "but another Boogie environment is still open.")
    25   in Boogie_Loader.load_b2i (not quiet) path thy end
    25   in Boogie_Loader.load_b2i (not quiet) path thy end
    26 
    26 
    27 
    27 
    28 datatype vc_opts = VC_full of bool | VC_only of string list
    28 datatype vc_opts =
       
    29   VC_Complete |
       
    30   VC_Take of int list * (bool * string list) option |
       
    31   VC_Only of string list |
       
    32   VC_Without of string list |
       
    33   VC_Examine of string list 
    29 
    34 
    30 fun get_vc thy vc_name =
    35 fun get_vc thy vc_name =
    31   (case Boogie_VCs.lookup thy vc_name of
    36   (case Boogie_VCs.lookup thy vc_name of
    32     SOME vc => vc
    37     SOME vc => vc
    33   | NONE => 
    38   | NONE => 
    35         SOME Boogie_VCs.Proved => error ("The verification condition " ^
    40         SOME Boogie_VCs.Proved => error ("The verification condition " ^
    36           quote vc_name ^ " has already been proved.")
    41           quote vc_name ^ " has already been proved.")
    37       | _ => error ("There is no verification condition " ^
    42       | _ => error ("There is no verification condition " ^
    38           quote vc_name ^ ".")))
    43           quote vc_name ^ ".")))
    39 
    44 
    40 fun boogie_vc (vc_opts, vc_name) lthy =
    45 fun boogie_vc (vc_name, vc_opts) lthy =
    41   let
    46   let
    42     val thy = ProofContext.theory_of lthy
    47     val thy = ProofContext.theory_of lthy
    43     val vc = get_vc thy vc_name
    48     val vc = get_vc thy vc_name
    44 
    49 
    45     val (sks, ts) = split_list
    50     val vcs =
    46       (case vc_opts of
    51       (case vc_opts of
    47         VC_full paths => [vc] |> paths ? maps Boogie_VCs.paths_of
    52         VC_Complete => [vc]
    48       | VC_only assertions => map_filter (Boogie_VCs.extract vc) assertions)
    53       | VC_Take (ps, NONE) => [Boogie_VCs.paths_and ps [] vc]
       
    54       | VC_Take (ps, SOME (true, ls)) => [Boogie_VCs.paths_and ps ls vc]
       
    55       | VC_Take (ps, SOME (false, ls)) => [Boogie_VCs.paths_without ps ls vc]
       
    56       | VC_Only ls => [Boogie_VCs.only ls vc]
       
    57       | VC_Without ls => [Boogie_VCs.without ls vc]
       
    58       | VC_Examine ls => map_filter (Boogie_VCs.extract vc) ls)
       
    59     val ts = map Boogie_VCs.prop_of_vc vcs
    49 
    60 
    50     val discharge = fold (Boogie_VCs.discharge o pair vc_name)
    61     val discharge = fold (Boogie_VCs.discharge o pair vc_name)
    51     fun after_qed [thms] = Local_Theory.theory (discharge (sks ~~ thms))
    62     fun after_qed [thms] = Local_Theory.theory (discharge (vcs ~~ thms))
    52       | after_qed _ = I
    63       | after_qed _ = I
    53   in
    64   in
    54     lthy
    65     lthy
    55     |> fold Variable.auto_fixes ts
    66     |> fold Variable.auto_fixes ts
    56     |> Proof.theorem_i NONE after_qed [map (rpair []) ts]
    67     |> Proof.theorem_i NONE after_qed [map (rpair []) ts]
    57   end
    68   end
    58 
    69 
    59 
    70 
    60 fun write_list head xs =
    71 fun write_list head =
    61   Pretty.writeln (Pretty.big_list head (map Pretty.str xs))
    72   map Pretty.str o sort (dict_ord string_ord o pairself explode) #>
    62 
    73   Pretty.writeln o Pretty.big_list head
    63 val prefix_string_ord = dict_ord string_ord o pairself explode
    74 
       
    75 fun parens s = "(" ^ s ^ ")"
    64 
    76 
    65 fun boogie_status thy =
    77 fun boogie_status thy =
    66   let
    78   let
    67     fun string_of_state Boogie_VCs.Proved = "proved"
    79     fun string_of_state Boogie_VCs.Proved = "proved"
    68       | string_of_state Boogie_VCs.NotProved = "not proved"
    80       | string_of_state Boogie_VCs.NotProved = "not proved"
    69       | string_of_state Boogie_VCs.PartiallyProved = "partially proved"
    81       | string_of_state Boogie_VCs.PartiallyProved = "partially proved"
    70   in
    82   in
    71     Boogie_VCs.state_of thy
    83     Boogie_VCs.state_of thy
    72     |> map (fn (name, proved) => name ^ " (" ^ string_of_state proved ^ ")")
    84     |> map (fn (name, proved) => name ^ " " ^ parens (string_of_state proved))
    73     |> sort prefix_string_ord
       
    74     |> write_list "Boogie verification conditions:"
    85     |> write_list "Boogie verification conditions:"
    75   end
    86   end
    76 
    87 
    77 fun boogie_status_vc (full, vc_name) thy =
    88 fun boogie_status_vc full vc_name thy =
    78   let
    89   let
    79     fun pretty tag s = s ^ " (" ^ tag ^ ")"
    90     fun pretty tag s = s ^ " " ^ parens tag
    80 
    91 
    81     val (ps, us) = Boogie_VCs.state_of_vc thy vc_name
    92     val (not_proved, proved) = Boogie_VCs.state_of_vc thy vc_name
    82   in
    93   in
    83     if full
    94     if full
    84     then write_list ("Assertions of Boogie verification condition " ^
    95     then write_list ("Assertions of Boogie verification condition " ^
    85       quote vc_name ^ ":") (sort prefix_string_ord
    96       quote vc_name ^ ":")
    86         (map (pretty "proved") ps @ map (pretty "not proved") us))
    97       (map (pretty "proved") proved @ map (pretty "not proved") not_proved)
    87     else write_list ("Unproved assertions of Boogie verification condition " ^
    98     else write_list ("Unproved assertions of Boogie verification condition " ^
    88       quote vc_name ^ ":") (sort prefix_string_ord us)
    99       quote vc_name ^ ":") not_proved
       
   100   end
       
   101 
       
   102 fun boogie_status_vc_paths full vc_name thy =
       
   103   let
       
   104     fun labels ls = Pretty.blk (0, Pretty.breaks (map Pretty.str ls))
       
   105 
       
   106     fun pp (i, ns) =
       
   107       if full
       
   108       then
       
   109         [Pretty.big_list ("Path " ^ string_of_int (i+1) ^ ":")
       
   110           [labels (map (fn (n, true) => n | (n, _) => parens n) ns)]]
       
   111       else
       
   112         let val ns' = map_filter (fn (n, true) => SOME n | _ => NONE) ns
       
   113         in
       
   114           if null ns' then []
       
   115           else
       
   116             [Pretty.big_list ("Unproved assertions of path " ^
       
   117               string_of_int (i+1) ^ ":") [labels ns']]
       
   118         end
       
   119   in
       
   120     Pretty.writeln
       
   121       (Pretty.big_list
       
   122         ("Paths of Boogie verification condition " ^ quote vc_name ^ ":")
       
   123         (flat (map_index pp (Boogie_VCs.path_names_of (get_vc thy vc_name)))))
    89   end
   124   end
    90 
   125 
    91 
   126 
    92 local
   127 local
    93   fun trying s = tracing ("Trying " ^ s ^ " ...")
   128   fun trying s = tracing ("Trying " ^ s ^ " ...")
    94   fun success_on s = tracing ("Succeeded on " ^ s ^ ".")
   129   fun success_on s = tracing ("Succeeded on " ^ s ^ ".")
    95   fun failure_on s c = tracing ("Failed on " ^ s ^ c)
   130   fun failure_on s c = tracing ("Failed on " ^ s ^ c)
    96 
   131 
    97   fun string_of_asserts goal = space_implode ", " (Boogie_VCs.names_of goal)
   132   fun string_of_asserts vc = space_implode ", " (fst (Boogie_VCs.names_of vc))
    98 
   133 
    99   fun string_of_path (i, n) =
   134   fun string_of_path (i, n) =
   100     "path " ^ string_of_int i ^ " of " ^ string_of_int n
   135     "path " ^ string_of_int i ^ " of " ^ string_of_int n
   101 
   136 
   102   fun itemize_paths ps =
   137   fun itemize_paths ps =
   103     let val n = length ps
   138     let val n = length ps
   104     in fst (fold_map (fn p => fn i => (((i, n), p), i+1)) ps 1) end
   139     in fst (fold_map (fn p => fn i => (((i, n), p), i+1)) ps 1) end
   105 
   140 
   106   fun par_map f = flat o Par_List.map f
   141   fun par_map f = flat o Par_List.map f
   107 
   142 
   108   fun divide f goal =
   143   fun divide f vc =
   109     let val n = Boogie_VCs.size_of goal
   144     let val n = Boogie_VCs.size_of vc
   110     in
   145     in
   111       if n = 1 then Boogie_VCs.names_of goal
   146       if n <= 1 then fst (Boogie_VCs.names_of vc)
   112       else
   147       else
   113         let val (goal1, goal2) = Boogie_VCs.split_path (n div 2) goal
   148         let val (vc1, vc2) = the (Boogie_VCs.split_path (n div 2) vc)
   114         in par_map f [goal1, goal2] end
   149         in par_map f [vc1, vc2] end
   115     end
   150     end
   116 
   151 
   117   fun prove thy meth (_, goal) =
   152   fun prove thy meth vc =
   118     ProofContext.init thy
   153     ProofContext.init thy
   119     |> Proof.theorem_i NONE (K I) [[(goal, [])]]
   154     |> Proof.theorem_i NONE (K I) [[(Boogie_VCs.prop_of_vc vc, [])]]
   120     |> Proof.apply meth
   155     |> Proof.apply meth
   121     |> Seq.hd
   156     |> Seq.hd
   122     |> Proof.global_done_proof
   157     |> Proof.global_done_proof
   123 in
   158 in
   124 fun boogie_narrow_vc ((timeout, vc_name), meth) thy =
   159 fun boogie_narrow_vc timeout vc_name meth thy =
   125   let
   160   let
   126     val tp = TimeLimit.timeLimit (Time.fromSeconds timeout) (prove thy meth)
   161     val tp = TimeLimit.timeLimit (Time.fromSeconds timeout) (prove thy meth)
   127 
   162 
   128     fun try_goal (tag, split_tag) split goal = (trying tag;
   163     fun try_vc (tag, split_tag) split vc = (trying tag;
   129       (case try tp goal of
   164       (case try tp vc of
   130         SOME _ => (success_on tag; [])
   165         SOME _ => (success_on tag; [])
   131       | NONE => (failure_on tag split_tag; split goal)))
   166       | NONE => (failure_on tag split_tag; split vc)))
   132 
   167 
   133     fun group_goal goal =
   168     fun some_asserts vc =
   134       try_goal (string_of_asserts goal,
   169       try_vc (string_of_asserts vc,
   135         if Boogie_VCs.size_of goal = 1 then "." else ", further splitting ...")
   170         if Boogie_VCs.size_of vc = 1 then "." else ", further splitting ...")
   136         (divide group_goal) goal
   171         (divide some_asserts) vc
   137 
   172 
   138     fun path_goal p =
   173     fun single_path p =
   139       try_goal (string_of_path p, ", splitting into assertions ...")
   174       try_vc (string_of_path p, ", splitting into assertions ...")
   140         (divide group_goal)
   175         (divide some_asserts)
   141 
   176 
   142     val full_goal =
   177     val complete_vc =
   143       try_goal ("full goal", ", splitting into paths ...")
   178       try_vc ("full goal", ", splitting into paths ...")
   144         (par_map (uncurry path_goal) o itemize_paths o Boogie_VCs.paths_of)
   179         (par_map (uncurry single_path) o itemize_paths o Boogie_VCs.paths_of)
   145 
   180 
   146     val unsolved = full_goal (get_vc thy vc_name)
   181     val unsolved = complete_vc (get_vc thy vc_name)
   147   in
   182   in
   148     if null unsolved
   183     if null unsolved
   149     then writeln ("Completely solved Boogie verification condition " ^
   184     then writeln ("Completely solved Boogie verification condition " ^
   150       quote vc_name ^ ".")
   185       quote vc_name ^ ".")
   151     else write_list ("Unsolved assertions of Boogie verification condition " ^
   186     else write_list ("Unsolved assertions of Boogie verification condition " ^
   152       quote vc_name ^ ":") (sort prefix_string_ord unsolved)
   187       quote vc_name ^ ":") unsolved
       
   188   end
       
   189 
       
   190 fun boogie_scan_vc timeout vc_name meth thy =
       
   191   let
       
   192     val tp = TimeLimit.timeLimit (Time.fromSeconds timeout) (prove thy meth)
       
   193 
       
   194     val vc = get_vc thy vc_name
       
   195     fun prove_assert name =
       
   196       (trying name; tp (the (Boogie_VCs.extract vc name)))
       
   197     val find_first_failure = find_first (is_none o try prove_assert)
       
   198   in
       
   199     (case find_first_failure (fst (Boogie_VCs.names_of vc)) of
       
   200       SOME name => writeln ("failed on " ^ quote name)
       
   201     | NONE => writeln "succeeded")
   153   end
   202   end
   154 end
   203 end
   155 
   204 
   156 
   205 
   157 
   206 
   172 
   221 
   173 (* syntax and setup *)
   222 (* syntax and setup *)
   174 
   223 
   175 fun scan_val n f = Args.$$$ n -- Args.colon |-- f
   224 fun scan_val n f = Args.$$$ n -- Args.colon |-- f
   176 fun scan_arg f = Args.parens f
   225 fun scan_arg f = Args.parens f
   177 fun scan_opt n = Scan.optional (scan_arg (Args.$$$ n) >> K true) false
   226 fun scan_opt n = Scan.optional (scan_arg (Args.$$$ n >> K true)) false
   178 
       
   179 
   227 
   180 val _ =
   228 val _ =
   181   OuterSyntax.command "boogie_open"
   229   OuterSyntax.command "boogie_open"
   182     "Open a new Boogie environment and load a Boogie-generated .b2i file."
   230     "Open a new Boogie environment and load a Boogie-generated .b2i file."
   183     OuterKeyword.thy_decl
   231     OuterKeyword.thy_decl
   184     (scan_opt "quiet" -- OuterParse.name >> (Toplevel.theory o boogie_load))
   232     (scan_opt "quiet" -- OuterParse.name >> (Toplevel.theory o boogie_open))
   185 
   233 
   186 
   234 
   187 val vc_name = OuterParse.name
   235 val vc_name = OuterParse.name
       
   236 
       
   237 val vc_labels = Scan.repeat1 OuterParse.name
       
   238 
       
   239 val vc_paths =
       
   240   OuterParse.nat -- (Args.$$$ "-" |-- OuterParse.nat) >> (op upto) ||
       
   241   OuterParse.nat >> single
       
   242 
   188 val vc_opts =
   243 val vc_opts =
   189   scan_arg (scan_val "only" (Scan.repeat1 OuterParse.name)) >> VC_only ||
   244   scan_arg
   190   scan_opt "paths" >> VC_full
   245    (scan_val "examine" vc_labels >> VC_Examine ||
   191 
   246     scan_val "take" ((OuterParse.list vc_paths >> flat) -- Scan.option (
   192 fun vc_cmd f = Toplevel.print o Toplevel.local_theory_to_proof NONE f
   247       scan_val "without" vc_labels >> pair false ||
       
   248       scan_val "and_also" vc_labels >> pair true) >> VC_Take) ||
       
   249     scan_val "only" vc_labels >> VC_Only ||
       
   250     scan_val "without" vc_labels >> VC_Without) ||
       
   251   Scan.succeed VC_Complete  
   193 
   252 
   194 val _ =
   253 val _ =
   195   OuterSyntax.command "boogie_vc"
   254   OuterSyntax.command "boogie_vc"
   196     "Enter into proof mode for a specific verification condition."
   255     "Enter into proof mode for a specific verification condition."
   197     OuterKeyword.thy_goal
   256     OuterKeyword.thy_goal
   198     (vc_opts -- vc_name >> (vc_cmd o boogie_vc))
   257     (vc_name -- vc_opts >> (fn args =>
       
   258       (Toplevel.print o Toplevel.local_theory_to_proof NONE (boogie_vc args))))
   199 
   259 
   200 
   260 
   201 val default_timeout = 5
   261 val default_timeout = 5
   202 
   262 
   203 val status_narrow_vc =
   263 val status_test =
   204   scan_arg (Args.$$$ "narrow" |--
   264   scan_arg (
       
   265     (Args.$$$ "scan" >> K boogie_scan_vc ||
       
   266      Args.$$$ "narrow" >> K boogie_narrow_vc) --
   205     Scan.optional (scan_val "timeout" OuterParse.nat) default_timeout) --
   267     Scan.optional (scan_val "timeout" OuterParse.nat) default_timeout) --
   206   vc_name --
   268   vc_name -- Method.parse >>
   207   scan_arg (scan_val "try" Method.parse)
   269   (fn (((f, timeout), vc_name), meth) => f timeout vc_name meth)
   208 
   270 
   209 val status_vc_opts = scan_opt "full"
   271 val status_vc =
       
   272   (scan_arg
       
   273     (Args.$$$ "full" |--
       
   274       (Args.$$$ "paths" >> K (boogie_status_vc_paths true) ||
       
   275        Scan.succeed (boogie_status_vc true)) ||
       
   276      Args.$$$ "paths" >> K (boogie_status_vc_paths false)) ||
       
   277    Scan.succeed (boogie_status_vc false)) --
       
   278   vc_name >> (fn (f, vc_name) => f vc_name)
   210 
   279 
   211 fun status_cmd f = Toplevel.no_timing o Toplevel.keep (fn state =>
   280 fun status_cmd f = Toplevel.no_timing o Toplevel.keep (fn state =>
   212   f (Toplevel.theory_of state))
   281   f (Toplevel.theory_of state))
   213 
   282 
   214 val _ =
   283 val _ =
   215   OuterSyntax.improper_command "boogie_status"
   284   OuterSyntax.improper_command "boogie_status"
   216     "Show the name and state of all loaded verification conditions."
   285     "Show the name and state of all loaded verification conditions."
   217     OuterKeyword.diag
   286     OuterKeyword.diag
   218     (status_narrow_vc >> (status_cmd o boogie_narrow_vc) ||
   287     (status_test >> status_cmd ||
   219      status_vc_opts -- vc_name >> (status_cmd o boogie_status_vc) ||
   288      status_vc >> status_cmd ||
   220      Scan.succeed (status_cmd boogie_status))
   289      Scan.succeed (status_cmd boogie_status))
   221 
   290 
   222 
   291 
   223 val _ =
   292 val _ =
   224   OuterSyntax.command "boogie_end"
   293   OuterSyntax.command "boogie_end"