main.ml 24.3 KB
Newer Older
Jean-Christophe Filliâtre's avatar
headers    
Jean-Christophe Filliâtre committed
1
2
(**************************************************************************)
(*                                                                        *)
MARCHE Claude's avatar
MARCHE Claude committed
3
(*  Copyright (C) 2010-2012                                               *)
4
5
6
(*    François Bobot                                                      *)
(*    Jean-Christophe Filliâtre                                           *)
(*    Claude Marché                                                       *)
MARCHE Claude's avatar
MARCHE Claude committed
7
(*    Guillaume Melquiond                                                 *)
Jean-Christophe Filliâtre's avatar
headers    
Jean-Christophe Filliâtre committed
8
(*    Andrei Paskevich                                                    *)
Jean-Christophe Filliâtre's avatar
headers    
Jean-Christophe Filliâtre committed
9
10
11
12
13
14
15
16
17
18
19
20
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU Library General Public           *)
(*  License version 2.1, with the special exception on linking            *)
(*  described in file LICENSE.                                            *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(**************************************************************************)

21
open Format
22
open Why3
23
24
open Util
open Whyconf
25
open Theory
Andrei Paskevich's avatar
Andrei Paskevich committed
26
open Task
Jean-Christophe Filliâtre's avatar
Jean-Christophe Filliâtre committed
27
open Driver
28
open Trans
29

30
31
let usage_msg = sprintf
  "Usage: %s [options] [[file|-] [-T <theory> [-G <goal>]...]...]..."
32
  (Filename.basename Sys.argv.(0))
33

34
35
36
let version_msg = sprintf "Why3 platform, version %s (build date: %s)"
  Config.version Config.builddate

37
let opt_queue = Queue.create ()
38

39
40
let opt_input = ref None
let opt_theory = ref None
41
let opt_trans = ref []
42
let opt_metas = ref []
43

44
45
let add_opt_file x =
  let tlist = Queue.create () in
46
  Queue.push (Some x, tlist) opt_queue;
47
  opt_input := Some tlist
48

49
50
51
let add_opt_theory =
  let rdot = (Str.regexp "\\.") in fun x ->
  let l = Str.split rdot x in
52
53
54
55
56
57
  let p, t = match List.rev l with
    | t::p -> List.rev p, t
    | _ -> assert false
  in
  match !opt_input, p with
  | None, [] ->
58
      eprintf "Option '-T'/'--theory' with a non-qualified \
59
60
61
62
        argument requires an input file.@.";
      exit 1
  | Some tlist, [] ->
      let glist = Queue.create () in
63
      Queue.push (x, p, t, glist) tlist;
64
65
66
      opt_theory := Some glist
  | _ ->
      let tlist = Queue.create () in
67
      Queue.push (None, tlist) opt_queue;
68
69
      opt_input := None;
      let glist = Queue.create () in
70
      Queue.push (x, p, t, glist) tlist;
71
72
73
74
      opt_theory := Some glist

let add_opt_goal x = match !opt_theory with
  | None ->
75
      eprintf "Option '-G'/'--goal' requires a theory.@.";
76
77
78
      exit 1
  | Some glist ->
      let l = Str.split (Str.regexp "\\.") x in
79
      Queue.push (x, l) glist
80
81

let add_opt_trans x = opt_trans := x::!opt_trans
82

83
let add_opt_meta meta =
84
  let meta_name, meta_arg =
85
86
87
88
89
90
91
    try
      let index = String.index meta '=' in
      (String.sub meta 0 index),
      Some (String.sub meta (index+1) (String.length meta - (index + 1)))
    with Not_found ->
      meta, None
  in
92
93
  opt_metas := (meta_name,meta_arg)::!opt_metas

94
let opt_config = ref None
95
let opt_extra = ref []
96
let opt_parser = ref None
97
let opt_prover = ref None
98
let opt_loadpath = ref []
99
100
let opt_driver = ref None
let opt_output = ref None
101
102
let opt_timelimit = ref None
let opt_memlimit = ref None
103
let opt_command = ref None
104
let opt_task = ref None
105
let opt_realize = ref false
106
let opt_extract = ref false
107
let opt_bisect = ref false
108

109
110
111
let opt_print_libdir = ref false
let opt_print_datadir = ref false

112
113
114
115
let opt_print_theory = ref false
let opt_print_namespace = ref false
let opt_list_transforms = ref false
let opt_list_printers = ref false
116
let opt_list_provers = ref false
117
let opt_list_formats = ref false
118
let opt_list_metas = ref false
119

120
let opt_token_count = ref false
121
122
let opt_parse_only = ref false
let opt_type_only = ref false
123
let opt_version = ref false
124
125
126
127

let option_list = Arg.align [
  "-", Arg.Unit (fun () -> add_opt_file "-"),
      " Read the input file from stdin";
128
  "-T", Arg.String add_opt_theory,
129
130
      "<theory> Select <theory> in the input file or in the library";
  "--theory", Arg.String add_opt_theory,
131
132
      " same as -T";
  "-G", Arg.String add_opt_goal,
133
134
      "<goal> Select <goal> in the last selected theory";
  "--goal", Arg.String add_opt_goal,
135
      " same as -G";
136
137
138
139
  "-C", Arg.String (fun s -> opt_config := Some s),
      "<file> Read configuration from <file>";
  "--config", Arg.String (fun s -> opt_config := Some s),
      " same as -C";
140
141
  "--extra-config", Arg.String (fun s -> opt_extra := !opt_extra @ [s]),
      "<file> Read additional configuration from <file>";
142
  "-L", Arg.String (fun s -> opt_loadpath := s :: !opt_loadpath),
143
      "<dir> Add <dir> to the library search path";
144
145
146
147
148
149
150
151
  "--library", Arg.String (fun s -> opt_loadpath := s :: !opt_loadpath),
      " same as -L";
  "-I", Arg.String (fun s -> opt_loadpath := s :: !opt_loadpath),
      " same as -L (obsolete)";
  "-P", Arg.String (fun s -> opt_prover := Some s),
      "<prover> Prove or print (with -o) the selected goals";
  "--prover", Arg.String (fun s -> opt_prover := Some s),
      " same as -P";
152
  "-F", Arg.String (fun s -> opt_parser := Some s),
153
      "<format> Select input format (default: \"why\")";
154
155
  "--format", Arg.String (fun s -> opt_parser := Some s),
      " same as -F";
156
  "-t", Arg.Int (fun i -> opt_timelimit := Some i),
157
158
      "<sec> Set the prover's time limit (default=10, no limit=0)";
  "--timelimit", Arg.Int (fun i -> opt_timelimit := Some i),
159
160
      " same as -t";
  "-m", Arg.Int (fun i -> opt_memlimit := Some i),
161
162
      "<MiB> Set the prover's memory limit (default: no limit)";
  "--memlimit", Arg.Int (fun i -> opt_timelimit := Some i),
163
      " same as -m";
164
165
166
167
  "-a", Arg.String add_opt_trans,
      "<transformation> Apply a transformation to every task";
  "--apply-transform", Arg.String add_opt_trans,
      " same as -a";
168
  "-M", Arg.String add_opt_meta,
169
      "<meta_name>[=<string>] Add a meta to every task";
170
171
  "--meta", Arg.String add_opt_meta,
      " same as -M";
172
  "-D", Arg.String (fun s -> opt_driver := Some (s, [])),
173
      "<file> Specify a prover's driver (conflicts with -P)";
174
  "--driver", Arg.String (fun s -> opt_driver := Some (s, [])),
175
176
177
178
179
      " same as -D";
  "-o", Arg.String (fun s -> opt_output := Some s),
      "<dir> Print the selected goals to separate files in <dir>";
  "--output", Arg.String (fun s -> opt_output := Some s),
      " same as -o";
180
181
  "--realize", Arg.Set opt_realize,
      " Realize selected theories from the library";
182
183
184
185
  "-E", Arg.Set opt_extract,
      " Generate OCaml code for selected theories/modules from the library";
  "--extract", Arg.Set opt_extract,
      " same as -E";
186
  "--bisect", Arg.Set opt_bisect,
187
188
      " Reduce the set of needed axioms which prove a goal, \
        and output the resulting task";
189
  "--print-theory", Arg.Set opt_print_theory,
190
      " Print selected theories";
191
  "--print-namespace", Arg.Set opt_print_namespace,
192
      " Print namespaces of selected theories";
193
  "--list-transforms", Arg.Set opt_list_transforms,
194
      " List known transformations";
195
  "--list-printers", Arg.Set opt_list_printers,
196
      " List known printers";
197
  "--list-provers", Arg.Set opt_list_provers,
198
      " List known provers";
199
  "--list-formats", Arg.Set opt_list_formats,
200
      " List known input formats";
201
  "--list-metas", Arg.Set opt_list_metas,
202
      " List known metas";
203
  Debug.Opt.desc_debug_list;
204
205
  "--token-count", Arg.Set opt_token_count,
      " Only lexing, and give numbers of tokens in spec vs in program";
206
207
208
209
210
  Debug.Opt.desc_shortcut "parse_only" "--parse-only" " Stop after parsing";
  Debug.Opt.desc_shortcut
    "type_only" "--type-only" " Stop after type checking";
  Debug.Opt.desc_debug_all;
  Debug.Opt.desc_debug;
211
212
213
214
  "--print-libdir", Arg.Set opt_print_libdir,
      " Print location of binary components (plugins, etc)";
  "--print-datadir", Arg.Set opt_print_datadir,
      " Print location of non-binary data (theories, modules, etc)";
215
216
  "--version", Arg.Set opt_version,
      " Print version information" ]
Jean-Christophe Filliâtre's avatar
Jean-Christophe Filliâtre committed
217

218
let () = try
219
220
  Arg.parse option_list add_opt_file usage_msg;

221
222
223
224
  if !opt_version then begin
    printf "%s@." version_msg;
    exit 0
  end;
225
226
227
228
229
230
231
232
  if !opt_print_libdir then begin
    printf "%s@." Config.libdir;
    exit 0
  end;
  if !opt_print_datadir then begin
    printf "%s@." Config.datadir;
    exit 0
  end;
233

François Bobot's avatar
François Bobot committed
234
  (** Configuration *)
235
  let config = read_config !opt_config in
François Bobot's avatar
François Bobot committed
236
237
238
  let main = get_main config in
  Whyconf.load_plugins main;

239
240
  Debug.Opt.set_flags_selected ();

François Bobot's avatar
François Bobot committed
241
242
  (** listings*)

243
  let sort_pair (x,_) (y,_) = String.compare x y in
244
  let opt_list = ref false in
Andrei Paskevich's avatar
minor    
Andrei Paskevich committed
245
  if !opt_list_transforms then begin
246
247
    opt_list := true;
    printf "@[<hov 2>Known non-splitting transformations:@\n%a@]@\n@."
248
249
      (Pp.print_list Pp.newline2 Trans.print_trans_desc)
      (List.sort sort_pair (Trans.list_transforms ()));
250
    printf "@[<hov 2>Known splitting transformations:@\n%a@]@\n@."
251
252
      (Pp.print_list Pp.newline2 Trans.print_trans_desc)
      (List.sort sort_pair (Trans.list_transforms_l ()))
Andrei Paskevich's avatar
minor    
Andrei Paskevich committed
253
  end;
254
  if !opt_list_printers then begin
255
256
    opt_list := true;
    printf "@[<hov 2>Known printers:@\n%a@]@\n@."
257
258
      (Pp.print_list Pp.newline2 Printer.print_printer_desc)
      (List.sort sort_pair (Printer.list_printers ()))
259
260
  end;
  if !opt_list_formats then begin
261
    opt_list := true;
262
    let print1 fmt s = fprintf fmt "%S" s in
263
264
265
266
    let print fmt (p, l, f) =
      fprintf fmt "@[%s [%a]@\n  @[%a@]@]"
        p (Pp.print_list Pp.comma print1) l
        Pp.formatted f
267
    in
268
269
    printf "@[Known input formats:@\n  @[%a@]@]@."
      (Pp.print_list Pp.newline2 print)
270
271
      (List.sort Pervasives.compare (Env.list_formats ()))
  end;
272
  if !opt_list_provers then begin
273
    opt_list := true;
274
    let config = read_config !opt_config in
275
    let config = List.fold_left merge_config config !opt_extra in
276
277
    let print = Pp.print_iter2 Mprover.iter Pp.newline Pp.nothing
      print_prover Pp.nothing in
278
279
    let provers = get_provers config in
    printf "@[<hov 2>Known provers:@\n%a@]@." print provers
280
  end;
281
  if !opt_list_metas then begin
282
    opt_list := true;
283
    let print fmt m = fprintf fmt "@[<h 2>%s %s%a@\n@[<hov>%a@]@]"
284
285
      (let s = m.meta_name in
        if String.contains s ' ' then "\"" ^ s ^ "\"" else s)
François Bobot's avatar
François Bobot committed
286
      (if m.meta_excl then "(flag) " else "")
287
      (Pp.print_list Pp.space Pretty.print_meta_arg_type) m.meta_type
288
      Pp.formatted m.meta_desc
289
    in
290
291
    let cmp m1 m2 = Pervasives.compare m1.meta_name m2.meta_name in
    printf "@[<hov 2>Known metas:@\n%a@]@\n@."
292
      (Pp.print_list Pp.newline2 print) (List.sort cmp (Theory.list_metas ()))
293
  end;
294
  opt_list :=  Debug.Opt.option_list () || !opt_list;
295
  if !opt_list then exit 0;
296
297
298
299
300
301

  if Queue.is_empty opt_queue then begin
    Arg.usage option_list usage_msg;
    exit 1
  end;

302
303
304
  if !opt_prover <> None && !opt_driver <> None then begin
    eprintf "Options '-P'/'--prover' and \
      '-D'/'--driver' cannot be used together.@.";
305
306
    exit 1
  end;
307

308
309
310
311
312
313
314
  if !opt_output <> None
  && !opt_driver = None && !opt_prover = None && not !opt_extract then begin
    eprintf
      "Option '-o'/'--output' requires a prover, a driver, or option '-E'.@.";
    exit 1
  end;

315
316
  if !opt_prover = None then begin
    if !opt_timelimit <> None then begin
317
      eprintf "Option '-t'/'--timelimit' requires a prover.@.";
318
319
320
      exit 1
    end;
    if !opt_memlimit <> None then begin
321
      eprintf "Option '-m'/'--memlimit' requires a prover.@.";
322
323
      exit 1
    end;
324
325
326
327
    if !opt_bisect then begin
      eprintf "Option '--bisect' requires a prover.@.";
      exit 1
    end;
328
329
    if !opt_driver = None && not !opt_print_namespace then
      opt_print_theory := true
330
331
  end;

332
333
334
335
336
337
  if !opt_extract && !opt_output = None then begin
    eprintf
      "Option '-E'/'--extract' require a directory to output the result.@.";
    exit 1
  end;

338
339
340
341
342
  if !opt_bisect && !opt_output = None then begin
    eprintf "Option '--bisect' require a directory to output the result.@.";
    exit 1
  end;

343
344
345
  opt_loadpath := List.rev_append !opt_loadpath (Whyconf.loadpath main);
  if !opt_timelimit = None then opt_timelimit := Some (Whyconf.timelimit main);
  if !opt_memlimit  = None then opt_memlimit  := Some (Whyconf.memlimit main);
346
  begin match !opt_prover with
347
  | Some s ->
348
349
350
351
352
    let filter_prover = Whyconf.parse_filter_prover s in
    let prover = Whyconf.filter_one_prover config filter_prover in
    opt_command :=
      Some (String.concat " " (prover.command :: prover.extra_options));
    opt_driver := Some (prover.driver, prover.extra_drivers)
353
  | None ->
354
355
      ()
  end;
356
  let add_meta task (meta,s) =
357
    let meta = lookup_meta meta in
358
359
360
361
362
    let args = match s with
      | Some s -> [MAstr s]
      | None -> []
    in
    add_meta task meta args
363
  in
364
  opt_task := List.fold_left add_meta !opt_task !opt_metas
365

366
367
368
  with e when not (Debug.test_flag Debug.stack_trace) ->
    eprintf "%a@." Exn_printer.exn_printer e;
    exit 1
369
370

let timelimit = match !opt_timelimit with
371
372
373
  | None -> 10
  | Some i when i <= 0 -> 0
  | Some i -> i
374

375
let memlimit = match !opt_memlimit with
376
377
378
379
  | None -> 0
  | Some i when i <= 0 -> 0
  | Some i -> i

380
let print_th_namespace fmt th =
381
  Pretty.print_namespace fmt th.th_name.Ident.id_string th
Jean-Christophe Filliâtre's avatar
Jean-Christophe Filliâtre committed
382

383
let fname_printer = ref (Ident.create_ident_printer [])
MARCHE Claude's avatar
MARCHE Claude committed
384

385
386
387
let output_task drv fname _tname th task dir =
  let fname = Filename.basename fname in
  let fname =
388
    try Filename.chop_extension fname with _ -> fname in
389
390
391
392
393
394
395
396
397
398
399
400
401
  let tname = th.th_name.Ident.id_string in
  let dest = Driver.file_of_task drv fname tname task in
  (* Uniquify the filename before the extension if it exists*)
  let i = try String.rindex dest '.' with _ -> String.length dest in
  let name = Ident.string_unique !fname_printer (String.sub dest 0 i) in
  let ext = String.sub dest i (String.length dest - i) in
  let cout = open_out (Filename.concat dir (name ^ ext)) in
  Driver.print_task drv (formatter_of_out_channel cout) task;
  close_out cout

let output_task_prepared drv fname _tname th task dir =
  let fname = Filename.basename fname in
  let fname =
402
    try Filename.chop_extension fname with _ -> fname in
403
404
405
406
407
408
409
410
411
412
  let tname = th.th_name.Ident.id_string in
  let dest = Driver.file_of_task drv fname tname task in
  (* Uniquify the filename before the extension if it exists*)
  let i = try String.rindex dest '.' with _ -> String.length dest in
  let name = Ident.string_unique !fname_printer (String.sub dest 0 i) in
  let ext = String.sub dest i (String.length dest - i) in
  let cout = open_out (Filename.concat dir (name ^ ext)) in
  Driver.print_task_prepared drv (formatter_of_out_channel cout) task;
  close_out cout

413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
let output_theory drv fname _tname th task dir =
  let fname = Filename.basename fname in
  let fname =
    try Filename.chop_extension fname with _ -> fname in
  let dest = Driver.file_of_theory drv fname th in
  let file = Filename.concat dir dest in
  let old =
    if Sys.file_exists file then begin
      let backup = file ^ ".bak" in
      Sys.rename file backup;
      Some (open_in backup)
    end else None in
  let cout = open_out file in
  Driver.print_task ?old drv (formatter_of_out_channel cout) task;
  close_out cout
428

Andrei Paskevich's avatar
Andrei Paskevich committed
429
let do_task drv fname tname (th : Theory.theory) (task : Task.task) =
430
  match !opt_output, !opt_command with
431
432
433
    | Some dir, _ when !opt_realize ->
        output_theory drv fname tname th task dir
    | None, _ when !opt_realize ->
Andrei Paskevich's avatar
Andrei Paskevich committed
434
435
        eprintf "Output directory (-o) is required.@.";
        exit 1
436
    | Some dir, Some command when !opt_bisect ->
437
438
439
440
441
442
443
444
445
446
447
        let test task =
          let call = Driver.prove_task_prepared
            ~command ~timelimit ~memlimit drv task in
          let res = Call_provers.wait_on_call (call ()) () in
          printf "%s %s %s : %a@." fname tname
            (task_goal task).Decl.pr_name.Ident.id_string
            Call_provers.print_prover_result res;
          res.Call_provers.pr_answer = Call_provers.Valid in
        let prepared_task = Driver.prepare_task drv task in
        if not (test prepared_task)
        then printf "I can't bisect %s %s %s which is not valid@." fname tname
448
          (task_goal task).Decl.pr_name.Ident.id_string
449
450
451
        else
          let prepared_task = Task.bisect test prepared_task in
          output_task_prepared drv fname tname th prepared_task dir
452
    | None, Some command ->
Andrei Paskevich's avatar
Andrei Paskevich committed
453
454
        let call = Driver.prove_task ~command ~timelimit ~memlimit drv task in
        let res = Call_provers.wait_on_call (call ()) () in
455
        printf "%s %s %s : %a@." fname tname
456
          (task_goal task).Decl.pr_name.Ident.id_string
457
458
          Call_provers.print_prover_result res
    | None, None ->
459
        Driver.print_task drv std_formatter task
460
    | Some dir, _ -> output_task drv fname tname th task dir
461

462
let do_tasks env drv fname tname th task =
463
  let lookup acc t =
464
465
    (try Trans.singleton (Trans.lookup_transform t env) with
       Trans.UnknownTrans _ -> Trans.lookup_transform_l t env) :: acc
466
  in
467
  let trans = List.fold_left lookup [] !opt_trans in
468
  let apply tasks tr =
469
    List.rev (List.fold_left (fun acc task ->
470
      List.rev_append (Trans.apply tr task) acc) [] tasks)
471
  in
472
  let tasks = List.fold_left apply [task] trans in
Andrei Paskevich's avatar
Andrei Paskevich committed
473
  List.iter (do_task drv fname tname th) tasks
474

475
let do_theory env drv fname tname th glist =
476
477
478
479
  if !opt_print_theory then
    printf "%a@." Pretty.print_theory th
  else if !opt_print_namespace then
    printf "%a@." print_th_namespace th
480
481
482
483
484
485
486
487
488
489
490
491
  else if !opt_realize then
    if th.th_path = [] then begin
      eprintf "Theory %s is not from the library.@." tname;
      exit 1
    end else if not (Queue.is_empty glist) then begin
      eprintf "Cannot realize individual goals.@.";
      exit 1
    end else begin
      let drv = Util.of_option drv in
      let task = Task.use_export None th in
      do_tasks env drv fname tname th task
    end
492
  else begin
493
    let add acc (x,l) =
494
495
496
497
      let pr = try ns_find_pr th.th_export l with Not_found ->
        eprintf "Goal '%s' not found in theory '%s'.@." x tname;
        exit 1
      in
498
      Decl.Spr.add pr acc
499
500
    in
    let drv = Util.of_option drv in
501
502
503
504
    let prs = Queue.fold add Decl.Spr.empty glist in
    let sel = if Decl.Spr.is_empty prs then None else Some prs in
    let tasks = List.rev (split_theory th sel !opt_task) in
    List.iter (do_tasks env drv fname tname th) tasks
505
  end
MARCHE Claude's avatar
MARCHE Claude committed
506

507
let do_global_theory env drv (tname,p,t,glist) =
508
  let format = Util.def_option "why" !opt_parser in
Andrei Paskevich's avatar
Andrei Paskevich committed
509
  let th = try Env.read_theory ~format env p t with Env.TheoryNotFound _ ->
510
511
    eprintf "Theory '%s' not found.@." tname;
    exit 1
MARCHE Claude's avatar
MARCHE Claude committed
512
  in
513
  do_theory env drv "lib" tname th glist
MARCHE Claude's avatar
MARCHE Claude committed
514

515
let do_local_theory env drv fname m (tname,_,t,glist) =
516
  let th = try Mstr.find t m with Not_found ->
517
518
    eprintf "Theory '%s' not found in file '%s'.@." tname fname;
    exit 1
MARCHE Claude's avatar
MARCHE Claude committed
519
  in
520
  do_theory env drv fname tname th glist
521

522
523
(* program extraction *)

524
525
let extract_to ?fname th extract =
  let dir = match !opt_output with Some dir -> dir | None -> assert false in
526
  let _fname = match fname, th.th_path with
527
528
529
530
531
532
    | Some fname, _ ->
        let fname = Filename.basename fname in
        (try Filename.chop_extension fname with _ -> fname)
    | None, [] -> assert false
    | None, path -> List.hd (List.rev path)
  in
533
534
535
  (* FIXME: use fname to forge the OCaml filename *)
  let mname = (* fname ^ "__" ^ *) th.th_name.Ident.id_string ^ ".ml" in
  (* let mname = String.uncapitalize mname in *)
536
537
538
539
540
541
542
543
544
545
  let file = Filename.concat dir mname in
  let old =
    if Sys.file_exists file then begin
      let backup = file ^ ".bak" in
      Sys.rename file backup;
      Some (open_in backup)
    end else None in
  let cout = open_out file in
  extract file ?old (formatter_of_out_channel cout);
  close_out cout
546

547
548
549
550
let do_extract_theory env ?fname tname th =
  let extract fname ?old fmt = ignore (old);
    Debug.dprintf Mlw_ocaml.debug "extract theory %s to file %s@." tname fname;
    Mlw_ocaml.extract_theory env ?old fmt th
551
552
553
  in
  extract_to ?fname th extract

554
555
556
557
let do_extract_module env ?fname tname m =
  let extract fname ?old fmt = ignore (old);
    Debug.dprintf Mlw_ocaml.debug "extract module %s to file %s@." tname fname;
    Mlw_ocaml.extract_module env ?old fmt m
558
559
  in
  extract_to ?fname m.Mlw_module.mod_theory extract
560

561
let do_global_extract env (tname,p,t,_) =
562
563
564
565
566
  try
    let lib = Mlw_main.library_of_env env in
    let mm, thm = Env.read_lib_file lib p in
    try
      let m = Mstr.find t mm in
567
      do_extract_module env tname m
568
569
    with Not_found ->
      let th = Mstr.find t thm in
570
      do_extract_theory env tname th
571
572
573
  with Env.LibFileNotFound _ | Not_found -> try
    let format = Util.def_option "why" !opt_parser in
    let th = Env.read_theory ~format env p t in
574
    do_extract_theory env tname th
575
576
577
578
  with Env.LibFileNotFound _ | Env.TheoryNotFound _ ->
    eprintf "Theory/module '%s' not found.@." tname;
    exit 1

579
let do_extract_theory_from env fname m (tname,_,t,_) =
580
581
582
583
  let th = try Mstr.find t m with Not_found ->
    eprintf "Theory '%s' not found in file '%s'.@." tname fname;
    exit 1
  in
584
  do_extract_theory env ~fname tname th
585

586
let do_extract_module_from env fname mm thm (tname,_,t,_) =
587
  try
588
    let m = Mstr.find t mm in do_extract_module env ~fname tname m
589
  with Not_found -> try
590
    let th = Mstr.find t thm in do_extract_theory env ~fname tname th
591
592
593
594
595
596
597
598
599
  with Not_found ->
    eprintf "Theory/module '%s' not found in file '%s'.@." tname fname;
    exit 1

let do_local_extract env fname cin tlist =
  if !opt_parser = Some "whyml" || Filename.check_suffix fname ".mlw" then begin
    let lib = Mlw_main.library_of_env env in
    let mm, thm = Mlw_main.read_channel lib [] fname cin in
    if Queue.is_empty tlist then begin
600
      let do_m t m thm =
601
        do_extract_module env ~fname t m; Mstr.remove t thm in
602
      let thm = Mstr.fold do_m mm thm in
603
      Mstr.iter (fun t th -> do_extract_theory env ~fname t th) thm
604
605
606
607
608
609
    end else
      Queue.iter (do_extract_module_from env fname mm thm) tlist
  end else begin
    let m = Env.read_channel ?format:!opt_parser env fname cin in
    if Queue.is_empty tlist then
      let add_th t th mi = Ident.Mid.add th.th_name (t,th) mi in
610
      let do_th _ (t,th) = do_extract_theory env ~fname t th in
611
612
613
614
615
      Ident.Mid.iter do_th (Mstr.fold add_th m Ident.Mid.empty)
    else
      Queue.iter (do_extract_theory_from env fname m) tlist
  end

616
617
618
let total_annot_tokens = ref 0
let total_program_tokens = ref 0

619
let do_input env drv = function
620
  | None, _ when !opt_parse_only || !opt_type_only ->
621
      ()
622
623
  | None, tlist when !opt_extract ->
      Queue.iter (do_global_extract env) tlist
624
  | None, tlist ->
MARCHE Claude's avatar
MARCHE Claude committed
625
      Queue.iter (do_global_theory env drv) tlist
626
  | Some f, tlist ->
627
628
629
630
      let fname, cin = match f with
        | "-" -> "stdin", stdin
        | f   -> f, open_in f
      in
631
      if !opt_token_count then begin
632
633
634
        let lb = Lexing.from_channel cin in
        let a,p = Lexer.token_counter lb in
        close_in cin;
635
636
637
638
639
640
641
642
643
644
645
646
647
648
        if a = 0 then begin
          (* hack: we assume it is a why file and not a mlw *)
          total_annot_tokens := !total_annot_tokens + p;
          Format.printf "File %s: %d tokens@." f p;
        end else begin
          total_program_tokens := !total_program_tokens + p;
          total_annot_tokens := !total_annot_tokens + a;
          Format.printf "File %s: %d tokens in annotations@." f a;
          Format.printf "File %s: %d tokens in programs@." f p
        end
      end else if !opt_extract then begin
        do_local_extract env fname cin tlist;
        close_in cin
      end else begin
649
650
651
652
        let m = Env.read_channel ?format:!opt_parser env fname cin in
        close_in cin;
        if !opt_type_only then
          ()
MARCHE Claude's avatar
MARCHE Claude committed
653
        else
654
655
656
657
658
659
660
          if Queue.is_empty tlist then
            let glist = Queue.create () in
            let add_th t th mi = Ident.Mid.add th.th_name (t,th) mi in
            let do_th _ (t,th) = do_theory env drv fname t th glist in
            Ident.Mid.iter do_th (Mstr.fold add_th m Ident.Mid.empty)
          else
            Queue.iter (do_local_theory env drv fname m) tlist
661
      end
MARCHE Claude's avatar
MARCHE Claude committed
662

663
664
let () =
  try
665
    let env = Env.create_env !opt_loadpath in
666
667
    let drv =
      Util.option_map (fun (f,ef) -> load_driver env f ef) !opt_driver in
668
669
    Queue.iter (do_input env drv) opt_queue;
    if !opt_token_count then
Andrei Paskevich's avatar
Andrei Paskevich committed
670
      Format.printf "Total: %d annot/%d programs, ratio = %.3f@."
671
672
        !total_annot_tokens !total_program_tokens
        ((float !total_annot_tokens) /. (float !total_program_tokens))
673
  with e when not (Debug.test_flag Debug.stack_trace) ->
674
    eprintf "%a@." Exn_printer.exn_printer e;
675
    exit 1
676

677
(*
678
Local Variables:
MARCHE Claude's avatar
MARCHE Claude committed
679
compile-command: "unset LANG; make -C .. byte"
680
End:
681
*)