Fixed bug in NextElement, improved caching
[SXSI/xpathcomp.git] / ata.ml
diff --git a/ata.ml b/ata.ml
index 42bf24e..7949921 100644 (file)
--- a/ata.ml
+++ b/ata.ml
@@ -382,7 +382,7 @@ let tags_of_state a q =
     module type ResultSet = 
     sig
       type t
-      type elt = [` Tree] Tree.node
+      type elt = [` Tree ] Tree.node
       val empty : t
       val cons : elt -> t -> t
       val concat : t -> t -> t
@@ -512,26 +512,43 @@ END
       let mk_fun f s = D_IGNORE_(register_funname f s,f)
       let mk_app_fun f arg s = let g = f arg in 
        D_IGNORE_(register_funname g ((get_funname f) ^ " " ^ s), g) 
+      let mk_app_fun2 f arg1 arg2 s = let g = f arg1 arg2 in 
+       D_IGNORE_(register_funname g ((get_funname f) ^ " " ^ s), g) 
 
       let string_of_ts tags = (Ptset.Int.fold (fun t a -> a ^ " " ^ (Tag.to_string t) ) tags "{")^ " }"
 
 
-
       module Algebra =
        struct
-         type jump = [ `LONG | `CLOSE | `NIL ]
-         type t = jump*Ptset.Int.t
-             
-         let merge_jump (j1,l1) (j2,l2) = 
+         type jump = [ `NIL | `ANY |`ANYNOTEXT | `JUMP ]
+         type t = jump*Ptset.Int.t*Ptset.Int.t
+         let jts = function 
+         | `JUMP -> "JUMP"
+         | `NIL -> "NIL"
+         | `ANY -> "ANY"
+         | `ANYNOTEXT -> "ANYNOTEXT"
+         let merge_jump (j1,c1,l1) (j2,c2,l2) = 
            match j1,j2 with
-             | _ when j1 = j2 -> (j1,Ptset.Int.union l1 l2)
-             | _,`NIL -> j1,l1
-             | `NIL,_ -> j2,l2
-             | _,_ -> (`CLOSE, Ptset.Int.union l1 l2)
+             | _,`NIL -> (j1,c1,l1)
+             | `NIL,_ -> (j2,c2,l2)
+             | `ANY,_ -> (`ANY,Ptset.Int.empty,Ptset.Int.empty)
+             | _,`ANY -> (`ANY,Ptset.Int.empty,Ptset.Int.empty)
+             | `ANYNOTEXT,_ -> 
+                 if Ptset.Int.mem Tag.pcdata (Ptset.Int.union c2 l2) then
+                 (`ANY,Ptset.Int.empty,Ptset.Int.empty)
+                 else
+                 (`ANYNOTEXT,Ptset.Int.empty,Ptset.Int.empty)
+             | _,`ANYNOTEXT -> 
+                 if Ptset.Int.mem Tag.pcdata (Ptset.Int.union c1 l1) then
+                 (`ANY,Ptset.Int.empty,Ptset.Int.empty)
+                 else
+                 (`ANYNOTEXT,Ptset.Int.empty,Ptset.Int.empty)
+             | `JUMP,`JUMP -> (`JUMP, Ptset.Int.union c1 c2,Ptset.Int.union l1 l2)
 
          let merge_jump_list = function 
-           | [] -> `NIL,Ptset.Int.empty
-           | p::r -> List.fold_left (merge_jump) p r
+           | [] -> `NIL,Ptset.Int.empty,Ptset.Int.empty
+           | p::r -> 
+               List.fold_left (merge_jump) p r
              
          let labels a s = 
            Hashtbl.fold 
@@ -551,90 +568,136 @@ END
          let is_rec a s access = 
            List.exists
              (fun (_,t) -> let _,_,f,_ = Transition.node t in
-             StateSet.mem s (access f)) (Hashtbl.find a.trans s) 
+             StateSet.mem s ((fun (_,_,x) -> x) (access (Formula.st f)))) (Hashtbl.find a.trans s) 
                     
 
-         let decide a c_label l_label dir_states access =
+         let decide a c_label l_label dir_states dir =
                        
            let l = StateSet.fold 
              (fun s l -> 
-                let s_rec= is_rec a s access in
-                let tlabels,jmp = 
-                  if s_rec then l_label,`LONG 
-                  else c_label,`CLOSE in                             
-                let slabels = TagSet.positive ((TagSet.cap (labels a s) tlabels))
+                let s_rec = is_rec a s (if dir then fst else snd) in
+                let s_rec = if dir then s_rec else
+                (* right move *)
+                is_rec a s fst
                 in
-                  (if Ptset.Int.is_empty slabels
-                   then `NIL,Ptset.Int.empty
-                   else  jmp,slabels)::l) dir_states []
-           in merge_jump_list l
-                
-
-           
+                let s_lab = labels a s in
+                let jmp,cc,ll = 
+                  if (not (TagSet.is_finite s_lab)) then
+                  if TagSet.mem Tag.pcdata s_lab then  (`ANY,Ptset.Int.empty,Ptset.Int.empty)
+                  else (`ANYNOTEXT,Ptset.Int.empty,Ptset.Int.empty)
+                  else 
+                  if s_rec 
+                  then (`JUMP,Ptset.Int.empty, TagSet.positive 
+                          (TagSet.cap (TagSet.inj_positive l_label) s_lab))
+                  else (`JUMP,TagSet.positive 
+                          (TagSet.cap (TagSet.inj_positive c_label) s_lab),
+                        Ptset.Int.empty )
+                in
+                  (if jmp != `ANY 
+                   && jmp != `ANYNOTEXT 
+                   && Ptset.Int.is_empty cc 
+                   && Ptset.Int.is_empty ll
+                   then (`NIL,Ptset.Int.empty,Ptset.Int.empty)
+                   else  (jmp,cc,ll))::l) dir_states []
+           in merge_jump_list l                            
            
              
        end 
 
 
-      let choose_jump tagset qtags1 qtagsn a f_nil  f_t1 f_s1 f_tn f_sn f_notext f_maytext =
-       let tags1,hastext1,fin1 = inter_text tagset (tags a qtags1) in
-       let tagsn,hastextn,finn = inter_text tagset (tags a qtagsn) in
-         (*if (hastext1||hastextn) then (`ANY,f_text)  (* jumping to text nodes doesn't work really well *)
-         else*)
-         if (Ptset.Int.is_empty tags1) && (Ptset.Int.is_empty tagsn) then (`NIL,f_nil)
-         else if (Ptset.Int.is_empty tagsn) then 
-           if (Ptset.Int.is_singleton tags1) 
-           then (* TaggedChild/Sibling *)
-             let tag = (Ptset.Int.choose tags1) in (`TAG(tag),mk_app_fun f_t1 tag (Tag.to_string tag))
-           else (* SelectChild/Sibling *)
-             (`ANY,mk_app_fun f_s1 tags1 (string_of_ts tags1))
-         else if (Ptset.Int.is_empty tags1) then 
-           if (Ptset.Int.is_singleton tagsn) 
-           then (* TaggedDesc/Following *)
-             let tag = (Ptset.Int.choose tagsn) in  (`TAG(tag),mk_app_fun f_tn tag (Tag.to_string tag))
-           else (* SelectDesc/Following *)
-             (`ANY,mk_app_fun f_sn tagsn (string_of_ts tagsn))
-         else if (hastext1||hastextn) then (`ANY,f_maytext)
-         else (`ANY,f_notext)
+
+      let choose_jump (d,cl,ll) f_nil f_t1 f_s1 f_tn f_sn f_s1n f_notext f_maytext =
+       match d with
+         | `NIL -> (`NIL,f_nil)
+         | `ANYNOTEXT -> `ANY,f_notext
+         | `ANY -> `ANY,f_maytext
+         | `JUMP -> 
+             if Ptset.Int.is_empty cl then
+             if Ptset.Int.is_singleton ll then
+             let tag = Ptset.Int.choose ll in 
+             (`TAG(tag),mk_app_fun f_tn tag (Tag.to_string tag))
+             else
+             (`ANY,mk_app_fun f_sn ll (string_of_ts ll))
+             else if Ptset.Int.is_empty ll then
+             if Ptset.Int.is_singleton cl then
+             let tag = Ptset.Int.choose cl in 
+             (`TAG(tag),mk_app_fun f_t1 tag (Tag.to_string tag))
+             else
+             (`ANY,mk_app_fun f_s1 cl (string_of_ts cl))
+             else
+             (`ANY,mk_app_fun2 f_s1n cl ll ((string_of_ts cl) ^ " " ^ (string_of_ts ll)))
+
+         | _ -> assert false
          
-      let choose_jump_down tree a b c d = 
-       choose_jump a b c d
+      let choose_jump_down tree d =
+       choose_jump d
          (mk_fun (fun _ -> Tree.nil) "Tree.mk_nil")
          (mk_fun (Tree.tagged_child tree) "Tree.tagged_child") 
          (mk_fun (Tree.select_child tree) "Tree.select_child")
          (mk_fun (Tree.tagged_desc tree) "Tree.tagged_desc")
          (mk_fun (Tree.select_desc tree) "Tree.select_desc") 
+         (mk_fun (fun _ _ -> Tree.first_child tree) "[FIRSTCHILD]Tree.select_child_desc")
          (mk_fun (Tree.first_element tree) "Tree.first_element")
-         (mk_fun (Tree.first_child tree) "Tree.first_child")
+         (mk_fun (Tree.first_child tree) "Tree.first_child") 
 
-      let choose_jump_next tree a b c d = 
-       choose_jump a b c d
+      let choose_jump_next tree d = 
+       choose_jump d
          (mk_fun (fun _ _ -> Tree.nil) "Tree.mk_nil2")
          (mk_fun (Tree.tagged_sibling_ctx tree) "Tree.tagged_sibling_ctx")
          (mk_fun (Tree.select_sibling_ctx tree) "Tree.select_sibling_ctx")
          (mk_fun (Tree.tagged_foll_ctx tree) "Tree.tagged_foll_ctx")
          (mk_fun (Tree.select_foll_ctx tree) "Tree.select_foll_ctx")
-         (mk_fun (Tree.next_element_ctx tree) "Tree.node_element_ctx")   
+         (mk_fun (fun _ _ -> Tree.next_sibling_ctx tree) "[NEXTSIBLING]Tree.select_sibling_foll_ctx")
+         (mk_fun (Tree.next_element_ctx tree) "Tree.next_element_ctx")   
          (mk_fun (Tree.next_sibling_ctx tree) "Tree.node_sibling_ctx")   
                          
-      module SetTagKey =
-      struct 
-       type t = Tag.t*SList.t
-       let equal (t1,s1) (t2,s2) =  t1 == t2 && s1 == s2
-       let hash (t,s) = HASHINT2(t,s.SList.Node.id)
+         
+      module SListTable = Hashtbl.Make(struct type t = SList.t
+                                             let equal = (==)
+                                             let hash t = t.SList.Node.id 
+                                      end)
+      module TransCache = 
+      struct
+       type 'a t = Obj.t array SListTable.t
+       let create n = SListTable.create n
+       let dummy = Obj.repr (fun _ -> assert false)
+       let find (h :'a t) tag slist : 'a =
+         let tab = 
+           try
+             SListTable.find h slist
+           with
+              Not_found -> 
+                SListTable.add h slist (Array.create 10000 dummy);
+                raise Not_found
+         in
+         let res = tab.(tag) in
+         if res == dummy then raise Not_found else (Obj.magic res)
+
+       let add (h : 'a t) tag slist (data : 'a) =
+         let tab = 
+           try
+             SListTable.find h slist
+           with
+              Not_found -> 
+                let arr = Array.create 10000 dummy in
+                SListTable.add h slist arr;
+                arr
+         in
+         tab.(tag) <- (Obj.repr data)
+         
+
       end
-       
-      module CachedTransTable = Hashtbl.Make(SetTagKey)
-      let td_trans = CachedTransTable.create 4093
-       
-       
+
+      let td_trans = TransCache.create 10000 (* should be number of tags *number of states^2
+                                               in the document *)
+
       let empty_size n =
        let rec loop acc = function 0 -> acc
          | n -> loop (SList.cons StateSet.empty acc) (n-1)
        in loop SList.nil n
             
 
-      module Fold2Res = Hashtbl.Make(struct 
+      module Fold2ResOld = Hashtbl.Make(struct 
                                       type t = Formlistlist.t*SList.t*SList.t
                                       let hash (f,s,t) = HASHINT3(f.Formlistlist.Node.id,
                                                                   s.SList.Node.id,
@@ -642,6 +705,39 @@ END
                                       let equal (a,b,c) (d,e,f) = a==d && b == e && c == f
                                     end)
 
+      module FllTable = Hashtbl.Make (struct type t = Formlistlist.t
+                                            let equal = (==)
+                                            let hash t = t.Formlistlist.Node.id
+                                     end)
+       
+      module Fold2Res =
+      struct
+       type 'a t = 'a SListTable.t SListTable.t FllTable.t
+
+       let create n = FllTable.create n
+
+       let find hf fl s1 s2 = 
+         let hs1 = FllTable.find hf fl in
+         let hs2 = SListTable.find hs1 s1 in
+         SListTable.find hs2 s2
+         
+       let add hf fl s1 s2 data = 
+         let hs1 =
+           try FllTable.find hf fl with
+             | Not_found -> 
+                 let hs1 = SListTable.create SMALL_H_SIZE
+                 in FllTable.add hf fl hs1;hs1
+         in
+         let hs2 =
+           try SListTable.find hs1 s1
+           with
+             | Not_found ->
+                 let hs2 = SListTable.create SMALL_H_SIZE
+                 in SListTable.add hs1 s1 hs2;hs2
+         in
+         SListTable.add hs2 s2 data
+      end
+
       let h_fold2 = Fold2Res.create BIG_H_SIZE
       
       let top_down ?(noright=false) a tree t slist ctx slot_size =     
@@ -651,7 +747,7 @@ END
        let eval_fold2_slist fll t (sl2,res2) (sl1,res1) =
          let res = Array.copy rempty in
          try
-           let r,b,btab = Fold2Res.find h_fold2 (fll,sl1,sl2) in
+           let r,b,btab = Fold2Res.find h_fold2 fll sl1 sl2  in
            if b then for i=0 to slot_size - 1 do
              res.(i) <- RS.merge btab.(i) t res1.(i) res2.(i);
            done;
@@ -674,7 +770,7 @@ END
                   | _ -> aq,ab
               in
               let r,b = fold sl1 sl2 fll 0 SList.nil false in
-              Fold2Res.add h_fold2 (fll,sl1,sl2) (r,b,btab);
+              Fold2Res.add h_fold2 fll sl1 sl2 (r,b,btab);
               if b then for i=0 to slot_size - 1 do
                 res.(i) <- RS.merge btab.(i) t res1.(i) res2.(i);
               done;
@@ -682,16 +778,16 @@ END
        in
 
        let null_result = (pempty,Array.copy rempty) in
-       let rec loop t slist ctx=
+       let rec loop t slist ctx =
          if t == Tree.nil then null_result else get_trans t slist (Tree.tag tree t) ctx
        and loop_tag tag t slist ctx =
          if t == Tree.nil then null_result else get_trans t slist tag ctx
        and loop_no_right t slist ctx = 
          if t == Tree.nil then null_result else get_trans ~noright:true t slist (Tree.tag tree t) ctx
-       and get_trans ?(noright=false) t slist tag ctx =          
+       and get_trans ?(noright=false) t slist tag ctx = 
          let cont = 
            try
-             CachedTransTable.find td_trans (tag,slist)
+             TransCache.find td_trans tag slist
            with        
              | Not_found ->
                  let fl_list,llist,rlist,ca,da,sa,fa = 
@@ -726,75 +822,87 @@ END
                      slist (Formlistlist.nil,SList.nil,SList.nil,StateSet.empty,StateSet.empty,StateSet.empty,StateSet.empty)
                  in                    
                    (* Logic to chose the first and next function *)
-                 let _,tags_below,_,tags_after = Tree.tags tree tag in
+                 let tags_child,tags_below,tags_siblings,tags_after = Tree.tags tree tag in
+                 let d_f = Algebra.decide a tags_child tags_below (StateSet.union ca da) true in
+                 let d_n = Algebra.decide a tags_siblings tags_after (StateSet.union sa fa) false in
 (*               let _ = Printf.eprintf "Tags below %s are : \n" (Tag.to_string tag) in
                  let _ = Ptset.Int.iter (fun i -> Printf.eprintf "%s " (Tag.to_string i)) tags_below in
                  let _ = Printf.eprintf "\n%!" in *)
-                 let tags_below = Ptset.Int.remove tag tags_below in
-                 let f_kind,first = choose_jump_down tree tags_below ca da a
+(*               let tags_below = Ptset.Int.remove tag tags_below in *)
+                 let f_kind,first = choose_jump_down tree d_f
                  and n_kind,next = if noright then (`NIL, fun _ _ -> Tree.nil )
-                 else choose_jump_next tree tags_after sa fa a in
+                 else choose_jump_next tree d_n in
                  let empty_res = null_result in
                   let cont =
                     match f_kind,n_kind with
                       | `NIL,`NIL ->
-                          (fun t _ -> eval_fold2_slist fl_list t empty_res empty_res )
-                      |  _,`NIL -> (
+                         (fun t _ -> eval_fold2_slist fl_list t empty_res empty_res)
+                     |  _,`NIL -> (
                            match f_kind with
                              |`TAG(tag) ->
                                 (fun t _ -> eval_fold2_slist fl_list t empty_res
-                                   (loop_tag tag (first t) llist t))
-                             | `ANY ->
-                                 (fun t _ -> eval_fold2_slist fl_list t empty_res
-                                    (loop (first t) llist t))
-                             | _ -> assert false)
-
+                                  (loop_tag tag (first t) llist t ))
+                            | `ANY ->
+                                (fun t _ -> eval_fold2_slist fl_list t empty_res
+                                  (loop (first t) llist t ))
+                            | _ -> assert false)
                       | `NIL,_ -> (
-                            match n_kind with
-                              |`TAG(tag) ->
-                                 (fun t ctx -> eval_fold2_slist fl_list t
-                                    (loop_tag tag (next t ctx) rlist ctx) empty_res)
-
-                              | `ANY ->
-                                  (fun t ctx -> eval_fold2_slist fl_list t
-                                     (loop (next t ctx) rlist ctx) empty_res)
-
-                              | _ -> assert false)
-
-                      | `TAG(tag1),`TAG(tag2) ->
-                            (fun t ctx -> eval_fold2_slist fl_list t
-                               (loop_tag tag2 (next t ctx) rlist ctx)
-                               (loop_tag tag1 (first t) llist t))
-
+                          match n_kind with
+                            |`TAG(tag) ->
+                              if SList.equal rlist slist then
+                              let rec loop t ctx = 
+                                if t == Tree.nil then empty_res
+                                else 
+                                let res2 = loop (next t ctx) ctx in                               
+                                eval_fold2_slist fl_list t res2 empty_res                                
+                              in loop
+                              else
+                               (fun t ctx -> eval_fold2_slist fl_list t
+                                 (loop_tag tag (next t ctx) rlist ctx ) empty_res)
+                                                                                            
+                            | `ANY ->
+                                (fun t ctx -> eval_fold2_slist fl_list t
+                                   (loop (next t ctx) rlist ctx ) empty_res)
+                                 
+                            | _ -> assert false)
+                         
+                      | `TAG(tag1),`TAG(tag2) ->                         
+                          (fun t ctx ->
+                            eval_fold2_slist fl_list t
+                               (loop_tag tag2 (next t ctx) rlist ctx )
+                               (loop_tag tag1 (first t) llist t ))
+                                                                           
                       | `TAG(tag),`ANY ->
-                         (fun t ctx -> eval_fold2_slist fl_list t
-                               (loop (next t ctx) rlist ctx)
-                               (loop_tag tag (first t) llist t))
+                         (fun t ctx ->
+                           eval_fold2_slist fl_list t
+                              (loop (next t ctx) rlist ctx )
+                              (loop_tag tag (first t) llist t ))
+                                                                          
                       | `ANY,`TAG(tag) ->
-                            (fun t ctx ->
-                               eval_fold2_slist fl_list t
-                                 (loop_tag tag (next t ctx) rlist ctx)
-                                 (loop (first t) llist t) )
+                          (fun t ctx ->
+                            eval_fold2_slist fl_list t
+                              (loop_tag tag (next t ctx) rlist ctx )
+                              (loop (first t) llist t ))
+                                                                  
                       | `ANY,`ANY ->
-                            (fun t ctx ->
-                               eval_fold2_slist fl_list t
-                                 (loop (next t ctx) rlist ctx)
-                                 (loop (first t) llist t) )
+                          (fun t ctx ->
+                             eval_fold2_slist fl_list t
+                               (loop (next t ctx) rlist ctx )
+                               (loop (first t) llist t ))
                       | _ -> assert false
-                  in
-                  let cont = D_IF_( (fun t ctx ->
+                 in
+                 let cont = D_IF_( (fun t ctx ->
                                        let a,b = cont t ctx in
                                        register_trace tree t (slist,a,fl_list,first,next,ctx);
-                                         (a,b)
+                                       (a,b)
                                     ) ,cont)
                   in
-                  (CachedTransTable.add td_trans (tag,slist) cont;cont)
-          in cont t ctx
-           
-       in
-       (if noright then loop_no_right else loop) t slist ctx
+                  (TransCache.add td_trans tag slist (Obj.repr cont) ;cont)
+         in (Obj.magic cont) t ctx
+              
+       in 
+         (if noright then loop_no_right else loop) t slist ctx
 
-  
        let run_top_down a tree =
          let init = SList.cons a.init SList.nil in
          let _,res = top_down a tree Tree.root init Tree.root 1