Release coccinelle-0.1.11rc1
[bpt/coccinelle.git] / commons / common.ml
CommitLineData
b1b2de81
C
1(* Yoann Padioleau
2 *
3 * Copyright (C) 1998-2009 Yoann Padioleau
34e49164
C
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * version 2.1 as published by the Free Software Foundation, with the
8 * special exception on linking described in file license.txt.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
13 * license.txt for more details.
14 *)
15
16(*****************************************************************************)
17(* Notes *)
18(*****************************************************************************)
19
20
21
22(* ---------------------------------------------------------------------- *)
23(* Maybe could split common.ml and use include tricks as in ofullcommon.ml or
24 * Jane Street core lib. But then harder to bundle simple scripts like my
25 * make_full_linux_kernel.ml because would then need to pass all the files
26 * either to ocamlc or either to some #load. Also as the code of many
27 * functions depends on other functions from this common, it would
28 * be tedious to add those dependencies. Here simpler (have just the
29 * pb of the Prelude, but it's a small problem).
30 *
31 * pixel means code from Pascal Rigaux
32 * julia means code from Julia Lawall
33 *)
34(* ---------------------------------------------------------------------- *)
35
36(*****************************************************************************)
37(* We use *)
38(*****************************************************************************)
39(*
40 * modules:
41 * - Pervasives, of course
42 * - List
43 * - Str
44 * - Hashtbl
45 * - Format
46 * - Buffer
47 * - Unix and Sys
48 * - Arg
49 *
50 * functions:
51 * - =, <=, max min, abs, ...
52 * - List.rev, List.mem, List.partition,
53 * - List.fold*, List.concat, ...
54 * - Str.global_replace
91eba41f 55 * - Filename.is_relative
0708f913 56 * - String.uppercase, String.lowercase
34e49164
C
57 *
58 *
59 * The Format library allows to hide passing an indent_level variable.
60 * You use as usual the print_string function except that there is
61 * this automatic indent_level variable handled for you (and maybe
62 * more services). src: julia in coccinelle unparse_cocci.
63 *
64 * Extra packages
65 * - ocamlbdb
91eba41f 66 * - ocamlgtk, and gtksourceview
34e49164
C
67 * - ocamlgl
68 * - ocamlpython
69 * - ocamlagrep
70 * - ocamlfuse
71 * - ocamlmpi
72 * - ocamlcalendar
73 *
91eba41f
C
74 * - pcre
75 * - sdl
76 *
77 * Many functions in this file were inspired by Haskell or Lisp librairies.
34e49164
C
78 *)
79
80(*****************************************************************************)
81(* Prelude *)
82(*****************************************************************************)
83
84(* The following functions should be in their respective sections but
85 * because some functions in some sections use functions in other
86 * sections, and because I don't want to take care of the order of
87 * those sections, of those dependencies, I put the functions causing
88 * dependency problem here. C is better than caml on this with the
89 * ability to declare prototype, enabling some form of forward
90 * reference. *)
91
92let (+>) o f = f o
93let (++) = (@)
94
95exception Timeout
96exception UnixExit of int
97
98let rec (do_n: int -> (unit -> unit) -> unit) = fun i f ->
99 if i = 0 then () else (f(); do_n (i-1) f)
100let rec (foldn: ('a -> int -> 'a) -> 'a -> int -> 'a) = fun f acc i ->
101 if i = 0 then acc else foldn f (f acc i) (i-1)
102
103let sum_int = List.fold_left (+) 0
104
105(* could really call it 'for' :) *)
106let fold_left_with_index f acc =
107 let rec fold_lwi_aux acc n = function
108 | [] -> acc
109 | x::xs -> fold_lwi_aux (f acc x n) (n+1) xs
110 in fold_lwi_aux acc 0
111
112
113let rec drop n xs =
114 match (n,xs) with
115 | (0,_) -> xs
116 | (_,[]) -> failwith "drop: not enough"
117 | (n,x::xs) -> drop (n-1) xs
118
119let rec enum_orig x n = if x = n then [n] else x::enum_orig (x+1) n
120
121let enum x n =
122 if not(x <= n)
123 then failwith (Printf.sprintf "bad values in enum, expect %d <= %d" x n);
124 let rec enum_aux acc x n =
125 if x = n then n::acc else enum_aux (x::acc) (x+1) n
126 in
127 List.rev (enum_aux [] x n)
128
129let rec take n xs =
130 match (n,xs) with
131 | (0,_) -> []
132 | (_,[]) -> failwith "take: not enough"
133 | (n,x::xs) -> x::take (n-1) xs
134
135
136let last_n n l = List.rev (take n (List.rev l))
137let last l = List.hd (last_n 1 l)
138
139
140let (list_of_string: string -> char list) = function
141 "" -> []
142 | s -> (enum 0 ((String.length s) - 1) +> List.map (String.get s))
143
144let (lines: string -> string list) = fun s ->
145 let rec lines_aux = function
146 | [] -> []
147 | [x] -> if x = "" then [] else [x]
148 | x::xs ->
149 x::lines_aux xs
150 in
151 Str.split_delim (Str.regexp "\n") s +> lines_aux
152
153
154let push2 v l =
155 l := v :: !l
156
b1b2de81 157let null xs = match xs with [] -> true | _ -> false
34e49164
C
158
159
160
161
162let debugger = ref false
163
164let unwind_protect f cleanup =
165 if !debugger then f() else
166 try f ()
167 with e -> begin cleanup e; raise e end
168
169let finalize f cleanup =
170 if !debugger then f() else
171 try
172 let res = f () in
173 cleanup ();
174 res
175 with e ->
176 cleanup ();
177 raise e
178
179let command2 s = ignore(Sys.command s)
180
181
182let (matched: int -> string -> string) = fun i s ->
183 Str.matched_group i s
184
185let matched1 = fun s -> matched 1 s
186let matched2 = fun s -> (matched 1 s, matched 2 s)
187let matched3 = fun s -> (matched 1 s, matched 2 s, matched 3 s)
188let matched4 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s)
189let matched5 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s)
190let matched6 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s, matched 6 s)
191let matched7 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s, matched 6 s, matched 7 s)
192
193let (with_open_stringbuf: (((string -> unit) * Buffer.t) -> unit) -> string) =
194 fun f ->
195 let buf = Buffer.create 1000 in
196 let pr s = Buffer.add_string buf (s ^ "\n") in
197 f (pr, buf);
198 Buffer.contents buf
199
200
485bce71
C
201let foldl1 p = function x::xs -> List.fold_left p x xs | _ -> failwith "foldl1"
202
34e49164
C
203(*****************************************************************************)
204(* Debugging/logging *)
205(*****************************************************************************)
206
207(* I used this in coccinelle where the huge logging of stuff ask for
208 * a more organized solution that use more visual indentation hints.
209 *
210 * todo? could maybe use log4j instead ? or use Format module more
211 * consistently ?
212 *)
213
214let _tab_level_print = ref 0
215let _tab_indent = 5
216
217
218let _prefix_pr = ref ""
219
220let indent_do f =
221 _tab_level_print := !_tab_level_print + _tab_indent;
222 finalize f
223 (fun () -> _tab_level_print := !_tab_level_print - _tab_indent;)
224
225
226let pr s =
227 print_string !_prefix_pr;
228 do_n !_tab_level_print (fun () -> print_string " ");
229 print_string s;
230 print_string "\n";
231 flush stdout
232
233let pr_no_nl s =
234 print_string !_prefix_pr;
235 do_n !_tab_level_print (fun () -> print_string " ");
236 print_string s;
237 flush stdout
238
239
708f4980
C
240
241
242
243
244let _chan_pr2 = ref (None: out_channel option)
245
246let out_chan_pr2 ?(newline=true) s =
247 match !_chan_pr2 with
248 | None -> ()
249 | Some chan ->
250 output_string chan (s ^ (if newline then "\n" else ""));
251 flush chan
252
253
34e49164
C
254let pr2 s =
255 prerr_string !_prefix_pr;
256 do_n !_tab_level_print (fun () -> prerr_string " ");
257 prerr_string s;
258 prerr_string "\n";
708f4980
C
259 flush stderr;
260 out_chan_pr2 s;
261 ()
34e49164
C
262
263let pr2_no_nl s =
264 prerr_string !_prefix_pr;
265 do_n !_tab_level_print (fun () -> prerr_string " ");
266 prerr_string s;
708f4980
C
267 flush stderr;
268 out_chan_pr2 ~newline:false s;
269 ()
270
34e49164
C
271
272let pr_xxxxxxxxxxxxxxxxx () =
273 pr "-----------------------------------------------------------------------"
274
275let pr2_xxxxxxxxxxxxxxxxx () =
276 pr2 "-----------------------------------------------------------------------"
277
278
279let reset_pr_indent () =
280 _tab_level_print := 0
281
282(* old:
283 * let pr s = (print_string s; print_string "\n"; flush stdout)
284 * let pr2 s = (prerr_string s; prerr_string "\n"; flush stderr)
285 *)
286
287(* ---------------------------------------------------------------------- *)
288
289(* I can not use the _xxx ref tech that I use for common_extra.ml here because
290 * ocaml don't like the polymorphism of Dumper mixed with refs.
291 *
292 * let (_dump_func : ('a -> string) ref) = ref
293 * (fun x -> failwith "no dump yet, have you included common_extra.cmo?")
294 * let (dump : 'a -> string) = fun x ->
295 * !_dump_func x
296 *
297 * So I have included directly dumper.ml in common.ml. It's more practical
298 * when want to give script that use my common.ml, I just have to give
299 * this file.
300 *)
301
302(* start of dumper.ml *)
303
304(* Dump an OCaml value into a printable string.
305 * By Richard W.M. Jones (rich@annexia.org).
306 * dumper.ml 1.2 2005/02/06 12:38:21 rich Exp
307 *)
308open Printf
309open Obj
310
311let rec dump r =
312 if is_int r then
313 string_of_int (magic r : int)
314 else ( (* Block. *)
315 let rec get_fields acc = function
316 | 0 -> acc
317 | n -> let n = n-1 in get_fields (field r n :: acc) n
318 in
319 let rec is_list r =
320 if is_int r then (
321 if (magic r : int) = 0 then true (* [] *)
322 else false
323 ) else (
324 let s = size r and t = tag r in
325 if t = 0 && s = 2 then is_list (field r 1) (* h :: t *)
326 else false
327 )
328 in
329 let rec get_list r =
330 if is_int r then []
331 else let h = field r 0 and t = get_list (field r 1) in h :: t
332 in
333 let opaque name =
334 (* XXX In future, print the address of value 'r'. Not possible in
335 * pure OCaml at the moment.
336 *)
337 "<" ^ name ^ ">"
338 in
339
340 let s = size r and t = tag r in
341
342 (* From the tag, determine the type of block. *)
343 if is_list r then ( (* List. *)
344 let fields = get_list r in
345 "[" ^ String.concat "; " (List.map dump fields) ^ "]"
346 )
347 else if t = 0 then ( (* Tuple, array, record. *)
348 let fields = get_fields [] s in
349 "(" ^ String.concat ", " (List.map dump fields) ^ ")"
350 )
351
352 (* Note that [lazy_tag .. forward_tag] are < no_scan_tag. Not
353 * clear if very large constructed values could have the same
354 * tag. XXX *)
355 else if t = lazy_tag then opaque "lazy"
356 else if t = closure_tag then opaque "closure"
357 else if t = object_tag then ( (* Object. *)
358 let fields = get_fields [] s in
359 let clasz, id, slots =
360 match fields with h::h'::t -> h, h', t | _ -> assert false in
361 (* No information on decoding the class (first field). So just print
362 * out the ID and the slots.
363 *)
364 "Object #" ^ dump id ^
365 " (" ^ String.concat ", " (List.map dump slots) ^ ")"
366 )
367 else if t = infix_tag then opaque "infix"
368 else if t = forward_tag then opaque "forward"
369
370 else if t < no_scan_tag then ( (* Constructed value. *)
371 let fields = get_fields [] s in
372 "Tag" ^ string_of_int t ^
373 " (" ^ String.concat ", " (List.map dump fields) ^ ")"
374 )
375 else if t = string_tag then (
376 "\"" ^ String.escaped (magic r : string) ^ "\""
377 )
378 else if t = double_tag then (
379 string_of_float (magic r : float)
380 )
381 else if t = abstract_tag then opaque "abstract"
382 else if t = custom_tag then opaque "custom"
383 else if t = final_tag then opaque "final"
384 else failwith ("dump: impossible tag (" ^ string_of_int t ^ ")")
385 )
386
387let dump v = dump (repr v)
388
389(* end of dumper.ml *)
390
391(*
392let (dump : 'a -> string) = fun x ->
393 Dumper.dump x
394*)
395
396
397(* ---------------------------------------------------------------------- *)
398let pr2_gen x = pr2 (dump x)
399
400
401
402(* ---------------------------------------------------------------------- *)
403
404
405let _already_printed = Hashtbl.create 101
406let disable_pr2_once = ref false
708f4980
C
407
408let xxx_once f s =
34e49164
C
409 if !disable_pr2_once then pr2 s
410 else
411 if not (Hashtbl.mem _already_printed s)
412 then begin
413 Hashtbl.add _already_printed s true;
708f4980 414 f ("(ONCE) " ^ s);
34e49164
C
415 end
416
708f4980
C
417let pr2_once s = xxx_once pr2 s
418
419(* ---------------------------------------------------------------------- *)
978fd7e5
C
420let mk_pr2_wrappers aref =
421 let fpr2 s =
708f4980
C
422 if !aref
423 then pr2 s
978fd7e5 424 else
708f4980
C
425 (* just to the log file *)
426 out_chan_pr2 s
427 in
978fd7e5 428 let fpr2_once s =
708f4980
C
429 if !aref
430 then pr2_once s
978fd7e5 431 else
708f4980
C
432 xxx_once out_chan_pr2 s
433 in
978fd7e5 434 fpr2, fpr2_once
34e49164
C
435
436(* ---------------------------------------------------------------------- *)
437(* could also be in File section *)
438
978fd7e5
C
439let redirect_stdout file f =
440 begin
441 let chan = open_out file in
442 let descr = Unix.descr_of_out_channel chan in
443
444 let saveout = Unix.dup Unix.stdout in
445 Unix.dup2 descr Unix.stdout;
446 flush stdout;
447 let res = f() in
448 flush stdout;
449 Unix.dup2 saveout Unix.stdout;
450 close_out chan;
451 res
452 end
453
454let redirect_stdout_opt optfile f =
455 match optfile with
456 | None -> f()
457 | Some outfile -> redirect_stdout outfile f
458
459let redirect_stdout_stderr file f =
34e49164
C
460 begin
461 let chan = open_out file in
462 let descr = Unix.descr_of_out_channel chan in
463
464 let saveout = Unix.dup Unix.stdout in
465 let saveerr = Unix.dup Unix.stderr in
466 Unix.dup2 descr Unix.stdout;
467 Unix.dup2 descr Unix.stderr;
468 flush stdout; flush stderr;
469 f();
470 flush stdout; flush stderr;
471 Unix.dup2 saveout Unix.stdout;
472 Unix.dup2 saveerr Unix.stderr;
473 close_out chan;
474 end
475
978fd7e5 476let redirect_stdin file f =
34e49164
C
477 begin
478 let chan = open_in file in
479 let descr = Unix.descr_of_in_channel chan in
480
481 let savein = Unix.dup Unix.stdin in
482 Unix.dup2 descr Unix.stdin;
483 f();
484 Unix.dup2 savein Unix.stdin;
485 close_in chan;
486 end
487
978fd7e5 488let redirect_stdin_opt optfile f =
34e49164
C
489 match optfile with
490 | None -> f()
491 | Some infile -> redirect_stdin infile f
492
493
708f4980
C
494(* cf end
495let with_pr2_to_string f =
496*)
497
34e49164
C
498
499(* ---------------------------------------------------------------------- *)
500
501include Printf
502
503(* cf common.mli, fprintf, printf, eprintf, sprintf.
504 * also what is this ?
505 * val bprintf : Buffer.t -> ('a, Buffer.t, unit) format -> 'a
506 * val kprintf : (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
507 *)
508
509(* ex of printf:
510 * printf "%02d" i
511 * for padding
512 *)
513
514let spf = sprintf
515
516(* ---------------------------------------------------------------------- *)
517
518let _chan = ref stderr
519let start_log_file () =
520 let filename = (spf "/tmp/debugml%d:%d" (Unix.getuid()) (Unix.getpid())) in
521 pr2 (spf "now using %s for logging" filename);
522 _chan := open_out filename
523
524
525let dolog s = output_string !_chan (s ^ "\n"); flush !_chan
526
527let verbose_level = ref 1
528let log s = if !verbose_level >= 1 then dolog s
529let log2 s = if !verbose_level >= 2 then dolog s
530let log3 s = if !verbose_level >= 3 then dolog s
531let log4 s = if !verbose_level >= 4 then dolog s
532
533let if_log f = if !verbose_level >= 1 then f()
534let if_log2 f = if !verbose_level >= 2 then f()
535let if_log3 f = if !verbose_level >= 3 then f()
536let if_log4 f = if !verbose_level >= 4 then f()
537
538(* ---------------------------------------------------------------------- *)
539
540let pause () = (pr2 "pause: type return"; ignore(read_line ()))
541
542(* src: from getopt from frish *)
543let bip () = Printf.printf "\007"; flush stdout
544let wait () = Unix.sleep 1
545
546(* was used by fix_caml *)
547let _trace_var = ref 0
548let add_var() = incr _trace_var
549let dec_var() = decr _trace_var
550let get_var() = !_trace_var
551
552let (print_n: int -> string -> unit) = fun i s ->
553 do_n i (fun () -> print_string s)
554let (printerr_n: int -> string -> unit) = fun i s ->
555 do_n i (fun () -> prerr_string s)
556
557let _debug = ref true
558let debugon () = _debug := true
559let debugoff () = _debug := false
560let debug f = if !_debug then f () else ()
561
562
563
564(* now in prelude:
565 * let debugger = ref false
566 *)
567
568
569(*****************************************************************************)
570(* Profiling *)
571(*****************************************************************************)
572
573let get_mem() =
574 command2("grep VmData /proc/" ^ string_of_int (Unix.getpid()) ^ "/status")
575
576let memory_stat () =
577 let stat = Gc.stat() in
578 let conv_mo x = x * 4 / 1000000 in
579 Printf.sprintf "maximal = %d Mo\n" (conv_mo stat.Gc.top_heap_words) ^
580 Printf.sprintf "current = %d Mo\n" (conv_mo stat.Gc.heap_words) ^
581 Printf.sprintf "lives = %d Mo\n" (conv_mo stat.Gc.live_words)
582 (* Printf.printf "fragments = %d Mo\n" (conv_mo stat.Gc.fragments); *)
583
584let timenow () =
585 "sys:" ^ (string_of_float (Sys.time ())) ^ " seconds" ^
586 ":real:" ^
587 (let tm = Unix.time () +> Unix.gmtime in
588 tm.Unix.tm_min +> string_of_int ^ " min:" ^
589 tm.Unix.tm_sec +> string_of_int ^ ".00 seconds")
590
591let _count1 = ref 0
592let _count2 = ref 0
593let _count3 = ref 0
594let _count4 = ref 0
595let _count5 = ref 0
596
597let count1 () = incr _count1
598let count2 () = incr _count2
599let count3 () = incr _count3
600let count4 () = incr _count4
601let count5 () = incr _count5
602
603let profile_diagnostic_basic () =
604 Printf.sprintf
605 "count1 = %d\ncount2 = %d\ncount3 = %d\ncount4 = %d\ncount5 = %d\n"
606 !_count1 !_count2 !_count3 !_count4 !_count5
607
608
609
610let time_func f =
611 (* let _ = Timing () in *)
612 let x = f () in
613 (* let _ = Timing () in *)
614 x
615
616(* ---------------------------------------------------------------------- *)
617
618type prof = PALL | PNONE | PSOME of string list
619let profile = ref PNONE
485bce71 620let show_trace_profile = ref false
34e49164
C
621
622let check_profile category =
623 match !profile with
624 PALL -> true
625 | PNONE -> false
626 | PSOME l -> List.mem category l
627
628let _profile_table = ref (Hashtbl.create 100)
485bce71
C
629
630let adjust_profile_entry category difftime =
631 let (xtime, xcount) =
632 (try Hashtbl.find !_profile_table category
633 with Not_found ->
634 let xtime = ref 0.0 in
635 let xcount = ref 0 in
636 Hashtbl.add !_profile_table category (xtime, xcount);
637 (xtime, xcount)
638 ) in
639 xtime := !xtime +. difftime;
640 xcount := !xcount + 1;
641 ()
642
34e49164
C
643let profile_start category = failwith "todo"
644let profile_end category = failwith "todo"
645
485bce71 646
34e49164
C
647(* subtil: don't forget to give all argumens to f, otherwise partial app
648 * and will profile nothing.
0708f913
C
649 *
650 * todo: try also detect when complexity augment each time, so can
651 * detect the situation for a function gets worse and worse ?
34e49164
C
652 *)
653let profile_code category f =
654 if not (check_profile category)
fc1ad971 655 then f()
34e49164 656 else begin
485bce71 657 if !show_trace_profile then pr2 (spf "p: %s" category);
34e49164
C
658 let t = Unix.gettimeofday () in
659 let res, prefix =
660 try Some (f ()), ""
661 with Timeout -> None, "*"
662 in
663 let category = prefix ^ category in (* add a '*' to indicate timeout func *)
664 let t' = Unix.gettimeofday () in
485bce71
C
665
666 adjust_profile_entry category (t' -. t);
34e49164
C
667 (match res with
668 | Some res -> res
669 | None -> raise Timeout
670 );
671 end
672
485bce71
C
673
674let _is_in_exclusif = ref (None: string option)
675
676let profile_code_exclusif category f =
677 if not (check_profile category)
678 then f()
679 else begin
680
681 match !_is_in_exclusif with
682 | Some s ->
683 failwith (spf "profile_code_exclusif: %s but already in %s " category s);
684 | None ->
685 _is_in_exclusif := (Some category);
686 finalize
687 (fun () ->
688 profile_code category f
689 )
690 (fun () ->
691 _is_in_exclusif := None
692 )
693
694 end
695
696let profile_code_inside_exclusif_ok category f =
697 failwith "Todo"
698
699
34e49164
C
700(* todo: also put % ? also add % to see if coherent numbers *)
701let profile_diagnostic () =
702 if !profile = PNONE then "" else
703 let xs =
704 Hashtbl.fold (fun k v acc -> (k,v)::acc) !_profile_table []
705 +> List.sort (fun (k1, (t1,n1)) (k2, (t2,n2)) -> compare t2 t1)
706 in
707 with_open_stringbuf (fun (pr,_) ->
708 pr "---------------------";
709 pr "profiling result";
710 pr "---------------------";
711 xs +> List.iter (fun (k, (t,n)) ->
712 pr (sprintf "%-40s : %10.3f sec %10d count" k !t !n)
713 )
714 )
715
716
717
718let report_if_take_time timethreshold s f =
719 let t = Unix.gettimeofday () in
720 let res = f () in
721 let t' = Unix.gettimeofday () in
722 if (t' -. t > float_of_int timethreshold)
978fd7e5 723 then pr2 (sprintf "Note: processing took %7.1fs: %s" (t' -. t) s);
34e49164
C
724 res
725
726let profile_code2 category f =
727 profile_code category (fun () ->
728 if !profile = PALL
729 then pr2 ("starting: " ^ category);
730 let t = Unix.gettimeofday () in
731 let res = f () in
732 let t' = Unix.gettimeofday () in
733 if !profile = PALL
734 then pr2 (spf "ending: %s, %fs" category (t' -. t));
735 res
736 )
737
738
739(*****************************************************************************)
740(* Test *)
741(*****************************************************************************)
742let example b = assert b
743
744let _ex1 = example (enum 1 4 = [1;2;3;4])
745
746let assert_equal a b =
747 if not (a = b)
748 then failwith ("assert_equal: those 2 values are not equal:\n\t" ^
749 (dump a) ^ "\n\t" ^ (dump b) ^ "\n")
750
751let (example2: string -> bool -> unit) = fun s b ->
752 try assert b with x -> failwith s
753
754(*-------------------------------------------------------------------*)
755let _list_bool = ref []
756
757let (example3: string -> bool -> unit) = fun s b ->
758 _list_bool := (s,b)::(!_list_bool)
759
760(* could introduce a fun () otherwise the calculus is made at compile time
761 * and this can be long. This would require to redefine test_all.
762 * let (example3: string -> (unit -> bool) -> unit) = fun s func ->
763 * _list_bool := (s,func):: (!_list_bool)
764 *
765 * I would like to do as a func that take 2 terms, and make an = over it
766 * avoid to add this ugly fun (), but pb of type, cant do that :(
767 *)
768
769
770let (test_all: unit -> unit) = fun () ->
771 List.iter (fun (s, b) ->
772 Printf.printf "%s: %s\n" s (if b then "passed" else "failed")
773 ) !_list_bool
774
775let (test: string -> unit) = fun s ->
776 Printf.printf "%s: %s\n" s
777 (if (List.assoc s (!_list_bool)) then "passed" else "failed")
778
779let _ex = example3 "++" ([1;2]++[3;4;5] = [1;2;3;4;5])
780
781(*-------------------------------------------------------------------*)
782(* Regression testing *)
783(*-------------------------------------------------------------------*)
784
785(* cf end of file. It uses too many other common functions so I
786 * have put the code at the end of this file.
787 *)
788
789
790
791(* todo? take code from julien signoles in calendar-2.0.2/tests *)
792(*
793
794(* Generic functions used in the tests. *)
795
796val reset : unit -> unit
797val nb_ok : unit -> int
798val nb_bug : unit -> int
799val test : bool -> string -> unit
800val test_exn : 'a Lazy.t -> string -> unit
801
802
803let ok_ref = ref 0
804let ok () = incr ok_ref
805let nb_ok () = !ok_ref
806
807let bug_ref = ref 0
808let bug () = incr bug_ref
809let nb_bug () = !bug_ref
810
811let reset () =
812 ok_ref := 0;
813 bug_ref := 0
814
815let test x s =
816 if x then ok () else begin Printf.printf "%s\n" s; bug () end;;
817
818let test_exn x s =
819 try
820 ignore (Lazy.force x);
821 Printf.printf "%s\n" s;
822 bug ()
823 with _ ->
824 ok ();;
825*)
826
827
828(*****************************************************************************)
829(* Quickcheck like (sfl) *)
830(*****************************************************************************)
831
832(* Better than quickcheck, cos cant do a test_all_prop in haskell cos
833 * prop were functions, whereas here we have not prop_Unix x = ... but
834 * laws "unit" ...
835 *
836 * How to do without overloading ? objet ? can pass a generator as a
837 * parameter, mais lourd, prefer automatic inferring of the
838 * generator? But at the same time quickcheck does not do better cos
839 * we must explictly type the property. So between a
840 * prop_unit:: [Int] -> [Int] -> bool ...
841 * prop_unit x = reverse [x] == [x]
842 * and
843 * let _ = laws "unit" (fun x -> reverse [x] = [x]) (listg intg)
844 * there is no real differences.
845 *
846 * Yes I define typeg generator but quickcheck too, he must define
847 * class instance. I emulate the context Gen a => Gen [a] by making
848 * listg take as a param a type generator. Moreover I have not the pb of
849 * monad. I can do random independently, so my code is more simple
850 * I think than the haskell code of quickcheck.
851 *
852 * update: apparently Jane Street have copied some of my code for their
853 * Ounit_util.ml and quichcheck.ml in their Core library :)
854 *)
855
856(*---------------------------------------------------------------------------*)
857(* generators *)
858(*---------------------------------------------------------------------------*)
859type 'a gen = unit -> 'a
860
861let (ig: int gen) = fun () ->
862 Random.int 10
863let (lg: ('a gen) -> ('a list) gen) = fun gen () ->
864 foldn (fun acc i -> (gen ())::acc) [] (Random.int 10)
865let (pg: ('a gen) -> ('b gen) -> ('a * 'b) gen) = fun gen1 gen2 () ->
866 (gen1 (), gen2 ())
867let polyg = ig
868let (ng: (string gen)) = fun () ->
869 "a" ^ (string_of_int (ig ()))
870
871let (oneofl: ('a list) -> 'a gen) = fun xs () ->
872 List.nth xs (Random.int (List.length xs))
873(* let oneofl l = oneof (List.map always l) *)
874
875let (oneof: (('a gen) list) -> 'a gen) = fun xs ->
876 List.nth xs (Random.int (List.length xs))
877
878let (always: 'a -> 'a gen) = fun e () -> e
879
880let (frequency: ((int * ('a gen)) list) -> 'a gen) = fun xs ->
881 let sums = sum_int (List.map fst xs) in
882 let i = Random.int sums in
883 let rec freq_aux acc = function
884 | (x,g)::xs -> if i < acc+x then g else freq_aux (acc+x) xs
885 | _ -> failwith "frequency"
886 in
887 freq_aux 0 xs
888let frequencyl l = frequency (List.map (fun (i,e) -> (i,always e)) l)
889
890(*
891let b = oneof [always true; always false] ()
892let b = frequency [3, always true; 2, always false] ()
893*)
894
895(* cant do this:
896 * let rec (lg: ('a gen) -> ('a list) gen) = fun gen -> oneofl [[]; lg gen ()]
897 * nor
898 * let rec (lg: ('a gen) -> ('a list) gen) = fun gen -> oneof [always []; lg gen]
899 *
900 * because caml is not as lazy as haskell :( fix the pb by introducing a size
901 * limit. take the bounds/size as parameter. morover this is needed for
902 * more complex type.
903 *
904 * how make a bintreeg ?? we need recursion
905 *
906 * let rec (bintreeg: ('a gen) -> ('a bintree) gen) = fun gen () ->
907 * let rec aux n =
908 * if n = 0 then (Leaf (gen ()))
909 * else frequencyl [1, Leaf (gen ()); 4, Branch ((aux (n / 2)), aux (n / 2))]
910 * ()
911 * in aux 20
912 *
913 *)
914
915
916(*---------------------------------------------------------------------------*)
917(* property *)
918(*---------------------------------------------------------------------------*)
919
920(* todo: a test_all_laws, better syntax (done already a little with ig in
921 * place of intg. En cas d'erreur, print the arg that not respect
922 *
923 * todo: with monitoring, as in haskell, laws = laws2, no need for 2 func,
924 * but hard i found
925 *
926 * todo classify, collect, forall
927 *)
928
929
930(* return None when good, and Just the_problematic_case when bad *)
931let (laws: string -> ('a -> bool) -> ('a gen) -> 'a option) = fun s func gen ->
932 let res = foldn (fun acc i -> let n = gen() in (n, func n)::acc) [] 1000 in
933 let res = List.filter (fun (x,b) -> not b) res in
934 if res = [] then None else Some (fst (List.hd res))
935
936let rec (statistic_number: ('a list) -> (int * 'a) list) = function
937 | [] -> []
938 | x::xs -> let (splitg, splitd) = List.partition (fun y -> y = x) xs in
939 (1+(List.length splitg), x)::(statistic_number splitd)
940
941(* in pourcentage *)
942let (statistic: ('a list) -> (int * 'a) list) = fun xs ->
943 let stat_num = statistic_number xs in
944 let totals = sum_int (List.map fst stat_num) in
945 List.map (fun (i, v) -> ((i * 100) / totals), v) stat_num
946
947let (laws2:
948 string -> ('a -> (bool * 'b)) -> ('a gen) ->
949 ('a option * ((int * 'b) list ))) =
950 fun s func gen ->
951 let res = foldn (fun acc i -> let n = gen() in (n, func n)::acc) [] 1000 in
952 let stat = statistic (List.map (fun (x,(b,v)) -> v) res) in
953 let res = List.filter (fun (x,(b,v)) -> not b) res in
954 if res = [] then (None, stat) else (Some (fst (List.hd res)), stat)
955
956
957(*
958let b = laws "unit" (fun x -> reverse [x] = [x] )ig
959let b = laws "app " (fun (xs,ys) -> reverse (xs++ys) = reverse ys++reverse xs)(pg (lg ig)(lg ig))
960let b = laws "rev " (fun xs -> reverse (reverse xs) = xs )(lg ig)
961let b = laws "appb" (fun (xs,ys) -> reverse (xs++ys) = reverse xs++reverse ys)(pg (lg ig)(lg ig))
962let b = laws "max" (fun (x,y) -> x <= y ==> (max x y = y) )(pg ig ig)
963
964let b = laws2 "max" (fun (x,y) -> ((x <= y ==> (max x y = y)), x <= y))(pg ig ig)
965*)
966
967
968(* todo, do with coarbitrary ?? idea is that given a 'a, generate a 'b
969 * depending of 'a and gen 'b, that is modify gen 'b, what is important is
970 * that each time given the same 'a, we must get the same 'b !!!
971 *)
972
973(*
974let (fg: ('a gen) -> ('b gen) -> ('a -> 'b) gen) = fun gen1 gen2 () ->
975let b = laws "funs" (fun (f,g,h) -> x <= y ==> (max x y = y) )(pg ig ig)
976 *)
977
978(*
979let one_of xs = List.nth xs (Random.int (List.length xs))
980let take_one xs =
981 if empty xs then failwith "Take_one: empty list"
982 else
983 let i = Random.int (List.length xs) in
984 List.nth xs i, filter_index (fun j _ -> i <> j) xs
985*)
986
987(*****************************************************************************)
988(* Persistence *)
989(*****************************************************************************)
990
991let get_value filename =
992 let chan = open_in filename in
993 let x = input_value chan in (* <=> Marshal.from_channel *)
994 (close_in chan; x)
995
996let write_value valu filename =
997 let chan = open_out filename in
998 (output_value chan valu; (* <=> Marshal.to_channel *)
999 (* Marshal.to_channel chan valu [Marshal.Closures]; *)
1000 close_out chan)
1001
1002let write_back func filename =
1003 write_value (func (get_value filename)) filename
1004
1005
485bce71
C
1006let read_value f = get_value f
1007
34e49164 1008
0708f913
C
1009let marshal__to_string2 v flags =
1010 Marshal.to_string v flags
1011let marshal__to_string a b =
1012 profile_code "Marshalling" (fun () -> marshal__to_string2 a b)
1013
1014let marshal__from_string2 v flags =
1015 Marshal.from_string v flags
1016let marshal__from_string a b =
1017 profile_code "Marshalling" (fun () -> marshal__from_string2 a b)
1018
1019
1020
34e49164
C
1021(*****************************************************************************)
1022(* Counter *)
1023(*****************************************************************************)
1024let _counter = ref 0
1025let counter () = (_counter := !_counter +1; !_counter)
1026
1027let _counter2 = ref 0
1028let counter2 () = (_counter2 := !_counter2 +1; !_counter2)
1029
1030let _counter3 = ref 0
1031let counter3 () = (_counter3 := !_counter3 +1; !_counter3)
1032
1033type timestamp = int
1034
1035(*****************************************************************************)
1036(* String_of *)
1037(*****************************************************************************)
1038(* To work with the macro system autogenerated string_of and print_ function
1039 (kind of deriving a la haskell) *)
1040
1041(* int, bool, char, float, ref ?, string *)
1042
1043let string_of_string s = "\"" ^ s "\""
1044
1045let string_of_list f xs =
1046 "[" ^ (xs +> List.map f +> String.concat ";" ) ^ "]"
1047
1048let string_of_unit () = "()"
1049
1050let string_of_array f xs =
1051 "[|" ^ (xs +> Array.to_list +> List.map f +> String.concat ";") ^ "|]"
1052
1053let string_of_option f = function
1054 | None -> "None "
1055 | Some x -> "Some " ^ (f x)
1056
1057
1058
1059
1060let print_bool x = print_string (if x then "True" else "False")
1061
1062let print_option pr = function
1063 | None -> print_string "None"
1064 | Some x -> print_string "Some ("; pr x; print_string ")"
1065
1066let print_list pr xs =
1067 begin
1068 print_string "[";
1069 List.iter (fun x -> pr x; print_string ",") xs;
1070 print_string "]";
1071 end
1072
1073(* specialised
1074let (string_of_list: char list -> string) =
1075 List.fold_left (fun acc x -> acc^(Char.escaped x)) ""
1076*)
1077
1078
1079let rec print_between between fn = function
1080 | [] -> ()
1081 | [x] -> fn x
1082 | x::xs -> fn x; between(); print_between between fn xs
1083
1084
1085
1086
1087let adjust_pp_with_indent f =
1088 Format.open_box !_tab_level_print;
1089 (*Format.force_newline();*)
1090 f();
1091 Format.close_box ();
1092 Format.print_newline()
1093
1094let adjust_pp_with_indent_and_header s f =
1095 Format.open_box (!_tab_level_print + String.length s);
1096 do_n !_tab_level_print (fun () -> Format.print_string " ");
1097 Format.print_string s;
1098 f();
1099 Format.close_box ();
1100 Format.print_newline()
1101
1102
1103
1104let pp_do_in_box f = Format.open_box 1; f(); Format.close_box ()
1105let pp_do_in_zero_box f = Format.open_box 0; f(); Format.close_box ()
1106
1107let pp_f_in_box f =
1108 Format.open_box 1;
1109 let res = f() in
1110 Format.close_box ();
1111 res
1112
1113let pp s = Format.print_string s
1114
0708f913
C
1115let mk_str_func_of_assoc_conv xs =
1116 let swap (x,y) = (y,x) in
1117
1118 (fun s ->
1119 let xs' = List.map swap xs in
1120 List.assoc s xs'
1121 ),
1122 (fun a ->
1123 List.assoc a xs
1124 )
1125
708f4980
C
1126
1127
1128(* julia: convert something printed using format to print into a string *)
1129(* now at bottom of file
1130let format_to_string f =
1131 ...
1132*)
1133
1134
1135
34e49164
C
1136(*****************************************************************************)
1137(* Macro *)
1138(*****************************************************************************)
1139
1140(* put your macro in macro.ml4, and you can test it interactivly as in lisp *)
1141let macro_expand s =
1142 let c = open_out "/tmp/ttttt.ml" in
1143 begin
1144 output_string c s; close_out c;
1145 command2 ("ocamlc -c -pp 'camlp4o pa_extend.cmo q_MLast.cmo -impl' " ^
1146 "-I +camlp4 -impl macro.ml4");
1147 command2 "camlp4o ./macro.cmo pr_o.cmo /tmp/ttttt.ml";
1148 command2 "rm -f /tmp/ttttt.ml";
1149 end
1150
1151(*
1152let t = macro_expand "{ x + y | (x,y) <- [(1,1);(2,2);(3,3)] and x>2 and y<3}"
1153let x = { x + y | (x,y) <- [(1,1);(2,2);(3,3)] and x > 2 and y < 3}
1154let t = macro_expand "{1 .. 10}"
1155let x = {1 .. 10} +> List.map (fun i -> i)
1156let t = macro_expand "[1;2] to append to [2;4]"
1157let t = macro_expand "{x = 2; x = 3}"
1158
1159let t = macro_expand "type 'a bintree = Leaf of 'a | Branch of ('a bintree * 'a bintree)"
1160*)
1161
1162
1163
1164(*****************************************************************************)
1165(* Composition/Control *)
1166(*****************************************************************************)
1167
1168(* I like the obj.func object notation. In OCaml cant use '.' so I use +>
1169 *
1170 * update: it seems that F# agrees with me :) but they use |>
1171 *)
1172
1173(* now in prelude:
1174 * let (+>) o f = f o
1175 *)
1176let (+!>) refo f = refo := f !refo
1177(* alternatives:
1178 * let ((@): 'a -> ('a -> 'b) -> 'b) = fun a b -> b a
1179 * let o f g x = f (g x)
1180 *)
1181
1182let ($) f g x = g (f x)
1183let compose f g x = f (g x)
1184