Remove support for outdated libGrammar, replaced by Grammar2
[SXSI/xpathcomp.git] / src / grammar2.ml
1 type t = {
2   start : Bp.t;
3   tags : int array;
4   rules : int array;
5   rules_offset : int;
6   tag_to_id : (string, int) Hashtbl.t;
7   tag_of_id : string array
8 }
9
10
11
12 module Parse =
13 struct
14
15   let buffer = Buffer.create 512
16
17   let parse_tree cin open_tag close_tag =
18     let rec loop () =
19       let c = input_char cin in
20       match c with
21         '\n'| '>' -> ()
22       | ' ' | ',' | '-' -> loop ()
23       | 'a'..'z' | 'B'..'Z' | '0'..'9' | '_' ->
24         Buffer.clear buffer;
25         Buffer.add_char buffer c;
26         loop_tag false
27
28       | 'A' ->  Buffer.clear buffer;
29         Buffer.add_char buffer c;
30         loop_tag true
31       | ')' -> close_tag (); loop ()
32       | _ -> failwith ("Invalid character: " ^ (String.make 1 c))
33
34     and loop_tag t =
35       let c = input_char cin in
36       match c with
37       | 'a'..'z' | 'A'..'Z' | '0'..'9' | '_' ->
38         Buffer.add_char buffer c;
39         loop_tag t
40       | '(' -> let s = Buffer.contents buffer in
41                open_tag s t;
42                Buffer.clear buffer;
43                loop ()
44       | ' ' -> loop_tag t
45       | ',' | '-'  -> let s = Buffer.contents buffer in
46                       open_tag s t;
47                       close_tag ();
48                       Buffer.clear buffer;
49                       loop ()
50       | ')' -> let s = Buffer.contents buffer in
51                open_tag s t;
52                Buffer.clear buffer;
53                close_tag ();
54                close_tag ();
55                loop ()
56       | _ -> failwith ("Invalid character: " ^ (String.make 1 c))
57     in
58     loop ()
59
60
61   let tag_info = Hashtbl.create 1023
62   let tag_of_id  = Hashtbl.create 1023
63   let current_id = ref 4
64   let init() =
65     Hashtbl.clear tag_info;
66     Hashtbl.clear tag_of_id;
67     current_id := 4;
68     Hashtbl.add tag_info "_ROOT" (0, ~-1, false);
69     Hashtbl.add tag_info "_A" (1, ~-1, false);
70     Hashtbl.add tag_info "_T" (2, ~-1, false);
71     Hashtbl.add tag_info "_AT" (3, ~-1, false);
72     Hashtbl.add tag_info "_"  (4, ~-1, false);
73     Hashtbl.add tag_of_id 0 "_ROOT";
74     Hashtbl.add tag_of_id 1 "_A";
75     Hashtbl.add tag_of_id 2 "_T";
76     Hashtbl.add tag_of_id 3 "_AT";
77     Hashtbl.add tag_of_id 4 "_"
78
79
80   let add_tag s nterm =
81     let id, count, nterm =
82       try Hashtbl.find tag_info s with
83         Not_found ->
84           incr current_id;
85           let id = !current_id in
86           Hashtbl.add tag_of_id id s;
87           (!current_id, ~-1, nterm || s = "START")
88     in
89     let r = id, count+1, nterm in
90     Hashtbl.replace tag_info s r;
91     r
92
93
94   type tree = Node of string * tree list
95
96   let parse_small_tree cin =
97     let stack = ref [ Node("", []) ] in
98     let open_tag s isnterm =
99       if s <> "y0" && s <> "y1" then ignore(add_tag s isnterm);
100       stack := Node(s, []) :: !stack
101     in
102     let close_tag () =
103       match !stack with
104         Node(t1, l1) :: Node(t2, l2) :: r ->
105           stack := Node(t2, Node(t1, List.rev l1)::l2) :: r
106       | _ -> assert false
107     in
108     parse_tree cin open_tag close_tag;
109     match !stack with
110       [ Node(_, [ l ]) ] -> l
111     | _ -> raise End_of_file
112
113   let parse_big_tree cin =
114     let bv = Bp.bitmap_create () in
115     let tags = IntArray.create () in
116     let open_tag s isnterm =
117       let id, _, _ = add_tag s isnterm in
118       Bp.bitmap_push_back bv 1;
119       IntArray.push_back tags id
120     in
121     let close_tag () =
122       Bp.bitmap_push_back bv 0
123     in
124     parse_tree cin open_tag close_tag;
125     Bp.create bv, IntArray.pack tags
126
127   let eat_char cin = ignore (input_char cin)
128
129   let h_find ?(msg="") h i =
130     try
131       Hashtbl.find h i
132     with
133       Not_found ->
134         let r = Obj.repr i in
135         if Obj.is_int r then Printf.eprintf "Not_found (%s): %i\n%!" msg (Obj.magic i);
136         if Obj.tag r = Obj.string_tag then Printf.eprintf "Not_found (%s): %s\n%!" msg (Obj.magic i);
137         raise Not_found
138   ;;
139
140   let parse cin =
141     let rules = Hashtbl.create 1023 in
142     init ();
143     (* START *)
144     ignore (parse_small_tree cin);
145     (* > *)
146     (* ignore (input_char cin); *)
147     let bv, tags = parse_big_tree cin in
148     let () =
149       try
150         while true do
151           let lhs = parse_small_tree cin in
152           let rhs = parse_small_tree cin in
153           Hashtbl.add rules lhs rhs
154         done;
155       with End_of_file -> ()
156     in
157     (* First, re-order the tags *)
158     let old_new_mapping =
159       Array.init (Hashtbl.length tag_of_id)
160         (fun i -> h_find ~msg:"1" tag_of_id i)
161     in
162     Array.fast_sort (fun tag1 tag2 ->
163       let t1, count1, isnterm1 =
164         h_find  ~msg:"2" tag_info tag1
165       and t2, count2, isnterm2 =
166         h_find  ~msg:"3" tag_info tag2
167       in
168       if t1 <= 4 && t2 <= 4 then compare t1 t2
169       else if t1 <= 4 then -1
170       else if t2 <= 4 then 1
171       else
172         if (not isnterm1) && (not isnterm2) then compare t1 t2
173         else if isnterm1 && isnterm2 then
174           match tag1, tag2 with
175             "START", "START" -> 0
176           | "START", _ -> ~-1
177           | _, "START" -> 1
178           | _ -> compare count2 count1
179         else if isnterm2 then -1
180         else 1) old_new_mapping;
181     let tag_to_id = Hashtbl.create 503 in
182     Array.iteri (fun i s ->
183       Hashtbl.add tag_to_id s i) old_new_mapping;
184     let renum_tags = Array.copy tags in
185     for i = 0 to Array.length tags - 1 do
186       renum_tags.(i) <-
187         h_find  ~msg:"4" tag_to_id (h_find  ~msg:"5" tag_of_id (tags.(i)))
188     done;
189     let r_array = Array.create (Hashtbl.length rules) 0 in
190     let rules_offset = h_find  ~msg:"6" tag_to_id "START" + 1 in
191     let pos_id2 l =
192       let rec loop i l =
193         match l with
194           [] -> assert false
195         | Node(tag, children) :: ll ->
196           if tag <> "y0" && tag <> "y1" then
197             tag, i
198           else loop (i+1) ll
199       in
200       loop 1 l
201     in
202     Hashtbl.iter (fun lhs rhs ->
203       let Node( head, args ) = lhs in
204       let Node( tag1, params) = rhs in
205       let tag2, pos2 = pos_id2 params in
206       let id1 = h_find ~msg:"7" tag_to_id tag1
207       and id2 = h_find ~msg:"8" tag_to_id tag2 in
208       let conf =
209         if List.length args = 0 then 0
210         else
211           if List.length args = 1 then
212           if List.length params = 1 then 1
213           else if pos2 = 1 then 2
214           else 3
215           else (* 2 parameters *)
216             if List.length params = 1 then 4
217             else if pos2 = 1 then 5
218             else 6
219       in
220       let rule_ = id2 lsl 27 in
221       let rule_ = (rule_ lor id1) lsl 3 in
222       let rule_ = rule_ lor conf in
223       r_array.((h_find  ~msg:"9" tag_to_id head) - rules_offset ) <- rule_
224     ) rules;
225     (*let l = Array.length renum_tags in *)
226     (*let tag32 = Array32.create l 0 in
227     for i = 0 to l - 1 do
228       Array32.set tag32 i (renum_tags.(i) land 0x7ffffff);
229     done; *)
230     (* Remove the non-terminal names from the hash tables *)
231     let tag_to_id2 = Hashtbl.create 31 in
232     Hashtbl.iter (fun s i -> if i < rules_offset then Hashtbl.add tag_to_id2 s i)
233       tag_to_id;
234     { start = bv;
235       tags = renum_tags;
236       rules = r_array;
237       rules_offset = rules_offset;
238       tag_to_id = tag_to_id2;
239       tag_of_id = Array.sub old_new_mapping 0 rules_offset
240     }
241
242 end
243
244 let parse file =
245   let cin = open_in file in
246   let g = Parse.parse cin in
247   close_in cin;
248   g
249
250 let _GRAMMAR_MAGIC = 0xaabbcc
251 let _GRAMMAR_VERSION = 3
252
253 let save g f =
254   let cout = open_out f in
255   let write a = Marshal.to_channel cout a [  ]
256   in
257   write _GRAMMAR_MAGIC;
258   write _GRAMMAR_VERSION;
259   write g.tags;
260   write g.rules;
261   write g.rules_offset;
262   write g.tag_to_id;
263   write g.tag_of_id;
264   flush cout;
265   let fd = Unix.descr_of_out_channel cout in
266   Bp.save g.start fd;
267   close_out cout
268
269 let load f =
270   let cin = open_in f in
271   let read () = Marshal.from_channel cin in
272   if read () != _GRAMMAR_MAGIC then failwith "Invalid grammar file";
273   if read () != _GRAMMAR_VERSION then failwith "Deprecated grammar format";
274   let tags : int array = read () in
275   let rules : int array = read () in
276   let rules_offset : int = read () in
277   let tag_to_id : (string, int) Hashtbl.t = read () in
278   let tag_of_id : string array = read () in
279   let fd = Unix.descr_of_in_channel cin in
280   let pos = pos_in cin in
281   ignore(Unix.lseek fd pos Unix.SEEK_SET);
282   let bp = Bp.load fd in
283   close_in cin;
284   let g = {
285     start = bp;
286     tags = tags;
287     rules = rules;
288     rules_offset = rules_offset;
289     tag_to_id = tag_to_id;
290     tag_of_id = tag_of_id;
291   } in
292   Printf.eprintf "Grammar size:%i kb\n%!"
293     ((Ocaml.size_b g  + Bp.alloc_stats ())/1024);
294   g
295
296
297 type node = [ `Start ] Node.t
298
299 type n_type = [ `NonTerminal ]
300 type t_type = [ `Terminal ]
301 type r_type = [ `Rule ]
302 type any_type = [ n_type | t_type ]
303 type rhs = [ r_type ] Node.t
304
305 type n_symbol = n_type Node.t
306 type t_symbol = t_type Node.t
307 type tn_symbol = [ any_type ] Node.t
308
309
310 type partial =
311     Leaf of node
312   | Node0 of tn_symbol (* No parameters *)
313   | Node1 of tn_symbol * partial
314   | Node2 of tn_symbol * partial * partial
315
316
317 let is_nil  (t : t_symbol) =
318   (Node.to_int t) == 4
319
320 let nil_symbol : t_symbol =
321   (Node.of_int 4)
322
323 let translate_tag _ t  = if t == 4 then ~-1 else t
324 let to_string t tag =
325   if tag < Array.length t.tag_of_id then t.tag_of_id.(Tag.to_int tag)
326   else "<!INVALID TAG!>"
327
328 let register_tag t s =
329   try Hashtbl.find t.tag_to_id s with
330     Not_found -> 4
331
332 let tag_operations t = {
333   Tag.tag = (fun s -> register_tag t s);
334   Tag.to_string = (fun s -> to_string t s);
335   Tag.translate = (fun s -> translate_tag t s);
336 }
337
338 let start_root : node = Node.of_int 0
339 let start_tag g (idx : node) : [<any_type] Node.t =
340   Node.of_int (g.tags.(Bp.preorder_rank g.start (Node.to_int idx)))
341
342 ;;
343
344 let start_first_child t (idx : node) =
345   Node.of_int (Bp.first_child t.start (Node.to_int idx))
346
347 let start_next_sibling t (idx : node) =
348   Node.of_int (Bp.next_sibling t.start (Node.to_int idx))
349
350 let is_non_terminal t (n : [< any_type ] Node.t) =
351   let n = Node.to_int n in
352   n >= t.rules_offset
353
354 let is_terminal t (n : [< any_type ] Node.t) = not(is_non_terminal t n)
355
356 external terminal : [< any_type ] Node.t -> t_symbol = "%identity"
357 external non_terminal : [< any_type ] Node.t -> n_symbol = "%identity"
358
359
360 let tag (n : t_symbol) : Tag.t = Obj.magic n
361
362 let get_rule g (r : n_symbol) : rhs =
363   Node.of_int (g.rules.((Node.to_int r) - g.rules_offset))
364
365 let get_id1 (r : rhs) : tn_symbol =
366   Node.of_int(((Node.to_int r) lsr 3) land 0x7ffffff)
367
368 let get_id2 (r : rhs) : tn_symbol =
369   Node.of_int((Node.to_int r) lsr 30)
370
371 type conf = | C0 (* B(C) *)
372             | C1 (* B(C(y0)) *)
373             | C2 (* B(C, y0) *)
374             | C3 (* B(y0, C) *)
375             | C4 (* B(C(y0, y1)) *)
376             | C5 (* B(C(y0), y1) *)
377             | C6 (* B(y0, C(y1)) *)
378
379 let get_conf (r : rhs) : conf =
380   (Obj.magic ((Node.to_int r) land 0b111))
381
382
383 let get_rank (r : rhs) : int =
384   match get_conf r with
385   | C0 -> 0
386   | C1 | C2 | C3 -> 1
387   | C4 | C5 | C6 -> 2
388
389 let get_id1_rank (r : rhs) : int =
390   match get_conf r with
391   | C0 | C1 | C4 -> 1
392   | _ -> 2
393
394 let get_id2_pos (r : rhs) : int =
395   match get_conf r with
396   | C0 | C1 |C2 | C4 | C5 -> 1
397   | _ -> 2
398
399 let get_id2_rank (r : rhs) : int =
400   match get_conf r with
401   | C0 | C2 | C3 -> 0
402   | C1 | C5 | C6 -> 1
403   | C4 -> 2
404