1(***********************************************************************) 2(* *) 3(* HEVEA *) 4(* *) 5(* Luc Maranget, projet PARA, INRIA Rocquencourt *) 6(* *) 7(* Copyright 1998 Institut National de Recherche en Informatique et *) 8(* Automatique. Distributed only by permission. *) 9(* *) 10(***********************************************************************) 11 12open Printf 13open Misc 14open Lexstate 15 16exception Failed 17 18module OString = struct 19 type t = string 20 let compare = Pervasives.compare 21end 22 23module Strings = Set.Make (OString) 24 25(* Data structures for TeX macro model *) 26let local_table = Hashtbl.create 97 27and global_table = Hashtbl.create 97 28and prim_table = Hashtbl.create 5 29and known_macros = ref Strings.empty 30 31let purge = ref Strings.empty 32and purge_stack = MyStack.create "purge" 33and group_level = ref 0 34 35(* Hot start *) 36type ctable = (string, pat * action) Hashtbl.t 37type ptable = (string, (unit -> unit)) Hashtbl.t 38type saved = 39 int * Strings.t * Strings.t MyStack.saved * 40 ptable * ctable * ctable * Strings.t 41 42 43let pretty_macro n acs = 44 pretty_pat n ; 45 prerr_string " -> " ; 46 pretty_action acs 47 48let hidden_pretty_table cmdtable = 49 let t = Hashtbl.create 97 50 and count = ref 0 in 51 let incr k = 52 incr count ; 53 let r = 54 try Hashtbl.find t k with 55 | Not_found -> 56 let r = ref 0 in 57 Hashtbl.add t k r ; 58 r in 59 incr r in 60 Hashtbl.iter (fun k (n,acc) -> 61 Printf.fprintf stderr "%s -> " k ; 62 pretty_macro n acc ; 63 prerr_endline "" ; 64 incr k) cmdtable ; 65 Printf.fprintf stderr 66 "Table size: %d\n" !count ; 67 Hashtbl.iter 68 (fun k r -> 69 if !r > 1 then 70 Printf.fprintf stderr "%s: %d\n" k !r) 71 t ; 72 flush stderr 73 74let pretty_table () = 75 Printf.fprintf stderr "Macro tables, level=%d\n" !group_level ; 76 prerr_endline "Global table" ; 77 hidden_pretty_table global_table ; 78 prerr_endline "Local table" ; 79 hidden_pretty_table local_table 80 81let checkpoint () = 82 !group_level, !purge, MyStack.save purge_stack, 83 Hashtbl.copy prim_table, 84 Hashtbl.copy global_table, Hashtbl.copy local_table, 85 !known_macros 86 87and hot_start (level_checked, purge_checked, purge_stack_checked, 88 prim_checked, 89 global_checked, local_checked, 90 known_checked) = 91 group_level := level_checked ; 92 purge := purge_checked ; 93 MyStack.restore purge_stack purge_stack_checked ; 94 Misc.copy_hashtbl prim_checked prim_table ; 95 Misc.copy_hashtbl global_checked global_table ; 96 Misc.copy_hashtbl local_checked local_table ; 97 known_macros := known_checked 98 99(* Controlling scope *) 100let open_group () = 101 incr group_level ; 102 MyStack.push purge_stack !purge ; 103 purge := Strings.empty 104 105and close_group () = 106 if !group_level > 0 then (* Undo bindings created at the closed level *) 107 Strings.iter 108 (fun name -> Hashtbl.remove local_table name) 109 !purge ; 110 decr group_level ; 111 purge := MyStack.pop purge_stack 112 113let get_level () = !group_level 114 115(* Remove one local definition in advance ... *) 116let pre_purge name purge = 117 if Strings.mem name purge then begin 118 Hashtbl.remove local_table name ; 119 Strings.remove name purge 120 end else 121 purge 122 123(* Definitions *) 124let hidden_global_def name x = 125 if !group_level > 0 && Hashtbl.mem local_table name then begin 126 (* 127 global definition of a localy defined macro, 128 undo all local bindings 129 *) 130 purge := pre_purge name !purge ; 131 MyStack.map purge_stack (fun purge -> pre_purge name purge) 132 end ; 133 Hashtbl.replace global_table name x 134 135let hidden_local_def name x = 136 if !group_level > 0 then begin (* indeed local *) 137 if Strings.mem name !purge then (* redefinition *) 138 Hashtbl.remove local_table name 139 else (* creation (at the current level) *) 140 purge := Strings.add name !purge ; 141 Hashtbl.add local_table name x 142 end else begin (* same as global *) 143 Hashtbl.replace global_table name x 144 end 145 146let hidden_find name = 147 if !group_level > 0 then begin 148 try Hashtbl.find local_table name with 149 | Not_found -> Hashtbl.find global_table name 150 end else 151 Hashtbl.find global_table name 152 153let set_saved_macros () = 154 known_macros := 155 Hashtbl.fold 156 (fun name _ -> Strings.add name) 157 global_table Strings.empty 158 159let get_saved_macro name = Strings.mem name !known_macros 160 161(* Primitives *) 162let register_init name f = 163 if !verbose > 1 then 164 prerr_endline ("Registering primitives for package: "^name); 165 try 166 let _f = Hashtbl.find prim_table name in 167 fatal 168 ("Attempt to initlialize primitives for package "^name^" twice") 169 with 170 | Not_found -> Hashtbl.add prim_table name f 171 172and exec_init name = 173 if !verbose > 1 then 174 prerr_endline ("Initializing primitives for package: "^name) ; 175 try 176 let f = Hashtbl.find prim_table name in 177 Hashtbl.remove prim_table name ; (* accidental double is possible... *) 178 try f () with 179 Failed -> 180 Misc.warning 181 ("Bad trip while initializing primitives for package: "^name) 182 with Not_found -> 183 Misc.warning ("Cannot find primitives for package: "^name) 184;; 185 186 187(* Interface *) 188 189let exists name = 190 try 191 let _ = hidden_find name in true 192 with 193 | Not_found -> false 194 195 196let find name = 197 try hidden_find name with 198 | Not_found -> 199 warning ("Command not found: "^name) ; 200 ([],[]),Subst [] 201 202and find_fail name = 203 try hidden_find name with 204 | Not_found -> raise Failed 205 206let pretty_command name = 207 let n,acc = find name in 208 eprintf "%s: " name ; 209 pretty_macro n acc ; 210 prerr_endline "" 211 212let def name pat action = 213 if !verbose > 1 then begin 214 Printf.fprintf stderr "def %s = " name; 215 pretty_macro pat action ; 216 prerr_endline "" 217 end ; 218 hidden_local_def name (pat,action) 219 220and global_def name pat action = 221 if !verbose > 1 then begin 222 Printf.fprintf stderr "global def %s = " name; 223 pretty_macro pat action ; 224 prerr_endline "" 225 end ; 226 hidden_global_def name (pat,action) 227 228and global_undef name = 229 Hashtbl.remove global_table name ; 230 Hashtbl.remove local_table name 231;; 232 233let def_init name f = 234 if exists name then begin 235 fatal ("Command: "^name^" defined at initialisation") 236 end ; 237 def name zero_pat (CamlCode f) 238 239(* 240let pretty_arg = function 241 | None -> prerr_string "<None>" 242 | Some (n,acc) -> pretty_macro n acc 243 244let pretty_replace s name old new_def = 245 Printf.fprintf stderr "%s: %s\n\told=" s name ; 246 pretty_arg old ; 247 Printf.fprintf stderr "\n\tnew=" ; 248 pretty_arg new_def ; 249 prerr_endline "" 250*) 251 252let replace name new_def = 253 let old_def = 254 try Some (hidden_find name) with 255 | Not_found -> None in 256(* 257 pretty_replace "replace" name old_def new_def ; 258 Printf.fprintf stderr "level=%d\n" !group_level ; 259*) 260 begin match new_def with 261 | Some d -> hidden_local_def name d 262 | None -> match old_def with 263 | None -> () 264 | Some _ -> (* what will happen if binding was global ??? *) 265 if !group_level > 0 then 266 purge := pre_purge name !purge 267 else 268 Hashtbl.remove global_table name 269 end ; 270 old_def 271 272(* addto *) 273let addto name body = 274 let old = try Some (hidden_find name) with Not_found -> None in 275 match old with 276 | Some (pat,Subst obody) -> 277 hidden_local_def name (pat,Subst (obody@("%\n"::body))) 278 | Some (_,_) -> 279 warning "\\addto on non-subst macro" 280 | None -> 281 hidden_local_def name (zero_pat,Subst body) 282 283 284 285(* macro static properties *) 286 287let invisible = function 288 "\\nofiles" 289| "\\pagebreak" | "\\nopagebreak" | "\\linebreak" 290| "\\nolinebreak" | "\\label" | "\\index" 291| "\\vspace" | "\\glossary" | "\\marginpar" 292| "\\figure" | "\\table" 293| "\\nostyle" | "\\rm" | "\\tt" 294| "\\bf" | "\\em" | "\\it" | "\\sl" 295| "\\tiny" | "\\footnotesize" | "\\scriptsize" 296| "\\small" | "\\normalsize" | "\\large" | "\\Large" | "\\LARGE" 297| "\\huge" | "\\Huge" 298| "\\purple" | "\\silver" | "\\gray" | "\\white" 299| "\\maroon" | "\\red" | "\\fuchsia" | "\\green" 300| "\\lime" | "\\olive" | "\\yellow" | "\\navy" 301| "\\blue" | "\\teal" | "\\aqua" | "\\else" | "\\fi" 302| "\\char" -> true 303| name -> 304 (String.length name >= 3 && String.sub name 0 3 = "\\if") 305;; 306 307