Download code

From LiteratePrograms

Jump to: navigation, search

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 


Views
Personal tools