(************************************************************************) (* v * The Coq Proof Assistant / The Coq Development Team *) (* st | _ -> hov 2 (pp_par par (st ++ spc () ++ prlist_with_sep spc identity args)) let pr_binding = function | [] -> mt () | l -> str " " ++ prlist_with_sep (fun () -> str " ") pr_id l let fnl2 () = fnl () ++ fnl () let space_if = function true -> str " " | false -> mt () let sec_space_if = function true -> spc () | false -> mt () let is_digit = function | '0'..'9' -> true | _ -> false let begins_with_CoqXX s = let n = String.length s in n >= 4 && s.[0] = 'C' && s.[1] = 'o' && s.[2] = 'q' && let i = ref 3 in try while !i < n do if s.[!i] = '_' then i:=n (*Stop*) else if is_digit s.[!i] then incr i else raise Not_found done; true with Not_found -> false let unquote s = if lang () <> Scheme then s else let s = String.copy s in for i=0 to String.length s - 1 do if s.[i] = '\'' then s.[i] <- '~' done; s let rec dottify = function | [] -> assert false | [s] -> unquote s | s::[""] -> unquote s | s::l -> (dottify l)^"."^(unquote s) (*s Uppercase/lowercase renamings. *) let is_upper s = match s.[0] with 'A' .. 'Z' -> true | _ -> false let is_lower s = match s.[0] with 'a' .. 'z' | '_' -> true | _ -> false let lowercase_id id = id_of_string (String.uncapitalize (string_of_id id)) let uppercase_id id = id_of_string (String.capitalize (string_of_id id)) (* [pr_upper_id id] makes 2 String.copy lesser than [pr_id (uppercase_id id)] *) let pr_upper_id id = str (String.capitalize (string_of_id id)) (*s de Bruijn environments for programs *) type env = identifier list * Idset.t (*s Generic renaming issues for local variable names. *) let rec rename_id id avoid = if Idset.mem id avoid then rename_id (lift_ident id) avoid else id let rec rename_vars avoid = function | [] -> [], avoid | id :: idl when id == dummy_name -> (* we don't rename dummy binders *) let (idl', avoid') = rename_vars avoid idl in (id :: idl', avoid') | id :: idl -> let (idl, avoid) = rename_vars avoid idl in let id = rename_id (lowercase_id id) avoid in (id :: idl, Idset.add id avoid) let rename_tvars avoid l = let rec rename avoid = function | [] -> [],avoid | id :: idl -> let id = rename_id (lowercase_id id) avoid in let idl, avoid = rename (Idset.add id avoid) idl in (id :: idl, avoid) in fst (rename avoid l) let push_vars ids (db,avoid) = let ids',avoid' = rename_vars avoid ids in ids', (ids' @ db, avoid') let get_db_name n (db,_) = let id = List.nth db (pred n) in if id = dummy_name then id_of_string "__" else id (*S Renamings of global objects. *) (*s Tables of global renamings *) let keywords = ref Idset.empty let set_keywords kws = keywords := kws let global_ids = ref Idset.empty let add_global_ids s = global_ids := Idset.add s !global_ids let global_ids_list () = Idset.elements !global_ids let empty_env () = [], !global_ids let mktable () = let h = Hashtbl.create 97 in (Hashtbl.add h, Hashtbl.find h, fun () -> Hashtbl.clear h) let mkset () = let h = Hashtbl.create 97 in (fun x -> Hashtbl.add h x ()), (Hashtbl.mem h), (fun () -> Hashtbl.clear h) let mktriset () = let h = Hashtbl.create 97 in (fun x y z -> Hashtbl.add h (x,y,z) ()), (fun x y z -> Hashtbl.mem h (x,y,z)), (fun () -> Hashtbl.clear h) (* For each [global_reference], this table will contain the different parts of its renaming, in [string list] form. *) let add_renaming, get_renaming, clear_renaming = mktable () (* Idem for [module_path]. *) let add_mp_renaming, get_mp_renaming, clear_mp_renaming = mktable () (* A table for function modfstlev_rename *) let add_modfstlev, get_modfstlev, clear_modfstlev = mktable () (* A set of all external objects that will have to be fully qualified *) let add_static_clash, static_clash, clear_static_clash = mkset () (* Two tables of triplets [kind * module_path * string]. The first one will record the first level of all MPfile, not only the current one. The second table will contains local renamings. *) type kind = Term | Type | Cons | Mod let add_ext_mpmem, ext_mpmem, clear_ext_mpmem = mktriset () let add_loc_mpmem, loc_mpmem, clear_loc_mpmem = mktriset () (* The list of external modules that will be opened initially *) let add_mpfiles, mem_mpfiles, list_mpfiles, clear_mpfiles = let m = ref MPset.empty in (fun mp -> m:= MPset.add mp !m), (fun mp -> MPset.mem mp !m), (fun () -> MPset.elements !m), (fun () -> m:= MPset.empty) (*s table containing the visible horizon at a precise moment *) let visible = ref ([] : module_path list) let pop_visible () = visible := List.tl !visible let push_visible mp = visible := mp :: !visible let top_visible_mp () = List.hd !visible (*s substitutions for printing signatures *) let substs = ref empty_subst let add_subst msid mp = substs := add_msid msid mp !substs let subst_mp mp = subst_mp !substs mp let subst_kn kn = subst_kn !substs kn let subst_con c = fst (subst_con !substs c) let subst_ref = function | ConstRef con -> ConstRef (subst_con con) | IndRef (kn,i) -> IndRef (subst_kn kn,i) | ConstructRef ((kn,i),j) -> ConstructRef ((subst_kn kn,i),j) | _ -> assert false let duplicate_index = ref 0 let to_duplicate = ref Gmap.empty let add_duplicate mp l = incr duplicate_index; let ren = "Coq__" ^ string_of_int (!duplicate_index) in to_duplicate := Gmap.add (mp,l) ren !to_duplicate let check_duplicate mp l = let mp' = subst_mp mp in Gmap.find (mp',l) !to_duplicate type reset_kind = OnlyLocal | AllButExternal | Everything let reset_allbutext () = clear_loc_mpmem (); global_ids := !keywords; clear_renaming (); clear_mp_renaming (); clear_modfstlev (); clear_static_clash (); clear_mpfiles (); duplicate_index := 0; to_duplicate := Gmap.empty; visible := []; substs := empty_subst let reset_everything () = reset_allbutext (); clear_ext_mpmem () let reset_renaming_tables = function | OnlyLocal -> clear_loc_mpmem () | AllButExternal -> reset_allbutext () | Everything -> reset_everything () (*S Renaming functions *) (* This function creates from [id] a correct uppercase/lowercase identifier. This is done by adding a [Coq_] or [coq_] prefix. To avoid potential clashes with previous [Coq_id] variable, these prefixes are duplicated if already existing. *) let modular_rename up id = let s = string_of_id id in let prefix = if up then "Coq_" else "coq_" in let check = if up then is_upper else is_lower in if not (check s) || (Idset.mem id !keywords) || (String.length s >= 4 && String.sub s 0 4 = prefix) then prefix ^ s else s (*s [record_contents_fstlev] finds the names of the first-level objects exported by the ground-level modules in [struc]. *) let rec record_contents_fstlev struc = let upper_type = (lang () = Haskell) in let addtyp mp id = add_ext_mpmem Type mp (modular_rename upper_type id) in let addcons mp id = add_ext_mpmem Cons mp (modular_rename true id) in let addterm mp id = add_ext_mpmem Term mp (modular_rename false id) in let addmod mp id = add_ext_mpmem Mod mp (modular_rename true id) in let addfix mp r = add_ext_mpmem Term mp (modular_rename false (id_of_global r)) in let f mp = function | (l,SEdecl (Dind (_,ind))) -> Array.iter (fun ip -> addtyp mp ip.ip_typename; Array.iter (addcons mp) ip.ip_consnames) ind.ind_packets | (l,SEdecl (Dtype _)) -> addtyp mp (id_of_label l) | (l,SEdecl (Dterm _)) -> addterm mp (id_of_label l) | (l,SEdecl (Dfix (rv,_,_))) -> Array.iter (addfix mp) rv | (l,SEmodule _) -> addmod mp (id_of_label l) | (l,SEmodtype _) -> addmod mp (id_of_label l) in List.iter (fun (mp,sel) -> List.iter (f mp) sel) struc (*s For monolithic extraction, first-level modules might have to be renamed with unique numbers *) let modfstlev_rename l = let coqid = id_of_string "Coq" in let id = id_of_label l in try let coqset = get_modfstlev id in let nextcoq = next_ident_away coqid coqset in add_modfstlev id (nextcoq::coqset); (string_of_id nextcoq)^"_"^(string_of_id id) with Not_found -> let s = string_of_id id in if is_lower s || begins_with_CoqXX s then (add_modfstlev id [coqid]; "Coq_"^s) else (add_modfstlev id []; s) (*s Creating renaming for a [module_path] *) let rec mp_create_renaming mp = try get_mp_renaming mp with Not_found -> let ren = match mp with | _ when not (modular ()) && at_toplevel mp -> [""] | MPdot (mp,l) -> let lmp = mp_create_renaming mp in if lmp = [""] then (modfstlev_rename l)::lmp else (modular_rename true (id_of_label l))::lmp | MPself msid -> [modular_rename true (id_of_msid msid)] | MPbound mbid -> [modular_rename true (id_of_mbid mbid)] | MPfile _ when not (modular ()) -> assert false | MPfile _ -> [string_of_modfile mp] in add_mp_renaming mp ren; ren (* [clash mp0 s mpl] checks if [mp0-s] can be printed as [s] when [mpl] is the context of visible modules. More precisely, we check if there exists a [mp] in [mpl] that contains [s]. The verification stops if we encounter [mp=mp0]. *) let rec clash mem mp0 s = function | [] -> false | mp :: _ when mp = mp0 -> false | mp :: mpl -> mem mp s || clash mem mp0 s mpl (*s Initial renamings creation, for modular extraction. *) let create_modular_renamings struc = let current_module = fst (List.hd struc) in let { typ = ty ; trm = tr ; cons = co } = struct_get_references_set struc in (* 1) creates renamings of objects *) let add upper r = let mp = modpath_of_r r in let l = mp_create_renaming mp in let s = modular_rename upper (id_of_global r) in add_global_ids (id_of_string s); add_renaming r (s::l); begin try let mp = modfile_of_mp mp in if mp <> current_module then add_mpfiles mp with Not_found -> () end; in Refset.iter (add (lang () = Haskell)) ty; Refset.iter (add true) co; Refset.iter (add false) tr; (* 2) determines the opened libraries. *) let used_modules = list_mpfiles () in let used_modules' = List.rev used_modules in let str_list = List.map string_of_modfile used_modules' in let rec check_elsewhere mpl sl = match mpl, sl with | [], [] -> [] | mp::mpl, _::sl -> if List.exists (ext_mpmem Mod mp) sl then check_elsewhere mpl sl else mp :: (check_elsewhere mpl sl) | _ -> assert false in let opened_modules = check_elsewhere used_modules' str_list in clear_mpfiles (); List.iter add_mpfiles opened_modules; (* 3) determines the potential clashes *) let needs_qualify k r = let mp = modpath_of_r r in if (is_modfile mp) && mp <> current_module && (clash (ext_mpmem k) mp (List.hd (get_renaming r)) opened_modules) then add_static_clash r in Refset.iter (needs_qualify Type) ty; Refset.iter (needs_qualify Term) tr; Refset.iter (needs_qualify Cons) co; List.rev opened_modules (*s Initial renamings creation, for monolithic extraction. *) let create_mono_renamings struc = let { typ = ty ; trm = tr ; cons = co } = struct_get_references_list struc in let add upper r = let mp = modpath_of_r r in let l = mp_create_renaming mp in let mycase = if upper then uppercase_id else lowercase_id in let id = if l = [""] then next_ident_away (mycase (id_of_global r)) (global_ids_list ()) else id_of_string (modular_rename upper (id_of_global r)) in add_global_ids id; add_renaming r ((string_of_id id)::l) in List.iter (add (lang () = Haskell)) (List.rev ty); List.iter (add false) (List.rev tr); List.iter (add true) (List.rev co); [] let create_renamings struc = if modular () then create_modular_renamings struc else create_mono_renamings struc (*s On-the-fly qualification issues for both monolithic or modular extraction. *) let pp_global k r = let ls = get_renaming r in assert (List.length ls > 1); let s = List.hd ls in let mp = modpath_of_r r in if mp = top_visible_mp () then (* simpliest situation: definition of r (or use in the same context) *) (* we update the visible environment *) (add_loc_mpmem k mp s; unquote s) else match lang () with | Scheme -> unquote s (* no modular Scheme extraction... *) | Haskell -> (* for the moment we always qualify in modular Haskell *) if modular () then dottify ls else s | Ocaml -> try (* has [mp] something in common with one of [!visible] ? *) let prefix = common_prefix_from_list mp !visible in let delta = mp_length mp - mp_length prefix in let ls = list_firstn (delta+1) ls in (* Difficulty: in ocaml we cannot qualify more than [ls], but this (not-so-long) name can in fact be hidden. Solution: duplication of the _definition_ of r in a Coq__XXX module *) let s,ls' = list_sep_last ls in let k' = if ls' = [] then k else Mod in if clash (loc_mpmem k') prefix s !visible then let front = if ls' = [] then [s] else ls' in let l = get_nth_label delta r in try dottify (front @ [check_duplicate prefix l]) with Not_found -> add_duplicate prefix l; dottify ls else dottify ls with Not_found -> (* [mp] belongs to a closed module, not one of [!visible]. *) let base = base_mp mp in let base_s,ls1 = list_sep_last ls in let s,ls2 = list_sep_last ls1 in let k' = if ls2 = [] then k else Mod in if modular () && (mem_mpfiles base) && not (static_clash r) && (* k' = Mod can't clash in an opened module, see earlier check *) not (clash (loc_mpmem k') base s !visible) then (* Standard situation of an object in another file: *) (* Thanks to the "open" of this file we remove its name *) dottify ls1 else if clash (loc_mpmem Mod) base base_s !visible then error_module_clash base_s else dottify ls (* The next function is used only in Ocaml extraction...*) let pp_module mp = let ls = mp_create_renaming mp in if List.length ls = 1 then dottify ls else match mp with | MPdot (mp0,_) when mp0 = top_visible_mp () -> (* simpliest situation: definition of mp (or use in the same context) *) (* we update the visible environment *) let s = List.hd ls in add_loc_mpmem Mod mp0 s; s | _ -> try (* has [mp] something in common with one of those in [!visible] ? *) let prefix = common_prefix_from_list mp !visible in assert (mp <> prefix); (* no use of mp as whole module from itself *) let delta = mp_length mp - mp_length prefix in let ls = list_firstn delta ls in (* Difficulty: in ocaml we cannot qualify more than [ls], but this (not-so-long) name can in fact be hidden. Solution: duplication of the _definition_ of mp via a Coq__XXX module *) let s,ls' = list_sep_last ls in if clash (loc_mpmem Mod) prefix s !visible then let l = get_nth_label_mp delta mp in try dottify (ls' @ [check_duplicate prefix l]) with Not_found -> add_duplicate prefix l; dottify ls else dottify ls with Not_found -> (* [mp] belongs to a closed module, not one of [!visible]. *) let base = base_mp mp in let base_s,ls' = list_sep_last ls in let s = fst (list_sep_last ls) in if modular () && (mem_mpfiles base) && not (clash (loc_mpmem Mod) base s !visible) then dottify ls' else if clash (loc_mpmem Mod) base base_s !visible then error_module_clash base_s else dottify ls