summaryrefslogtreecommitdiff
path: root/cil/ocamlutil/util.ml
blob: e6c2c679122c0cbe50e0e3c9d70d6fbd0886f89b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
(** Utility functions for Coolaid *)
module E = Errormsg
module H = Hashtbl
module IH = Inthash

open Pretty

exception GotSignal of int

let withTimeout (secs: float) (* Seconds for timeout *)
                (handler: int -> 'b) (* What to do if we have a timeout. The 
                                        * argument passed is the signal number 
                                        * received. *)
                (f: 'a -> 'b) (* The function to run *)
                (arg: 'a) (* And its argument *)
   : 'b = 
  let oldHandler = 
    Sys.signal Sys.sigalrm 
      (Sys.Signal_handle 
         (fun i -> 
           ignore (E.log "Got signal %d\n" i);
           raise (GotSignal i)))
  in
  let reset_sigalrm () = 
    ignore (Unix.setitimer Unix.ITIMER_REAL { Unix.it_value = 0.0;
                                              Unix.it_interval = 0.0;});
    Sys.set_signal Sys.sigalrm oldHandler;
  in
  ignore (Unix.setitimer Unix.ITIMER_REAL 
            { Unix.it_value    = secs;
              Unix.it_interval = 0.0;});
  (* ignore (Unix.alarm 2); *)
  try
    let res = f arg in 
    reset_sigalrm ();
    res
  with exc -> begin
    reset_sigalrm ();
    ignore (E.log "Got an exception\n");
    match exc with 
      GotSignal i -> 
        handler i
    | _ -> raise exc
  end

(** Print a hash table *)
let docHash ?(sep=chr ',') (one: 'a -> 'b -> doc) () (h: ('a, 'b) H.t) = 
  (H.fold 
     (fun key data acc -> 
       if acc == align then acc ++ one key data
       else acc ++ sep ++ one key data)
     h
     align) ++ unalign
    


let hash_to_list (h: ('a, 'b) H.t) : ('a * 'b) list =
  H.fold
    (fun key data acc -> (key, data) :: acc)
    h
    []

let keys (h: ('a, 'b) H.t) : 'a list =
  H.fold
    (fun key data acc -> key :: acc)
    h
    []

let hash_copy_into (hfrom: ('a, 'b) H.t) (hto: ('a, 'b) H.t) : unit = 
  H.clear hto;
  H.iter (H.add hto) hfrom

let anticompare a b = compare b a
;;


let rec list_drop (n : int) (xs : 'a list) : 'a list =
  if n < 0 then invalid_arg "Util.list_drop";
  if n = 0 then 
    xs
  else begin 
    match xs with
    | [] -> invalid_arg "Util.list_drop"
    | y::ys -> list_drop (n-1) ys
  end

let list_droptail (n : int) (xs : 'a list) : 'a list =
  if n < 0 then invalid_arg "Util.list_droptail";
  let (ndrop,r) =
    List.fold_right
      (fun x (ndrop,acc) ->
	if ndrop = 0 then (ndrop, x :: acc)
	else (ndrop-1, acc))
      xs
      (n,[])
  in
  if ndrop > 0 then invalid_arg "Util.listdroptail"
  else r

let rec list_span (p : 'a -> bool) (xs : 'a list) : 'a list * 'a list = 
  begin match xs with
  | [] -> ([],[])
  | x::xs' -> 
      if p x then
        let (ys,zs) = list_span p xs' in (x::ys,zs)
      else ([],xs)
  end
;;

let rec list_rev_append revxs ys =
  begin match revxs with
  | [] -> ys
  | x::xs -> list_rev_append xs (x::ys)
  end
;;
let list_insert_by (cmp : 'a -> 'a -> int) 
    (x : 'a) (xs : 'a list) : 'a list =
  let rec helper revhs ts =
    begin match ts with
    | [] -> List.rev (x::revhs)
    | t::ts' -> 
        if cmp x t >= 0 then helper (t::revhs) ts'
        else list_rev_append (x::revhs) ts
    end
  in
  helper [] xs
;;

let list_head_default (d : 'a) (xs : 'a list) : 'a =
  begin match xs with
  | [] -> d
  | x::_ -> x
  end
;;

let rec list_iter3 f xs ys zs =
  begin match xs, ys, zs with
  | [], [], [] -> ()
  | x::xs, y::ys, z::zs -> f x y z; list_iter3 f xs ys zs
  | _ -> invalid_arg "Util.list_iter3"
  end
;;
  
let rec get_some_option_list (xs : 'a option list) : 'a list =
  begin match xs with
  | [] -> []
  | None::xs  -> get_some_option_list xs
  | Some x::xs -> x :: get_some_option_list xs
  end
;;

(* tail-recursive append: reverses xs twice *)
let list_append (xs: 'a list) (ys: 'a list): 'a list =
  match xs with (* optimize some common cases *)
      [] -> ys
    | [x] -> x::ys
    | _ -> list_rev_append (List.rev xs) ys

let list_iteri (f: int -> 'a -> unit) (l: 'a list) : unit = 
  let rec loop (i: int) (l: 'a list) : unit = 
    match l with 
      [] -> ()
    | h :: t -> f i h; loop (i + 1) t
  in
  loop 0 l

let list_mapi (f: int -> 'a -> 'b) (l: 'a list) : 'b list = 
  let rec loop (i: int) (l: 'a list) : 'b list = 
    match l with 
      [] -> []
    | h :: t -> 
	let headres = f i h in
	headres :: loop (i + 1) t
  in
  loop 0 l

let list_fold_lefti (f: 'acc -> int -> 'a -> 'acc) (start: 'acc) 
                   (l: 'a list) : 'acc = 
  let rec loop (i, acc) l = 
    match l with
      [] -> acc
    | h :: t -> loop (i + 1, f acc i h) t
  in
  loop (0, start) l


let list_init (len : int) (init_fun : int -> 'a) : 'a list =
  let rec loop n acc =
    if n < 0 then acc
    else loop (n-1) ((init_fun n)::acc)
  in
  loop (len - 1) []
;;


let rec list_find_first (l: 'a list) (f: 'a -> 'b option) : 'b option = 
  match l with 
    [] -> None
  | h :: t -> begin
      match f h with 
        None -> list_find_first t f
      | r -> r
  end
  
(** Generates the range of integers starting with a and ending with b *)
let int_range_list (a: int) (b: int) = 
  list_init (b - a + 1) (fun i -> a + i)


(** Some handling of registers *)
type 'a growArrayFill =
    Elem of 'a
  | Susp of (int -> 'a)

type 'a growArray = {
            gaFill: 'a growArrayFill;
            (** Stuff to use to fill in the array as it grows *)

    mutable gaMaxInitIndex: int;
            (** Maximum index that was written to. -1 if no writes have 
             * been made.  *)

    mutable gaData: 'a array;
  } 

let growTheArray (ga: 'a growArray) (len: int) 
                 (toidx: int) (why: string) : unit = 
  if toidx >= len then begin
    (* Grow the array by 50% *)
    let newlen = toidx + 1 + len  / 2 in
(*
    ignore (E.log "growing an array to idx=%d (%s)\n" toidx why);
*)
    let data' = begin match ga.gaFill with
      Elem x ->

	let data'' = Array.create newlen x in
	Array.blit ga.gaData 0 data'' 0 len;
	data''
    | Susp f -> Array.init newlen
	  (fun i -> if i < len then ga.gaData.(i) else f i)
    end
    in
    ga.gaData <- data'
  end

let getReg (ga: 'a growArray) (r: int) : 'a = 
  let len = Array.length ga.gaData in
  if r >= len then 
    growTheArray ga len r "get";

  ga.gaData.(r)

let setReg (ga: 'a growArray) (r: int) (what: 'a) : unit = 
  let len = Array.length ga.gaData in
  if r >= len then 
    growTheArray ga len r "set";
  if r > ga.gaMaxInitIndex then ga.gaMaxInitIndex <- r;
  ga.gaData.(r) <- what

let newGrowArray (initsz: int) (fill: 'a growArrayFill) : 'a growArray = 
  { gaFill = fill;
    gaMaxInitIndex = -1;
    gaData = begin match fill with
      Elem x -> Array.create initsz x
    | Susp f -> Array.init initsz f
    end; }

let copyGrowArray (ga: 'a growArray) : 'a growArray = 
  { ga with gaData = Array.copy ga.gaData } 

let deepCopyGrowArray (ga: 'a growArray) (copy: 'a -> 'a): 'a growArray = 
  { ga with gaData = Array.map copy ga.gaData } 



(** Iterate over the initialized elements of the array *)
let growArray_iteri  (f: int -> 'a -> unit) (ga: 'a growArray) = 
  for i = 0 to ga.gaMaxInitIndex do 
    f i ga.gaData.(i)
  done


(** Fold left over the initialized elements of the array *)
let growArray_foldl (f: 'acc -> 'a -> 'acc) 
                    (acc: 'acc) (ga: 'a growArray) : 'acc = 
  let rec loop (acc: 'acc) (idx: int) : 'acc = 
    if idx > ga.gaMaxInitIndex then 
      acc
    else
      loop (f acc ga.gaData.(idx)) (idx + 1)
  in
  loop acc 0




let hasPrefix (prefix: string) (what: string) : bool = 
  let pl = String.length prefix in
  try String.sub what 0 pl = prefix 
  with Invalid_argument _ -> false



let restoreRef ?(deepCopy=(fun x -> x)) (r: 'a ref) : (unit -> unit) = 
  let old = deepCopy !r in
  (fun () -> r := old)

let restoreHash ?deepCopy (h: ('a, 'b) H.t) : (unit -> unit) = 
  let old = 
    match deepCopy with 
      None -> H.copy h 
    | Some f -> 
        let old = H.create (H.length h) in 
        H.iter (fun k d -> H.add old k (f d)) h;
        old
  in
  (fun () -> hash_copy_into old h)

let restoreIntHash ?deepCopy (h: 'a IH.t) : (unit -> unit) = 
  let old = 
    match deepCopy with 
      None -> IH.copy h 
    | Some f -> 
        let old = IH.create 13 in 
        IH.iter (fun k d -> IH.add old k (f d)) h;
        old
  in
  (fun () -> 
    IH.clear old;
    IH.iter (fun i k -> IH.add old i k) h)

let restoreArray ?deepCopy (a: 'a array) : (unit -> unit) = 
  let old = Array.copy a in
  (match deepCopy with 
    None -> ()
  | Some f -> Array.iteri (fun i v -> old.(i) <- f v) old);
  (fun () -> Array.blit old 0 a 0 (Array.length a))

let runThunks (l: (unit -> unit) list) : (unit -> unit) = 
  fun () -> List.iter (fun f -> f ()) l



(* Memoize *)
let memoize (h: ('a, 'b) Hashtbl.t) 
            (arg: 'a) 
            (f: 'a -> 'b) : 'b = 
  try
    Hashtbl.find h arg
  with Not_found -> begin
    let res = f arg in
    Hashtbl.add h arg res;
    res
  end

(* Just another name for memoize *)
let findOrAdd h arg f = memoize h arg f

(* A tryFinally function *)
let tryFinally 
    (main: 'a -> 'b) (* The function to run *)
    (final: 'b option -> unit)  (* Something to run at the end *)
    (arg: 'a) : 'b = 
  try
    let res: 'b = main arg in
    final (Some res);
    res
  with e -> begin
    final None;
    raise e
  end




let valOf : 'a option -> 'a = function
    None -> raise (Failure "Util.valOf")
  | Some x -> x

(**
 * An accumulating for loop.
 *
 * Initialize the accumulator with init.  The current index and accumulator
 * from the previous iteration is passed to f.
 *)
let fold_for ~(init: 'a) ~(lo: int) ~(hi: int) (f: int -> 'a -> 'a) =
  let rec forloop i acc =
    if i > hi then acc
    else forloop (i+1) (f i acc)
  in
  forloop lo init

(************************************************************************)

module type STACK = sig
  type 'a t
  (** The type of stacks containing elements of type ['a]. *)

  exception Empty
  (** Raised when {!Stack.pop} or {!Stack.top} is applied to an empty stack. *)

  val create : unit -> 'a t
  (** Return a new stack, initially empty. *)

  val push : 'a -> 'a t -> unit
  (** [push x s] adds the element [x] at the top of stack [s]. *)

  val pop : 'a t -> 'a
  (** [pop s] removes and returns the topmost element in stack [s],
     or raises [Empty] if the stack is empty. *)

  val top : 'a t -> 'a
  (** [top s] returns the topmost element in stack [s],
     or raises [Empty] if the stack is empty. *)
  
  val clear : 'a t -> unit
  (** Discard all elements from a stack. *)
  
  val copy : 'a t -> 'a t
  (** Return a copy of the given stack. *)
  
  val is_empty : 'a t -> bool
  (** Return [true] if the given stack is empty, [false] otherwise. *)
  
  val length : 'a t -> int
  (** Return the number of elements in a stack. *)
  
  val iter : ('a -> unit) -> 'a t -> unit
  (** [iter f s] applies [f] in turn to all elements of [s],
     from the element at the top of the stack to the element at the
     bottom of the stack. The stack itself is unchanged. *)
end

module Stack = struct

  type 'a t = { mutable length : int;
                stack : 'a Stack.t; }

  exception Empty

  let create () = { length = 0;
                    stack = Stack.create(); }

  let push x s =
    s.length <- s.length + 1;
    Stack.push x s.stack

  let pop s =
    s.length <- s.length - 1;
    Stack.pop s.stack

  let top s =
    Stack.top s.stack

  let clear s =
    s.length <- 0;
    Stack.clear s.stack

  let copy s = { length = s.length;
		 stack = Stack.copy s.stack; }
    
  let is_empty s =
    Stack.is_empty s.stack

  let length s = s.length

  let iter f s =
    Stack.iter f s.stack

end

(************************************************************************)

let absoluteFilename (fname: string) = 
  if Filename.is_relative fname then 
    Filename.concat (Sys.getcwd ()) fname
  else
    fname


(* mapNoCopy is like map but avoid copying the list if the function does not 
 * change the elements. *)
let rec mapNoCopy (f: 'a -> 'a) = function
    [] -> []
  | (i :: resti) as li -> 
      let i' = f i in
      let resti' = mapNoCopy f resti in
      if i' != i || resti' != resti then i' :: resti' else li 

let rec mapNoCopyList (f: 'a -> 'a list) = function
    [] -> []
  | (i :: resti) as li -> 
      let il' = f i in
      let resti' = mapNoCopyList f resti in
      match il' with
        [i'] when i' == i && resti' == resti -> li
      | _ -> il' @ resti'


(* Use a filter function that does not rewrite the list unless necessary *)
let rec filterNoCopy (f: 'a -> bool) (l: 'a list) : 'a list = 
  match l with 
    [] -> []
  | h :: rest when not (f h) -> filterNoCopy f rest 
  | h :: rest -> 
      let rest' = filterNoCopy f rest in
      if rest == rest' then l else h :: rest'

(** Join a list of strings *)
let rec joinStrings (sep: string) (sl: string list) = 
  match sl with 
    [] -> ""
  | [s1] -> s1
  | s1 :: ((_ :: _) as rest) -> s1 ^ sep ^ joinStrings sep rest


(************************************************************************

 Configuration 

 ************************************************************************)
(** The configuration data can be of several types **)
type configData = 
    ConfInt of int
  | ConfBool of bool
  | ConfFloat of float
  | ConfString of string
  | ConfList of configData list


(* Store here window configuration file *)
let configurationData: (string, configData) H.t = H.create 13

let clearConfiguration () = H.clear configurationData

let setConfiguration (key: string) (c: configData) = 
  H.replace configurationData key c

let findConfiguration (key: string) : configData = 
  H.find configurationData key 

let findConfigurationInt (key: string) : int = 
  match findConfiguration key with 
    ConfInt i -> i
  | _ -> 
      ignore (E.warn "Configuration %s is not an integer" key);
      raise Not_found

let useConfigurationInt (key: string) (f: int -> unit) = 
  try f (findConfigurationInt key)
  with Not_found -> ()

let findConfigurationString (key: string) : string = 
  match findConfiguration key with 
    ConfString s -> s
  | _ -> 
      ignore (E.warn "Configuration %s is not a string" key);
      raise Not_found

let useConfigurationString (key: string) (f: string -> unit) = 
  try f (findConfigurationString key)
  with Not_found -> ()


let findConfigurationBool (key: string) : bool = 
  match findConfiguration key with 
    ConfBool b -> b
  | _ -> 
      ignore (E.warn "Configuration %s is not a boolean" key);
      raise Not_found

let useConfigurationBool (key: string) (f: bool -> unit) = 
  try f (findConfigurationBool key)
  with Not_found -> ()

let findConfigurationList (key: string) : configData list  = 
  match findConfiguration key with 
    ConfList l -> l
  | _ -> 
      ignore (E.warn "Configuration %s is not a list" key);
      raise Not_found

let useConfigurationList (key: string) (f: configData list -> unit) = 
  try f (findConfigurationList key)
  with Not_found -> ()


let saveConfiguration (fname: string) = 
  (** Convert configuration data to a string, for saving externally *)
  let configToString (c: configData) : string = 
    let buff = Buffer.create 80 in
    let rec loop (c: configData) : unit = 
      match c with 
        ConfInt i -> 
          Buffer.add_char buff 'i';
          Buffer.add_string buff (string_of_int i);
          Buffer.add_char buff ';'
            
      | ConfBool b -> 
          Buffer.add_char buff 'b';
          Buffer.add_string buff (string_of_bool b);
          Buffer.add_char buff ';'
            
      | ConfFloat f -> 
          Buffer.add_char buff 'f';
          Buffer.add_string buff (string_of_float f);
          Buffer.add_char buff ';'
            
      | ConfString s -> 
          if String.contains s '"' then 
            E.s (E.unimp "Guilib: configuration string contains quotes");
          Buffer.add_char buff '"';
          Buffer.add_string buff s;
          Buffer.add_char buff '"'; (* '"' *)
          
      | ConfList l -> 
          Buffer.add_char buff '[';
          List.iter loop l;
          Buffer.add_char buff ']'
    in
    loop c;
    Buffer.contents buff
  in
  try 
    let oc = open_out fname in
    ignore (E.log "Saving configuration to %s\n" (absoluteFilename fname));
    H.iter (fun k c -> 
      output_string oc (k ^ "\n");
      output_string oc ((configToString c) ^ "\n"))
      configurationData;
    close_out oc
  with _ -> 
    ignore (E.warn "Cannot open configuration file %s\n" fname)


(** Make some regular expressions early *)
let intRegexp = Str.regexp "i\\([0-9]+\\);"
let floatRegexp = Str.regexp "f\\([0-9]+\\.[0-9]+\\);"
let boolRegexp = Str.regexp "b\\(\\(true\\)\\|\\(false\\)\\);"
let stringRegexp = Str.regexp "\"\\([^\"]*\\)\""

let loadConfiguration (fname: string) : unit = 
  H.clear configurationData;

  let stringToConfig (s: string) : configData = 
    let idx = ref 0 in (** the current index *)
    let l = String.length s in 
    
    let rec getOne () : configData = 
      if !idx >= l then raise Not_found;
      
      if Str.string_match intRegexp s !idx then begin
        idx := Str.match_end ();
        ConfInt (int_of_string (Str.matched_group 1 s))
      end else if Str.string_match floatRegexp s !idx then begin
        idx := Str.match_end ();
        ConfFloat (float_of_string (Str.matched_group 1 s))
      end else if Str.string_match boolRegexp s !idx then begin
        idx := Str.match_end ();
        ConfBool (bool_of_string (Str.matched_group 1 s))
      end else if  Str.string_match stringRegexp s !idx then begin
        idx := Str.match_end ();
        ConfString (Str.matched_group 1 s)
      end else if String.get s !idx = '[' then begin
        (* We are starting a list *)
        incr idx;
        let rec loop (acc: configData list) : configData list = 
          if !idx >= l then begin
            ignore (E.warn "Non-terminated list in configuration %s" s);
            raise Not_found
          end;
          if String.get s !idx = ']' then begin
            incr idx;
            List.rev acc
          end else
            loop (getOne () :: acc)
        in
        ConfList (loop [])
      end else begin
        ignore (E.warn "Bad configuration element in a list: %s\n"
                  (String.sub s !idx (l - !idx)));
        raise Not_found
      end
    in
    getOne ()
  in
  (try 
    let ic = open_in fname in
    ignore (E.log "Loading configuration from %s\n" (absoluteFilename fname));
    (try 
      while true do
        let k = input_line ic in
        let s = input_line ic in
        try 
          let c = stringToConfig s in 
          setConfiguration k c
        with Not_found -> ()
      done
    with End_of_file -> ());
    close_in ic;
  with _ -> () (* no file, ignore *));
  
  ()

   
 
(*********************************************************************)
type symbol = int

(**{ Registering symbol names} *)
let registeredSymbolNames: (string, symbol) H.t = H.create 113
let symbolNames: string IH.t = IH.create 113 
let nextSymbolId = ref 0 

(* When we register symbol ranges, we store a naming function for use later 
 * when we print the symbol *)
let symbolRangeNaming: (int * int * (int -> string)) list ref = ref []

(* Reset the symbols. We want to allow the registration of symbols at the 
 * top-level. This means that we cannot simply clear the hash tables. The 
 * first time we call "reset" we actually remember the state. *)
let resetThunk: (unit -> unit) option ref = ref None

let snapshotSymbols () : unit -> unit = 
  runThunks [ restoreIntHash symbolNames;
              restoreRef nextSymbolId;
              restoreHash registeredSymbolNames;
              restoreRef symbolRangeNaming ]

let resetSymbols () = 
  match !resetThunk with 
    None -> resetThunk := Some (snapshotSymbols ())
  | Some t -> t ()
  

let dumpSymbols () = 
  ignore (E.log "Current symbols\n");
  IH.iter (fun i k -> ignore (E.log " %s -> %d\n" k i)) symbolNames;
  ()

let newSymbol (n: string) : symbol = 
  assert(not (H.mem registeredSymbolNames n));
  let id = !nextSymbolId in
  incr nextSymbolId;
  H.add registeredSymbolNames n id;
  IH.add symbolNames id n;
  id

let registerSymbolName (n: string) : symbol = 
  try H.find registeredSymbolNames n
  with Not_found -> begin
    newSymbol n
  end

(** Register a range of symbols. The mkname function will be invoked for 
 * indices starting at 0 *)
let registerSymbolRange (count: int) (mkname: int -> string) : symbol = 
  if count < 0 then E.s (E.bug "registerSymbolRange: invalid counter");
  let first = !nextSymbolId in
  nextSymbolId := !nextSymbolId + count;
  symbolRangeNaming := 
    (first, !nextSymbolId - 1, mkname) :: !symbolRangeNaming;
  first
    
let symbolName (id: symbol) : string = 
  try IH.find symbolNames id
  with Not_found -> 
    (* Perhaps it is one of the lazily named symbols *)
    try 
      let (fst, _, mkname) = 
        List.find 
          (fun (fst,lst,_) -> fst <= id && id <= lst) 
          !symbolRangeNaming in
      let n = mkname (id - fst) in
      IH.add symbolNames id n;
      n
    with Not_found ->
      ignore (E.warn "Cannot find the name of symbol %d" id);
      "symbol" ^ string_of_int id

(************************************************************************)

(** {1 Int32 Operators} *)

module Int32Op = struct
   exception IntegerTooLarge
   let to_int (i: int32) = 
     let i' = Int32.to_int i in (* Silently drop the 32nd bit *)
     if i = Int32.of_int i' then i'
     else raise IntegerTooLarge

   let (<%) = (fun x y -> (Int32.compare x y) < 0)
   let (<=%) = (fun x y -> (Int32.compare x y) <= 0)
   let (>%) = (fun x y -> (Int32.compare x y) > 0)
   let (>=%) = (fun x y -> (Int32.compare x y) >= 0)
   let (<>%) = (fun x y -> (Int32.compare x y) <> 0)
   
   let (+%) = Int32.add
   let (-%) = Int32.sub
   let ( *% ) = Int32.mul
   let (/%) = Int32.div
   let (~-%) = Int32.neg

   (* We cannot use the <<% because it trips camlp4 *)
   let sll = fun i j -> Int32.shift_left i (to_int j)
   let (>>%) = fun i j -> Int32.shift_right i (to_int j)
   let (>>>%) = fun i j -> Int32.shift_right_logical i (to_int j)
end


(*********************************************************************)

let equals x1 x2 : bool =
  (compare x1 x2) = 0