aboutsummaryrefslogtreecommitdiffhomepage
path: root/contrib/extraction/extract_env.ml
blob: cd6160b2ac4e80e76acf597d3f33c86c547eec89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
(***********************************************************************)
(*  v      *   The Coq Proof Assistant  /  The Coq Development Team    *)
(* <O___,, *        INRIA-Rocquencourt  &  LRI-CNRS-Orsay              *)
(*   \VV/  *************************************************************)
(*    //   *      This file is distributed under the terms of the      *)
(*         *       GNU Lesser General Public License Version 2.1       *)
(***********************************************************************)

(*i $Id$ i*)

open Pp
open Util
open Term
open Lib
open Extraction
open Miniml
open Mlutil

(*s Recursive computation of the global references to extract. 
    We use a set of functions visiting the extracted objects in
    a depth-first way ([visit_type], [visit_ast] and [visit_decl]).
    We maintain an (imperative) structure [extracted_env] containing
    the set of already visited references and the list of 
    references to extract. The entry point is the function [visit_reference]:
    it first normalizes the reference, and then check it has already been 
    visisted; if not, it adds it to the set of visited references, then
    recursively traverses its extraction and finally adds it to the 
    list of references to extract. *) 

type extracted_env = {
  mutable visited : Refset.t;
  mutable to_extract : global_reference list 
}

let empty () = { visited = ml_extractions (); to_extract = [] }

let rec visit_reference eenv r =
  let r' = match r with
    | ConstructRef ((sp,_),_) -> IndRef (sp,0)
    | IndRef (sp,i) -> if i = 0 then r else IndRef (sp,0)
    | _ -> r
  in
  if not (Refset.mem r' eenv.visited) then begin
    (* we put [r'] in [visited] first to avoid loops in inductive defs *)
    eenv.visited <- Refset.add r' eenv.visited;
    visit_decl eenv (extract_declaration r);
    eenv.to_extract <- r' :: eenv.to_extract
  end

and visit_type eenv t =
  let rec visit = function
    | Tglob r -> visit_reference eenv r
    | Tapp l -> List.iter visit l
    | Tarr (t1,t2) -> visit t1; visit t2
    | Tvar _ | Tprop | Tarity -> ()
  in
  visit t

and visit_ast eenv a =
  let rec visit = function
    | MLglob r -> visit_reference eenv r
    | MLapp (a,l) -> visit a; List.iter visit l
    | MLlam (_,a) -> visit a
    | MLletin (_,a,b) -> visit a; visit b
    | MLcons (r,l) -> visit_reference eenv r; List.iter visit l
    | MLcase (a,br) -> 
	visit a; Array.iter (fun (r,_,a) -> visit_reference eenv r; visit a) br
    | MLfix (_,_,l) -> Array.iter visit l
    | MLcast (a,t) -> visit a; visit_type eenv t
    | MLmagic a -> visit a
    | MLrel _ | MLprop | MLarity | MLexn _ -> ()
  in
  visit a

and visit_inductive eenv inds =
  let visit_constructor (_,tl) = List.iter (visit_type eenv) tl in
  let visit_ind (_,_,cl) = List.iter visit_constructor cl in
  List.iter visit_ind inds

and visit_decl eenv = function
  | Dtype (inds,_) ->
      visit_inductive eenv inds
  | Dabbrev (_,_,t) ->
      visit_type eenv t
  | Dglob (_,a) ->
      visit_ast eenv a

(*s Recursive extracted environment for a list of reference: we just
    iterate [visit_reference] on the list, starting with an empty
    extracted environment, and we return the reversed list of 
    references in the field [to_extract]. *)

let extract_env rl =
  let eenv = empty () in
  List.iter (visit_reference eenv) rl;
  List.rev eenv.to_extract

(*s Extraction in the Coq toplevel. We display the extracted term in
    Ocaml syntax and we use the Coq printers for globals. The
    vernacular command is \verb!Extraction! [term]. Whenever [term] is
    a global, its definition is displayed. *)

module ToplevelParams = struct
  let rename_global r = Names.id_of_string (Global.string_of_global r)
  let pp_type_global = Printer.pr_global
  let pp_global = Printer.pr_global
  let cofix_warning = false
end

module Pp = Ocaml.Make(ToplevelParams)

open Vernacinterp

let refs_set_of_list l = List.fold_right Refset.add l Refset.empty 

let decl_of_refs refs =
  let params = 
    { modular = false ; module_name = "" ; 
    optimization = true ; to_keep = refs_set_of_list refs;
    to_expand = Refset.empty } in
  let rl = List.map extract_declaration (extract_env refs) in
  optimize params rl

let print_user_extract r = 
  mSGNL [< 'sTR "User defined extraction:"; 'sPC; 'sTR (find_ml_extraction r) ; 'fNL>]

let extract_reference r =
  if is_ml_extraction r then
    print_user_extract r 
  else
    mSGNL (Pp.pp_decl (list_last (decl_of_refs [r])))

let _ = 
  vinterp_add "Extraction"
    (function 
       | [VARG_CONSTR ast] ->
	   (fun () -> 
	      let c = Astterm.interp_constr Evd.empty (Global.env()) ast in
	      match kind_of_term c with
		(* If it is a global reference, then output the declaration *)
		| IsConst (sp,_) -> extract_reference (ConstRef sp)
		| IsMutInd (ind,_) -> extract_reference (IndRef ind)
		| IsMutConstruct (cs,_) -> extract_reference (ConstructRef cs)
		(* Otherwise, output the ML type or expression *)
		| _ ->
		    match extract_constr (Global.env()) [] c with
		      | Emltype (t,_,_) -> mSGNL (Pp.pp_type t)
		      | Emlterm a -> mSGNL (Pp.pp_ast (normalize a)))
       | _ -> assert false)

(*s Recursive extraction in the Coq toplevel. The vernacular command is
    \verb!Recursive Extraction! [qualid1] ... [qualidn]. We use [extract_env]
    to get the saturated environment to extract. *)

let no_such_reference q =
  errorlabstrm "reference_of_varg" 
    [< Nametab.pr_qualid q; 'sTR ": no such reference" >]

let reference_of_varg = function
  | VARG_QUALID q -> 
      (try Nametab.locate q with Not_found -> no_such_reference q)
  | _ -> assert false

let refs_of_vargl = List.map reference_of_varg

let _ = 
  vinterp_add "ExtractionRec"
    (fun vl () ->
       let rl = refs_of_vargl vl in 
       let ml_rl = List.filter is_ml_extraction rl in
       let rl = List.filter (fun x -> not (is_ml_extraction x)) rl in 
       let dl = decl_of_refs rl in
       List.iter print_user_extract ml_rl ;
       List.iter (fun d -> mSGNL (Pp.pp_decl d)) dl)

(*s Extraction parameters. *)

let strict_language = function
  | "ocaml" -> true
  | "haskell" -> false
  | _ -> assert false

let interp_options lang keep modular m = function
  | [VARG_STRING "noopt"] ->
      { optimization = false; modular = modular; module_name = m;
	to_keep = refs_set_of_list keep; to_expand = Refset.empty }
  | [VARG_STRING "nooption"] ->
      { optimization = strict_language lang; 
	modular = modular; module_name = m;
	to_keep = refs_set_of_list keep; to_expand = Refset.empty }
  | VARG_STRING "expand" :: l ->
      { optimization = strict_language lang; 
	modular = modular; module_name = m;
	to_keep = refs_set_of_list keep; 
	to_expand = refs_set_of_list (refs_of_vargl l) }
  | _ -> 
      assert false

(*s Extraction to a file (necessarily recursive). 
    The vernacular command is \verb!Extraction "file"! [qualid1] ... [qualidn].
    We just call [extract_to_file] on the saturated environment. *)

let extract_to_file = function
  | "ocaml" -> Ocaml.extract_to_file 
  | "haskell" -> Haskell.extract_to_file
  | _ -> assert false

let _ = 
  vinterp_add "ExtractionFile"
    (function 
       | VARG_STRING lang :: VARG_VARGLIST o :: VARG_STRING f :: vl ->
	   (fun () -> 
	      let refs = refs_of_vargl vl in
	      let prm = interp_options lang refs false "" o in
	      let decls = decl_of_refs refs in 
	      let decls = optimize prm decls in
	      let ml_decls = 
		list_subtract 
		  (List.filter is_ml_extraction refs)
		  (fst (ml_cst_extractions ()))
	      in
	      extract_to_file lang f prm decls ml_decls)
       | _ -> assert false)

(*s Extraction of a module. The vernacular command is \verb!Extraction Module!
    [M]. We build the environment to extract by traversing the segment of
    module [M]. We just keep constants and inductives, and we remove
    those having an ML extraction. *)

let extract_module m =
  let m = Nametab.locate_loaded_library (Nametab.make_qualid [] m) in
  let seg = Library.module_segment (Some m) in
  let get_reference = function
    | sp, Leaf o ->
	(match Libobject.object_tag o with
	   | "CONSTANT" | "PARAMETER" -> ConstRef sp
	   | "INDUCTIVE" -> IndRef (sp,0)
	   | _ -> failwith "caught")
    | _ -> failwith "caught"
  in
  Util.map_succeed get_reference seg

let decl_mem rl = function 
  | Dglob (r,_) -> List.mem r rl 
  | Dabbrev (r,_,_) -> List.mem r rl 
  | Dtype ((_,r,_)::_, _) -> List.mem r rl
  | Dtype ([],_) -> false

let file_suffix = function
  | "ocaml" -> ".ml"
  | "haskell" -> ".hs"
  | _ -> assert false

let _ = 
  vinterp_add "ExtractionModule"
    (function 
       | [VARG_STRING lang; VARG_VARGLIST o; VARG_IDENTIFIER m] ->
	   (fun () -> 
	      Ocaml.current_module := Some m;
	      let ms = Names.string_of_id m in
	      let f = (String.uncapitalize ms) ^ (file_suffix lang) in
	      let prm = interp_options lang [] true ms o in
	      let rl = extract_module m in 
	      let decls = optimize prm (decl_of_refs rl) in
	      let decls = List.filter (decl_mem rl) decls in
	      extract_to_file lang f prm decls [])
       | _ -> assert false)