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