]> matita.cs.unibo.it Git - helm.git/blob - helm/ocaml/cic_disambiguation/disambiguate.ml
added Trivial module with a disambiguate_term implementation which took
[helm.git] / helm / ocaml / cic_disambiguation / disambiguate.ml
1 (* Copyright (C) 2004, HELM Team.
2  * 
3  * This file is part of HELM, an Hypertextual, Electronic
4  * Library of Mathematics, developed at the Computer Science
5  * Department, University of Bologna, Italy.
6  * 
7  * HELM is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * 
12  * HELM is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with HELM; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20  * MA  02111-1307, USA.
21  * 
22  * For details, see the HELM World-Wide-Web page,
23  * http://helm.cs.unibo.it/
24  *)
25
26 open Printf
27
28 open DisambiguateTypes
29 open UriManager
30
31 exception No_choices of domain_item
32 exception NoWellTypedInterpretation
33
34   (** raised when an environment is not enough informative to decide *)
35 exception Try_again
36
37 let debug = false
38 let debug_print = if debug then prerr_endline else ignore
39
40 (*
41   (** print benchmark information *)
42 let benchmark = true
43 let max_refinements = ref 0       (* benchmarking is not thread safe *)
44 let actual_refinements = ref 0
45 let domain_size = ref 0
46 let choices_avg = ref 0.
47 *)
48
49 let descr_of_domain_item = function
50  | Id s -> s
51  | Symbol (s, _) -> s
52  | Num i -> string_of_int i
53
54 type test_result =
55   | Ok of Cic.term * Cic.metasenv
56   | Ko
57   | Uncertain
58
59 let refine metasenv context term ugraph =
60 (*   if benchmark then incr actual_refinements; *)
61   let metasenv, term = 
62     CicMkImplicit.expand_implicits metasenv [] context term in
63     debug_print (sprintf "TEST_INTERPRETATION: %s" (CicPp.ppterm term));
64     try
65       let term', _, metasenv',ugraph1 = 
66         CicRefine.type_of_aux' metasenv context term ugraph in
67         (Ok (term', metasenv')),ugraph1
68     with
69       | CicRefine.Uncertain _ ->
70           debug_print ("UNCERTAIN!!! " ^ CicPp.ppterm term) ;
71           Uncertain,ugraph
72       | CicRefine.RefineFailure msg ->
73           debug_print (sprintf "PRUNED!!!\nterm%s\nmessage:%s"
74             (CicPp.ppterm term) msg);
75           Ko,ugraph
76       | CicUnification.UnificationFailure s -> 
77          prerr_endline ("PASSADI QUI: " ^ s);
78            raise ( CicUnification.UnificationFailure s )
79
80 let resolve (env: environment) (item: domain_item) ?(num = "") ?(args = []) () =
81   try
82     snd (Environment.find item env) env num args
83   with Not_found -> 
84     failwith ("Domain item not found: " ^ 
85       (DisambiguateTypes.string_of_domain_item item))
86
87   (* TODO move it to Cic *)
88 let find_in_environment name context =
89   let rec aux acc = function
90     | [] -> raise Not_found
91     | Cic.Name hd :: tl when hd = name -> acc
92     | _ :: tl ->  aux (acc + 1) tl
93   in
94   aux 1 context
95
96 let interpretate ~context ~env ast =
97   let rec aux loc context = function
98     | CicAst.AttributedTerm (`Loc loc, term) ->
99         aux loc context term
100     | CicAst.AttributedTerm (_, term) -> aux loc context term
101     | CicAst.Appl (CicAst.Symbol (symb, i) :: args) ->
102         let cic_args = List.map (aux loc context) args in
103         resolve env (Symbol (symb, i)) ~args:cic_args ()
104     | CicAst.Appl terms -> Cic.Appl (List.map (aux loc context) terms)
105     | CicAst.Binder (binder_kind, (var, typ), body) ->
106         let cic_type = aux_option loc context typ in
107         let cic_body = aux loc (var :: context) body in
108         (match binder_kind with
109         | `Lambda -> Cic.Lambda (var, cic_type, cic_body)
110         | `Pi | `Forall -> Cic.Prod (var, cic_type, cic_body)
111         | `Exists ->
112             resolve env (Symbol ("exists", 0))
113               ~args:[ cic_type; Cic.Lambda (var, cic_type, cic_body) ] ())
114     | CicAst.Case (term, indty_ident, outtype, branches) ->
115         let cic_term = aux loc context term in
116         let cic_outtype = aux_option loc context outtype in
117         let do_branch ((head, args), term) =
118           let rec do_branch' context = function
119             | [] -> aux loc context term
120             | (name, typ) :: tl ->
121                 let cic_body = do_branch' (name :: context) tl in
122                 let typ =
123                   match typ with
124                   | None -> Cic.Implicit (Some `Type)
125                   | Some typ -> aux loc context typ
126                 in
127                 Cic.Lambda (name, typ, cic_body)
128           in
129           do_branch' context args
130         in
131         let (indtype_uri, indtype_no) =
132           match indty_ident with
133           | Some indty_ident ->
134               (match resolve env (Id indty_ident) () with
135               | Cic.MutInd (uri, tyno, _) -> (uri, tyno)
136               | Cic.Implicit _ -> raise Try_again
137               | _ -> raise DisambiguateChoices.Invalid_choice)
138           | None ->
139               let fst_constructor =
140                 match branches with
141                 | ((head, _), _) :: _ -> head
142                 | [] -> raise DisambiguateChoices.Invalid_choice
143               in
144               (match resolve env (Id fst_constructor) () with
145               | Cic.MutConstruct (indtype_uri, indtype_no, _, _) ->
146                   (indtype_uri, indtype_no)
147               | Cic.Implicit _ -> raise Try_again
148               | _ -> raise DisambiguateChoices.Invalid_choice)
149         in
150         Cic.MutCase (indtype_uri, indtype_no, cic_outtype, cic_term,
151           (List.map do_branch branches))
152     | CicAst.LetIn ((name, typ), def, body) ->
153         let cic_def = aux loc context def in
154         let cic_def =
155           match typ with
156           | None -> cic_def
157           | Some t -> Cic.Cast (cic_def, aux loc context t)
158         in
159         let cic_body = aux loc (name :: context) body in
160         Cic.LetIn (name, cic_def, cic_body)
161     | CicAst.LetRec (kind, defs, body) ->
162         let context' =
163           List.fold_left (fun acc ((name, _), _, _) -> name :: acc)
164             context defs
165         in
166         let cic_body = aux loc context' body in
167         let inductiveFuns =
168           List.map
169             (fun ((name, typ), body, decr_idx) ->
170               let cic_body = aux loc context' body in
171               let cic_type = aux_option loc context typ in
172               let name =
173                 match name with
174                 | Cic.Anonymous ->
175                     CicTextualParser2.fail loc
176                       "Recursive functions cannot be anonymous"
177                 | Cic.Name name -> name
178               in
179               (name, decr_idx, cic_type, cic_body))
180             defs
181         in
182         let counter = ref ~-1 in
183         let build_term funs =
184           (* this is the body of the fold_right function below. Rationale: Fix
185            * and CoFix cases differs only in an additional index in the
186            * inductiveFun list, see Cic.term *)
187           match kind with
188           | `Inductive ->
189               (fun (var, _, _, _) cic ->
190                 incr counter;
191                 Cic.LetIn (Cic.Name var, Cic.Fix (!counter, funs), cic))
192           | `CoInductive ->
193               let funs =
194                 List.map (fun (name, _, typ, body) -> (name, typ, body)) funs
195               in
196               (fun (var, _, _, _) cic ->
197                 incr counter;
198                 Cic.LetIn (Cic.Name var, Cic.CoFix (!counter, funs), cic))
199         in
200         List.fold_right (build_term inductiveFuns) inductiveFuns cic_body
201     | CicAst.Ident (name, subst)
202     | CicAst.Uri (name, subst) as ast ->
203         let is_uri = function CicAst.Uri _ -> true | _ -> false in
204         (try
205           if is_uri ast then raise Not_found;(* don't search the env for URIs *)
206           let index = find_in_environment name context in
207           if subst <> None then
208             CicTextualParser2.fail loc
209               "Explicit substitutions not allowed here";
210           Cic.Rel index
211         with Not_found ->
212           let cic =
213             if is_uri ast then  (* we have the URI, build the term out of it *)
214               try
215                 CicUtil.term_of_uri name
216               with UriManager.IllFormedUri _ ->
217                 CicTextualParser2.fail loc "Ill formed URI"
218             else
219               resolve env (Id name) ()
220           in
221           let mk_subst uris =
222             let ids_to_uris =
223               List.map (fun uri -> UriManager.name_of_uri uri, uri) uris
224             in
225             (match subst with
226             | Some subst ->
227                 List.map
228                   (fun (s, term) ->
229                     (try
230                       List.assoc s ids_to_uris, aux loc context term
231                      with Not_found ->
232                        raise DisambiguateChoices.Invalid_choice))
233                   subst
234             | None -> List.map (fun uri -> uri, Cic.Implicit None) uris)
235           in
236           (try 
237             match cic with
238             | Cic.Const (uri, []) ->
239                 let o,_ = CicEnvironment.get_obj CicUniv.empty_ugraph uri in
240                 let uris = CicUtil.params_of_obj o in
241                 Cic.Const (uri, mk_subst uris)
242             | Cic.Var (uri, []) ->
243                 let o,_ = CicEnvironment.get_obj CicUniv.empty_ugraph uri in
244                 let uris = CicUtil.params_of_obj o in
245                 Cic.Var (uri, mk_subst uris)
246             | Cic.MutInd (uri, i, []) ->
247                 let o,_ = CicEnvironment.get_obj CicUniv.empty_ugraph uri in
248                 let uris = CicUtil.params_of_obj o in
249                 Cic.MutInd (uri, i, mk_subst uris)
250             | Cic.MutConstruct (uri, i, j, []) ->
251                 let o,_ = CicEnvironment.get_obj CicUniv.empty_ugraph uri in
252                 let uris = CicUtil.params_of_obj o in
253                 Cic.MutConstruct (uri, i, j, mk_subst uris)
254             | Cic.Meta _ | Cic.Implicit _ as t ->
255 (*
256                 prerr_endline (sprintf
257                   "Warning: %s must be instantiated with _[%s] but we do not enforce it"
258                   (CicPp.ppterm t)
259                   (String.concat "; "
260                     (List.map
261                       (fun (s, term) -> s ^ " := " ^ CicAstPp.pp_term term)
262                       subst)));
263 *)
264                 t
265             | _ ->
266               raise DisambiguateChoices.Invalid_choice
267            with 
268              CicEnvironment.CircularDependency _ -> 
269                raise DisambiguateChoices.Invalid_choice))
270     | CicAst.Implicit -> Cic.Implicit None
271     | CicAst.Num (num, i) -> resolve env (Num i) ~num ()
272     | CicAst.Meta (index, subst) ->
273         let cic_subst =
274           List.map
275             (function None -> None | Some term -> Some (aux loc context term))
276             subst
277         in
278         Cic.Meta (index, cic_subst)
279     | CicAst.Sort `Prop -> Cic.Sort Cic.Prop
280     | CicAst.Sort `Set -> Cic.Sort Cic.Set
281     | CicAst.Sort `Type -> Cic.Sort (Cic.Type (CicUniv.fresh())) (* TASSI *)
282     | CicAst.Sort `CProp -> Cic.Sort Cic.CProp
283     | CicAst.Symbol (symbol, instance) ->
284         resolve env (Symbol (symbol, instance)) ()
285     | CicAst.UserInput -> assert false
286   and aux_option loc context = function
287     | None -> Cic.Implicit (Some `Type)
288     | Some term -> aux loc context term
289   in
290   match ast with
291   | CicAst.AttributedTerm (`Loc loc, term) -> aux loc context term
292   | term -> aux CicAst.dummy_floc context term
293
294 let domain_of_term ~context ast =
295     (* "aux" keeps domain in reverse order and doesn't care about duplicates.
296      * Domain item more in deep in the list will be processed first.
297      *)
298   let rec aux loc context = function
299     | CicAst.AttributedTerm (`Loc loc, term) -> aux loc context term
300     | CicAst.AttributedTerm (_, term) -> aux loc context term
301     | CicAst.Appl terms ->
302         List.fold_left (fun dom term -> aux loc context term @ dom) [] terms
303     | CicAst.Binder (kind, (var, typ), body) ->
304         let kind_dom =
305           match kind with
306           | `Exists -> [ Symbol ("exists", 0) ]
307           | _ -> []
308         in
309         let type_dom = aux_option loc context typ in
310         let body_dom = aux loc (var :: context) body in
311         body_dom @ type_dom @ kind_dom
312     | CicAst.Case (term, indty_ident, outtype, branches) ->
313         let term_dom = aux loc context term in
314         let outtype_dom = aux_option loc context outtype in
315         let do_branch ((head, args), term) =
316           let (term_context, args_domain) =
317             List.fold_left
318               (fun (cont, dom) (name, typ) ->
319                 (name :: cont,
320                  (match typ with
321                  | None -> dom
322                  | Some typ -> aux loc cont typ @ dom)))
323               (context, []) args
324           in
325           args_domain @ aux loc term_context term
326         in
327         let branches_dom =
328           List.fold_left (fun dom branch -> do_branch branch @ dom) [] branches
329         in
330         branches_dom @ outtype_dom @ term_dom @
331         (match indty_ident with None -> [] | Some ident -> [ Id ident ])
332     | CicAst.LetIn ((var, typ), body, where) ->
333         let body_dom = aux loc context body in
334         let type_dom = aux_option loc context typ in
335         let where_dom = aux loc (var :: context) where in
336         where_dom @ type_dom @ body_dom
337     | CicAst.LetRec (kind, defs, where) ->
338         let context' =
339           List.fold_left (fun acc ((var, typ), _, _) -> var :: acc)
340             context defs
341         in
342         let where_dom = aux loc context' where in
343         let defs_dom =
344           List.fold_left
345             (fun dom ((_, typ), body, _) ->
346               aux loc context' body @ aux_option loc context typ)
347             [] defs
348         in
349         where_dom @ defs_dom
350     | CicAst.Ident (name, subst) ->
351         (try
352           let index = find_in_environment name context in
353           if subst <> None then
354             CicTextualParser2.fail loc
355               "Explicit substitutions not allowed here"
356           else
357             []
358         with Not_found ->
359           (match subst with
360           | None -> [Id name]
361           | Some subst ->
362               List.fold_left
363                 (fun dom (_, term) ->
364                   let dom' = aux loc context term in
365                   dom' @ dom)
366                 [Id name] subst))
367     | CicAst.Uri _ -> []
368     | CicAst.Implicit -> []
369     | CicAst.Num (num, i) -> [ Num i ]
370     | CicAst.Meta (index, local_context) ->
371         List.fold_left (fun dom term -> aux_option loc context term @ dom) []
372           local_context
373     | CicAst.Sort _ -> []
374     | CicAst.Symbol (symbol, instance) -> [ Symbol (symbol, instance) ]
375     | CicAst.UserInput -> assert false
376
377   and aux_option loc context = function
378     | None -> []
379     | Some t -> aux loc context t
380   in
381
382     (* e.g. [5;1;1;1;2;3;4;1;2] -> [2;1;4;3;5] *)
383   let rev_uniq =
384     let module SortedItem =
385       struct
386         type t = DisambiguateTypes.domain_item
387         let compare = Pervasives.compare
388       end
389     in
390     let module Set = Set.Make (SortedItem) in
391     fun l ->
392       let rev_l = List.rev l in
393       let (_, uniq_rev_l) =
394         List.fold_left
395           (fun (members, rev_l) elt ->
396             if Set.mem elt members then
397               (members, rev_l)
398             else
399               Set.add elt members, elt :: rev_l)
400           (Set.empty, []) rev_l
401       in
402       List.rev uniq_rev_l
403   in
404             
405   rev_uniq
406     (match ast with
407     | CicAst.AttributedTerm (`Loc loc, term) -> aux loc context term
408     | term -> aux CicAst.dummy_floc context term)
409
410
411   (* dom1 \ dom2 *)
412 let domain_diff dom1 dom2 =
413 (* let domain_diff = Domain.diff *)
414   let is_in_dom2 =
415     List.fold_left (fun pred elt -> (fun elt' -> elt' = elt || pred elt'))
416       (fun _ -> false) dom2
417   in
418   List.filter (fun elt -> not (is_in_dom2 elt)) dom1
419
420 module Make (C: Callbacks) =
421   struct
422     let choices_of_id dbd id =
423       let uris = MetadataQuery.locate ~dbd id in
424       let uris =
425        match uris with
426         | [] ->
427            [UriManager.string_of_uri (C.input_or_locate_uri
428             ~title:("URI matching \"" ^ id ^ "\" unknown.") ~id ())]
429         | [uri] -> [uri]
430         | _ ->
431             C.interactive_user_uri_choice ~selection_mode:`MULTIPLE
432              ~ok:"Try selected." ~enable_button_for_non_vars:true
433              ~title:"Ambiguous input." ~id
434              ~msg: ("Ambiguous input \"" ^ id ^
435                 "\". Please, choose one or more interpretations:")
436              uris
437       in
438       List.map
439         (fun uri ->
440           (uri,
441            let term =
442              try
443                CicUtil.term_of_uri uri
444              with exn ->
445                prerr_endline uri;
446                prerr_endline (Printexc.to_string exn);
447                assert false
448             in
449            fun _ _ _ -> term))
450         uris
451
452     let disambiguate_term ~(dbd:Mysql.dbd) context metasenv term
453       ?(initial_ugraph = CicUniv.empty_ugraph) ~aliases:current_env
454     =
455       debug_print "NEW DISAMBIGUATE INPUT";
456       let disambiguate_context =  (* cic context -> disambiguate context *)
457         List.map
458           (function None -> Cic.Anonymous | Some (name, _) -> name)
459           context
460       in
461       let term_dom = domain_of_term ~context:disambiguate_context term in
462       debug_print (sprintf "DISAMBIGUATION DOMAIN: %s"
463         (string_of_domain term_dom));
464       let current_dom =
465         Environment.fold (fun item _ dom -> item :: dom) current_env []
466       in
467       let todo_dom = domain_diff term_dom current_dom in
468       (* (2) lookup function for any item (Id/Symbol/Num) *)
469       let lookup_choices =
470         let id_choices = Hashtbl.create 1023 in
471         fun item ->
472         let choices =
473           match item with
474           | Id id ->
475               (try
476                 Hashtbl.find id_choices id
477               with Not_found ->
478                 let choices = choices_of_id dbd id in
479                 Hashtbl.add id_choices id choices;
480                 choices)
481           | Symbol (symb, _) -> DisambiguateChoices.lookup_symbol_choices symb
482           | Num instance -> DisambiguateChoices.lookup_num_choices ()
483         in
484         if choices = [] then raise (No_choices item);
485         choices
486       in
487
488 (*
489       (* <benchmark> *)
490       let _ =
491         if benchmark then begin
492           let per_item_choices =
493             List.map
494               (fun dom_item ->
495                 try
496                   let len = List.length (lookup_choices dom_item) in
497                   prerr_endline (sprintf "BENCHMARK %s: %d"
498                     (string_of_domain_item dom_item) len);
499                   len
500                 with No_choices _ -> 0)
501               term_dom
502           in
503           max_refinements := List.fold_left ( * ) 1 per_item_choices;
504           actual_refinements := 0;
505           domain_size := List.length term_dom;
506           choices_avg :=
507             (float_of_int !max_refinements) ** (1. /. float_of_int !domain_size)
508         end
509       in
510       (* </benchmark> *)
511 *)
512
513       (* (3) test an interpretation filling with meta uninterpreted identifiers
514        *)
515       let test_env current_env todo_dom ugraph = 
516         let filled_env =
517           List.fold_left
518             (fun env item ->
519                Environment.add item
520                ("Implicit",
521                  (match item with
522                     | Id _ | Num _ -> (fun _ _ _ -> Cic.Implicit (Some `Closed))
523                     | Symbol _ -> (fun _ _ _ -> Cic.Implicit None))) env)
524             current_env todo_dom 
525         in
526         try
527           let cic_term =
528             interpretate ~context:disambiguate_context ~env:filled_env term
529           in
530           let k,ugraph1 = refine metasenv context cic_term ugraph in
531             (k , ugraph1 )
532         with
533         | Try_again -> Uncertain,ugraph
534         | DisambiguateChoices.Invalid_choice -> Ko,ugraph
535       in
536       (* (4) build all possible interpretations *)
537       let rec aux current_env todo_dom base_univ =
538         match todo_dom with
539         | [] ->
540             (match test_env current_env [] base_univ with
541             | Ok (term, metasenv),new_univ -> 
542                                [ current_env, metasenv, term, new_univ ]
543             | Ko,_ | Uncertain,_ -> [])
544         | item :: remaining_dom ->
545             debug_print (sprintf "CHOOSED ITEM: %s"
546              (string_of_domain_item item));
547             let choices = lookup_choices item in
548             let rec filter univ = function 
549               | [] -> []
550               | codomain_item :: tl ->
551                   debug_print (sprintf "%s CHOSEN" (fst codomain_item)) ;
552                   let new_env =
553                     Environment.add item codomain_item current_env
554                   in
555                   (match test_env new_env remaining_dom univ with
556                   | Ok (term, metasenv),new_univ ->
557                       (match remaining_dom with
558                       | [] -> [ new_env, metasenv, term, new_univ ]
559                       | _ -> aux new_env remaining_dom new_univ )@ 
560                         filter univ tl
561                   | Uncertain,new_univ ->
562                       (match remaining_dom with
563                       | [] -> []
564                       | _ -> aux new_env remaining_dom new_univ )@ 
565                         filter univ tl
566                   | Ko,_ -> filter univ tl)
567             in
568             filter base_univ choices 
569       in
570       let base_univ = initial_ugraph in
571       try
572         let res =
573          match aux current_env todo_dom base_univ with
574          | [] -> raise NoWellTypedInterpretation
575          | [ e,me,t,u ] as l ->
576              debug_print "UNA SOLA SCELTA";
577              [ e,me,t,u]
578          | l ->
579              debug_print (sprintf "PIU' SCELTE (%d)" (List.length l));
580              let choices =
581                List.map
582                  (fun (env, _, _, _) ->
583                    List.map
584                      (fun domain_item ->
585                        let description =
586                          fst (Environment.find domain_item env)
587                        in
588                        (descr_of_domain_item domain_item, description))
589                      term_dom)
590                  l
591              in
592              let choosed = C.interactive_interpretation_choice choices in
593              List.map (List.nth l) choosed
594         in
595 (*
596         (if benchmark then
597           let res_size = List.length res in
598           prerr_endline (sprintf
599             ("BENCHMARK: %d/%d refinements performed, domain size %d, interps %d, k %.2f\n" ^^
600             "BENCHMARK:   estimated %.2f")
601             !actual_refinements !max_refinements !domain_size res_size
602             !choices_avg
603             (float_of_int (!domain_size - 1) *. !choices_avg *. (float_of_int res_size) +. !choices_avg)));
604 *)
605         res
606      with
607       CicEnvironment.CircularDependency s -> 
608         failwith "Disambiguate: circular dependency"
609   end
610
611 module Trivial =
612 struct
613   exception Ambiguous_term of string
614   exception Exit
615   module Callbacks =
616   struct
617     let interactive_user_uri_choice ~selection_mode ?ok
618           ?(enable_button_for_non_vars = true) ~title ~msg ~id uris =
619               raise Exit
620     let interactive_interpretation_choice interp = raise Exit
621     let input_or_locate_uri ~(title:string) ?id = raise Exit
622   end
623   module Disambiguator = Make (Callbacks)
624   let disambiguate_string ~dbd context metasenv ?initial_ugraph
625         ?(aliases = DisambiguateTypes.Environment.empty) term =
626     let ast = CicTextualParser2.parse_term (Stream.of_string term) in
627     try
628       Disambiguator.disambiguate_term ~dbd context metasenv ast ?initial_ugraph
629         ~aliases
630     with Exit -> raise (Ambiguous_term term)
631 end
632