3 * Stefano Zacchiroli <zack@cs.unibo.it>
4 * for the HELM Team http://helm.cs.unibo.it/
6 * This file is part of HELM, an Hypertextual, Electronic
7 * Library of Mathematics, developed at the Computer Science
8 * Department, University of Bologna, Italy.
10 * HELM is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * HELM is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with HELM; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * For details, see the HELM World-Wide-Web page,
26 * http://helm.cs.unibo.it/
36 let debug = 2;; (* 0 -> no debug
37 1 -> waiting for an answer / answer received
38 2 -> XML messages dumping
41 exception Attribute_not_found of string;;
42 exception Empty_node;; (** found a node with no _element_ children *)
43 exception No_element_found of string;;
44 exception Parse_error of string * string;; (* parsing subject, reason *)
45 exception Unexpected_message of message;;
47 let is_xml_element n = match n#node_type with T_element _ -> true | _ -> false
48 let get_attr node name =
50 (match node#attribute name with
52 | _ -> raise Not_found)
53 with Not_found -> raise (Attribute_not_found name)
54 let assert_element n name =
55 match n#node_type with
56 | T_element n when n = name ->
58 | _ -> raise (Parse_error ("", "Expected node: " ^ name))
60 (** given a string representation of a proof asistant state (e.g. the first
61 child of the XML root of a State_change or Start_musing message), build from
62 it an HBugs view of a proof assistant state *)
63 let parse_state (root: ('a node extension as 'a) node) =
64 if (List.filter is_xml_element root#sub_nodes) = [] then
66 let buf = Buffer.create 10240 in
67 let node_to_string (node: ('a node extension as 'a) node) =
69 node#write (`Out_buffer buf) `Enc_utf8;
70 let res = Buffer.contents buf in
74 let (goal_node, type_node, body_node) =
76 (find_element "CurrentGoal" root,
77 find_element "ConstantType" root,
78 find_element "CurrentProof" root)
80 raise (Parse_error ("", "Malformed HBugs status XML document"))
82 assert_element root "gTopLevelStatus";
83 assert_element goal_node "CurrentGoal";
84 assert_element type_node "ConstantType";
85 assert_element body_node "CurrentProof";
86 goal_node#write (`Out_buffer buf) `Enc_utf8;
87 let (type_string, body_string) =
88 (node_to_string type_node, node_to_string body_node)
92 int_of_string (goal_node#data)
93 with Failure "int_of_string" ->
94 raise (Parse_error (goal_node#data, "can't parse goal"))
96 (type_string, body_string, goal)
98 (** parse an hint from an XML node, XML node should have type 'T_element _'
99 (the name is ignored), attributes on it are ignored *)
100 let parse_hint node =
101 let rec parse_hint_node node =
102 match node#node_type with
103 | T_element "ring" -> Use_ring
104 | T_element "fourier" -> Use_fourier
105 | T_element "reflexivity" -> Use_reflexivity
106 | T_element "symmetry" -> Use_symmetry
107 | T_element "assumption" -> Use_assumption
108 | T_element "contradiction" -> Use_contradiction
109 | T_element "exists" -> Use_exists
110 | T_element "split" -> Use_split
111 | T_element "left" -> Use_left
112 | T_element "right" -> Use_right
113 | T_element "apply" -> Use_apply node#data
114 | T_element "hints" ->
116 (List.map parse_hint_node (List.filter is_xml_element node#sub_nodes))
117 | _ -> assert false (* CSC: should this assert false be a raise something? *)
119 match List.filter is_xml_element node#sub_nodes with
120 [node] -> parse_hint_node node
121 | _ -> assert false (* CSC: should this assert false be a raise something? *)
123 let parse_hint_type n = n#data (* TODO parsare il possibile tipo di suggerimento *)
124 let parse_tutor_dscs n =
126 (fun n -> (get_attr n "id", n#data))
127 (List.filter is_xml_element n#sub_nodes)
128 let parse_tutor_ids node =
130 (fun n -> get_attr n "id") (List.filter is_xml_element node#sub_nodes)
132 let tutors_sep = Pcre.regexp ",\\s*"
134 let pxp_config = PxpHelmConf.pxp_config
135 let msg_of_string' s =
136 let root = (* xml tree's root *)
137 parse_wfcontent_entity pxp_config (from_string s) PxpHelmConf.pxp_spec
139 match root#node_type with
141 (* general purpose *)
142 | T_element "help" -> Help
143 | T_element "usage" -> Usage root#data
144 | T_element "exception" -> Exception (get_attr root "name", root#data)
146 (* client -> broker *)
147 | T_element "register_client" ->
148 Register_client (get_attr root "id", get_attr root "url")
149 | T_element "unregister_client" -> Unregister_client (get_attr root "id")
150 | T_element "list_tutors" -> List_tutors (get_attr root "id")
151 | T_element "subscribe" ->
152 Subscribe (get_attr root "id", parse_tutor_ids root)
153 | T_element "state_change" ->
156 Some (find_element ~deeply:false "gTopLevelStatus" root)
157 with Not_found -> None
161 match state_node with
162 | Some n -> (try Some (parse_state n) with Empty_node -> None)
164 | T_element "wow" -> Wow (get_attr root "id")
166 (* tutor -> broker *)
167 | T_element "register_tutor" ->
168 let hint_node = find_element "hint_type" root in
169 let dsc_node = find_element "description" root in
171 (get_attr root "id", get_attr root "url",
172 parse_hint_type hint_node, dsc_node#data)
173 | T_element "unregister_tutor" -> Unregister_tutor (get_attr root "id")
174 | T_element "musing_started" ->
175 Musing_started (get_attr root "id", get_attr root "musing_id")
176 | T_element "musing_aborted" ->
177 Musing_started (get_attr root "id", get_attr root "musing_id")
178 | T_element "musing_completed" ->
181 find_element "eureka" root
182 with Not_found -> find_element "sorry" root
185 (get_attr root "id", get_attr root "musing_id",
186 (match main_node#node_type with
187 | T_element "eureka" ->
188 Eureka (parse_hint main_node)
189 | T_element "sorry" -> Sorry
190 | _ -> assert false)) (* can't be there, see 'find_element' above *)
192 (* broker -> client *)
193 | T_element "client_registered" -> Client_registered (get_attr root "id")
194 | T_element "client_unregistered" -> Client_unregistered (get_attr root "id")
195 | T_element "tutor_list" ->
196 Tutor_list (get_attr root "id", parse_tutor_dscs root)
197 | T_element "subscribed" ->
198 Subscribed (get_attr root "id", parse_tutor_ids root)
199 | T_element "state_accepted" ->
203 (fun n -> get_attr n "id")
204 (List.filter is_xml_element (find_element "stopped" root)#sub_nodes),
206 (fun n -> get_attr n "id")
207 (List.filter is_xml_element (find_element "started" root)#sub_nodes))
208 | T_element "hint" -> Hint (get_attr root "id", parse_hint root)
210 (* broker -> tutor *)
211 | T_element "tutor_registered" -> Tutor_registered (get_attr root "id")
212 | T_element "tutor_unregistered" -> Tutor_unregistered (get_attr root "id")
213 | T_element "start_musing" ->
216 find_element ~deeply:false "gTopLevelStatus" root
217 with Not_found -> raise (No_element_found "gTopLevelStatus")
219 Start_musing (get_attr root "id", parse_state state_node)
220 | T_element "abort_musing" ->
221 Abort_musing (get_attr root "id", get_attr root "musing_id")
222 | T_element "thanks" -> Thanks (get_attr root "id", get_attr root "musing_id")
223 | T_element "too_late" ->
224 Too_late (get_attr root "id", get_attr root "musing_id")
226 | _ -> raise (No_element_found s)
228 let msg_of_string s =
231 with e -> raise (Parse_error (s, Printexc.to_string e))
233 let pp_state = function
234 | Some (type_string, body_string, goal) ->
235 (* ASSUMPTION: type_string and body_string are well formed XML document
236 contents (i.e. they don't contain heading <?xml ... ?> declaration nor
238 "<gTopLevelStatus>\n" ^
239 (sprintf "<CurrentGoal>%d</CurrentGoal>\n" goal) ^
242 "</gTopLevelStatus>\n"
243 | None -> "<gTopLevelStatus />\n"
245 let rec pp_hint = function
246 | Use_ring -> sprintf "<ring />"
247 | Use_fourier -> sprintf "<fourier />"
248 | Use_reflexivity -> sprintf "<reflexivity />"
249 | Use_symmetry -> sprintf "<symmetry />"
250 | Use_assumption -> sprintf "<assumption />"
251 | Use_contradiction -> sprintf "<contradiction />"
252 | Use_exists -> sprintf "<exists />"
253 | Use_split -> sprintf "<split />"
254 | Use_left -> sprintf "<left />"
255 | Use_right -> sprintf "<right />"
256 | Use_apply term -> sprintf "<apply>%s</apply>" term
258 sprintf "<hints>\n%s\n</hints>"
259 (String.concat "\n" (List.map pp_hint hints))
261 let pp_hint_type s = s (* TODO pretty print hint_type *)
265 sprintf "%s<tutor_dsc id=\"%s\">%s</tutor_dsc>" s id dsc)
268 List.fold_left (fun s id -> sprintf "%s<tutor id=\"%s\" />" s id) ""
270 let string_of_msg = function
272 | Usage usage_string -> sprintf "<usage>%s</usage>" usage_string
273 | Exception (name, value) ->
274 sprintf "<exception name=\"%s\">%s</exception>" name value
275 | Register_client (id, url) ->
276 sprintf "<register_client id=\"%s\" url=\"%s\" />" id url
277 | Unregister_client id -> sprintf "<unregister_client id=\"%s\" />" id
278 | List_tutors id -> sprintf "<list_tutors id=\"%s\" />" id
279 | Subscribe (id, tutor_ids) ->
280 sprintf "<subscribe id=\"%s\">%s</subscribe>"
281 id (pp_tutor_ids tutor_ids)
282 | State_change (id, state) ->
283 sprintf "<state_change id=\"%s\">%s</state_change>"
285 | Wow id -> sprintf "<wow id=\"%s\" />" id
286 | Register_tutor (id, url, hint_type, dsc) ->
288 "<register_tutor id=\"%s\" url=\"%s\">
289 <hint_type>%s</hint_type>
290 <description>%s</description>
292 id url (pp_hint_type hint_type) dsc
293 | Unregister_tutor id -> sprintf "<unregister_tutor id=\"%s\" />" id
294 | Musing_started (id, musing_id) ->
295 sprintf "<musing_started id=\"%s\" musing_id=\"%s\" />" id musing_id
296 | Musing_aborted (id, musing_id) ->
297 sprintf "<musing_aborted id=\"%s\" musing_id=\"%s\" />" id musing_id
298 | Musing_completed (id, musing_id, result) ->
300 "<musing_completed id=\"%s\" musing_id=\"%s\">%s</musing_completed>"
303 | Sorry -> "<sorry />"
304 | Eureka hint -> sprintf "<eureka>%s</eureka>" (pp_hint hint))
305 | Client_registered id -> sprintf "<client_registered id=\"%s\" />" id
306 | Client_unregistered id -> sprintf "<client_unregistered id=\"%s\" />" id
307 | Tutor_list (id, tutor_dscs) ->
308 sprintf "<tutor_list id=\"%s\">%s</tutor_list>"
309 id (pp_tutor_dscs tutor_dscs)
310 | Subscribed (id, tutor_ids) ->
311 sprintf "<subscribed id=\"%s\">%s</subscribed>"
312 id (pp_tutor_ids tutor_ids)
313 | State_accepted (id, stop_ids, start_ids) ->
315 "<state_accepted id=\"%s\">
316 <stopped>%s</stopped>
317 <started>%s</started>
321 (List.map (fun id -> sprintf "<musing id=\"%s\" />" id) stop_ids))
323 (List.map (fun id -> sprintf "<musing id=\"%s\" />" id) start_ids))
324 | Hint (id, hint) -> sprintf "<hint id=\"%s\">%s</hint>" id (pp_hint hint)
325 | Tutor_registered id -> sprintf "<tutor_registered id=\"%s\" />" id
326 | Tutor_unregistered id -> sprintf "<tutor_unregistered id=\"%s\" />" id
327 | Start_musing (id, state) ->
328 sprintf "<start_musing id=\"%s\">%s</start_musing>"
329 id (pp_state (Some state))
330 | Abort_musing (id, musing_id) ->
331 sprintf "<abort_musing id=\"%s\" musing_id=\"%s\" />" id musing_id
332 | Thanks (id, musing_id) ->
333 sprintf "<thanks id=\"%s\" musing_id=\"%s\" />" id musing_id
334 | Too_late (id, musing_id) ->
335 sprintf "<too_late id=\"%s\" musing_id=\"%s\" />" id musing_id
338 (* debugging function that dump on stderr the sent messages *)
342 (sprintf "<SENDING_MESSAGE>\n%s\n</SENDING_MESSAGE>"
344 | State_change _ -> "<state_change>omissis ...</state_change>"
345 | msg -> string_of_msg msg))
348 let submit_req ~url msg =
350 if debug >= 1 then (prerr_string "Waiting for an answer ... "; flush stderr);
352 msg_of_string (Hbugs_misc.http_post ~body:(string_of_msg msg) url)
354 if debug >= 1 then (prerr_string "answer received!\n"; flush stderr);
357 let return_xml_msg body outchan =
358 Http_daemon.respond ~headers:["Content-Type", "text/xml"] ~body outchan
360 let respond_msg msg outchan =
362 return_xml_msg (string_of_msg msg) outchan
363 (* close_out outchan *)
365 let respond_exc name value = respond_msg (Exception (name, value));;