(* Copyright (C) 2004-2005, HELM Team. * * This file is part of HELM, an Hypertextual, Electronic * Library of Mathematics, developed at the Computer Science * Department, University of Bologna, Italy. * * HELM is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * HELM 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HELM; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * * For details, see the HELM World-Wide-Web page, * http://helm.cs.unibo.it/ *) open Printf type pattern_id = int type interpretation_id = pattern_id type pretty_printer_id = pattern_id type term_info = { sort: (Cic.id, CicNotationPt.sort_kind) Hashtbl.t; uri: (Cic.id, string) Hashtbl.t; } exception No_match module OrderedInt = struct type t = int let compare (x1:t) (x2:t) = Pervasives.compare x2 x1 (* reverse order *) end module IntSet = Set.Make (OrderedInt) let int_set_of_int_list l = List.fold_left (fun acc i -> IntSet.add i acc) IntSet.empty l let warning s = prerr_endline ("CicNotation WARNING: " ^ s) module type PATTERN = sig type pattern_t val compatible : pattern_t -> pattern_t -> bool end module Patterns (P: PATTERN) = struct type row_t = P.pattern_t list * pattern_id type t = row_t list let empty = [] let first_column t = List.map (fun (patterns, _) -> List.hd patterns) t let pattern_ids t = List.map snd t let partition t pidl = let partitions = Hashtbl.create 11 in let add pid row = Hashtbl.add partitions pid row in (try List.iter2 add pidl t with Invalid_argument _ -> assert false); let pidset = int_set_of_int_list pidl in IntSet.fold (fun pid acc -> match Hashtbl.find_all partitions pid with | [] -> acc | patterns -> (pid, List.rev patterns) :: acc) pidset [] let are_empty t = fst (List.hd t) = [] (* if first row has an empty list of patterns, then others will as well *) (* return 2 lists of rows, first one containing homogeneous rows according * to "compatible" below *) let horizontal_split t = let ap, first_row, t' = match t with | [] -> assert false | ([], _) :: _ -> assert false (* are_empty should have been invoked in advance *) | ((hd :: _ , _) as row) :: tl -> hd, row, tl in let rec aux prev_t = function | [] -> List.rev prev_t, [] | ([], _) :: _ -> assert false | (((hd :: _), _) as row) :: tl when P.compatible ap hd -> aux (row :: prev_t) tl | t -> List.rev prev_t, t in aux [first_row] t' (* return 2 lists, first one representing first column, second one * representing rows stripped of the first element *) let vertical_split t = let l = List.map (function | (hd :: tl, pid) -> hd, (tl, pid) | _ -> assert false) t in List.split l end module Patterns21 = Patterns (CicNotationTag) module Patterns32 = struct type row_t = CicNotationPt.cic_appl_pattern list * pattern_id type t = row_t list let empty = [] let first_column t = List.map (fun (patterns, _) -> List.hd patterns) t let pattern_ids t = List.map snd t let partition t pidl = let partitions = Hashtbl.create 11 in let add pid row = Hashtbl.add partitions pid row in (try List.iter2 add pidl t with Invalid_argument _ -> assert false); let pidset = int_set_of_int_list pidl in IntSet.fold (fun pid acc -> match Hashtbl.find_all partitions pid with | [] -> acc | patterns -> (pid, List.rev patterns) :: acc) pidset [] let are_empty t = fst (List.hd t) = [] (* if first row has an empty list of patterns, then others will as well *) (* return 2 lists of rows, first one containing homogeneous rows according * to "compatible" below *) let horizontal_split t = let compatible ap1 ap2 = match ap1, ap2 with | CicNotationPt.UriPattern _, CicNotationPt.UriPattern _ | CicNotationPt.ArgPattern _, CicNotationPt.ArgPattern _ | CicNotationPt.ApplPattern _, CicNotationPt.ApplPattern _ -> true | _ -> false in let ap = match t with | [] -> assert false | ([], _) :: _ -> assert false (* are_empty should have been invoked in advance *) | (hd :: _ , _) :: _ -> hd in let rec aux prev_t = function | [] -> List.rev prev_t, [] | ([], _) :: _ -> assert false | (((hd :: _), _) as row) :: tl when compatible ap hd -> aux (row :: prev_t) tl | t -> List.rev prev_t, t in aux [] t (* return 2 lists, first one representing first column, second one * representing rows stripped of the first element *) let vertical_split t = let l = List.map (function | (hd :: tl, pid) -> hd, (tl, pid) | _ -> assert false) t in List.split l end (* acic -> ast auxiliary function s *) let get_types uri = let o,_ = CicEnvironment.get_obj CicUniv.empty_ugraph uri in match o with | Cic.InductiveDefinition (l,_,_,_) -> l | _ -> assert false let name_of_inductive_type uri i = let types = get_types uri in let (name, _, _, _) = try List.nth types i with Not_found -> assert false in name (* returns pairs *) let constructors_of_inductive_type uri i = let types = get_types uri in let (_, _, _, constructors) = try List.nth types i with Not_found -> assert false in constructors (* returns name only *) let constructor_of_inductive_type uri i j = (try fst (List.nth (constructors_of_inductive_type uri i) (j-1)) with Not_found -> assert false) module Ast = CicNotationPt let string_of_name = function | Cic.Name s -> s | Cic.Anonymous -> "_" let ident_of_name n = Ast.Ident (string_of_name n, None) let idref id t = Ast.AttributedTerm (`IdRef id, t) let pp_ast0 t k = prerr_endline "pp_ast0"; let rec aux t = CicNotationUtil.visit_ast ~special_k k t and special_k = function | Ast.AttributedTerm (attrs, t) -> Ast.AttributedTerm (attrs, aux t) | _ -> assert false in aux t let ast_of_acic0 term_info acic k = (* prerr_endline "ast_of_acic0"; *) let k = k term_info in let register_uri id uri = Hashtbl.add term_info.uri id uri in let sort_of_id id = try Hashtbl.find term_info.sort id with Not_found -> assert false in let aux_substs substs = Some (List.map (fun (uri, annterm) -> (UriManager.name_of_uri uri, k annterm)) substs) in let aux_context context = List.map (function | None -> None | Some annterm -> Some (k annterm)) context in let aux = function | Cic.ARel (id,_,_,b) -> idref id (Ast.Ident (b, None)) | Cic.AVar (id,uri,substs) -> register_uri id (UriManager.string_of_uri uri); idref id (Ast.Ident (UriManager.name_of_uri uri, aux_substs substs)) | Cic.AMeta (id,n,l) -> idref id (Ast.Meta (n, aux_context l)) | Cic.ASort (id,Cic.Prop) -> idref id (Ast.Sort `Prop) | Cic.ASort (id,Cic.Set) -> idref id (Ast.Sort `Set) | Cic.ASort (id,Cic.Type _) -> idref id (Ast.Sort `Type) | Cic.ASort (id,Cic.CProp) -> idref id (Ast.Sort `CProp) | Cic.AImplicit _ -> assert false | Cic.AProd (id,n,s,t) -> let binder_kind = match sort_of_id id with | `Set | `Type -> `Pi | `Prop | `CProp -> `Forall in idref id (Ast.Binder (binder_kind, (ident_of_name n, Some (k s)), k t)) | Cic.ACast (id,v,t) -> idref id (Ast.Appl [idref id (Ast.Symbol ("cast", 0)); k v; k t]) | Cic.ALambda (id,n,s,t) -> idref id (Ast.Binder (`Lambda, (ident_of_name n, Some (k s)), k t)) | Cic.ALetIn (id,n,s,t) -> idref id (Ast.LetIn ((ident_of_name n, None), k s, k t)) | Cic.AAppl (aid,args) -> idref aid (Ast.Appl (List.map k args)) | Cic.AConst (id,uri,substs) -> register_uri id (UriManager.string_of_uri uri); idref id (Ast.Ident (UriManager.name_of_uri uri, aux_substs substs)) | Cic.AMutInd (id,uri,i,substs) as t -> let name = name_of_inductive_type uri i in let uri_str = UriManager.string_of_uri uri in let puri_str = uri_str ^ "#xpointer(1/" ^ (string_of_int (i + 1)) ^ ")" in register_uri id puri_str; idref id (Ast.Ident (name, aux_substs substs)) | Cic.AMutConstruct (id,uri,i,j,substs) -> let name = constructor_of_inductive_type uri i j in let uri_str = UriManager.string_of_uri uri in let puri_str = sprintf "%s#xpointer(1/%d/%d)" uri_str (i + 1) j in register_uri id puri_str; idref id (Ast.Ident (name, aux_substs substs)) | Cic.AMutCase (id,uri,typeno,ty,te,patterns) -> let name = name_of_inductive_type uri typeno in let constructors = constructors_of_inductive_type uri typeno in let rec eat_branch ty pat = match (ty, pat) with | Cic.Prod (_, _, t), Cic.ALambda (_, name, s, t') -> let (cv, rhs) = eat_branch t t' in (ident_of_name name, Some (k s)) :: cv, rhs | _, _ -> [], k pat in let patterns = List.map2 (fun (name, ty) pat -> let (capture_variables, rhs) = eat_branch ty pat in ((name, capture_variables), rhs)) constructors patterns in idref id (Ast.Case (k te, Some name, Some (k ty), patterns)) | Cic.AFix (id, no, funs) -> let defs = List.map (fun (_, n, decr_idx, ty, bo) -> ((Ast.Ident (n, None), Some (k ty)), k bo, decr_idx)) funs in let name = try (match List.nth defs no with | (Ast.Ident (n, _), _), _, _ when n <> "_" -> n | _ -> assert false) with Not_found -> assert false in idref id (Ast.LetRec (`Inductive, defs, Ast.Ident (name, None))) | Cic.ACoFix (id, no, funs) -> let defs = List.map (fun (_, n, ty, bo) -> ((Ast.Ident (n, None), Some (k ty)), k bo, 0)) funs in let name = try (match List.nth defs no with | (Ast.Ident (n, _), _), _, _ when n <> "_" -> n | _ -> assert false) with Not_found -> assert false in idref id (Ast.LetRec (`CoInductive, defs, Ast.Ident (name, None))) in aux acic (* persistent state *) let level1_patterns21 = Hashtbl.create 211 let level2_patterns32 = Hashtbl.create 211 let (compiled21: (CicNotationPt.term -> CicNotationPt.term) option ref) = ref None let (compiled32: (term_info -> Cic.annterm -> CicNotationPt.term) option ref) = ref None let pattern21_matrix = ref Patterns21.empty let pattern32_matrix = ref Patterns32.empty let get_compiled21 () = match !compiled21 with | None -> assert false | Some f -> f let get_compiled32 () = match !compiled32 with | None -> assert false | Some f -> f let set_compiled21 f = compiled21 := Some f let set_compiled32 f = compiled32 := Some f (* "envl" is a list of triples: * , where * name environment: (string * string) list * term environment: (string * Cic.annterm) list *) let return_closure success_k = (fun term_info terms envl -> (* prerr_endline "return_closure"; *) match terms with | [] -> (try success_k term_info (List.hd envl) with Failure _ -> assert false) | _ -> assert false) let variable_closure names k = (fun term_info terms envl -> (* prerr_endline "variable_closure"; *) match terms with | hd :: tl -> let envl' = List.map2 (fun arg (name_env, term_env, pid) -> let rec aux name_env term_env pid arg term = match arg, term with Ast.IdentArg name, _ -> (name_env, (name, term) :: term_env, pid) | Ast.EtaArg (Some name, arg'), Cic.ALambda (id, name', ty, body) -> aux ((name, (string_of_name name', Some (ty, id))) :: name_env) term_env pid arg' body | Ast.EtaArg (Some name, arg'), _ -> let name' = CicNotationUtil.fresh_name () in aux ((name, (name', None)) :: name_env) term_env pid arg' term | Ast.EtaArg (None, arg'), Cic.ALambda (id, name, ty, body) -> assert false | Ast.EtaArg (None, arg'), _ -> assert false in aux name_env term_env pid arg hd) names envl in k term_info tl envl' | _ -> assert false) let appl_closure ks k = (fun term_info terms envl -> (* prerr_endline "appl_closure"; *) (match terms with | Cic.AAppl (_, args) :: tl -> (try let k' = List.assoc (List.length args) ks in k' term_info (args @ tl) envl with Not_found -> k term_info terms envl) | [] -> assert false | _ -> k term_info terms envl)) let uri_of_term t = CicUtil.uri_of_term (Deannotate.deannotate_term t) let uri_closure ks k = (fun term_info terms envl -> (* prerr_endline "uri_closure"; *) (match terms with | [] -> assert false | hd :: tl -> (* prerr_endline (sprintf "uri_of_term = %s" (uri_of_term hd)); *) begin try let k' = List.assoc (uri_of_term hd) ks in k' term_info tl envl with | Invalid_argument _ (* raised by uri_of_term *) | Not_found -> k term_info terms envl end)) (* compiler from level 3 to level 2 *) let compiler32 (t: Patterns32.t) success_k fail_k = let rec aux t k = (* k is a continuation *) if t = [] then k else if Patterns32.are_empty t then begin (match t with | _::_::_ -> (* XXX optimization possible here: throw away all except one of the * rules which lead to ambiguity *) warning "ambiguous interpretation" | _ -> ()); return_closure success_k end else match Patterns32.horizontal_split t with | t', [] -> (match t' with | [] | ([], _) :: _ -> assert false | (Ast.ArgPattern (Ast.IdentArg _) :: _, _) :: _ | (Ast.ArgPattern (Ast.EtaArg _) :: _, _) :: _ -> let first_column, t'' = Patterns32.vertical_split t' in let names = List.map (function | Ast.ArgPattern arg -> arg | _ -> assert false) first_column in variable_closure names (aux t'' k) | (Ast.ApplPattern _ :: _, _) :: _ -> let pidl = List.map (function | (Ast.ApplPattern args) :: _, _ -> List.length args | _ -> assert false) t' in (* arity partitioning *) let clusters = Patterns32.partition t' pidl in let ks = (* k continuation list *) List.map (fun (len, cluster) -> let cluster' = List.map (* add args as patterns heads *) (function | (Ast.ApplPattern args) :: tl, pid -> (* let's throw away "teste di cluster" *) args @ tl, pid | _ -> assert false) cluster in len, aux cluster' k) clusters in appl_closure ks k | (Ast.UriPattern _ :: _, _) :: _ -> let uidmap, pidl = let urimap = ref [] in let uidmap = ref [] in let get_uri_id uri = try List.assoc uri !urimap with Not_found -> let uid = List.length !urimap in urimap := (uri, uid) :: !urimap ; uidmap := (uid, uri) :: !uidmap ; uid in let uidl = List.map (function | (Ast.UriPattern uri) :: _, _ -> get_uri_id uri | _ -> assert false) t' in !uidmap, uidl in let clusters = Patterns32.partition t' pidl in let ks = List.map (fun (uid, cluster) -> let cluster' = List.map (function | (Ast.UriPattern uri) :: tl, pid -> tl, pid | _ -> assert false) cluster in List.assoc uid uidmap, aux cluster' k) clusters in uri_closure ks k) | t', tl -> aux t' (aux tl k) in let matcher = aux t (fun _ _ -> raise No_match) in (fun term_info annterm -> try matcher term_info [annterm] (List.map (fun (_, pid) -> [], [], pid) t) with No_match -> fail_k term_info annterm) let return_closure21 success_k = (fun terms envl -> prerr_endline "return_closure21"; match terms with | [] -> (try success_k (List.hd envl) with Failure _ -> assert false) | _ -> assert false) let variable_closure21 vars k = (fun terms envl -> prerr_endline "variable_closure21"; match terms with | hd :: tl -> let envl' = List.map2 (fun (name, ty) (env, pid) -> (name, (ty, CicNotationEnv.value_of_term hd)) :: env, pid) vars envl in k tl envl' | _ -> assert false) let constructor_closure21 ks k = (fun terms envl -> prerr_endline "constructor_closure21"; (match terms with | p :: tl -> prerr_endline (sprintf "on term %s" (CicNotationPp.pp_term p)); (try let tag, subterms = CicNotationTag.get_tag p in let k' = List.assoc tag ks in k' (subterms @ tl) envl with Not_found -> k terms envl) | [] -> assert false)) let compiler21 (t: Patterns21.t) success_k fail_k = let rec aux t k = if t = [] then k else if Patterns21.are_empty t then begin (match t with | _::_::_ -> (* XXX optimization possible here: throw away all except one of the * rules which lead to ambiguity *) warning "ambiguous notation" | _ -> ()); return_closure21 success_k end else match Patterns21.horizontal_split t with | t', [] -> (match t' with | [] | ([], _) :: _ -> assert false | (Ast.Variable _ :: _, _) :: _ -> let first_column, t'' = Patterns21.vertical_split t' in let vars = List.map (function | Ast.Variable v -> CicNotationEnv.declaration_of_var v | _ -> assert false) first_column in variable_closure21 vars (aux t'' k) | _ -> let pidl = List.map (function | p :: _, _ -> fst (CicNotationTag.get_tag p) | [], _ -> assert false) t' in let clusters = Patterns21.partition t' pidl in let ks = List.map (fun (pid, cluster) -> let cluster' = List.map (* add args as patterns heads *) (function | p :: tl, pid -> let _, subpatterns = CicNotationTag.get_tag p in subpatterns @ tl, pid | _ -> assert false) cluster in pid, aux cluster' k) clusters in constructor_closure21 ks k) | t', tl -> aux t' (aux tl k) in let matcher = aux t (fun _ _ -> raise No_match) in (fun ast -> try matcher [ast] (List.map (fun (_, pid) -> [], pid) t) with No_match -> fail_k ast) let ast_of_acic1 term_info annterm = (get_compiled32 ()) term_info annterm let pp_ast1 term = (get_compiled21 ()) term let instantiate21 env pid = prerr_endline "instantiate21"; let precedence, associativity, l1 = try Hashtbl.find level1_patterns21 pid with Not_found -> assert false in let rec subst = function | Ast.AttributedTerm (_, t) -> subst t | Ast.Variable var -> let name, expected_ty = CicNotationEnv.declaration_of_var var in let ty, value = try List.assoc name env with Not_found -> assert false in assert (CicNotationEnv.well_typed ty value); (* INVARIANT *) (* following assertion should be a conditional that makes this * instantiation fail *) assert (CicNotationEnv.well_typed expected_ty value); CicNotationEnv.term_of_value value | Ast.Magic _ -> assert false (* TO BE IMPLEMENTED *) | Ast.Literal _ as t -> t | Ast.Layout l -> Ast.Layout (subst_layout l) | t -> CicNotationUtil.visit_ast subst t and subst_layout l = CicNotationUtil.visit_layout subst l in subst l1 let instantiate32 term_info name_env term_env pid = let symbol, args = try Hashtbl.find level2_patterns32 pid with Not_found -> assert false in let rec instantiate_arg = function | Ast.IdentArg name -> (try List.assoc name term_env with Not_found -> assert false) | Ast.EtaArg (None, _) -> assert false (* TODO *) | Ast.EtaArg (Some name, arg) -> let (name', ty_opt) = try List.assoc name name_env with Not_found -> assert false in let body = instantiate_arg arg in let name' = Ast.Ident (name', None) in match ty_opt with | None -> Ast.Binder (`Lambda, (name', None), body) | Some (ty, id) -> idref id (Ast.Binder (`Lambda, (name', Some ty), body)) in let args' = List.map instantiate_arg args in Ast.Appl (Ast.Symbol (symbol, 0) :: args') let load_patterns32 t = let ast_env_of_name_env term_info name_env = List.map (fun (name, (name', ty_opt)) -> let ast_ty_opt = match ty_opt with | None -> None | Some (annterm, id) -> Some (ast_of_acic1 term_info annterm, id) in (name, (name', ast_ty_opt))) name_env in let ast_env_of_term_env term_info = List.map (fun (name, term) -> (name, ast_of_acic1 term_info term)) in let fail_k term_info annterm = ast_of_acic0 term_info annterm ast_of_acic1 in let success_k term_info (name_env, term_env, pid) = instantiate32 term_info (ast_env_of_name_env term_info name_env) (ast_env_of_term_env term_info term_env) pid in let compiled32 = compiler32 t success_k fail_k in set_compiled32 compiled32 let load_patterns21 t = let rec pp_value = function | CicNotationEnv.NumValue _ as v -> v | CicNotationEnv.StringValue _ as v -> v | CicNotationEnv.TermValue t -> CicNotationEnv.TermValue (pp_ast1 t) | CicNotationEnv.OptValue None as v -> v | CicNotationEnv.OptValue (Some v) -> CicNotationEnv.OptValue (Some (pp_value v)) | CicNotationEnv.ListValue vl -> CicNotationEnv.ListValue (List.map pp_value vl) in let ast_env_of_env env = List.map (fun (var, (ty, value)) -> (var, (ty, pp_value value))) env in let fail_k term = pp_ast0 term pp_ast1 in let success_k (env, pid) = instantiate21 (ast_env_of_env env) pid in let compiled21 = compiler21 t success_k fail_k in set_compiled21 compiled21 let ast_of_acic id_to_sort annterm = let term_info = { sort = id_to_sort; uri = Hashtbl.create 211 } in let ast = ast_of_acic1 term_info annterm in ast, term_info.uri let pp_ast term = pp_ast1 term let fresh_id = let counter = ref ~-1 in fun () -> incr counter; !counter let add_interpretation (symbol, args) appl_pattern = let id = fresh_id () in Hashtbl.add level2_patterns32 id (symbol, args); pattern32_matrix := ([appl_pattern], id) :: !pattern32_matrix; load_patterns32 !pattern32_matrix; id let add_pretty_printer ?precedence ?associativity l2 l1 = let id = fresh_id () in let l2' = CicNotationUtil.strip_attributes l2 in Hashtbl.add level1_patterns21 id (precedence, associativity, l1); pattern21_matrix := ([l2'], id) :: !pattern21_matrix; load_patterns21 !pattern21_matrix; id exception Interpretation_not_found exception Pretty_printer_not_found let remove_interpretation id = (try Hashtbl.remove level2_patterns32 id; with Not_found -> raise Interpretation_not_found); pattern32_matrix := List.filter (fun (_, id') -> id <> id') !pattern32_matrix; load_patterns32 !pattern32_matrix let remove_pretty_printer id = (try Hashtbl.remove level1_patterns21 id; with Not_found -> raise Pretty_printer_not_found); pattern21_matrix := List.filter (fun (_, id') -> id <> id') !pattern21_matrix; load_patterns21 !pattern21_matrix let _ = load_patterns21 []; load_patterns32 []