.
[SXSI/xpathcomp.git] / xPath.ml
1 (******************************************************************************)
2 (*  SXSI : XPath evaluator                                                    *)
3 (*  Kim Nguyen (Kim.Nguyen@nicta.com.au)                                      *)
4 (*  Copyright NICTA 2008                                                      *)
5 (*  Distributed under the terms of the LGPL (see LICENCE)                     *)
6 (******************************************************************************)
7
8
9 INCLUDE "debug.ml";;
10 #load "pa_extend.cmo";;      
11
12
13 module Ast =
14 struct
15
16 type path = Absolute of step list | AbsoluteDoS of step list| Relative of step list
17 and step = axis*test*predicate
18 and axis = Self | Attribute | Child | Descendant | DescendantOrSelf | FollowingSibling
19            | Parent
20
21 and test = TagSet.Xml.t
22
23 and predicate = Or of predicate*predicate
24                 | And of predicate*predicate
25                 | Not of predicate      
26                 | Expr of expression
27 and expression =  Path of path
28                 | Function of string*expression list
29                 | Int of int
30                 | String of string
31                 | True | False
32 type t = path
33       
34
35 let pp fmt = Format.fprintf fmt
36 let print_list printer fmt sep l =
37   match l with
38       [] -> ()
39     | [e] -> printer fmt e
40     | e::es -> printer fmt e; List.iter (fun x -> pp fmt sep;printer fmt x) es
41         
42
43 let rec print fmt p = 
44   let l = match p with 
45     | Absolute l -> pp fmt "/"; l 
46     | AbsoluteDoS l -> pp fmt "/"; 
47         print_step fmt (DescendantOrSelf,TagSet.Xml.node,Expr True);
48         pp fmt "/"; l
49     | Relative l -> l 
50   in
51     print_list print_step fmt "/" (List.rev l)
52 and print_step fmt (axis,test,predicate) =
53     print_axis fmt axis;pp fmt "::";print_test fmt test;
54   pp fmt "["; print_predicate fmt predicate; pp fmt "]"
55 and print_axis fmt a = pp fmt "%s" (match a with 
56                                         Self -> "self"
57                                       | Child -> "child"
58                                       | Descendant -> "descendant"
59                                       | DescendantOrSelf -> "descendant-or-self"
60                                       | FollowingSibling -> "following-sibling"
61                                       | Attribute -> "attribute"
62                                       | Parent -> "parent")
63 and print_test fmt ts =  
64   try 
65     pp fmt "%s" (List.assoc ts 
66                    [ (TagSet.Xml.pcdata,"text()"); (TagSet.Xml.node,"node()");
67                      (TagSet.Xml.star),"*"])
68   with
69       Not_found -> pp fmt "%s"
70         (if TagSet.Xml.is_finite ts 
71          then Tag.to_string (TagSet.Xml.choose ts)
72          else "<INFINITE>")
73
74 and print_predicate fmt = function
75   | Or(p,q) -> print_predicate fmt p; pp fmt " or "; print_predicate fmt q
76   | And(p,q) -> print_predicate fmt p; pp fmt " and "; print_predicate fmt q
77   | Not p -> pp fmt "not "; print_predicate fmt p
78   | Expr e -> print_expression fmt e
79
80 and print_expression fmt = function
81   | Path p -> print fmt p
82   | Function (f,l) -> pp fmt "%s(" f;print_list print_expression fmt "," l;pp fmt ")"
83   | Int i -> pp fmt "%i" i
84   | String s -> pp fmt "\"%s\"" s
85   | t -> pp fmt "%b" (t== True)
86         
87 end
88 module Parser = 
89 struct
90   open Ast    
91   open Ulexer
92   let predopt = function None -> Expr True | Some p -> p
93
94   module Gram =  Camlp4.Struct.Grammar.Static.Make(Ulexer)
95   let query = Gram.Entry.mk "query"
96     
97   exception Error of Gram.Loc.t*string
98   let test_of_keyword t loc = 
99     match t with
100       | "text()" -> TagSet.Xml.pcdata
101       | "node()" -> TagSet.Xml.node
102       | "*" -> TagSet.Xml.star
103       | "and" | "not" | "or" -> TagSet.Xml.singleton (Tag.tag t)
104       | _ -> raise (Error(loc,"Invalid test name "^t ))
105
106   let axis_to_string a = let r = Format.str_formatter in
107     print_axis r a; Format.flush_str_formatter()
108 EXTEND Gram
109
110 GLOBAL: query;
111
112  query : [ [ p = path; `EOI -> p ]]
113 ;
114      
115  path : [ 
116    [ "//" ; l = slist -> AbsoluteDoS l ]
117  | [ "/" ; l = slist -> Absolute l ]
118  | [ l = slist  -> Relative l ]
119  ]
120 ;
121
122 slist: [
123   [ l = slist ;"/"; s = step -> s::l ]
124 | [ l = slist ; "//"; s = step -> s::(DescendantOrSelf,TagSet.Xml.node,Expr True)::l]
125 | [ s = step -> [ s ] ]
126 ];
127
128 step : [
129   (* yurk, this is done to parse stuff like
130      a/b/descendant/a where descendant is actually a tag name :(
131      if OPT is None then this is a child::descendant if not, this is a real axis name
132   *)
133 [ axis = axis ; o = OPT ["::" ; t = test -> t ] ; p = top_pred  ->
134     match o with
135       | Some(t) ->  (axis,t,p) 
136       | None -> (Child,TagSet.Xml.singleton (Tag.tag (axis_to_string axis)),p) ]
137  
138 | [ "." ; p = top_pred ->  (Self,TagSet.Xml.node,p)  ]
139 | [ test = test; p = top_pred  -> (Child,test, p) ]
140 | [ att = ATT ; p = top_pred -> 
141       match att with
142         | "*" -> (Attribute,TagSet.Xml.star,p)
143         | _ ->  (Attribute, TagSet.Xml.singleton (Tag.tag att) ,p )]
144 ]
145 ;
146 top_pred  : [
147   [ p = OPT [ "["; p=predicate ;"]" -> p ] -> predopt p ]
148 ]
149 ;
150 axis : [ 
151   [ "self" -> Self | "child" -> Child | "descendant" -> Descendant 
152       | "descendant-or-self" -> DescendantOrSelf
153       | "following-sibling" -> FollowingSibling
154       | "attribute" -> Attribute
155       | "parent" -> Parent
156   ]
157
158     
159 ];
160 test : [ 
161   [ s = KWD -> test_of_keyword s _loc  ]
162 | [ t = TAG -> TagSet.Xml.singleton (Tag.tag t) ]
163 ];
164
165
166 predicate: [ 
167   [ p = predicate; "or"; q = predicate -> Or(p,q) ]
168 | [ p = predicate; "and"; q = predicate -> And(p,q) ]
169 | [ "not" ; p = predicate -> Not p ]
170 | [ "("; p = predicate ;")" -> p ]
171 | [ e = expression -> Expr e ]
172 ];
173
174 expression: [
175   [ f = TAG; "("; args = LIST0 expression SEP "," ; ")" -> Function(f,args)]
176 | [ `INT(i) -> Int (i) ]
177 | [ s = STRING -> String s ]
178 | [ p = path -> Path p ]
179 | [ "("; e = expression ; ")" -> e ]
180 ]
181 ;
182 END
183 ;;
184   let parse_string = Gram.parse_string query (Ulexer.Loc.mk "<string>")
185   let parse = Gram.parse_string query (Ulexer.Loc.mk "<string>")
186 end    
187
188 module Functions = struct
189
190   type value = [ `NodeSet of Automaton.BST.t 
191   | `Int of int | `String of string
192   | `Bool of bool | `True | `False ]
193
194   type expr = [ value | `Call of (string*(expr list))
195   | `Auto of Automaton.t | `Contains of expr list ]
196
197
198   let count = function [`NodeSet(s) ] -> `Int(Automaton.BST.cardinal s)
199     | _ -> failwith "count"
200         
201   let contains_old = function [`NodeSet(s) ; `String(str) ] ->
202     `Bool(Automaton.BST.exists (fun tree -> Tree.Binary.contains_old tree str
203                                ) s)
204     | _ -> failwith "contains_old"
205  
206   let equal = function [ `Int i; `Int j ] -> `Bool (i == j)
207     |_ -> failwith "equal"
208
209   let globals : (string*(value list -> value)) list = [
210
211     ("count",count);
212     ("equal",equal);
213     ("contains_old",contains_old);
214 ]
215
216   let text t = Tree.Binary.string (Tree.Binary.left t)
217
218   let rec eval_expr tree (e:expr) : value = match e with 
219     | `Call (f,args) -> (List.assoc f globals) (List.map (eval_expr tree) args)
220     | `Auto(a) -> `NodeSet(ignore (Automaton.BottomUp.accept a tree);
221                            a.Automaton.result)
222     | `Contains(args) ->
223         begin
224           match args with
225               [ `Auto(a); `String(s) ] ->
226                 let docs = Tree.Binary.contains tree s
227                 in 
228                 let _ = Automaton.BottomUp.accept ~strings:(Some docs) a tree
229                 in `NodeSet(a.Automaton.result)         
230             | _ -> failwith "contains invalid"
231         end
232     | #value as x  -> x
233         
234   let truth_value = 
235     function `NodeSet s -> not (Automaton.BST.is_empty s)
236       |`Bool(b) -> b
237       | _ -> failwith "truth_value"
238     
239 end
240 module Compile = struct
241   open Ast
242   open Automaton
243
244
245   type direction = Left | Right | Final
246   let (==>) a (b,c,d) = Transition.Label(a,b,c,d)
247   let (@@) b (c,d) = (b,c,d)
248
249   let star = TagSet.Xml.star
250   let any = TagSet.Xml.any
251   let notstar = TagSet.Xml.add Tag.pcdata (TagSet.Xml.add Tag.attribute TagSet.Xml.empty)
252   let swap dir a b = match dir with
253     | Left | Final -> (a,b)
254     | Right -> (b,a)
255    
256   let split_dest q l = 
257     let rec aux ((qacc,nqacc) as acc) = function
258       | [] -> acc
259       | t::r -> 
260           aux (if State.equal (Transition.dest1 t) q
261                  || State.equal (Transition.dest2 t) q
262                then t::qacc , nqacc
263                else qacc , (t::nqacc)) r
264     in
265       aux ([],[]) l
266
267
268   let mk_tag_t dir s ts q1 q2 = (s==> ts @@ (swap dir q1 q2));;
269   let mk_self_trs ts acc l =  
270     List.fold_left 
271       (fun acc t ->
272          let s = Transition.source t in
273          let d1 = Transition.dest1 t in
274          let d2 = Transition.dest2 t in
275          let tself = (s ==> ts @@ (d1,d2)) in
276            (Transition.cap t tself)::acc ) (acc) l
277
278   let mk_pred_trs f acc l =
279     List.fold_left 
280       (fun acc t ->
281          let s = Transition.source t in
282          let d1 = Transition.dest1 t in
283          let d2 = Transition.dest2 t in
284          let tself = Transition.External(s,f,d1,d2) in
285            (Transition.cap t tself)::acc ) (acc) l
286
287   let mk_dself_trs q' ts acc l =  
288     List.fold_left 
289       (fun acc t -> 
290          let t',s,d2 = match t with
291            | Transition.Label(s,ts,_,d2) -> Transition.Label(s,ts,q',d2),s,d2
292            | Transition.External (s,f,_,d2) -> Transition.External(s,f,q',d2),s,d2
293        in
294          let tself = (s ==> ts @@ (q',d2)) in
295            (Transition.cap t' tself)::acc ) (acc) l
296
297   let from_list = List.fold_left (fun acc e -> SSet.add e acc) SSet.empty
298
299   let dir = function (FollowingSibling,_,_) -> Right
300     | _ -> Left
301
302   let rev_map_dir p = 
303     let rec map_dir (d,acc) = function
304       | [] -> acc
305       | s::r -> map_dir ((dir s),(s,d)::acc) r
306     in let l = match p with
307       | Absolute p | Relative p -> map_dir (Final,[]) p
308       | AbsoluteDoS p -> 
309           let l = (map_dir (Final,[]) p)
310           in ((DescendantOrSelf,TagSet.Xml.node,Expr True),dir (fst(List.hd l)))::l
311     in ((Child,TagSet.Xml.node,Expr True),dir (fst(List.hd l)))::l
312
313
314   let rec compile_step q dir trs final initial ignore (axis,test,pred) = 
315     let q' = State.mk() in
316     let trs,final,initial =  match axis,test with
317         | Self,ts -> 
318             let tchange,tkeep = split_dest q trs in
319             let trs' = mk_self_trs ts tkeep tchange in 
320               (trs',q::final,initial)
321
322         | Child,ts -> 
323             (mk_tag_t dir q ts q' ignore) ::( q==> any @@ (ignore,q))::trs, q'::final,initial
324
325         | Descendant,ts ->
326             (mk_tag_t dir q ts q' ignore) ::
327               (q==> star @@ (q,q))::(q==> notstar @@ (ignore,q))::trs,q'::final,initial
328                 
329         | DescendantOrSelf,ts ->
330             let tchange,tkeep = split_dest q trs in
331             let trs' = mk_dself_trs q' ts trs tchange in 
332               (mk_tag_t dir q ts q' ignore) ::
333                 (q==> star @@ (q,q))::(q==> notstar @@ (ignore,q))::trs',q'::final,initial 
334
335         | FollowingSibling,ts ->
336             (mk_tag_t dir q ts q' ignore) :: (q ==> any @@ (ignore,q))::trs,q'::final,initial
337
338               (* q' is not returned and thus not added to the set of final states.
339                  It's ok since we should never be in a final state on a node
340                  <@> *)
341
342         | Attribute,ts -> let q'' = State.mk() in
343             (mk_tag_t Left q (TagSet.Xml.attribute) q' ignore)::
344               (mk_tag_t Left q' (ts) q'' ignore)::( q==> any @@ (ignore,q))::trs, q''::q'::final,initial
345
346         | Parent,ts -> let q'' = List.hd initial in
347             (mk_tag_t Left q' (star) q q')::
348               ( q'' ==> ts @@ (q',q''))::
349               ( q'' ==> star @@ (q'',q''))::
350               ( q'' ==> notstar @@ (ignore,q''))::trs,q'::q''::final,q''::initial
351               
352     in
353     let q_out = List.hd final in
354     let tchange,tkeep = split_dest q_out trs in
355     let trs' = compile_pred q_out tkeep tchange pred in 
356       (trs',final,initial)
357
358   and compile_pred q_out tkeep tchange p =
359     let rec pred_rec = function
360
361       | Or(p1,p2) -> cup (pred_rec p1) (pred_rec p2)
362       | And(p1,p2) -> cap (pred_rec p1) (pred_rec p2)
363       | Not(p) -> neg (pred_rec p)
364       | Expr e -> match compile_expr e with
365           | `True -> `Label (TagSet.Xml.any)
366           | `False -> `Label (TagSet.Xml.empty)
367           | e -> `Fun (fun t -> Functions.truth_value (Functions.eval_expr t e))
368
369     in match pred_rec p with
370         `Fun f -> mk_pred_trs f tkeep tchange
371       | `Label ts -> mk_self_trs ts tkeep tchange
372
373     and compile_expr = function 
374         True -> `True
375       | False -> `False
376       | Path p -> `Auto(compile p)
377       | Int i -> `Int i
378       | String s -> `String s
379       | Function ("contains",elist) ->`Contains(List.map compile_expr elist)
380       | Function (f,elist) -> `Call(f,List.map compile_expr elist) 
381           
382   and cup a b = match a,b with
383     | `Label l1 , `Label l2 -> `Label(TagSet.Xml.cup l1 l2)
384     | `Fun f1 , `Fun f2 -> `Fun (fun x -> (f1 x)||(f2 x))
385     | `Fun f , `Label l | `Label l, `Fun f ->
386         `Fun (fun x -> 
387                 (TagSet.Xml.mem (Tree.Binary.tag x) l)
388                 || (f x))
389
390   and cap a b = match a,b with
391     | `Label l1, `Label l2 -> `Label (TagSet.Xml.cap l1 l2)
392     | `Fun f1,`Fun f2 -> `Fun (fun x -> (f1 x)&&(f2 x))
393     | `Fun f,`Label l | `Label l,`Fun f ->
394         `Fun (fun x -> 
395                 (TagSet.Xml.mem (Tree.Binary.tag x) l)
396                 && f x)
397   and neg = function
398       `Label l -> `Label(TagSet.Xml.neg l)
399     | `Fun f -> `Fun (fun x -> not (f x))
400         
401   and compile p = 
402     let p = rev_map_dir p in
403     let ignore = State.mk()
404     in    
405     let q0 = State.mk() in
406     let transitions = Transition.empty () in      
407     let tlist,qlist,initacc = List.fold_left 
408       (fun (tlist,qlist,initacc) (s,dir) ->
409          let q = List.hd qlist in
410            compile_step q dir tlist qlist initacc ignore s ) ([],[q0;ignore],[q0]) p
411     in
412       List.iter (Transition.add transitions) tlist;
413       let qmark = List.hd qlist in
414         { Automaton.mk() with 
415             initial = from_list initacc;
416             final = from_list qlist;
417             transitions = transitions;
418             marking = from_list [qmark];
419             ignore = from_list [qmark;ignore];  
420         }
421 end