Add a bullet symbol.
[tatoo.git] / src / naive_tree.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 type node = {
17   tag : QName.t;
18   preorder : int;
19   mutable kind : Tree.NodeKind.t;
20   mutable data : string;
21   mutable first_child : node;
22   mutable next_sibling : node;
23   mutable parent: node;
24 }
25
26
27
28 let rec nil = {
29   tag = QName.nil;
30   kind = Tree.NodeKind.Element;
31   preorder = -1;
32   data = "";
33   first_child = nil;
34   next_sibling = nil;
35   parent = nil;
36 }
37
38 let dummy_tag = QName.of_string "#dummy"
39 let rec dummy = {
40   tag = dummy_tag;
41   kind = Tree.NodeKind.Element;
42   preorder = -1;
43   data = "";
44   first_child = dummy;
45   next_sibling = dummy;
46   parent = dummy;
47 }
48
49
50 type t = {
51   root : node;
52   size : int;
53   (* TODO add other intersting stuff *)
54 }
55
56
57
58 module Parser =
59 struct
60
61   type context = {
62     mutable stack : node list;
63     text_buffer : Buffer.t;
64     mutable current_preorder : int;
65   }
66
67   let print_node_ptr fmt n =
68     Format.fprintf fmt "<%s>"
69       (if n == nil then "NIL" else
70         if n == dummy then "DUMMY" else
71           "NODE " ^  string_of_int n.preorder)
72
73   let debug_node fmt node =
74     Format.fprintf fmt "{ tag=%s; preorder=%i; data=%S; first_child=%a; next_sibling=%a; parent=%a }"
75       (QName.to_string node.tag)
76       node.preorder
77       node.data
78       print_node_ptr node.first_child
79       print_node_ptr node.next_sibling
80       print_node_ptr node.parent
81
82
83   let debug_ctx fmt ctx =
84     Format.fprintf fmt "Current context: { preorder = %i\n; stack = \n%a\n }\n-------------\n"
85       ctx.current_preorder
86       (Pretty.print_list ~sep:";\n" debug_node) ctx.stack
87
88
89   let push n ctx = ctx.stack <- n :: ctx.stack
90   let pop ctx =
91     match ctx.stack with
92       [] -> failwith "XML parse error"
93     | e :: l -> ctx.stack <- l; e
94
95   let top ctx = match ctx.stack with
96     | [] -> failwith "XML parse error"
97     | e :: _ -> e
98
99   let next ctx =
100     let i = ctx.current_preorder in
101     ctx.current_preorder <- 1 + i;
102     i
103
104   let is_left n = n.next_sibling == dummy
105
106
107   let text_string = QName.to_string QName.text
108   let comment_string = QName.to_string QName.comment
109
110
111   let rec start_element_handler parser_ ctx tag attr_list =
112     do_text parser_ ctx;
113     let parent = top ctx in
114     let n = { tag = QName.of_string tag;
115               kind = Tree.NodeKind.Element;
116               preorder = next ctx;
117               data = "";
118               first_child = dummy;
119               next_sibling = dummy;
120               parent = parent;
121             }
122     in
123     if parent.first_child == dummy then parent.first_child <- n
124     else parent.next_sibling <- n;
125     push n ctx;
126     List.iter (do_attribute parser_ ctx) attr_list
127
128   and do_attribute parser_ ctx (att, value) =
129     let att_tag = QName.to_string (QName.attribute (QName.of_string att)) in
130     start_element_handler parser_ ctx att_tag [];
131     let n = top ctx in
132     n.data <- value;
133     n.kind <- Tree.NodeKind.Attribute;
134     end_element_handler parser_ ctx att_tag
135
136   and consume_closing ctx n =
137     if n.next_sibling != dummy then
138       let _ = pop ctx in consume_closing ctx (top ctx)
139
140   and end_element_handler parser_ ctx _ =
141     do_text parser_ ctx;
142     let node = top ctx in
143     if node.first_child == dummy then node.first_child <- nil
144     else begin
145       node.next_sibling <- nil;
146       consume_closing ctx node
147     end
148
149   and do_text parser_ ctx =
150     if Buffer.length ctx.text_buffer != 0 then
151       let s = Buffer.contents ctx.text_buffer in
152       Buffer.clear  ctx.text_buffer;
153       start_element_handler parser_ ctx text_string [];
154       let node = top ctx in
155       node.data <- s;
156       node.kind <- Tree.NodeKind.Text;
157       end_element_handler parser_ ctx text_string
158
159   and comment_handler parser_ ctx s =
160     do_text parser_ ctx;
161     start_element_handler parser_ ctx comment_string [];
162     let node = top ctx in
163     node.data <- s;
164     node.kind <- Tree.NodeKind.Comment;
165     end_element_handler parser_ ctx comment_string
166
167   and processing_instruction_handler parser_ ctx tag data =
168     do_text parser_ ctx;
169     let pi = QName.to_string
170       (QName.processing_instruction (QName.of_string tag))
171     in
172     start_element_handler parser_ ctx pi [];
173     let node = top ctx in
174     node.data <- data;
175     node.kind <- Tree.NodeKind.ProcessingInstruction;
176     end_element_handler parser_ ctx pi
177
178
179   let character_data_handler _parser ctx text =
180     Buffer.add_string ctx.text_buffer text
181
182   let create_parser () =
183     let ctx = { text_buffer = Buffer.create 512;
184                 current_preorder = 0;
185                 stack = [] } in
186     let psr = Expat.parser_create ~encoding:None in
187     Expat.set_start_element_handler psr (start_element_handler psr ctx);
188     Expat.set_end_element_handler psr (end_element_handler psr ctx);
189     Expat.set_character_data_handler
190       psr (character_data_handler psr ctx);
191     Expat.set_comment_handler psr (comment_handler psr ctx);
192     Expat.set_processing_instruction_handler psr
193       (processing_instruction_handler psr ctx);
194     push { tag = QName.document;
195            preorder = next ctx;
196            kind = Tree.NodeKind.Document;
197            data = "";
198            first_child = dummy;
199            next_sibling = dummy;
200            parent = nil;
201          } ctx;
202     (psr,
203      fun () ->
204        let node = top ctx in
205        node.next_sibling <- nil;
206        consume_closing ctx node;
207        Expat.final psr;
208        let root = List.hd ctx.stack in
209        root.next_sibling <- nil;
210        { root = root;
211          size = ctx.current_preorder
212        }
213     )
214
215   let error e parser_ =
216     let msg = Printf.sprintf "%i.%i %s"
217       (Expat.get_current_line_number parser_)
218       (Expat.get_current_column_number parser_)
219       (Expat.xml_error_to_string e)
220     in
221     raise (Tree.Parse_error msg)
222
223   let parse_string s =
224     let parser_, finalize = create_parser () in
225     try
226       Expat.parse parser_ s;
227       finalize ()
228     with
229       Expat.Expat_error e -> error e parser_
230
231   let parse_file fd =
232     let buffer = String.create 4096 in
233     let parser_, finalize = create_parser () in
234     let rec loop () =
235       let read = input fd buffer 0 4096 in
236       if read != 0 then
237         let () = Expat.parse_sub parser_ buffer 0 read in
238         loop ()
239     in try
240          loop (); finalize ()
241       with
242         Expat.Expat_error e -> error e parser_
243
244 end
245
246
247 let load_xml_file = Parser.parse_file
248 let load_xml_string = Parser.parse_string
249
250 let output_escape_string out s =
251   for i = 0 to String.length s - 1 do
252     match s.[i] with
253     | '<' -> output_string out "&lt;"
254     | '>' -> output_string out "&gt;"
255     | '&' -> output_string out "&amp;"
256     | '"' -> output_string out "&quot;"
257     | '\'' -> output_string out "&apos;"
258     | c -> output_char out c
259   done
260
261
262 let rec print_attributes ?(sep=true) out tree_ node =
263   if (node.kind == Tree.NodeKind.Attribute) then
264     let tag = QName.to_string (QName.remove_prefix node.tag) in
265     if sep then output_char out ' ';
266     output_string out tag;
267     output_string out "=\"";
268     output_escape_string out node.data;
269     output_char out '\"';
270     print_attributes out tree_ node.next_sibling
271   else
272     node
273
274 let rec print_xml out tree_ node =
275   if node != nil then
276   let () =
277     let open Tree.NodeKind in
278     match node.kind with
279     | Node -> ()
280     | Text -> output_escape_string out node.data
281     | Element | Document ->
282         let tag = QName.to_string node.tag in
283         output_char out '<';
284         output_string out tag;
285         let fchild = print_attributes out tree_ node.first_child in
286         if fchild == nil then output_string out "/>"
287         else begin
288           output_char out '>';
289           print_xml out tree_ fchild;
290           output_string out "</";
291           output_string out tag;
292           output_char out '>'
293         end
294     | Attribute -> ignore (print_attributes ~sep:false out tree_ node)
295     | Comment ->
296         output_string out "<!--";
297         output_string out node.data;
298         output_string out "-->"
299     | ProcessingInstruction ->
300         output_string out "<?";
301         output_string out (QName.to_string (QName.remove_prefix node.tag));
302         output_char out ' ';
303         output_string out node.data;
304         output_string out "?>"
305   in
306   print_xml out tree_ node.next_sibling
307
308 let print_xml out tree_ node =
309   let nnode =  { node with next_sibling = nil } in print_xml out tree_ nnode
310
311 let root t = t.root
312 let size t = t.size
313 let first_child _ n = n.first_child
314 let next_sibling _ n = n.next_sibling
315 let parent _ n = n.parent
316 let tag _ n = n.tag
317 let data _ n = n.data
318 let kind _ n = n.kind
319 let preorder _ n = n.preorder
320
321 let print_node fmt n = Parser.debug_node fmt n