type error = | CopyTag of Types.t * Types.t | CopyAttr of Types.t * Types.t | UnderTag of Types.t * exn exception Error of error let nil_atom = Atoms.V.mk_ascii "nil" let nil_type = Types.atom (Atoms.atom nil_atom) let nil_node = Types.cons nil_type let nil_cst = Types.Atom nil_atom let decompose t = (Types.Atom.has_atom t nil_atom, Types.Product.get t) module V = Types.Positive module H = Map.Make(Types) module H2 = Map.Make(Custom.Pair(Types)(Types)) let memoize f = let memo = ref H.empty in fun t -> try H.find t !memo with Not_found -> let z = f t in memo := H.add t z !memo; z let mapping f t queue = let memo = ref H.empty in let rec aux t = try H.find t !memo with Not_found -> let v = V.forward () in memo := H.add t v !memo; let (has_nil,rect) = decompose t in let l = List.map (fun (t1,t2) -> f t1 (aux t2)) rect in let l = if has_nil then queue :: l else l in V.define v (V.cup l); v in aux t let aux_concat = mapping (fun t v -> V.times (V.ty t) v) let aux_flatten t = mapping aux_concat t (V.ty nil_type) let aux_map f t = let f = memoize f in mapping (fun t v -> V.times (V.ty (f t)) v) t (V.ty nil_type) let solve x = Types.descr (V.solve x) let concat t1 t2 = solve (aux_concat t1 (V.ty t2)) let flatten t = let t = Types.normalize t in solve (aux_flatten t) let map f t = solve (aux_map f t) let map_mono t = let ts = ref [] in let vs = ref [] in let r = mapping (fun t v -> let v' = V.forward () in ts := t :: !ts; vs := v' :: !vs; V.times v' v) t (V.ty nil_type) in !ts, (fun fts -> List.iter2 (fun t v -> V.define v (V.ty t)) fts !vs; solve r) let recurs f = let n = Types.make () in Types.define n (f n); Types.internalize n let star_node t = recurs (fun n -> Types.cup nil_type (Types.times t n )) let any_node = star_node (Types.cons Types.any) let any = Types.descr any_node let seqseq = Types.descr (star_node any_node) let star t = Types.descr (star_node (Types.cons t)) let plus t = let t = Types.cons t in Types.times t (star_node t) let string = star (Types.Char.any) let option t = Types.cup (Types.times t nil_node) nil_type let repet min max t = let t = Types.cons t in let tail = match max with | None -> star_node t | Some m -> let rec aux accu l = function | 0 -> Types.cons accu | m -> let l = Types.times t (Types.cons l) in aux (Types.cup accu l) l (pred m) in aux Types.empty nil_type m in let rec aux = function | 0 -> tail | n -> Types.cons (Types.times t (aux (pred n))) in Types.descr (aux min) (* Mmmmh, it may be faster to add pi1(rect) and iterate over pi2(rect) ... ? *) let approx t = let memo = ref H.empty in let res = ref Types.empty in let rec aux t = try H.find t !memo with Not_found -> memo := H.add t () !memo; let rect = Types.Product.get t in List.iter (fun (t1,t2) -> res := Types.cup t1 !res; aux t2) rect; in aux t; !res let precise = ref true (* Note: the precision of this implementation depends on the constraint... This is bad and should be fixed. *) let map_tree cstr f seq = let memo = ref H2.empty in let rec aux cstr t = let x = (cstr,t) in try H2.find x !memo with Not_found -> let v = V.forward () in memo := H2.add x v !memo; let v' = mapping (descr_aux cstr) t (V.ty nil_type) in V.define v v'; v and descr_aux cstr t v = let cstr0 = if !precise then Types.Product.normal ~kind:`XML cstr else [] in let cstr_tag = if !precise then Types.Product.pi1 cstr0 else Types.any in let (result,residual) = f (star cstr) t in let f2 cstr_attr cstr1 (attr,child) = let cstr_sub = if !precise then ( if not (Types.subtype attr cstr_attr) then raise (Error (CopyAttr (attr,cstr_attr))); approx (Types.Product.constraint_on_2 cstr1 attr)) else Types.any in V.times (V.ty attr) (aux cstr_sub child) in let f1 (tag,x) = let cstr1 = if !precise then ( if not (Types.subtype tag cstr_tag) then raise (Error (CopyTag (tag,cstr_tag))); Types.Product.normal (Types.Product.constraint_on_2 cstr0 tag)) else [] in let cstr_attr = if !precise then Types.Product.pi1 cstr1 else Types.any in try let x = V.cup (List.map (f2 cstr_attr cstr1) (Types.Product.get x)) in V.xml (V.ty tag) x with exn -> raise (Error (UnderTag (tag,exn))) in let iter = List.map f1 (Types.Product.get ~kind:`XML residual) in let resid = Types.Product.other ~kind:`XML residual in let iter = if Types.is_empty resid then iter else V.ty resid :: iter in let result = aux_concat result v in if iter = [] then result else V.cup [V.times (V.cup iter) v; result ] in let cstr = if !precise then approx cstr else Types.any in Types.descr (V.solve (aux cstr seq)) let map_tree_mono domain seq = let inp = ref Types.empty in let ts = ref [] in let vs = ref [] in let memo = ref H.empty in let rec aux t = try H.find t !memo with Not_found -> let v = V.forward () in memo := H.add t v !memo; let v' = mapping descr_aux t (V.ty nil_type) in V.define v v'; v and descr_aux t v = inp := Types.cup !inp t; let residual = Types.diff t domain in let f2 (attr,child) = V.times (V.ty attr) (aux child) in let f1 (tag,x) = let x = V.cup (List.map f2 (Types.Product.get x)) in V.xml (V.ty tag) x in let iter = List.map f1 (Types.Product.get ~kind:`XML residual) in let resid = Types.Product.other ~kind:`XML residual in let iter = if Types.is_empty resid then iter else V.ty resid :: iter in let result = V.forward () in ts := (Types.cap domain t) :: !ts; vs := (result,v) :: !vs; if iter = [] then result else V.cup [V.times (V.cup iter) v; result ] in let r = aux seq in !inp, !ts, (fun fts -> List.iter2 (fun t (result,v) -> V.define result (aux_concat t v)) fts !vs; solve r) (* TODO: avoid flushing the memo between calls to mapping inside map_tree *) let seq_of_list l = let times' t acc = Types.times (Types.cons t) (Types.cons acc) in List.fold_right times' l nil_type let char_latin1 = Types.char (Chars.mk_classes [ (0,255) ]) let string_latin1 = star char_latin1 let any_xtransformable = Types.descr (recurs (fun n -> let elt = Types.xml Types.any_node (Types.cons (Types.times Types.any_node n)) in let non_elt = Types.neg (Types.xml Types.any_node Types.any_node) in let t = Types.cons (Types.cup elt non_elt) in Types.cup nil_type (Types.times t n))) let ub_concat t = star (approx (Types.cap any t))