Download code
From LiteratePrograms
Back to OCAMAWEB/Sources
Download for Windows: single file, zip
Download for UNIX: single file, zip, tar.gz, tar.bz2
ocamaweb.ml
1 (* Copyright (c) 2010 the authors listed at the following URL, and/or 2 the authors of referenced articles or incorporated external code: 3 http://en.literateprograms.org/OCAMAWEB/Sources?action=history&offset=20060320073235 4 5 Permission is hereby granted, free of charge, to any person obtaining 6 a copy of this software and associated documentation files (the 7 "Software"), to deal in the Software without restriction, including 8 without limitation the rights to use, copy, modify, merge, publish, 9 distribute, sublicense, and/or sell copies of the Software, and to 10 permit persons to whom the Software is furnished to do so, subject to 11 the following conditions: 12 13 The above copyright notice and this permission notice shall be 14 included in all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 24 Retrieved from: http://en.literateprograms.org/OCAMAWEB/Sources?oldid=3468 25 *) 26 27 28 let version = "6.0";; 29 30 open Str;; 31 open Xml;; 32 33 (*< 34 \section{Fonction globales et définitions} 35 36 La variable d'environnement {\sf OCAMAWEB} est utilisée comme 37 chemin pour le fichier de log (son nom est \code{"ocamaweb.log"}) 38 et pour le fichier {\tt ocamaweb.sty}. 39 40 41 La variable d'environnement {\sf OCAMAWEB\_DEST} est utilisée comme 42 repository de fichiers de doc. 43 44 Attention: j'ai besoin du slash de fin! 45 46 *) 47 48 let log_file = 49 try 50 open_out ((Sys.getenv "OCAMAWEB") ^ "ocamaweb.log") 51 with Not_found -> 52 Printf.fprintf stderr "OCAMAWEB: WARNING! WARNING! WARNING!\n environment variable 'OCAMAWEB' not defined!\n"; 53 open_out "ocamaweb.log" 54 ;; 55 56 (* to be used as output for undefined special keys *) 57 let not_defined = "***NOT DEFINED***";; 58 59 let root_string = "***ROOT***";; 60 61 let empty_string = "***EMPTY***";; 62 63 let relations = Hashtbl.create 20;; 64 65 let sub_relations = Hashtbl.create 20;; 66 67 68 (*< 69 \subsection{parsing d'une ligne} 70 Lors du parcours du fichier de CODE, chaque ligne va se voir attribuer un type [state].\\ 71 Les différents éléments de ce type sont : 72 \begin{itemize} 73 74 \item \code{Into_comments} : il s'agit d'une ligne de commentaire "classique" 75 (elle fera partie soit du code soit des commentaires du bloc suivant sa position 76 relative p/r aux autres lignes) 77 78 \item \code{Begin_section} : il s'agit de la déclaration d'une section (par %%) 79 (de cette ligne on va tirer le titre -d'où la référence sera déduite- et le début 80 des commentaires de bloc) 81 82 \item \code{Begin_subsection} : il s'agit d'une section incluse (par %<) 83 (de cette ligne on va tirer le titre -d'où la référence sera déduite- et le début 84 des commentaires de bloc) 85 86 \item \code{End_subsection} : il s'agit d'une ligne de code 87 88 \item \code{Begin_file} : c'est l'état en début de fichier 89 90 \item \code{Unknown} : c'est un état piégeant destiné aux erreurs 91 \end{itemize} 92 *) 93 94 type state = Into_comments | Begin_section | Begin_subsection | 95 End_subsection | Into_code | Begin_file | Unknown;; 96 97 type action = Add_comment | Add_code | Add_comments_into_code | New_section | 98 New_subsection | Into_first_section | Close_subsection | Stop;; 99 100 (* used to attribute a level to sections *) 101 type state_level = Not_relevant | Star | Double_star | Level of string;; 102 103 (* j'ai le titre X le nombre d'espaces X un code de référence (numéro d'ordre) *) 104 type indented_ref = Indented_ref of string * int * int;; 105 106 (* quelques utilitaires pour clarifier le code *) 107 let next1 s = 108 try 109 (String.sub s 1 ((String.length s) - 1)) 110 with Invalid_argument( ia_s) -> Printf.fprintf log_file "next1: String.sub problem on %s\n" s; 111 (* flush log_file; *) 112 "" 113 ;; 114 115 let next2 s = 116 try 117 (String.sub s 2 ((String.length s) - 2)) 118 with Invalid_argument( ia_s) -> Printf.fprintf log_file "next2: String.sub problem on %s\n" s; 119 (* flush log_file; *) 120 "" 121 ;; 122 123 let char1 s = 124 try 125 (String.sub s 0 1) 126 with Invalid_argument( ia_s) -> Printf.fprintf log_file "char1: String.sub problem on %s\n" s; 127 (* flush log_file; *) 128 "" 129 ;; 130 131 let char1_c s = (String.get s 0);; 132 133 let char2 s = 134 try 135 (String.sub s 0 2) 136 with Invalid_argument( ia_s) -> Printf.fprintf log_file "char2: String.sub problem on %s\n" s; 137 (* flush log_file; *) 138 "" 139 ;; 140 141 142 (*< 143 \subsection{Récupération de variables globales\label{sec:keys:glob}} 144 145 Les mots clefs suivants sont associés à des variables "globales" qui seront utilisées pour 146 l'en-tête de la documentation. 147 \def\keyline#1#2#3#4{\tt #1 & \tt #2 & \tt #3 & \tt #4\\} 148 \def\globalkeys{% 149 \begin{center} 150 \begin{tabular}{|l|l|c|c|}\hline 151 clef & déclencheur & début & fin\\\hline\hline 152 \keyline{author}{node.author}{'}{'} 153 \keyline{}{\% author}{'}{'} 154 \keyline{}{\% auteur}{'}{'}\hline 155 \keyline{title}{\% titre}{'}{'} 156 \keyline{}{\% title}{'}{'}\hline 157 \keyline{project}{\% project}{'}{'} 158 \keyline{}{\% projet}{'}{'}\hline 159 \keyline{mailto}{node.mailto}{'}{'} 160 \keyline{}{\% mailto}{'}{'}\hline 161 \keyline{date}{node.date}{'}{'} 162 \keyline{}{\% date}{'}{'}\hline 163 \keyline{version}{VERSION}{=}{;} 164 \keyline{}{\% version}{'}{'}\hline 165 \end{tabular} 166 \end{center}}\globalkeys 167 Je vais utiliser la structure suivante : 168 \begin{itemize} 169 \item une hashtable qui contient une description de chaque association de clef 170 (cf tableau plus haut) avec un entier comme index. 171 Une telle description est une structure de type {\tt key\_pattern}, qui contient comme 172 champs : 173 \begin{itemize} 174 \item {\tt name\_key} : le nom de la clef. 175 \item {\tt trigger\_pat} : le "déclencheur" 176 \item {\tt before\_pat} : le tag de début 177 \item {\tt after\_pat} : le tag de fin 178 \item {\tt is\_found} : un booléen qui signale si cette clef a déjà été trouvée dans le texte 179 \item {\tt value} : la valeur de la clef 180 \end{itemize} 181 Pour résumer, la clef {\tt name\_key} prend sa valeur dans la zone crochetée du pattern :\\ 182 {\tt ... trigger\_pat ... before\_pat [ ... ] after\_pat} 183 \item un type {\tt Line( string, bool, int)} qui permet de décrire l'état de la ligne en 184 cours de recherche de clef.\\ 185 {\tt Line( S, B, K)} décrit la ligne de contenu {\tt S} dans laquelle une clef a déjà été trouvée 186 si {\tt B} est à {\it true}, et ne l'a pas si {\tt B} est à {\it false}. Lorsque {\tt B} vaut 187 {\it false} : {\tt K} vaut $-1$, et sinon il vaut le numéro de la clef trouvée. 188 \end{itemize} 189 *) 190 191 type key_pattern = { name_key : string; 192 trigger_pat : string; 193 before_pat : string; 194 after_pat : string; 195 mutable is_found : bool; 196 mutable value : string; 197 } ;; 198 199 let build_key_pattern name trigger before after = 200 { name_key = name; 201 trigger_pat = trigger; 202 before_pat = before; 203 after_pat = after; 204 is_found = false; 205 value = ""; 206 } ;; 207 208 let specify_key_pattern name value = 209 { name_key = name; 210 trigger_pat = not_defined; 211 before_pat = not_defined; 212 after_pat = not_defined; 213 is_found = true; 214 value = value; 215 } ;; 216 217 let keys_counter = ref 0;; 218 let keys_hashtbl = Hashtbl.create 20;; 219 220 let add_key_pattern name trigger before after = 221 Hashtbl.add keys_hashtbl !keys_counter 222 (build_key_pattern name trigger before after); 223 keys_counter := !keys_counter + 1 224 ;; 225 226 let add_specified_key_pattern name value = 227 Hashtbl.add keys_hashtbl !keys_counter 228 (specify_key_pattern name value); 229 keys_counter := !keys_counter + 1 230 ;; 231 232 (* one line is the line string X a boolean (found or not) X the key into the hashtable 233 a "neutral line" is (string, false, -1) 234 a "resolved line" is (string, true, nb) with nb a key in keys_hashtbl 235 *) 236 type line_state_for_keys = Line of string * bool * int;; 237 238 (*< 239 \subsubsection{Recherche de sous chaînes de charactères} 240 241 Je n'ai pas trouvé en {\sf ocaml} de fonction {\tt String.index\_substring str1 str2} qui 242 renvoie la position de {\tt str2} dans {\tt str1}. 243 244 J'ai donc implémenté les fonctions {\tt compare\_strings} et {\tt contains\_substring} qui 245 émulent cette fonctionnalité. J'ai recours pour cela à {\tt contains\_substring\_rec} qui 246 cherche récursivement la position d'une chaîne dans un string. 247 *) 248 let compare_strings str1 str2 = 249 let len = min (String.length str1) (String.length str2) in 250 try 251 String.sub str1 0 len = String.sub str2 0 len 252 with Invalid_argument( ia_s) -> Printf.fprintf log_file "compare_...: String.sub problem on %s\n" str1; 253 (* flush log_file; *) 254 false 255 ;; 256 257 (* use: contains_substring str substr (String.length str) (String. length substr) 0 258 return the index of the end of substr into str, raises Not_found if not found *) 259 let rec contains_substring_rec str sub_str l sl n = 260 if char1 str = char1 sub_str then 261 begin 262 if compare_strings str sub_str then 263 n + sl 264 else 265 if l > sl then 266 contains_substring_rec (next1 str) sub_str (l - 1) sl (n + 1) 267 else 268 raise Not_found; 269 end 270 else 271 if l > sl then 272 contains_substring_rec (next1 str) sub_str (l - 1) sl (n + 1) 273 else 274 raise Not_found; 275 ;; 276 277 (* I do not understand why this does not exist in ocaml!! 278 returns the index of the end of substr into str or raises Not_found *) 279 let contains_substring str substr = 280 contains_substring_rec str substr (String.length str) (String.length substr) 0;; 281 (*> *) 282 283 (*< 284 \subsubsection{Recherche des occurences des clefs dans un string} 285 286 J'utilise la fonction {\tt contains\_substring} pour déterminer si une des clefs contenues dans la hashtable 287 {\tt keys\_hastbl} est dans un string donné. 288 *) 289 (* to be used as : (confront_key_to_string k d str) *) 290 let confront_key_to_string k key one_line = 291 match one_line with 292 Line( content, is_found, _) -> 293 begin 294 if is_found || key.is_found then 295 one_line 296 else 297 try 298 (* If the key trigger is in the line *) 299 let deb = contains_substring content key.trigger_pat in 300 begin 301 try 302 Line( String.sub content deb ((String.length content) - deb) , true, k) 303 with Invalid_argument( ia_s) -> 304 Printf.fprintf log_file "confront_...: String.sub problem on %s\n" content; 305 (* flush log_file; *) 306 Line( content , false, -1) 307 end 308 with 309 Not_found -> 310 Line( content, false, -1) 311 end 312 ;; 313 (*> *) 314 315 (* to find which key is in the string *) 316 let has_key one_string = 317 if String.length one_string = 0 then 318 false 319 else 320 let line_s = Line( one_string, false, -1) in 321 let is_it_a_key = Hashtbl.fold confront_key_to_string keys_hashtbl line_s in 322 match is_it_a_key with 323 Line( str, is_found, nb) -> 324 if is_found then 325 let this_key = Hashtbl.find keys_hashtbl nb in 326 try 327 (* extraction du morceau important *) 328 let find_first = contains_substring str this_key.before_pat in 329 let first_part = String.sub str find_first ((String.length str) - find_first) in 330 (* en fait c'est bon : 331 - si c'est le dernier 332 - ou si le suivant n'est pas le même *) 333 let find_last = contains_substring first_part this_key.after_pat in 334 let this_value = String.sub first_part 0 (find_last - (String.length this_key.after_pat)) in 335 this_key.is_found <- true; 336 this_key.value <- this_value; 337 Printf.fprintf log_file "KEY: %s -> %s\n" this_value this_key.name_key; 338 Hashtbl.replace keys_hashtbl nb this_key; 339 true; 340 with 341 Not_found -> false 342 | Invalid_argument( ia_s) -> 343 Printf.fprintf log_file "has_key: String.sub problem on %s\n" str; 344 (* flush log_file; *) 345 false 346 else 347 false 348 ;; 349 350 (* a key value is N x B x S, (N is a key name) where B is false is S is not relevant, true otherwise. *) 351 type key_value = Key_value of string * bool * string;; 352 353 (* to be used on the keys hashtable to get the value of a key *) 354 let get_key_value_fold index key current_key_value = 355 match current_key_value with 356 Key_value( name, is_found, value) -> 357 if is_found then 358 current_key_value 359 else 360 if (key.name_key = name) && key.is_found then 361 Key_value( name, true, key.value) 362 else 363 current_key_value 364 ;; 365 366 (* to get the value of a given key *) 367 let get_key_value k = 368 let init_key_value = Key_value( k, false, "") in 369 let found_result = Hashtbl.fold get_key_value_fold keys_hashtbl init_key_value in 370 match found_result with 371 Key_value( s, b, value) -> 372 if b then 373 value 374 else 375 not_defined 376 ;; 377 378 (* to get all the defined keys *) 379 let get_keys_names _ = 380 let init_gkn = [] in 381 let add_kname k d init = 382 List.append init [ d.name_key] 383 in 384 Hashtbl.fold add_kname keys_hashtbl init_gkn 385 ;; 386 387 (*> *) 388 389 (*> *) 390 391 392 (*< 393 \subsection{Niveau d'étoile} 394 395 Comme cela a été exposé plus haut, les spécifications de {\sf CWEB} précisent différents niveaux 396 de blocs, marqués par un "étoilage" des marques de début de bloc. 397 *) 398 399 (* to determine the "star level" of the section mark *) 400 let star_level str = 401 let str_l = String.length str in 402 if str_l < 3 then 403 (str, Not_relevant) 404 else 405 let short_str = next2 str in 406 (* Printf.fprintf log_file ">>>| %s\n" str; *) 407 Printf.fprintf log_file "***| %s(%c)\n" short_str (char1_c short_str); 408 match char1_c short_str with 409 '*' -> 410 if str_l < 4 then 411 (next1 short_str, Star) 412 else 413 begin 414 let very_short_str = next1 short_str in 415 Printf.fprintf log_file "***| %s(%c)\n" very_short_str (char1_c very_short_str); 416 match char1_c very_short_str with 417 '*' -> (next1 very_short_str, Double_star) 418 | '0' ..'9' -> (next1 very_short_str, Level( char1 very_short_str)) 419 | _ -> (very_short_str, Star) 420 end 421 | _ -> (short_str, Not_relevant) 422 ;; 423 424 (* debug tool *) 425 let get_star_level l = 426 match l with 427 Not_relevant -> "NR" 428 | Star -> "STAR" 429 | Double_star -> "2STAR" 430 | Level( f) -> "Level(" ^ f ^ ")" 431 ;; 432 433 (*> *) 434 435 436 (*< caractère de commentaire 437 Si on le change on accède à n'importe quel langage (sans commentaires de bloc... 438 *) 439 let comment_char = ref "%";; 440 (*> *) 441 442 (*< 443 \subsection{Explosion d'une ligne et attribution de son type} 444 La tâche principale est de na pas tenir compte des blanc en début de ligne 445 et de repérer les 4 types de marqueurs de début de ligne : 446 \begin{itemize} 447 \item {\tt \%\_} pour les commentaires simples 448 \item {\tt \%\%} pour les début de section 449 \item {\tt \%<} pour les débuts de sous-section (avec retour charriot) 450 \item {\tt \%>} pour les fins de sous-section 451 \end{itemize} 452 le reste étant du code 453 *) 454 let rec explode s = 455 if (String.length s) = 0 then 456 (Into_code, "", Not_relevant) 457 else 458 (* before I used a match sentence but it seems that it's not compliant with the use of variables : 459 let truc = "%";; match str with truc -> alpha | _ -> beta;; 460 does not work as I imagined *) 461 if (char1 s) = " " then explode (next1 s) 462 else 463 if (char1 s) = (!comment_char) then 464 begin 465 if (String.length s) > 1 then 466 (* dans les deux premier cas il faut regarder plus loin pour voir si la section est étoilée *) 467 if (char2 s) = (!comment_char ^ !comment_char) then 468 let (next_s, s_level) = star_level s in 469 Printf.fprintf log_file "+++| %s[%s]\n" next_s (get_star_level s_level); 470 (Begin_section, next_s, s_level) 471 else 472 if (char2 s) = (!comment_char ^ "<") then 473 let (next_s, s_level) = star_level s in 474 Printf.fprintf log_file "+++| %s[%s]\n" next_s (get_star_level s_level); 475 (Begin_subsection, next_s, s_level) 476 else 477 if (char2 s) = (!comment_char ^ ">") then 478 (End_subsection, (next2 s), Not_relevant) 479 else 480 (Into_comments, (next1 s), Not_relevant) 481 else 482 (Into_comments, (next1 s), Not_relevant) 483 end 484 else 485 (Into_code, s, Not_relevant) 486 ;; 487 (*> *) 488 489 490 (*< 491 \subsection{debug tools} 492 493 Différentes fonctions de parcours de résultats intermédiaires, destinées à 494 du débug. 495 *) 496 497 (* pour résumer un string*) 498 let short_string str = (* str ;;*) 499 try 500 String.sub str 0 (min (String.length str) 10) 501 with Invalid_argument( ia_s) -> 502 Printf.fprintf log_file "short_string: String.sub problem on %s\n" str; 503 (* flush log_file; *) 504 "" 505 ;; 506 507 (* pour décrire l'état *) 508 let say_state (s, str) = 509 let s_str = short_string str in 510 match s with 511 Begin_section -> Printf.fprintf log_file "Debut de section <-- %s\n" s_str 512 | Into_comments -> Printf.fprintf log_file "Dans les commentaires <-- %s\n" s_str 513 | Begin_subsection -> Printf.fprintf log_file "Debut de sous section <-- %s\n" s_str 514 | End_subsection -> Printf.fprintf log_file "Fin de sous section <-- %s\n" s_str 515 | Into_code -> Printf.fprintf log_file "Dans le code <-- %s\n" s_str 516 | Begin_file -> Printf.fprintf log_file "Tout début de fichier <-- %s\n" s_str 517 | _ -> Printf.fprintf log_file "ERREUR!!!<-- %s\n" s_str 518 ;; 519 520 (*> *) 521 522 523 (*< 524 \subsection{Structure de blocs\label{title:sec}} 525 Un bloc (une \code{section}) est composé d'une structure comportant : 526 \begin{itemize} 527 \item une référence, 528 \item un titre, 529 \item une zone de commentaires, 530 \item une zone de code. 531 \end{itemize} 532 *) 533 534 (* je vais ranger mon code dans un arbre *) 535 type 'a tree = Empty | Noeud of 'a * 'a tree list;; 536 (*type arbre = Empty | Noeud of section * arbre list;; *) 537 538 539 (* fabrication d'un noeud vide *) 540 type section = { refcode : indented_ref ; 541 level : state_level ; 542 title : string ; 543 comments : string ; 544 code : string ; 545 };; 546 547 let get_refcode_s d = 548 match d.refcode with 549 Indented_ref( s, _, _) -> s 550 ;; 551 552 (* pour plus tard *) 553 let root_section = { refcode = Indented_ref( root_string, 0, 0); 554 level = Not_relevant ; 555 title = "" ; 556 comments = "" ; 557 code = "" ; 558 };; 559 560 (* pour plus tard *) 561 let empty_section = { refcode = Indented_ref( empty_string, 0, 0); 562 level = Not_relevant ; 563 title = "" ; 564 comments = "" ; 565 code = "" 566 };; 567 568 (*< 569 \paragraph{Encapsulation --- mise en page.}~\\ 570 Il s'agit de l'encapsulation d'une section. 571 *) 572 573 (* se débarasser des charactères spéciaux *) 574 let rec without_escapes s = 575 if (String.length s) = 0 then 576 "" 577 else 578 match (char1 s) with 579 | "}" -> "\\}" ^ (without_escapes (next1 s)) 580 | "{" -> "\\{" ^ (without_escapes (next1 s)) 581 | "_" -> "\\_" ^ (without_escapes (next1 s)) 582 | "^" -> "\\^\\ " ^ (without_escapes (next1 s)) 583 | "#" -> "\\#" ^ (without_escapes (next1 s)) 584 | "\\" -> "\\bs{}" ^ (without_escapes (next1 s)) 585 | _ -> (char1 s) ^ (without_escapes (next1 s)) 586 ;; 587 588 (*< 589 \paragraph{En ce qui concerne les inclusions.}~\\ 590 Ca se passe là: 591 *) 592 let get_relation f = 593 try 594 Hashtbl.find relations (get_refcode_s f) 595 with Not_found -> 596 Printf.fprintf log_file "--[%s:notfound]\n" (get_refcode_s f); 597 empty_string 598 ;; 599 600 let get_subrelations f = 601 try 602 Hashtbl.find sub_relations (get_refcode_s f) 603 with Not_found -> 604 Printf.fprintf log_file "--[%s:notfound]\n" (get_refcode_s f); 605 [ empty_string ] 606 ;; 607 608 let rec pretty_print_rel_list l= 609 match l with 610 [] -> "" 611 | head :: tail -> 612 if (head = root_string) || (head = empty_string) then 613 Printf.sprintf "%s" (pretty_print_rel_list tail) 614 else 615 Printf.sprintf "\\refangles{refcode:%s} %s" head (pretty_print_rel_list tail) 616 ;; 617 618 let pretty_print_relations s empty_comments= 619 Printf.fprintf log_file "PPR>\n"; 620 let prefix_relations = ref "" in 621 if empty_comments then 622 prefix_relations := "" 623 else 624 prefix_relations := "~\\\\\n"; 625 let p = get_relation s in 626 let f = get_subrelations s in 627 if p = "0" then 628 if (List.length f) = 1 && (((List.hd f) = empty_string) || ((List.hd f) = root_string)) then 629 "" 630 else 631 Printf.sprintf "%s{\\footnotesize \\uses %s}" !prefix_relations (pretty_print_rel_list f) 632 else 633 if f = [] then 634 Printf.sprintf "%s{\\footnotesize \\usedby \\refangles{refcode:%s}}" !prefix_relations p 635 else 636 begin 637 if (List.length f) = 1 && (((List.hd f) = empty_string) || ((List.hd f) = root_string)) then 638 Printf.sprintf "%s{\\footnotesize \\usedby \\refangles{refcode:%s}}" !prefix_relations p 639 else 640 Printf.sprintf "%s{\\footnotesize \\usedby \\anglesref{refcode:%s} --- \\uses %s}" 641 !prefix_relations p (pretty_print_rel_list f) 642 end 643 ;; 644 (*> *) 645 646 647 let pretty_print_level l = 648 match l with 649 Not_relevant -> "0" 650 | Star -> "10" 651 | Double_star -> "100" 652 | Level( f) -> f 653 ;; 654 655 let before_code_symbol = "\\bcodesymbol\n%END LATEX\n\\begin{quote}\n\\begin{alltt}\n";; 656 let after_code_symbol = "\\end{alltt}\n\\end{quote}\n%BEGIN LATEX\n}\n";; 657 let before_section_symbol = "\\bsecsymbol";; 658 659 let pretty_print_section sec = 660 let after_comments_carriage = ref "" in 661 if sec.comments = "" then 662 after_comments_carriage := "\n" 663 else 664 after_comments_carriage := "~\\\\\n"; 665 match sec.refcode with 666 Indented_ref( s, n, c) -> 667 "\\noindent" ^ before_section_symbol ^ "\\nopagebreak\\\\\n" ^ 668 "\\sectitle{" ^ (pretty_print_level sec.level) ^ "}{" ^ sec.title ^ "}" ^ 669 "{" ^ (string_of_int c) ^ "}\n" ^ sec.comments ^ !after_comments_carriage ^ 670 (pretty_print_relations sec (sec.comments = "")) ^ 671 "\n\n" ^ 672 "\\nopagebreak\n{\\footnotesize\n" ^ 673 before_code_symbol ^ 674 sec.code ^ 675 after_code_symbol ;; 676 677 678 679 (* to gain CPU time*) 680 let process_strings_regexp = (regexp "\([, (\[]'\)\([^']*\)\('\)");; 681 682 (* 683 Pas mieux pour l'instant: 684 Test: 685 # get_strings "ceci est un 'test' '''' assez 'étrange': A' + ['test''m''test', 'test','a'] ('al').";; 686 *) 687 let process_strings str = global_replace process_strings_regexp "\1\\stringc{\2}\3" str;; 688 689 (* let process_cmts str = global_replace (regexp "\(%[^']*\)\(\\n\)") "\\cmt{\1}\\2" str;; *) 690 (* for the "end of line" comments *) 691 692 (* to gain CPU time*) 693 let colorize_regexps = ref [];; 694 (* [(regexp "|") ; (regexp "&") ; (regexp "=") ; (regexp "==") ; (regexp "~") ; (regexp "<=") ; (regexp ">=")];; *) 695 let colorize_regexpr = ref [];; 696 (* "\\vt" ; "\\va" ; "\\oeq" ; "\\ooeq" ; "\\otdl" ; "\\(\\leq\\)" ; "\\(\geq\\)" ];;*) 697 698 let colorize_regexp_oo = ref (regexp "\\b\(function\|while\|continue\|try\|catch\|for\|end\|persistent\|switch\|case\|if\|else\|elseif\|return\|break\|otherwise\)\\b");; 699 700 (*< 701 \subsection{Déclarations de clefs globales spécifiques à {\sf OCAMAWEB}} 702 703 Je déclare ici les clefs de la section \ref{sec:keys:glob} : 704 \globalkeys 705 706 *) 707 708 let dvipdfm = ref "<ocamaweb.xml> FILE NOT FOUND";; 709 let dviext = ref "<ocamaweb.xml> FILE NOT FOUND";; 710 711 try 712 let xml_inits = Xml.parse_file ((Sys.getenv "OCAMAWEB") ^ "ocamaweb.xml") in 713 let caracts = (get_childs xml_inits) in 714 (* caracts in a list of xml structs, I want the one with 'keypatterns' tag *) 715 let is_pdfgenerator xml_struct = 716 (get_name xml_struct) = "pdfgenerator" 717 in 718 let pdfgenerator = match (List.find is_pdfgenerator caracts) with 719 _,a,_ -> match (List.hd a) with 720 "file", d -> d 721 | _ -> "**bad xml file**" 722 in 723 let pdfgenerator_ext = match (List.find is_pdfgenerator caracts) with 724 _,a,_ -> match (List.nth a 1) with 725 "ext", d -> d 726 | _ -> "pdf generator ext attribute not found in <ocamaweb.xml>" 727 in 728 (* here I want to get the regexps *) 729 let is_regexp xml_struct = 730 (get_name xml_struct) = "regexp" 731 in 732 let regexp_defs = (get_childs (List.find is_regexp caracts)) in 733 let regexp_match_mine v = 734 match v with 735 _,a,_ -> a 736 in 737 let regexp_info = (List.map regexp_match_mine regexp_defs) in 738 let get_regexp_from v = 739 match (List.nth v 0) with 740 _,a -> (regexp a) 741 in 742 let regexp_from = (List.map get_regexp_from regexp_info) in 743 let get_regexp_to v = 744 match (List.nth v 1) with 745 _,a -> a 746 in 747 let regexp_to = (List.map get_regexp_to regexp_info) in 748 (* now I have the pdfgenerators name *) 749 let is_comment_key xml_struct = 750 (get_name xml_struct) = "commentkey" 751 in 752 let commentkey = match (List.find is_comment_key caracts) with 753 _,a,_ -> begin 754 match (List.hd a) with 755 "value", v -> 756 (* c'est ici que j'attribue sa nouvelle valeur à |comment_char| *) 757 comment_char := v; 758 v 759 | _ -> "**bad xml file : commentkey not properly defined**" 760 end 761 in 762 let is_keypatterns xml_struct = 763 (get_name xml_struct) = "keypatterns" 764 in 765 let key_patterns = (get_childs (List.find is_keypatterns caracts)) in 766 (* now I have to put the keypatterns into my hashtable *) 767 let xml_key_atomic_value xml_list n = 768 let couple = List.nth xml_list n in 769 match couple with 770 a,b -> b 771 in 772 let add_key_pattern_from_xml xml_struct = 773 match xml_struct with 774 name,attribs,childs -> 775 let key_name = xml_key_atomic_value attribs 0 in 776 let key_sign = xml_key_atomic_value attribs 1 in 777 let before_name = xml_key_atomic_value attribs 2 in 778 let after_name = xml_key_atomic_value attribs 3 in 779 add_key_pattern key_name key_sign before_name after_name 780 in 781 let is_matlabwords xml_struct = 782 (get_name xml_struct) = "matlabwords" 783 in 784 let matlab_words = (get_childs (List.find is_matlabwords caracts)) in 785 (* now I have to put the matlabwords into a list *) 786 let extract_name_from_matlabwords xml_struct = 787 match xml_struct with 788 name,attribs,childs -> 789 xml_key_atomic_value attribs 0 790 in 791 (* here I form a list with all the matlab words *) 792 let matlabwords_list = List.map extract_name_from_matlabwords matlab_words in 793 let matlabwords_concat mw_init mw_next = 794 (mw_init ^ "\|" ^ mw_next) 795 in 796 let matlabwords_regexp = (regexp ("\\b\(" ^ (List.fold_left matlabwords_concat (List.hd matlabwords_list) (List.tl matlabwords_list) 797 ^ "\)\\b"))) in 798 (* here I have got the correct regexp, for instance : 799 (global_replace matlabwords_regexp "\\ocamawebdefinition{\\0}" "if a==0 then 1 else 2 end;") 800 returns a correct answer *) 801 colorize_regexp_oo := matlabwords_regexp; 802 803 (* get the keypattern informations *) 804 List.iter add_key_pattern_from_xml key_patterns; 805 806 (* get the pdfgenerator *) 807 dvipdfm := pdfgenerator; 808 dviext := pdfgenerator_ext; 809 810 (* get the regexps *) 811 colorize_regexps := regexp_from; 812 colorize_regexpr := regexp_to 813 814 with e -> 815 Printf.fprintf stderr "OCAMAWEB: I cannot find <ocamaweb.xml> file in <%s> or this file is not completed\n" (Sys.getenv "OCAMAWEB") ; 816 match e with 817 Not_found -> Printf.fprintf stderr "NOT FOUND\n" 818 | Xml_error(e) -> Printf.fprintf stderr "XML error during <ocamaweb.xml> parsing : %s\n" e 819 | _ -> Printf.fprintf stderr "UNKOWN ERROR\n" 820 ; 821 822 add_key_pattern "author" "node.author" "'" "'"; 823 add_key_pattern "author" "% author" "'" "'"; 824 add_key_pattern "author" "% auteur" "'" "'"; 825 add_key_pattern "title" "% title" "'" "'"; 826 add_key_pattern "title" "% titre" "'" "'"; 827 add_key_pattern "project" "% project" "'" "'"; 828 add_key_pattern "project" "% projet" "'" "'"; 829 add_key_pattern "mailto" "node.mailto" "'" "'"; 830 add_key_pattern "mailto" "% mailto" "'" "'"; 831 add_key_pattern "date" "node.date" "'" "'"; 832 add_key_pattern "date" "% date" "'" "'"; 833 add_key_pattern "version" "% version" "'" "'"; 834 add_key_pattern "version" "VERSION" "=" ";" 835 ;; 836 837 (*> *) 838 839 840 841 (* to colorize the code parts *) 842 (* [let garzol = follow_file "sample.m";;] *) 843 let colorize str = 844 let internal_str = (global_replace !colorize_regexp_oo "\\ocamawebdefinition{\\0}" (without_escapes str)) in 845 let regexp_this str regs regr = 846 (global_replace regs ( "{" ^ regr ^ "}" ) str) 847 in 848 process_strings (List.fold_left2 regexp_this internal_str !colorize_regexps !colorize_regexpr) 849 (* let 850 process_strings 851 (global_replace colorize_regexp_1 "{\\vt}" 852 (global_replace colorize_regexp_2 "{\\va}" 853 (global_replace colorize_regexp_3 "{\\oeq}" 854 (global_replace colorize_regexp_4 "{\\ooeq}" 855 (global_replace colorize_regexp_5 "{\\otdl}" 856 (global_replace colorize_regexp_6 "{\\(\\leq\\)}" 857 (global_replace colorize_regexp_7 "{\\(\geq\\)}" 858 (global_replace !colorize_regexp_oo "\\ocamawebdefinition{\\0}" 859 (without_escapes str))))))))) 860 *) 861 ;; 862 863 let without_path f = 864 try 865 let deb_path = 1 + (String.rindex f '/') in 866 String.sub f deb_path ((String.length f) - deb_path) 867 with Not_found -> 868 try 869 let deb_path = 1 + (String.rindex f '\\') in 870 String.sub f deb_path ((String.length f) - deb_path) 871 with Not_found -> 872 f 873 ;; 874 875 let neutralize_filenames_regexp = (regexp "_");; 876 877 (* juste pour neutraliser les _ et \ dans les noms de (fichiers) *) 878 let neutralize_filenames str_fname = 879 let str = without_path str_fname in 880 try 881 let ridx = (1 + (String.rindex str '/')) in 882 let only_filename = String.sub str ridx ((String.length str) - ridx) in 883 global_replace neutralize_filenames_regexp "\\_" only_filename 884 with 885 Not_found -> global_replace neutralize_filenames_regexp "\\_" str 886 ;; 887 888 let rec first_defined str_lst = 889 match str_lst with 890 [] -> not_defined 891 | head :: tail -> 892 if head = not_defined then 893 first_defined tail 894 else 895 head 896 ;; 897 898 let get_email str = 899 if str = not_defined then 900 "" 901 else 902 "\\\\{\\tt mailto:" ^ str ^ "}" 903 ;; 904 905 (* to include ocamaweb.sty file into the LaTeX one *) 906 let windows_path_to_unix str = global_replace (regexp "[\\]") "/" str;; 907 908 let get_style_path = 909 try 910 (windows_path_to_unix (Sys.getenv "OCAMAWEB")) 911 with Not_found -> 912 "" 913 ;; 914 915 let get_first_line _ = 916 let project_name_v = (get_key_value "project") in 917 if project_name_v = not_defined then 918 "" 919 else 920 project_name_v ^ "\\\\" 921 ;; 922 923 let title_or_filename _ = 924 let tit = (get_key_value "title") in 925 if tit = not_defined then 926 (get_key_value "filename") 927 else 928 tit 929 ;; 930 931 932 let head_of_file _ = 933 let all_knames = 934 (* all_knames_full sans les doublons ni filename *) 935 let uniquel clist str = 936 let this_equal v = 937 (str = v) 938 in 939 (* filename keyword is a very special one *) 940 if (not (List.exists this_equal clist)) && (not (str = "filename")) then 941 (List.append clist [ str ]) 942 else 943 clist 944 in 945 List.fold_left uniquel [] (get_keys_names ()) 946 in 947 let form_latexdef kname = 948 "\\def\\ocamaweb" ^ kname ^ "{" ^ (get_key_value kname) ^ "}\n" 949 in 950 let all_kvalues = String.concat "" (List.map form_latexdef all_knames) in 951 "\\ifx\\firstocamaweb\\undefined\n" ^ 952 "\\input{" ^ get_style_path ^ "ocamaweb.sty}\n\n" ^ 953 "\\fi\n" ^ 954 (* filename keyword is a very special one *) 955 "\\def\\ocamawebtitle{" ^ (title_or_filename ()) ^ "}\n" ^ 956 "\\def\\ocamawebfilename{" ^ (get_key_value "filename") ^ "}\n" ^ 957 "\\def\\ocamawebv{" ^ version ^ "}\n" ^ 958 all_kvalues ^ 959 (* 960 "\\def\\ocamawebauthor{" ^ (get_key_value "author") ^ "}\n" ^ 961 "\\def\\ocamawebproject{" ^ (get_key_value "project") ^ "}\n" ^ 962 "\\def\\ocamawebfilename{" ^ (get_key_value "filename") ^ "}\n" ^ 963 "\\def\\ocamawebmailto{" ^ (get_key_value "mailto") ^ "}\n" ^ 964 "\\def\\ocamawebdate{" ^ (get_key_value "date") ^ "}\n" ^ 965 "\\def\\ocamawebversion{" ^ (get_key_value "version") ^ "}\n\n" ^ 966 *) 967 "\n\\ocamawebstart\n\n" 968 ;; 969 (* 970 "\\title{" ^ 971 (get_first_line ()) ^ 972 (first_defined [(get_key_value "title") ; (get_key_value "filename") ]) ^ "}\n" ^ 973 "\\author{" ^ (get_key_value "author") ^ (get_email (get_key_value "mailto")) ^ "}\n" ^ 974 "\\date{Imprimé le \\today,\\\\dernière modification le " ^ (get_key_value "date") ^ "}\n" ^ 975 "\\ocamawebstart\n\n";; 976 *) 977 978 let foot_of_file = 979 "\n\\noindent\\rule{5cm}{1pt}\\nopagebreak\\\\\n\\ocamawebend\n";; 980 981 (*> *) 982 983 (*< 984 \subsection{fonctions de manipulation de l'arbre} 985 986 Un arbre est ou bien vide, ou bien un \code{Noeud} contenant un label 987 et une liste de n\oe uds.\\ 988 989 L'arbre d'un fichier est donc une racine qui a comme fils les blocs 990 principaux. Chaque bloc principal a comme fils les blocs arbitraire qu'il 991 contient et de même chaque bloc arbitraire a comme fils les blocs 992 arbitraire qu'il contient.\\ 993 994 Le label d'un n\oe ud est composé d'une section (cf \ref{title:sec}). 995 *) 996 997 (* pour résoudre un problème d'empilage: je vais créer une fonction 998 qui inverse l'ordre du premier niveau de l'arbre *) 999 let reverse_sons t = 1000 match t with 1001 Empty -> t 1002 | Noeud(s, f) -> (* je vais inverser l'ordre des fils |f| et les remettre dans s: *) 1003 Noeud(s, List.rev f) 1004 ;; 1005 1006 1007 (* affiche un titre *) 1008 let print_title s = 1009 Printf.fprintf stdout "%s%s\n" (String.make 1 ' ') s.title;; 1010 1011 (* listing des titres des noeuds d'un arbre *) 1012 let rec get_section = function 1013 Empty -> empty_section 1014 | Noeud(s, _) -> s 1015 ;; 1016 1017 (* liste les différents titres d'un arbre *) 1018 let rec get_t nb n = 1019 match n with 1020 Empty -> Printf.fprintf stdout "%s\n" (String.make nb '@'); 1021 | Noeud(s, l) -> 1022 begin 1023 Printf.fprintf stdout "%s%s\n" (String.make nb '@') s.title; 1024 ignore (List.map (get_t (2 + nb)) l); 1025 end; 1026 ;; 1027 1028 (* liste les différents commentaires d'un arbre *) 1029 let rec get_c nb n = 1030 match n with 1031 Empty -> ignore (Printf.fprintf stdout "%s\n" (String.make nb '@')); 1032 | Noeud(s, l) -> 1033 begin 1034 Printf.fprintf stdout "%s%s\n%s\n" (String.make nb '@') s.title s.comments; 1035 ignore (List.map (get_c (2 + nb)) l); 1036 end; 1037 () 1038 ;; 1039 1040 (*< 1041 \paragraph{remplissage d'un vecteur avec la correspondance péres --- fils.}~\\ 1042 J'utilise deux variables globales pour cela (je sais ce n'est pas très élégant, mais je 1043 suis pressé pour l'instant). Ces deux variables sont remplies à la fin du parsing. 1044 1045 Il s'agit de Hashtables qui contiennent respectivement les fils et les pères de chaque 1046 section. 1047 *) 1048 1049 let concat_subrelation k v = 1050 try 1051 let l = Hashtbl.find sub_relations k in 1052 Hashtbl.replace sub_relations k (List.append l [ v]) 1053 with Not_found -> 1054 Hashtbl.add sub_relations k [v] 1055 ;; 1056 1057 let get_ref_number s = 1058 match s.refcode with 1059 Indented_ref(_, _, s) -> (string_of_int s) 1060 ;; 1061 1062 let add_to_relations f d = 1063 (* Printf.fprintf stdout "REL> %s --son of-- %s\n" (get_refcode_s d) (get_refcode_s f); *) 1064 Hashtbl.add relations (get_refcode_s d) (get_ref_number f); 1065 (* Printf.fprintf stdout "REL> %s --father of-- %s\n" (get_refcode_s f) (get_refcode_s d); *) 1066 concat_subrelation (get_refcode_s f) (get_ref_number d) 1067 ;; 1068 1069 (* renseigne récursivement les hashtables d'indexation des pères et fils *) 1070 let rec build_relations_rec f a_tree = 1071 match a_tree with 1072 Empty -> () 1073 | Noeud(desc, fils) -> 1074 add_to_relations f desc; 1075 ignore (List.map (build_relations_rec desc) fils) 1076 ;; 1077 1078 (*> *) 1079 1080 (*< 1081 \paragraph{Affichage itératif des fils de la racine d'un arbre.}~\\ 1082 C'est une des fonctions importantes du code, elle appelle \code{son_to_string} 1083 sur toutes les branches de l'arbre. 1084 *) 1085 1086 (* affichage du contenu d'un élément puis de ses fils comme déclarations de blocs *) 1087 let rec son_to_string str l_tree = 1088 match l_tree with 1089 Empty -> str 1090 | Noeud(t, l) -> 1091 str ^ (pretty_print_section t ) ^ (List.fold_left son_to_string "" l) 1092 ;; 1093 1094 let to_string t = 1095 match t with 1096 Empty -> ""; 1097 | Noeud(s, l) -> (* j'ai les noeud principaux, 1098 je vais afficher : 1099 - le contenu du premier avec un code c 1100 - puis ses blocs de référence 1101 - puis pour chaque autre élément de la liste : 1102 - le contenu du premier 1103 - puis ses blocs de référence 1104 *) 1105 let first = List.nth l 0 in 1106 List.fold_left son_to_string "" l 1107 ;; 1108 (*> *) 1109 1110 (*> *) 1111 1112 (*> *) 1113 1114 (*> *) 1115 1116 (*< 1117 \section{file parsing} 1118 *) 1119 1120 (* récupération de l'état d'une ligne *) 1121 let get_line_state (s, _, _) = s;; 1122 let get_line_str (_, s, _) = s;; 1123 let get_line_level (_, _, l) = l;; 1124 1125 (* croisement de l'état global et de celui d'une ligne. 1126 C'est LA grosse machine à états du parseur 1127 *) 1128 let cross_states g_state pline = 1129 let this_level = get_line_level pline in 1130 let l_state = get_line_state pline in 1131 let str = short_string (get_line_str pline) in 1132 match g_state with 1133 Begin_section | Into_comments | Begin_subsection -> begin 1134 match l_state with 1135 Begin_section -> Printf.fprintf log_file "a(1) new section: %s ...\n" str; 1136 ( Begin_section, New_section, this_level) 1137 | Into_comments -> Printf.fprintf log_file "b(2) add comments to current (sub?)section: %s ...\n" str; 1138 ( Into_comments, Add_comment, this_level) 1139 | Begin_subsection -> Printf.fprintf log_file "c(3) new subsection (insert into former code): %s ...\n" str; 1140 ( Begin_subsection, New_subsection, this_level) 1141 | End_subsection -> Printf.fprintf log_file "d(4) close current (sub)section: %s ...\n" str; 1142 ( End_subsection, Close_subsection, this_level) 1143 | Into_code -> Printf.fprintf log_file "e(5) close comment part and begin code part: %s ...\n" str; 1144 ( Into_code, Add_code, this_level) 1145 | Begin_file -> Printf.fprintf log_file "f(*) ERROR!!: %s ...\n" str; 1146 ( Unknown, Stop, this_level) 1147 | _ -> ( Unknown, Stop, this_level) 1148 end; 1149 | End_subsection | Into_code -> begin 1150 match l_state with 1151 Begin_section -> Printf.fprintf log_file "g(1) new section: %s ...\n" str; 1152 ( Begin_section, New_section, this_level) 1153 | Into_comments -> Printf.fprintf log_file "h(6) stay into code: %s ...\n" str; 1154 ( Into_code, Add_comments_into_code, this_level) 1155 | Begin_subsection -> Printf.fprintf log_file "i(3) new subsection (insert into former code): %s ...\n" str; 1156 ( Begin_subsection, New_subsection, this_level) 1157 | End_subsection -> Printf.fprintf log_file "j(4) close current (sub)section: %s ...\n" str; 1158 (End_subsection, Close_subsection, this_level) 1159 | Into_code -> Printf.fprintf log_file "k(6) stay into code: %s ...\n" str; 1160 (Into_code, Add_code, this_level) 1161 | Begin_file -> Printf.fprintf log_file "l(*) ERROR!!: %s ...\n" str; 1162 (Unknown, Stop, this_level) 1163 | _ -> ( Unknown, Stop, this_level) 1164 end; 1165 | Begin_file ->begin 1166 match l_state with 1167 Begin_section -> Printf.fprintf log_file "m(7) compose the first section: %s ...\n" str; 1168 (Into_comments, Into_first_section, this_level) 1169 | Into_comments -> Printf.fprintf log_file "n(8) stay into Begin_file (add to code): %s ...\n" str; 1170 (Begin_file, Add_code, this_level) 1171 | Begin_subsection -> Printf.fprintf log_file "o(*) ERROR!!: %s ...\n" str; 1172 (Unknown, Stop, this_level) 1173 | End_subsection -> Printf.fprintf log_file "p(*) ERROR!!: %s ...\n" str; 1174 (Unknown, Stop, this_level) 1175 | Into_code -> Printf.fprintf log_file "q(8) stay into Begin_file (add to code): %s ...\n" str; 1176 (Begin_file, Add_code, this_level) 1177 | Begin_file -> Printf.fprintf log_file "l(*) ERROR!!: %s ...\n" str; 1178 ( Unknown, Stop, this_level) 1179 | _ -> ( Unknown, Stop, this_level) 1180 end; 1181 | _ -> ( Unknown, Stop, this_level) 1182 ;; 1183 1184 (* variable globale *) 1185 let global_state = ref Begin_file ;; 1186 1187 (*< 1188 \section{outils de manipulation de fichier} 1189 *) 1190 1191 (* renvoie tout avant le premier ["."] rencontré *) 1192 let title_part str_ = 1193 if String.length str_ = 0 then 1194 "" 1195 else 1196 let spaces_nb = ref 0 in 1197 try 1198 while (String.sub str_ !spaces_nb 1= " ") do 1199 spaces_nb := !spaces_nb + 1 1200 done; 1201 let str = String.sub str_ !spaces_nb (String.length str_ - !spaces_nb) in 1202 begin 1203 try 1204 let point_pos = String.index str '.' in 1205 if point_pos > 0 then 1206 String.sub str 0 point_pos 1207 else 1208 "" 1209 with 1210 Not_found -> str 1211 end 1212 with Invalid_argument( ia_s) -> 1213 Printf.fprintf log_file "title_part: String.sub problem on %s\n" str_; 1214 (* flush log_file; *) 1215 str_ 1216 ;; 1217 1218 (* renvoie tout après le premier ["."] rencontré *) 1219 let comment_part str = 1220 try 1221 let point_pos = String.index str '.' in 1222 let str_len = String.length str in 1223 if str_len > (1 + point_pos) then 1224 String.sub str (1 + point_pos) ( str_len - point_pos - 1) 1225 else 1226 "" 1227 with 1228 Not_found -> "" 1229 ;; 1230 1231 (* I had my xml parsing functions here *) 1232 1233 (*< 1234 \subsection{Parsing spécifique aux langages sans commentaires par blocs} 1235 1236 Cette partie doit être remplacée par une autre pour les langages (comme le {\tt c} ou {\tt ocaml}) 1237 qui offrent la possibilité de créer des blocs de commentaires ({\tt /* ... */} ou {\tt (* *)}). 1238 *) 1239 1240 let refcode_counter = ref 0;; 1241 1242 let make_generic_refcode str_ dec = 1243 refcode_counter := !refcode_counter + 1 - dec; 1244 if String.length str_ = 0 then 1245 Indented_ref ("", 0, !refcode_counter) 1246 else 1247 let spaces_nb = ref 0 in 1248 while (String.sub str_ !spaces_nb 1= " ") do 1249 spaces_nb := !spaces_nb + 1 1250 done; 1251 let str = String.sub str_ !spaces_nb (String.length str_ - !spaces_nb) in 1252 let (str_unstar, s) = star_level str in 1253 let title = title_part str_unstar in 1254 (* Printf.fprintf log_file "RRR> %s\n-*-> %s\n" str str_unstar; *) 1255 if dec = 1 then 1256 begin 1257 Printf.fprintf log_file "RR1> %s |%d|%d (%d)\n" title !spaces_nb (!refcode_counter + dec) dec; 1258 Indented_ref (title, !spaces_nb, !refcode_counter + dec); 1259 end 1260 else 1261 begin 1262 Printf.fprintf log_file "RR0> %s |%d|%d (%d)\n" str 0 (!refcode_counter + dec) dec; 1263 Indented_ref (str, 0, !refcode_counter + dec) 1264 end 1265 ;; 1266 (* renvoie un refcode pour le string : 1267 un "nettoyage" (pour WEB) de la partie du string avant le 1er ["."] rencontré. 1268 D'abord j'enlève (et je compte) le nombre d'espaces qu'il y a avant 1269 *) 1270 let make_refcode str_ = 1271 make_generic_refcode str_ 0;; 1272 1273 (* le même mais dans le vide *) 1274 let make_fake_refcode str_ = 1275 make_generic_refcode str_ 1;; 1276 1277 1278 (* référence un bloc par refcode (les retours charriot ne sont pas compris) *) 1279 let insert_refcode my_refcode = 1280 match my_refcode with 1281 | Indented_ref( s, n, c) -> 1282 (String.make n ' ') ^ "\\refcode{" ^ s ^ "}{" ^ (string_of_int c) ^ "}" ;; 1283 1284 (* declare un bloc par refcode (les retours charriot ne sont pas compris) 1285 let declare_refcode str = "@<" ^ str ^ "@>=";; 1286 1287 let end_of_first_comments = "@c\n";; 1288 let end_of_comments = "@p\n";; 1289 *) 1290 1291 (*> *) 1292 1293 (*> *) 1294 1295 (*< 1296 \section{Traitements principaux} 1297 *) 1298 1299 (* j'ai besoin d'une racine : 1300 une liste de noeuds (je la met à l'extérieur pour que cela ne soit pas trop récursif -mal de tête-) *) 1301 let root_nodes = ref [];; 1302 1303 let regexp_no_tabs = regexp "[\t]";; 1304 1305 (*< 1306 \subsection{parcours récursif d'un fichier (channel)} 1307 *) 1308 let rec make_section first_line channel_name first_line_level = 1309 let refcode_ = ref (Indented_ref("", 0, 0)) in 1310 let title_ = ref "" in 1311 let level_ = ref first_line_level in 1312 let comments_ = ref "" in 1313 let code_ = ref "" in 1314 let into_parsing = ref true in 1315 let fils = ref [] in 1316 if !global_state <> Begin_file then 1317 begin 1318 title_ := title_part first_line; 1319 comments_ := comment_part first_line; 1320 refcode_ := make_refcode first_line; 1321 1322 Printf.fprintf log_file "**>> %s\n" (get_star_level !level_); 1323 end; 1324 1325 (* tant je ne dois pas créer une section de façon récursive *) 1326 while !into_parsing do 1327 (* flush log_file; *) 1328 try 1329 let this_line = 1330 (global_replace regexp_no_tabs " " (input_line channel_name)) in 1331 let has_any_special_keys = has_key this_line in 1332 let this_parsed_line = explode this_line in 1333 let this_str = (get_line_str this_parsed_line) in 1334 let carriage = "\n" in 1335 let (new_state, this_action, this_state_level) = cross_states !global_state this_parsed_line in 1336 1337 global_state := new_state; 1338 1339 (* j'ai remplacé certains this_str par des this_line car pour l'instant 1340 j'utiliser le package alltt, j'espère bien y remédier... *) 1341 match this_action with 1342 Add_comment -> comments_ := !comments_ ^ carriage ^ this_str 1343 (* ATTENTION: je devrais chercher les commentaires de fin de ligne 1344 problème des '%'!!! *) 1345 | Add_code -> code_ := !code_ ^ carriage ^ (colorize this_line) 1346 | Add_comments_into_code -> code_ := !code_ ^ carriage ^ "\comments{" ^ this_str ^ "}" 1347 | New_section -> 1348 begin 1349 Printf.fprintf log_file "*>>> (%s)%s\n" this_str (get_star_level this_state_level); 1350 root_nodes := !root_nodes @ [ (make_section this_str channel_name this_state_level) ]; 1351 (* root_nodes := [ (make_section this_str channel_name) ] @ !root_nodes; :pas bon du tout *) 1352 into_parsing := false 1353 end; 1354 | New_subsection -> 1355 begin 1356 code_ := !code_ ^ carriage ^ (insert_refcode (make_fake_refcode this_line)); (* this_str *) 1357 Printf.fprintf log_file "*>>> (%s)%s\n" this_str (get_star_level this_state_level); 1358 fils := !fils @ [ (make_section this_str channel_name this_state_level) ] 1359 end; 1360 | Into_first_section -> 1361 begin 1362 title_ := title_part this_str; 1363 level_ := get_line_level this_parsed_line; 1364 comments_ := comment_part this_str; 1365 refcode_ := make_refcode this_str; 1366 end; 1367 | Close_subsection -> into_parsing := false 1368 | Stop -> into_parsing := false 1369 1370 with End_of_file -> into_parsing := false 1371 1372 done; 1373 1374 (* je renvoie un noeud quicontient la description de cette section *) 1375 let this_section = { refcode = !refcode_ ; 1376 level = !level_ ; 1377 title = !title_ ; 1378 comments = !comments_ ; 1379 code = !code_ 1380 } in 1381 Noeud( this_section, !fils ) 1382 ;; 1383 (*> *) 1384 1385 (*< 1386 \subsection{fonction principale} 1387 \begin{itemize} 1388 \item ouverture du fichier 1389 \item mise à "vide" de l'ensemble des noeuds racine 1390 \item mise à \code{Begin_file} du \code{global_state} 1391 \item lancement du parcours recursif 1392 \end{itemize} 1393 *) 1394 let follow_file fname = 1395 root_nodes := []; 1396 global_state := Begin_file ; 1397 add_specified_key_pattern "filename" (neutralize_filenames fname) ; 1398 let this_channel = open_in fname in 1399 let first_node = make_section "" this_channel Not_relevant in 1400 (* attention: je fais ici un List.rev pour supprimer un comportement INEXPLIQUE: 1401 le premier niveau de liste se retrouve empilé à l'envers! *) 1402 let all_nodes = Noeud( root_section, List.rev (!root_nodes @ [ first_node ])) in 1403 flush log_file; 1404 all_nodes 1405 ;; 1406 1407 (*> *) 1408 1409 (*< 1410 \subsection{for testing purpose} 1411 *) 1412 1413 let send_to_file a_tree filename = 1414 build_relations_rec empty_section a_tree; 1415 let this_chan = open_out_bin filename in 1416 Printf.fprintf this_chan "%s%s%s" (head_of_file ()) (to_string a_tree) foot_of_file; 1417 close_out this_chan 1418 ;; 1419 1420 let without_ext f = 1421 let deb_ext = String.rindex f '.' in 1422 String.sub f 0 deb_ext 1423 ;; 1424 1425 let rec move_files name dest exts = 1426 match exts with 1427 [] -> 0 1428 | head :: tail -> 1429 (Sys.command( "move " ^ name ^ head ^ " " ^ dest)) + 1430 move_files name dest tail 1431 ;; 1432 1433 let get_dest_path = 1434 try 1435 (Sys.getenv "OCAMAWEB_DEST") 1436 with Not_found -> 1437 Printf.fprintf log_file "ENV: OCAMAWEB_DEST not found!!!\n"; 1438 "" 1439 ;; 1440 1441 let latexize f mode = 1442 let rez = ref 0 in 1443 let new_f = without_ext f in 1444 rez := !rez + (Sys.command( "del " ^ get_dest_path ^ (without_path new_f) ^ ".* ")); 1445 rez := !rez + 2 * (Sys.command( "latex " ^ (without_path f))); 1446 rez := !rez + 4 * (Sys.command( "latex " ^ (without_path f))); 1447 rez := !rez + 8 * (Sys.command( !dvipdfm ^ " " ^ (without_path new_f) ^ !dviext)); 1448 if mode = 2 then 1449 begin 1450 !rez + 100 * (move_files (without_path (without_ext f)) 1451 (Sys.getenv "OCAMAWEB_DEST") 1452 [ ".pdf" ; ".tex" ; ".dvi" ; ".log" ; ".aux" ; ".toc" ; ".ps"] ); 1453 end 1454 else 1455 !rez + 16; 1456 ;; 1457 1458 let ocamaweb_process f1 f2 mode = 1459 Printf.fprintf stderr "%s --> %s\n" f1 f2; 1460 let garzol = follow_file f1 in 1461 if mode < 3 then 1462 begin 1463 send_to_file garzol (without_path f2); 1464 latexize f2 mode 1465 end 1466 else 1467 begin 1468 send_to_file garzol f2; 1469 1 1470 end 1471 ;; 1472 1473 1474 let main () = 1475 Printf.fprintf stderr "OCAMAWEB version-%s\n" version; 1476 if !Sys.interactive then 1477 begin 1478 2 1479 end 1480 else 1481 begin 1482 match Array.length Sys.argv with 1483 2 -> 1484 let file_to_comment = Sys.argv.(1) in 1485 let file_to_write = (Sys.getenv "OCAMAWEB_DEST") ^ 1486 (without_path (without_ext file_to_comment)) ^ ".tex" in 1487 ocamaweb_process file_to_comment file_to_write 2 1488 | 3 -> 1489 let file_to_comment = Sys.argv.(1) in 1490 let file_to_write = Sys.argv.(2) in 1491 ocamaweb_process file_to_comment file_to_write 3 1492 | _ -> 1493 ignore (Printf.fprintf stdout "ocamaweb input_file.m [output_file.tex]\n"); 1494 1495 close_out log_file; 1496 1; 1497 end 1498 ;; 1499 1500 main ();; 1501 1502 1503 1504 (*> *) 1505 1506 (*> *) 1507
