Remove AbsoluteDOS from the XPath AST.
[tatoo.git] / src / xPath.ml
1 (***********************************************************************)
2 (*                                                                     *)
3 (*                               TAToo                                 *)
4 (*                                                                     *)
5 (*                     Kim Nguyen, LRI UMR8623                         *)
6 (*                   Université Paris-Sud & CNRS                       *)
7 (*                                                                     *)
8 (*  Copyright 2010-2012 Université Paris-Sud and Centre National de la *)
9 (*  Recherche Scientifique. All rights reserved.  This file is         *)
10 (*  distributed under the terms of the GNU Lesser General Public       *)
11 (*  License, with the special exception on linking described in file   *)
12 (*  ../LICENSE.                                                        *)
13 (*                                                                     *)
14 (***********************************************************************)
15
16 #load "pa_extend.cmo";;
17 module Ast =
18 struct
19
20   type path = Absolute of step list | Relative of step list
21   and step = axis * test *predicate
22   and axis = Self | Attribute | Child | Descendant | DescendantOrSelf | FollowingSibling
23              | Parent | Ancestor | AncestorOrSelf | PrecedingSibling | Preceding | Following
24
25   and test = Simple of QNameSet.t
26
27
28   and predicate = Or of predicate*predicate
29                   | And of predicate*predicate
30                   | Not of predicate
31                   | Expr of expression
32   and expression =  Path of path
33                     | Function of string*expression list
34                     | Int of int
35                     | String of string
36                     | True | False
37   type t = path
38
39
40   let text = QNameSet.singleton QName.text
41   let node = QNameSet.any
42   let star =
43     QNameSet.complement (
44       QNameSet.from_list [ QName.text;
45                          QName.document;
46                          QName.cdata_section;
47                          QName.comment])
48   let t_text = Simple text
49   let t_node = Simple node
50   let t_star = Simple star
51
52
53
54   let pp fmt = Format.fprintf fmt
55   let print_list printer fmt sep l =
56     match l with
57       [] -> ()
58       | [e] -> printer fmt e
59       | e::es -> printer fmt e; List.iter (fun x -> pp fmt sep;printer fmt x) es
60
61
62   let rec print fmt p =
63     let l = match p with
64       | Absolute l -> pp fmt "/"; l
65       | Relative l -> l
66     in
67       Pretty.print_list ~sep:"/" print_step fmt l
68   and print_step fmt (axis, test, predicate) =
69     print_axis fmt axis;pp fmt "::";print_test fmt test;
70     match predicate with
71       Expr True -> ()
72       |  _ -> pp fmt "["; print_predicate fmt predicate; pp fmt "]"
73   and print_axis fmt a = pp fmt "%s" (match a with
74       Self -> "self"
75     | Child -> "child"
76     | Descendant -> "descendant"
77     | DescendantOrSelf -> "descendant-or-self"
78     | FollowingSibling -> "following-sibling"
79     | Attribute -> "attribute"
80     | Ancestor -> "ancestor"
81     | AncestorOrSelf -> "ancestor-or-self"
82     | PrecedingSibling -> "preceding-sibling"
83     | Parent -> "parent"
84     | _ -> assert false
85   )
86   and print_test fmt ts =
87     try
88       pp fmt "%s" (List.assoc ts
89                      [ t_text,"text()";
90                        t_node,"node()";
91                        t_star, "*" ] )
92     with
93       Not_found -> pp fmt "%s"
94         (match ts with
95           Simple t -> if QNameSet.is_finite t
96             then QName.to_string (QNameSet.choose t)
97             else "<INFINITE>"
98         )
99
100   and print_predicate fmt = function
101     | Or(p,q) -> print_predicate fmt p; pp fmt " or "; print_predicate fmt q
102     | And(p,q) -> print_predicate fmt p; pp fmt " and "; print_predicate fmt q
103     | Not p -> pp fmt "not "; print_predicate fmt p
104     | Expr e -> print_expression fmt e
105
106   and print_expression fmt = function
107     | Path p -> print fmt p
108     | Function (f,l) ->
109       pp fmt "%s(" f;
110       Pretty.print_list ~sep:"," print_expression fmt l;
111       pp fmt ")"
112     | Int i -> pp fmt "%i" i
113     | String s -> pp fmt "\"%s\"" s
114     | t -> pp fmt "%b" (t== True)
115
116 end
117 module Parser =
118 struct
119   open Ast
120   open Ulexer
121   let predopt = function None -> Expr True | Some p -> p
122
123   module Gram =  Camlp4.Struct.Grammar.Static.Make(Ulexer)
124   let query = Gram.Entry.mk "query"
125
126   exception Error of Gram.Loc.t*string
127   let test_of_keyword t loc =
128     match t with
129       | "text()" -> text
130       | "node()" -> node
131       | "*" -> star
132       | "and" | "not" | "or" -> QNameSet.singleton (QName.of_string t)
133       | _ -> raise (Error(loc,"Invalid test name "^t ))
134
135   let axis_to_string a = let r = Format.str_formatter in
136     print_axis r a; Format.flush_str_formatter()
137
138
139 EXTEND Gram
140
141 GLOBAL: query;
142
143  query : [ [ p = path; `EOI -> p ]]
144 ;
145
146  path : [
147  [ "/" ; l = slist -> Absolute (List.rev l) ]
148  | [ l = slist  -> Relative (List.rev l) ]
149  ]
150 ;
151
152 slist: [
153   [ l = slist ;"/"; s = step -> s @ l ]
154 | [ l = slist ; "//"; s = step -> s@[(DescendantOrSelf, t_node ,Expr True)]@l]
155 | [ s = step ->  s ]
156 ];
157
158 step : [
159   (* yurk, this is done to parse stuff like
160      a/b/descendant/a where descendant is actually a tag name :(
161      if OPT is None then this is a child::descendant if not, this is a real axis name
162   *)
163
164
165 [ axis = axis ; o = OPT ["::" ; t = test -> t ] ; p = top_pred  ->
166     let a,t,p =
167       match o with
168       | Some(t) ->  (axis,t,p)
169       | None -> (Child,Simple (QNameSet.singleton (QName.of_string (axis_to_string axis))),p)
170     in match a with
171       | Following -> [ (DescendantOrSelf,t,p); 
172               (FollowingSibling, t_star,Expr(True));
173                (Ancestor, t_star ,Expr(True)) ]
174
175       | Preceding -> [ (DescendantOrSelf,t,p);
176                        (PrecedingSibling,t_star,Expr(True));
177                        (Ancestor,t_star,Expr(True)) ]
178       | _ -> [ a,t,p ]
179
180 ]
181
182 | [ "." ; p = top_pred ->  [(Self, t_node,p)]  ]
183 | [ ".." ; p = top_pred ->  [(Parent,t_star,p)]  ]
184 | [ test = test; p = top_pred  -> [(Child,test, p)] ]
185 | [ att = ATT ; p = top_pred ->
186       match att with
187       | "*" -> [(Attribute,t_star,p)]
188       | _ ->  [(Attribute, Simple (QNameSet.singleton (QName.of_string att)) ,p )]]
189 ]
190 ;
191 top_pred  : [
192   [ p = OPT [ "["; p=predicate ;"]" -> p ] -> predopt p ]
193 ]
194 ;
195 axis : [
196   [ "self" -> Self | "child" -> Child | "descendant" -> Descendant
197       | "descendant-or-self" -> DescendantOrSelf
198       | "ancestor-or-self" -> AncestorOrSelf
199       | "following-sibling" -> FollowingSibling
200       | "attribute" -> Attribute
201       | "parent" -> Parent
202       | "ancestor" -> Ancestor
203       | "preceding-sibling" -> PrecedingSibling
204       | "preceding" -> Preceding
205       | "following" -> Following
206   ]
207
208
209 ];
210 test : [
211   [ s = KWD -> Simple (test_of_keyword s _loc) ]
212 | [ t = TAG -> Simple (QNameSet.singleton (QName.of_string t)) ]
213 ];
214
215
216 predicate: [
217
218  [ p = predicate; "or"; q = predicate -> Or(p,q) ]
219 | [ p = predicate; "and"; q = predicate -> And(p,q) ]
220 | [ "not" ; p = predicate -> Not p ]
221 | [ "("; p = predicate ;")" -> p ]
222 |  [ e = expression -> Expr e ]
223 ];
224
225 expression: [
226   [ f = TAG; "("; args = LIST0 expression SEP "," ; ")" -> Function(f,args)]
227 | [ `INT(i) -> Int (i) ]
228 | [ s = STRING -> String s ]
229 | [ p = path -> Path p ]
230 | [ "("; e = expression ; ")" -> e ]
231 ]
232 ;
233 END
234 ;;
235 (*
236
237 GLOBAL: query;
238
239  query : [ [ p = location_path; `EOI -> p ]]
240 ;
241
242
243  location_path : [
244   [ "/" ; l = OPT relative_location_path ->
245          let l = match l with None -> [] | Some l' -> l' in Absolute l ]
246  |  [ l = relative_location_path -> Relative l ]
247  | [ l = abbrev_absolute_location_path -> l ]
248
249  ]
250 ;
251
252  relative_location_path : [
253    [ s = step -> [ s ] ]
254  | [ l = relative_location_path ; "/"; s = step -> l @ [ s ] ]
255  | [ l = abbrev_relative_location_path -> l ]
256  ]
257 ;
258
259
260  step : [
261    [ a = axis_specifier ; n = node_test ; p = OPT predicate ->
262       let p = match p with Some p' -> p' | None -> Expr(True) in
263             a, n, p
264    ]
265  | [ a = abbrev_step -> a ]
266  ]
267 ;
268  axis_specifier : [
269    [ a = axis_name ; "::" -> a ]
270  | [ a = abbrev_axis_specifier -> a ]
271  ];
272
273  axis_name : [
274   [ "self" -> Self | "child" -> Child | "descendant" -> Descendant
275       | "descendant-or-self" -> DescendantOrSelf
276       | "ancestor-or-self" -> AncestorOrSelf
277       | "following-sibling" -> FollowingSibling
278       | "attribute" -> Attribute
279       | "parent" -> Parent
280       | "ancestor" -> Ancestor
281       | "preceding-sibling" -> PrecedingSibling
282       | "preceding" -> Preceding
283       | "following" -> Following
284   ]
285  ]
286 ;
287  node_test : [
288    [ n = name_test -> n ]
289  | [ n = node_type ; "("; ")" -> n ]
290  (* | [ "processing-instruction" ; "(" ... ")" ] *)
291  ]
292 ;
293  name_test : [
294    [ "*" -> Simple(TagSet.star) ]
295  | [ t = axis_name -> Simple(TagSet.singleton (Tag.tag (axis_to_string t))) ]
296  | [ t = TAG -> Simple(TagSet.singleton (Tag.tag t)) ]
297  ]
298 ;
299  node_type : [
300    [ "text" -> Simple(TagSet.pcdata) ]
301  | [ "node" -> Simple(TagSet.node) ]
302  ]
303 ;
304  predicate : [
305    [ "["; e = expr ; "]" -> e ]
306  ]
307 ;
308  abbrev_absolute_location_path : [
309    [ "//"; l = relative_location_path -> AbsoluteDoS l ]
310  ];
311
312  abbrev_relative_location_path : [
313    [  l = relative_location_path; "//"; s = step ->
314    l @ [ (DescendantOrSelf,Simple(TagSet.node),Expr(True)); s ]
315    ]
316  ];
317
318  abbrev_step : [
319    [ "." -> (Self, Simple(TagSet.node), Expr(True)) ]
320  | [ ".." -> (Parent, Simple(TagSet.node), Expr(True)) ]
321  ];
322
323  abbrev_axis_specifier: [
324    [ a = OPT "@" -> match a with None -> Attribute | _ -> Child ]
325  ];
326
327  expr : [
328    [ o = or_expr -> o ]
329  ];
330
331  primary_expr : [
332    [ "("; e = expr ; ")" -> e ]
333  | [ s = STRING -> Expr (String s) ]
334  | [ `INT(i) -> Expr (Int (i)) ]
335  | [ f = TAG; "("; args = LIST0 expr SEP "," ; ")" ->
336     Expr(Function(f, List.map (function Expr e -> e | _ -> assert false) args))]
337  ]
338 ;
339
340  or_expr : [
341     [ o1 = or_expr ; "or" ; o2 = and_expr -> Or(o1, o2) ]
342  |  [ a = and_expr -> a ]
343  ]
344  ;
345
346  and_expr : [
347    [ a1 = and_expr; "and"; a2 = unary_expr -> And(a1, a2) ]
348  | [ p = unary_expr -> p ]
349  ]
350 ;
351  unary_expr : [
352    [ l = location_path  -> Expr(Path l) ]
353  | [ "not"; "("; e = expr ; ")" -> Not e ]
354  | [ p = primary_expr ->  p ]
355
356  ];
357
358 END
359 ;;
360
361 *)
362
363   let parse_string = Gram.parse_string query (Ulexer.Loc.mk "<string>")
364   let parse_file fd = parse_string (input_line fd)
365
366 end
367 let parse_string = Parser.parse_string
368 let parse_file = Parser.parse_file