Release coccinelle-0.1.8
[bpt/coccinelle.git] / commons / common.ml
1 (* Yoann Padioleau
2 *
3 * Copyright (C) 1998-2009 Yoann Padioleau
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
55 * - Filename.is_relative
56 * - String.uppercase, String.lowercase
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
66 * - ocamlgtk, and gtksourceview
67 * - ocamlgl
68 * - ocamlpython
69 * - ocamlagrep
70 * - ocamlfuse
71 * - ocamlmpi
72 * - ocamlcalendar
73 *
74 * - pcre
75 * - sdl
76 *
77 * Many functions in this file were inspired by Haskell or Lisp librairies.
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
92 let (+>) o f = f o
93 let (++) = (@)
94
95 exception Timeout
96 exception UnixExit of int
97
98 let rec (do_n: int -> (unit -> unit) -> unit) = fun i f ->
99 if i = 0 then () else (f(); do_n (i-1) f)
100 let 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
103 let sum_int = List.fold_left (+) 0
104
105 (* could really call it 'for' :) *)
106 let 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
113 let 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
119 let rec enum_orig x n = if x = n then [n] else x::enum_orig (x+1) n
120
121 let 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
129 let 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
136 let last_n n l = List.rev (take n (List.rev l))
137 let last l = List.hd (last_n 1 l)
138
139
140 let (list_of_string: string -> char list) = function
141 "" -> []
142 | s -> (enum 0 ((String.length s) - 1) +> List.map (String.get s))
143
144 let (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
154 let push2 v l =
155 l := v :: !l
156
157 let null xs = match xs with [] -> true | _ -> false
158
159
160
161
162 let debugger = ref false
163
164 let unwind_protect f cleanup =
165 if !debugger then f() else
166 try f ()
167 with e -> begin cleanup e; raise e end
168
169 let 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
179 let command2 s = ignore(Sys.command s)
180
181
182 let (matched: int -> string -> string) = fun i s ->
183 Str.matched_group i s
184
185 let matched1 = fun s -> matched 1 s
186 let matched2 = fun s -> (matched 1 s, matched 2 s)
187 let matched3 = fun s -> (matched 1 s, matched 2 s, matched 3 s)
188 let matched4 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s)
189 let matched5 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s)
190 let matched6 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s, matched 6 s)
191 let 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
193 let (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
201 let foldl1 p = function x::xs -> List.fold_left p x xs | _ -> failwith "foldl1"
202
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
214 let _tab_level_print = ref 0
215 let _tab_indent = 5
216
217
218 let _prefix_pr = ref ""
219
220 let 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
226 let 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
233 let 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
240
241
242
243
244 let _chan_pr2 = ref (None: out_channel option)
245
246 let 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
254 let pr2 s =
255 prerr_string !_prefix_pr;
256 do_n !_tab_level_print (fun () -> prerr_string " ");
257 prerr_string s;
258 prerr_string "\n";
259 flush stderr;
260 out_chan_pr2 s;
261 ()
262
263 let pr2_no_nl s =
264 prerr_string !_prefix_pr;
265 do_n !_tab_level_print (fun () -> prerr_string " ");
266 prerr_string s;
267 flush stderr;
268 out_chan_pr2 ~newline:false s;
269 ()
270
271
272 let pr_xxxxxxxxxxxxxxxxx () =
273 pr "-----------------------------------------------------------------------"
274
275 let pr2_xxxxxxxxxxxxxxxxx () =
276 pr2 "-----------------------------------------------------------------------"
277
278
279 let 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 *)
308 open Printf
309 open Obj
310
311 let 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
387 let dump v = dump (repr v)
388
389 (* end of dumper.ml *)
390
391 (*
392 let (dump : 'a -> string) = fun x ->
393 Dumper.dump x
394 *)
395
396
397 (* ---------------------------------------------------------------------- *)
398 let pr2_gen x = pr2 (dump x)
399
400
401
402 (* ---------------------------------------------------------------------- *)
403
404
405 let _already_printed = Hashtbl.create 101
406 let disable_pr2_once = ref false
407
408 let xxx_once f s =
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;
414 f ("(ONCE) " ^ s);
415 end
416
417 let pr2_once s = xxx_once pr2 s
418
419 (* ---------------------------------------------------------------------- *)
420 let mk_pr2_wrappers aref =
421 let fpr2 s =
422 if !aref
423 then pr2 s
424 else
425 (* just to the log file *)
426 out_chan_pr2 s
427 in
428 let fpr2_once s =
429 if !aref
430 then pr2_once s
431 else
432 xxx_once out_chan_pr2 s
433 in
434 fpr2, fpr2_once
435
436
437 (* ---------------------------------------------------------------------- *)
438 (* could also be in File section *)
439
440 let redirect_stdout_stderr file f =
441 begin
442 let chan = open_out file in
443 let descr = Unix.descr_of_out_channel chan in
444
445 let saveout = Unix.dup Unix.stdout in
446 let saveerr = Unix.dup Unix.stderr in
447 Unix.dup2 descr Unix.stdout;
448 Unix.dup2 descr Unix.stderr;
449 flush stdout; flush stderr;
450 f();
451 flush stdout; flush stderr;
452 Unix.dup2 saveout Unix.stdout;
453 Unix.dup2 saveerr Unix.stderr;
454 close_out chan;
455 end
456
457 let redirect_stdin file f =
458 begin
459 let chan = open_in file in
460 let descr = Unix.descr_of_in_channel chan in
461
462 let savein = Unix.dup Unix.stdin in
463 Unix.dup2 descr Unix.stdin;
464 f();
465 Unix.dup2 savein Unix.stdin;
466 close_in chan;
467 end
468
469 let redirect_stdin_opt optfile f =
470 match optfile with
471 | None -> f()
472 | Some infile -> redirect_stdin infile f
473
474
475 (* cf end
476 let with_pr2_to_string f =
477 *)
478
479
480 (* ---------------------------------------------------------------------- *)
481
482 include Printf
483
484 (* cf common.mli, fprintf, printf, eprintf, sprintf.
485 * also what is this ?
486 * val bprintf : Buffer.t -> ('a, Buffer.t, unit) format -> 'a
487 * val kprintf : (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
488 *)
489
490 (* ex of printf:
491 * printf "%02d" i
492 * for padding
493 *)
494
495 let spf = sprintf
496
497 (* ---------------------------------------------------------------------- *)
498
499 let _chan = ref stderr
500 let start_log_file () =
501 let filename = (spf "/tmp/debugml%d:%d" (Unix.getuid()) (Unix.getpid())) in
502 pr2 (spf "now using %s for logging" filename);
503 _chan := open_out filename
504
505
506 let dolog s = output_string !_chan (s ^ "\n"); flush !_chan
507
508 let verbose_level = ref 1
509 let log s = if !verbose_level >= 1 then dolog s
510 let log2 s = if !verbose_level >= 2 then dolog s
511 let log3 s = if !verbose_level >= 3 then dolog s
512 let log4 s = if !verbose_level >= 4 then dolog s
513
514 let if_log f = if !verbose_level >= 1 then f()
515 let if_log2 f = if !verbose_level >= 2 then f()
516 let if_log3 f = if !verbose_level >= 3 then f()
517 let if_log4 f = if !verbose_level >= 4 then f()
518
519 (* ---------------------------------------------------------------------- *)
520
521 let pause () = (pr2 "pause: type return"; ignore(read_line ()))
522
523 (* src: from getopt from frish *)
524 let bip () = Printf.printf "\007"; flush stdout
525 let wait () = Unix.sleep 1
526
527 (* was used by fix_caml *)
528 let _trace_var = ref 0
529 let add_var() = incr _trace_var
530 let dec_var() = decr _trace_var
531 let get_var() = !_trace_var
532
533 let (print_n: int -> string -> unit) = fun i s ->
534 do_n i (fun () -> print_string s)
535 let (printerr_n: int -> string -> unit) = fun i s ->
536 do_n i (fun () -> prerr_string s)
537
538 let _debug = ref true
539 let debugon () = _debug := true
540 let debugoff () = _debug := false
541 let debug f = if !_debug then f () else ()
542
543
544
545 (* now in prelude:
546 * let debugger = ref false
547 *)
548
549
550 (*****************************************************************************)
551 (* Profiling *)
552 (*****************************************************************************)
553
554 let get_mem() =
555 command2("grep VmData /proc/" ^ string_of_int (Unix.getpid()) ^ "/status")
556
557 let memory_stat () =
558 let stat = Gc.stat() in
559 let conv_mo x = x * 4 / 1000000 in
560 Printf.sprintf "maximal = %d Mo\n" (conv_mo stat.Gc.top_heap_words) ^
561 Printf.sprintf "current = %d Mo\n" (conv_mo stat.Gc.heap_words) ^
562 Printf.sprintf "lives = %d Mo\n" (conv_mo stat.Gc.live_words)
563 (* Printf.printf "fragments = %d Mo\n" (conv_mo stat.Gc.fragments); *)
564
565 let timenow () =
566 "sys:" ^ (string_of_float (Sys.time ())) ^ " seconds" ^
567 ":real:" ^
568 (let tm = Unix.time () +> Unix.gmtime in
569 tm.Unix.tm_min +> string_of_int ^ " min:" ^
570 tm.Unix.tm_sec +> string_of_int ^ ".00 seconds")
571
572 let _count1 = ref 0
573 let _count2 = ref 0
574 let _count3 = ref 0
575 let _count4 = ref 0
576 let _count5 = ref 0
577
578 let count1 () = incr _count1
579 let count2 () = incr _count2
580 let count3 () = incr _count3
581 let count4 () = incr _count4
582 let count5 () = incr _count5
583
584 let profile_diagnostic_basic () =
585 Printf.sprintf
586 "count1 = %d\ncount2 = %d\ncount3 = %d\ncount4 = %d\ncount5 = %d\n"
587 !_count1 !_count2 !_count3 !_count4 !_count5
588
589
590
591 let time_func f =
592 (* let _ = Timing () in *)
593 let x = f () in
594 (* let _ = Timing () in *)
595 x
596
597 (* ---------------------------------------------------------------------- *)
598
599 type prof = PALL | PNONE | PSOME of string list
600 let profile = ref PNONE
601 let show_trace_profile = ref false
602
603 let check_profile category =
604 match !profile with
605 PALL -> true
606 | PNONE -> false
607 | PSOME l -> List.mem category l
608
609 let _profile_table = ref (Hashtbl.create 100)
610
611 let adjust_profile_entry category difftime =
612 let (xtime, xcount) =
613 (try Hashtbl.find !_profile_table category
614 with Not_found ->
615 let xtime = ref 0.0 in
616 let xcount = ref 0 in
617 Hashtbl.add !_profile_table category (xtime, xcount);
618 (xtime, xcount)
619 ) in
620 xtime := !xtime +. difftime;
621 xcount := !xcount + 1;
622 ()
623
624 let profile_start category = failwith "todo"
625 let profile_end category = failwith "todo"
626
627
628 (* subtil: don't forget to give all argumens to f, otherwise partial app
629 * and will profile nothing.
630 *
631 * todo: try also detect when complexity augment each time, so can
632 * detect the situation for a function gets worse and worse ?
633 *)
634 let profile_code category f =
635 if not (check_profile category)
636 then f()
637 else begin
638 if !show_trace_profile then pr2 (spf "p: %s" category);
639 let t = Unix.gettimeofday () in
640 let res, prefix =
641 try Some (f ()), ""
642 with Timeout -> None, "*"
643 in
644 let category = prefix ^ category in (* add a '*' to indicate timeout func *)
645 let t' = Unix.gettimeofday () in
646
647 adjust_profile_entry category (t' -. t);
648 (match res with
649 | Some res -> res
650 | None -> raise Timeout
651 );
652 end
653
654
655 let _is_in_exclusif = ref (None: string option)
656
657 let profile_code_exclusif category f =
658 if not (check_profile category)
659 then f()
660 else begin
661
662 match !_is_in_exclusif with
663 | Some s ->
664 failwith (spf "profile_code_exclusif: %s but already in %s " category s);
665 | None ->
666 _is_in_exclusif := (Some category);
667 finalize
668 (fun () ->
669 profile_code category f
670 )
671 (fun () ->
672 _is_in_exclusif := None
673 )
674
675 end
676
677 let profile_code_inside_exclusif_ok category f =
678 failwith "Todo"
679
680
681 (* todo: also put % ? also add % to see if coherent numbers *)
682 let profile_diagnostic () =
683 if !profile = PNONE then "" else
684 let xs =
685 Hashtbl.fold (fun k v acc -> (k,v)::acc) !_profile_table []
686 +> List.sort (fun (k1, (t1,n1)) (k2, (t2,n2)) -> compare t2 t1)
687 in
688 with_open_stringbuf (fun (pr,_) ->
689 pr "---------------------";
690 pr "profiling result";
691 pr "---------------------";
692 xs +> List.iter (fun (k, (t,n)) ->
693 pr (sprintf "%-40s : %10.3f sec %10d count" k !t !n)
694 )
695 )
696
697
698
699 let report_if_take_time timethreshold s f =
700 let t = Unix.gettimeofday () in
701 let res = f () in
702 let t' = Unix.gettimeofday () in
703 if (t' -. t > float_of_int timethreshold)
704 then pr2 (sprintf "NOTE: this code takes more than: %ds %s" timethreshold s);
705 res
706
707 let profile_code2 category f =
708 profile_code category (fun () ->
709 if !profile = PALL
710 then pr2 ("starting: " ^ category);
711 let t = Unix.gettimeofday () in
712 let res = f () in
713 let t' = Unix.gettimeofday () in
714 if !profile = PALL
715 then pr2 (spf "ending: %s, %fs" category (t' -. t));
716 res
717 )
718
719
720 (*****************************************************************************)
721 (* Test *)
722 (*****************************************************************************)
723 let example b = assert b
724
725 let _ex1 = example (enum 1 4 = [1;2;3;4])
726
727 let assert_equal a b =
728 if not (a = b)
729 then failwith ("assert_equal: those 2 values are not equal:\n\t" ^
730 (dump a) ^ "\n\t" ^ (dump b) ^ "\n")
731
732 let (example2: string -> bool -> unit) = fun s b ->
733 try assert b with x -> failwith s
734
735 (*-------------------------------------------------------------------*)
736 let _list_bool = ref []
737
738 let (example3: string -> bool -> unit) = fun s b ->
739 _list_bool := (s,b)::(!_list_bool)
740
741 (* could introduce a fun () otherwise the calculus is made at compile time
742 * and this can be long. This would require to redefine test_all.
743 * let (example3: string -> (unit -> bool) -> unit) = fun s func ->
744 * _list_bool := (s,func):: (!_list_bool)
745 *
746 * I would like to do as a func that take 2 terms, and make an = over it
747 * avoid to add this ugly fun (), but pb of type, cant do that :(
748 *)
749
750
751 let (test_all: unit -> unit) = fun () ->
752 List.iter (fun (s, b) ->
753 Printf.printf "%s: %s\n" s (if b then "passed" else "failed")
754 ) !_list_bool
755
756 let (test: string -> unit) = fun s ->
757 Printf.printf "%s: %s\n" s
758 (if (List.assoc s (!_list_bool)) then "passed" else "failed")
759
760 let _ex = example3 "++" ([1;2]++[3;4;5] = [1;2;3;4;5])
761
762 (*-------------------------------------------------------------------*)
763 (* Regression testing *)
764 (*-------------------------------------------------------------------*)
765
766 (* cf end of file. It uses too many other common functions so I
767 * have put the code at the end of this file.
768 *)
769
770
771
772 (* todo? take code from julien signoles in calendar-2.0.2/tests *)
773 (*
774
775 (* Generic functions used in the tests. *)
776
777 val reset : unit -> unit
778 val nb_ok : unit -> int
779 val nb_bug : unit -> int
780 val test : bool -> string -> unit
781 val test_exn : 'a Lazy.t -> string -> unit
782
783
784 let ok_ref = ref 0
785 let ok () = incr ok_ref
786 let nb_ok () = !ok_ref
787
788 let bug_ref = ref 0
789 let bug () = incr bug_ref
790 let nb_bug () = !bug_ref
791
792 let reset () =
793 ok_ref := 0;
794 bug_ref := 0
795
796 let test x s =
797 if x then ok () else begin Printf.printf "%s\n" s; bug () end;;
798
799 let test_exn x s =
800 try
801 ignore (Lazy.force x);
802 Printf.printf "%s\n" s;
803 bug ()
804 with _ ->
805 ok ();;
806 *)
807
808
809 (*****************************************************************************)
810 (* Quickcheck like (sfl) *)
811 (*****************************************************************************)
812
813 (* Better than quickcheck, cos cant do a test_all_prop in haskell cos
814 * prop were functions, whereas here we have not prop_Unix x = ... but
815 * laws "unit" ...
816 *
817 * How to do without overloading ? objet ? can pass a generator as a
818 * parameter, mais lourd, prefer automatic inferring of the
819 * generator? But at the same time quickcheck does not do better cos
820 * we must explictly type the property. So between a
821 * prop_unit:: [Int] -> [Int] -> bool ...
822 * prop_unit x = reverse [x] == [x]
823 * and
824 * let _ = laws "unit" (fun x -> reverse [x] = [x]) (listg intg)
825 * there is no real differences.
826 *
827 * Yes I define typeg generator but quickcheck too, he must define
828 * class instance. I emulate the context Gen a => Gen [a] by making
829 * listg take as a param a type generator. Moreover I have not the pb of
830 * monad. I can do random independently, so my code is more simple
831 * I think than the haskell code of quickcheck.
832 *
833 * update: apparently Jane Street have copied some of my code for their
834 * Ounit_util.ml and quichcheck.ml in their Core library :)
835 *)
836
837 (*---------------------------------------------------------------------------*)
838 (* generators *)
839 (*---------------------------------------------------------------------------*)
840 type 'a gen = unit -> 'a
841
842 let (ig: int gen) = fun () ->
843 Random.int 10
844 let (lg: ('a gen) -> ('a list) gen) = fun gen () ->
845 foldn (fun acc i -> (gen ())::acc) [] (Random.int 10)
846 let (pg: ('a gen) -> ('b gen) -> ('a * 'b) gen) = fun gen1 gen2 () ->
847 (gen1 (), gen2 ())
848 let polyg = ig
849 let (ng: (string gen)) = fun () ->
850 "a" ^ (string_of_int (ig ()))
851
852 let (oneofl: ('a list) -> 'a gen) = fun xs () ->
853 List.nth xs (Random.int (List.length xs))
854 (* let oneofl l = oneof (List.map always l) *)
855
856 let (oneof: (('a gen) list) -> 'a gen) = fun xs ->
857 List.nth xs (Random.int (List.length xs))
858
859 let (always: 'a -> 'a gen) = fun e () -> e
860
861 let (frequency: ((int * ('a gen)) list) -> 'a gen) = fun xs ->
862 let sums = sum_int (List.map fst xs) in
863 let i = Random.int sums in
864 let rec freq_aux acc = function
865 | (x,g)::xs -> if i < acc+x then g else freq_aux (acc+x) xs
866 | _ -> failwith "frequency"
867 in
868 freq_aux 0 xs
869 let frequencyl l = frequency (List.map (fun (i,e) -> (i,always e)) l)
870
871 (*
872 let b = oneof [always true; always false] ()
873 let b = frequency [3, always true; 2, always false] ()
874 *)
875
876 (* cant do this:
877 * let rec (lg: ('a gen) -> ('a list) gen) = fun gen -> oneofl [[]; lg gen ()]
878 * nor
879 * let rec (lg: ('a gen) -> ('a list) gen) = fun gen -> oneof [always []; lg gen]
880 *
881 * because caml is not as lazy as haskell :( fix the pb by introducing a size
882 * limit. take the bounds/size as parameter. morover this is needed for
883 * more complex type.
884 *
885 * how make a bintreeg ?? we need recursion
886 *
887 * let rec (bintreeg: ('a gen) -> ('a bintree) gen) = fun gen () ->
888 * let rec aux n =
889 * if n = 0 then (Leaf (gen ()))
890 * else frequencyl [1, Leaf (gen ()); 4, Branch ((aux (n / 2)), aux (n / 2))]
891 * ()
892 * in aux 20
893 *
894 *)
895
896
897 (*---------------------------------------------------------------------------*)
898 (* property *)
899 (*---------------------------------------------------------------------------*)
900
901 (* todo: a test_all_laws, better syntax (done already a little with ig in
902 * place of intg. En cas d'erreur, print the arg that not respect
903 *
904 * todo: with monitoring, as in haskell, laws = laws2, no need for 2 func,
905 * but hard i found
906 *
907 * todo classify, collect, forall
908 *)
909
910
911 (* return None when good, and Just the_problematic_case when bad *)
912 let (laws: string -> ('a -> bool) -> ('a gen) -> 'a option) = fun s func gen ->
913 let res = foldn (fun acc i -> let n = gen() in (n, func n)::acc) [] 1000 in
914 let res = List.filter (fun (x,b) -> not b) res in
915 if res = [] then None else Some (fst (List.hd res))
916
917 let rec (statistic_number: ('a list) -> (int * 'a) list) = function
918 | [] -> []
919 | x::xs -> let (splitg, splitd) = List.partition (fun y -> y = x) xs in
920 (1+(List.length splitg), x)::(statistic_number splitd)
921
922 (* in pourcentage *)
923 let (statistic: ('a list) -> (int * 'a) list) = fun xs ->
924 let stat_num = statistic_number xs in
925 let totals = sum_int (List.map fst stat_num) in
926 List.map (fun (i, v) -> ((i * 100) / totals), v) stat_num
927
928 let (laws2:
929 string -> ('a -> (bool * 'b)) -> ('a gen) ->
930 ('a option * ((int * 'b) list ))) =
931 fun s func gen ->
932 let res = foldn (fun acc i -> let n = gen() in (n, func n)::acc) [] 1000 in
933 let stat = statistic (List.map (fun (x,(b,v)) -> v) res) in
934 let res = List.filter (fun (x,(b,v)) -> not b) res in
935 if res = [] then (None, stat) else (Some (fst (List.hd res)), stat)
936
937
938 (*
939 let b = laws "unit" (fun x -> reverse [x] = [x] )ig
940 let b = laws "app " (fun (xs,ys) -> reverse (xs++ys) = reverse ys++reverse xs)(pg (lg ig)(lg ig))
941 let b = laws "rev " (fun xs -> reverse (reverse xs) = xs )(lg ig)
942 let b = laws "appb" (fun (xs,ys) -> reverse (xs++ys) = reverse xs++reverse ys)(pg (lg ig)(lg ig))
943 let b = laws "max" (fun (x,y) -> x <= y ==> (max x y = y) )(pg ig ig)
944
945 let b = laws2 "max" (fun (x,y) -> ((x <= y ==> (max x y = y)), x <= y))(pg ig ig)
946 *)
947
948
949 (* todo, do with coarbitrary ?? idea is that given a 'a, generate a 'b
950 * depending of 'a and gen 'b, that is modify gen 'b, what is important is
951 * that each time given the same 'a, we must get the same 'b !!!
952 *)
953
954 (*
955 let (fg: ('a gen) -> ('b gen) -> ('a -> 'b) gen) = fun gen1 gen2 () ->
956 let b = laws "funs" (fun (f,g,h) -> x <= y ==> (max x y = y) )(pg ig ig)
957 *)
958
959 (*
960 let one_of xs = List.nth xs (Random.int (List.length xs))
961 let take_one xs =
962 if empty xs then failwith "Take_one: empty list"
963 else
964 let i = Random.int (List.length xs) in
965 List.nth xs i, filter_index (fun j _ -> i <> j) xs
966 *)
967
968 (*****************************************************************************)
969 (* Persistence *)
970 (*****************************************************************************)
971
972 let get_value filename =
973 let chan = open_in filename in
974 let x = input_value chan in (* <=> Marshal.from_channel *)
975 (close_in chan; x)
976
977 let write_value valu filename =
978 let chan = open_out filename in
979 (output_value chan valu; (* <=> Marshal.to_channel *)
980 (* Marshal.to_channel chan valu [Marshal.Closures]; *)
981 close_out chan)
982
983 let write_back func filename =
984 write_value (func (get_value filename)) filename
985
986
987 let read_value f = get_value f
988
989
990 let marshal__to_string2 v flags =
991 Marshal.to_string v flags
992 let marshal__to_string a b =
993 profile_code "Marshalling" (fun () -> marshal__to_string2 a b)
994
995 let marshal__from_string2 v flags =
996 Marshal.from_string v flags
997 let marshal__from_string a b =
998 profile_code "Marshalling" (fun () -> marshal__from_string2 a b)
999
1000
1001
1002 (*****************************************************************************)
1003 (* Counter *)
1004 (*****************************************************************************)
1005 let _counter = ref 0
1006 let counter () = (_counter := !_counter +1; !_counter)
1007
1008 let _counter2 = ref 0
1009 let counter2 () = (_counter2 := !_counter2 +1; !_counter2)
1010
1011 let _counter3 = ref 0
1012 let counter3 () = (_counter3 := !_counter3 +1; !_counter3)
1013
1014 type timestamp = int
1015
1016 (*****************************************************************************)
1017 (* String_of *)
1018 (*****************************************************************************)
1019 (* To work with the macro system autogenerated string_of and print_ function
1020 (kind of deriving a la haskell) *)
1021
1022 (* int, bool, char, float, ref ?, string *)
1023
1024 let string_of_string s = "\"" ^ s "\""
1025
1026 let string_of_list f xs =
1027 "[" ^ (xs +> List.map f +> String.concat ";" ) ^ "]"
1028
1029 let string_of_unit () = "()"
1030
1031 let string_of_array f xs =
1032 "[|" ^ (xs +> Array.to_list +> List.map f +> String.concat ";") ^ "|]"
1033
1034 let string_of_option f = function
1035 | None -> "None "
1036 | Some x -> "Some " ^ (f x)
1037
1038
1039
1040
1041 let print_bool x = print_string (if x then "True" else "False")
1042
1043 let print_option pr = function
1044 | None -> print_string "None"
1045 | Some x -> print_string "Some ("; pr x; print_string ")"
1046
1047 let print_list pr xs =
1048 begin
1049 print_string "[";
1050 List.iter (fun x -> pr x; print_string ",") xs;
1051 print_string "]";
1052 end
1053
1054 (* specialised
1055 let (string_of_list: char list -> string) =
1056 List.fold_left (fun acc x -> acc^(Char.escaped x)) ""
1057 *)
1058
1059
1060 let rec print_between between fn = function
1061 | [] -> ()
1062 | [x] -> fn x
1063 | x::xs -> fn x; between(); print_between between fn xs
1064
1065
1066
1067
1068 let adjust_pp_with_indent f =
1069 Format.open_box !_tab_level_print;
1070 (*Format.force_newline();*)
1071 f();
1072 Format.close_box ();
1073 Format.print_newline()
1074
1075 let adjust_pp_with_indent_and_header s f =
1076 Format.open_box (!_tab_level_print + String.length s);
1077 do_n !_tab_level_print (fun () -> Format.print_string " ");
1078 Format.print_string s;
1079 f();
1080 Format.close_box ();
1081 Format.print_newline()
1082
1083
1084
1085 let pp_do_in_box f = Format.open_box 1; f(); Format.close_box ()
1086 let pp_do_in_zero_box f = Format.open_box 0; f(); Format.close_box ()
1087
1088 let pp_f_in_box f =
1089 Format.open_box 1;
1090 let res = f() in
1091 Format.close_box ();
1092 res
1093
1094 let pp s = Format.print_string s
1095
1096 let mk_str_func_of_assoc_conv xs =
1097 let swap (x,y) = (y,x) in
1098
1099 (fun s ->
1100 let xs' = List.map swap xs in
1101 List.assoc s xs'
1102 ),
1103 (fun a ->
1104 List.assoc a xs
1105 )
1106
1107
1108
1109 (* julia: convert something printed using format to print into a string *)
1110 (* now at bottom of file
1111 let format_to_string f =
1112 ...
1113 *)
1114
1115
1116
1117 (*****************************************************************************)
1118 (* Macro *)
1119 (*****************************************************************************)
1120
1121 (* put your macro in macro.ml4, and you can test it interactivly as in lisp *)
1122 let macro_expand s =
1123 let c = open_out "/tmp/ttttt.ml" in
1124 begin
1125 output_string c s; close_out c;
1126 command2 ("ocamlc -c -pp 'camlp4o pa_extend.cmo q_MLast.cmo -impl' " ^
1127 "-I +camlp4 -impl macro.ml4");
1128 command2 "camlp4o ./macro.cmo pr_o.cmo /tmp/ttttt.ml";
1129 command2 "rm -f /tmp/ttttt.ml";
1130 end
1131
1132 (*
1133 let t = macro_expand "{ x + y | (x,y) <- [(1,1);(2,2);(3,3)] and x>2 and y<3}"
1134 let x = { x + y | (x,y) <- [(1,1);(2,2);(3,3)] and x > 2 and y < 3}
1135 let t = macro_expand "{1 .. 10}"
1136 let x = {1 .. 10} +> List.map (fun i -> i)
1137 let t = macro_expand "[1;2] to append to [2;4]"
1138 let t = macro_expand "{x = 2; x = 3}"
1139
1140 let t = macro_expand "type 'a bintree = Leaf of 'a | Branch of ('a bintree * 'a bintree)"
1141 *)
1142
1143
1144
1145 (*****************************************************************************)
1146 (* Composition/Control *)
1147 (*****************************************************************************)
1148
1149 (* I like the obj.func object notation. In OCaml cant use '.' so I use +>
1150 *
1151 * update: it seems that F# agrees with me :) but they use |>
1152 *)
1153
1154 (* now in prelude:
1155 * let (+>) o f = f o
1156 *)
1157 let (+!>) refo f = refo := f !refo
1158 (* alternatives:
1159 * let ((@): 'a -> ('a -> 'b) -> 'b) = fun a b -> b a
1160 * let o f g x = f (g x)
1161 *)
1162
1163 let ($) f g x = g (f x)
1164 let compose f g x = f (g x)
1165 (* dont work :( let ( ° ) f g x = f(g(x)) *)
1166
1167 (* trick to have something similar to the 1 `max` 4 haskell infix notation.
1168 by Keisuke Nakano on the caml mailing list.
1169 > let ( /* ) x y = y x
1170 > and ( */ ) x y = x y
1171 or
1172 let ( <| ) x y = y x
1173 and ( |> ) x y = x y
1174
1175 > Then we can make an infix operator <| f |> for a binary function f.
1176 *)
1177
1178 let flip f = fun a b -> f b a
1179
1180 let curry f x y = f (x,y)
1181 let uncurry f (a,b) = f a b
1182
1183 let id = fun x -> x
1184
1185 let do_nothing () = ()
1186
1187 let rec applyn n f o = if n = 0 then o else applyn (n-1) f (f o)
1188
1189 let forever f =
1190 while true do
1191 f();
1192 done
1193
1194
1195 class ['a] shared_variable_hook (x:'a) =
1196 object(self)
1197 val mutable data = x
1198 val mutable registered = []
1199 method set x =
1200 begin
1201 data <- x;
1202 pr "refresh registered";
1203 registered +> List.iter (fun f -> f());
1204 end
1205 method get = data
1206 method modify f = self#set (f self#get)
1207 method register f =
1208 registered <- f :: registered
1209 end
1210
1211 (* src: from aop project. was called ptFix *)
1212 let rec fixpoint trans elem =
1213 let image = trans elem in
1214 if (image = elem)
1215 then elem (* point fixe *)
1216 else fixpoint trans image
1217
1218 (* le point fixe pour les objets. was called ptFixForObjetct *)
1219 let rec fixpoint_for_object trans elem =
1220 let image = trans elem in
1221 if (image#equal elem) then elem (* point fixe *)
1222 else fixpoint_for_object trans image
1223
1224 let (add_hook: ('a -> ('a -> 'b) -> 'b) ref -> ('a -> ('a -> 'b) -> 'b) -> unit) =
1225 fun var f ->
1226 let oldvar = !var in
1227 var := fun arg k -> f arg (fun x -> oldvar x k)
1228
1229 let (add_hook_action: ('a -> unit) -> ('a -> unit) list ref -> unit) =
1230 fun f hooks ->
1231 push2 f hooks
1232
1233 let (run_hooks_action: 'a -> ('a -> unit) list ref -> unit) =
1234 fun obj hooks ->
1235 !hooks +> List.iter (fun f -> try f obj with _ -> ())
1236
1237
1238 type 'a mylazy = (unit -> 'a)
1239
1240 (* a la emacs *)
1241 let save_excursion reference f =
1242 let old = !reference in
1243 let res = f() in
1244 reference := old;
1245 res
1246
1247 let save_excursion_and_disable reference f =
1248 save_excursion reference (fun () ->
1249 reference := false;
1250 f ()
1251 )
1252
1253 let save_excursion_and_enable reference f =
1254 save_excursion reference (fun () ->
1255 reference := true;
1256 f ()
1257 )
1258
1259
1260 let memoized h k f =
1261 try Hashtbl.find h k
1262 with Not_found ->
1263 let v = f () in
1264 begin
1265 Hashtbl.add h k v;
1266 v
1267 end
1268
1269 let cache_in_ref myref f =
1270 match !myref with
1271 | Some e -> e
1272 | None ->
1273 let e = f () in
1274 myref := Some e;
1275 e
1276
1277 let once f =
1278 let already = ref false in
1279 (fun x ->
1280 if not !already
1281 then begin already := true; f x end
1282 )
1283
1284 (* cache_file, cf below *)
1285
1286 let before_leaving f x =
1287 f x;
1288 x
1289
1290 (* finalize, cf prelude *)
1291
1292
1293 (* cheat *)
1294 let rec y f = fun x -> f (y f) x
1295
1296 (*****************************************************************************)
1297 (* Concurrency *)
1298 (*****************************************************************************)
1299
1300 (* from http://en.wikipedia.org/wiki/File_locking
1301 *
1302 * "When using file locks, care must be taken to ensure that operations
1303 * are atomic. When creating the lock, the process must verify that it
1304 * does not exist and then create it, but without allowing another
1305 * process the opportunity to create it in the meantime. Various
1306 * schemes are used to implement this, such as taking advantage of
1307 * system calls designed for this purpose (but such system calls are
1308 * not usually available to shell scripts) or by creating the lock file
1309 * under a temporary name and then attempting to move it into place."
1310 *
1311 * => can't use 'if(not (file_exist xxx)) then create_file xxx' because
1312 * file_exist/create_file are not in atomic section (classic problem).
1313 *
1314 * from man open:
1315 *
1316 * "O_EXCL When used with O_CREAT, if the file already exists it
1317 * is an error and the open() will fail. In this context, a
1318 * symbolic link exists, regardless of where it points to.
1319 * O_EXCL is broken on NFS file systems; programs which
1320 * rely on it for performing locking tasks will contain a
1321 * race condition. The solution for performing atomic file
1322 * locking using a lockfile is to create a unique file on
1323 * the same file system (e.g., incorporating host- name and
1324 * pid), use link(2) to make a link to the lockfile. If
1325 * link(2) returns 0, the lock is successful. Otherwise,
1326 * use stat(2) on the unique file to check if its link
1327 * count has increased to 2, in which case the lock is also
1328 * successful."
1329
1330 *)
1331
1332 exception FileAlreadyLocked
1333
1334 (* Racy if lock file on NFS!!! But still racy with recent Linux ? *)
1335 let acquire_file_lock filename =
1336 pr2 ("Locking file: " ^ filename);
1337 try
1338 let _fd = Unix.openfile filename [Unix.O_CREAT;Unix.O_EXCL] 0o777 in
1339 ()
1340 with Unix.Unix_error (e, fm, argm) ->
1341 pr2 (spf "exn Unix_error: %s %s %s\n" (Unix.error_message e) fm argm);
1342 raise FileAlreadyLocked
1343
1344
1345 let release_file_lock filename =
1346 pr2 ("Releasing file: " ^ filename);
1347 Unix.unlink filename;
1348 ()
1349
1350
1351
1352 (*****************************************************************************)
1353 (* Error managment *)
1354 (*****************************************************************************)
1355
1356 exception Todo
1357 exception Impossible
1358 exception Here
1359 exception ReturnExn
1360
1361 exception Multi_found (* to be consistent with Not_found *)
1362
1363 exception WrongFormat of string
1364
1365 (* old: let _TODO () = failwith "TODO", now via fix_caml with raise Todo *)
1366
1367 let internal_error s = failwith ("internal error: "^s)
1368 let error_cant_have x = internal_error ("cant have this case" ^(dump x))
1369 let myassert cond = if cond then () else failwith "assert error"
1370
1371
1372
1373 (* before warning I was forced to do stuff like this:
1374 *
1375 * let (fixed_int_to_posmap: fixed_int -> posmap) = fun fixed ->
1376 * let v = ((fix_to_i fixed) / (power 2 16)) in
1377 * let _ = Printf.printf "coord xy = %d\n" v in
1378 * v
1379 *
1380 * The need for printf make me force to name stuff :(
1381 * How avoid ? use 'it' special keyword ?
1382 * In fact dont have to name it, use +> (fun v -> ...) so when want
1383 * erase debug just have to erase one line.
1384 *)
1385 let warning s v = (pr2 ("Warning: " ^ s ^ "; value = " ^ (dump v)); v)
1386
1387
1388
1389
1390 let exn_to_s exn =
1391 Printexc.to_string exn
1392
1393 (* alias *)
1394 let string_of_exn exn = exn_to_s exn
1395
1396
1397 (* want or of merd, but cant cos cant put die ... in b (strict call) *)
1398 let (|||) a b = try a with _ -> b
1399
1400 (* emacs/lisp inspiration, (vouillon does that too in unison I think) *)
1401
1402 (* now in Prelude:
1403 * let unwind_protect f cleanup = ...
1404 * let finalize f cleanup = ...
1405 *)
1406
1407 type error = Error of string
1408
1409 (* sometimes to get help from ocaml compiler to tell me places where
1410 * I should update, we sometimes need to change some type from pair
1411 * to triple, hence this kind of fake type.
1412 *)
1413 type evotype = unit
1414 let evoval = ()
1415
1416 (*****************************************************************************)
1417 (* Environment *)
1418 (*****************************************************************************)
1419
1420 let check_stack = ref true
1421 let check_stack_size limit =
1422 if !check_stack then begin
1423 pr2 "checking stack size (do ulimit -s 50000 if problem)";
1424 let rec aux i =
1425 if i = limit
1426 then 0
1427 else 1 + aux (i + 1)
1428 in
1429 assert(aux 0 = limit);
1430 ()
1431 end
1432
1433 let test_check_stack_size limit =
1434 (* bytecode: 100000000 *)
1435 (* native: 10000000 *)
1436 check_stack_size (int_of_string limit)
1437
1438
1439 (* only relevant in bytecode, in native the stacklimit is the os stacklimit
1440 * (adjustable by ulimit -s)
1441 *)
1442 let _init_gc_stack =
1443 Gc.set {(Gc.get ()) with Gc.stack_limit = 100 * 1024 * 1024}
1444
1445
1446
1447
1448 (* if process a big set of files then dont want get overflow in the middle
1449 * so for this we are ready to spend some extra time at the beginning that
1450 * could save far more later.
1451 *)
1452 let check_stack_nbfiles nbfiles =
1453 if nbfiles > 200
1454 then check_stack_size 10000000
1455
1456 (*****************************************************************************)
1457 (* Arguments/options and command line (cocci and acomment) *)
1458 (*****************************************************************************)
1459
1460 (*
1461 * Why define wrappers ? Arg not good enough ? Well the Arg.Rest is not that
1462 * good and I need a way sometimes to get a list of argument.
1463 *
1464 * I could define maybe a new Arg.spec such as
1465 * | String_list of (string list -> unit), but the action may require
1466 * some flags to be set, so better to process this after all flags have
1467 * been set by parse_options. So have to split. Otherwise it would impose
1468 * an order of the options such as
1469 * -verbose_parsing -parse_c file1 file2. and I really like to use bash
1470 * history and add just at the end of my command a -profile for instance.
1471 *
1472 *
1473 * Why want a -action arg1 arg2 arg3 ? (which in turn requires this
1474 * convulated scheme ...) Why not use Arg.String action such as
1475 * "-parse_c", Arg.String (fun file -> ...) ?
1476 * I want something that looks like ocaml function but at the UNIX
1477 * command line level. So natural to have this scheme instead of
1478 * -taxo_file arg2 -sample_file arg3 -parse_c arg1.
1479 *
1480 *
1481 * Why not use the toplevel ?
1482 * - because to debug, ocamldebug is far superior to the toplevel
1483 * (can go back, can go directly to a specific point, etc).
1484 * I want a kind of testing at cmdline level.
1485 * - Also I don't have file completion when in the ocaml toplevel.
1486 * I have to type "/path/to/xxx" without help.
1487 *
1488 *
1489 * Why having variable flags ? Why use 'if !verbose_parsing then ...' ?
1490 * why not use strings and do stuff like the following
1491 * 'if (get_config "verbose_parsing") then ...'
1492 * Because I want to make the interface for flags easier for the code
1493 * that use it. The programmer should not be bothered wether this
1494 * flag is set via args cmd line or a config file, so I want to make it
1495 * as simple as possible, just use a global plain caml ref variable.
1496 *
1497 * Same spirit a little for the action. Instead of having function such as
1498 * test_parsing_c, I could do it only via string. But I still prefer
1499 * to have plain caml test functions. Also it makes it easier to call
1500 * those functions from a toplevel for people who prefer the toplevel.
1501 *
1502 *
1503 * So have flag_spec and action_spec. And in flag have debug_xxx flags,
1504 * verbose_xxx flags and other flags.
1505 *
1506 * I would like to not have to separate the -xxx actions spec from the
1507 * corresponding actions, but those actions may need more than one argument
1508 * and so have to wait for parse_options, which in turn need the options
1509 * spec, so circle.
1510 *
1511 * Also I dont want to mix code with data structures, so it's better that the
1512 * options variable contain just a few stuff and have no side effects except
1513 * setting global variables.
1514 *
1515 * Why not have a global variable such as Common.actions that
1516 * other modules modify ? No, I prefer to do less stuff behind programmer's
1517 * back so better to let the user merge the different options at call
1518 * site, but at least make it easier by providing shortcut for set of options.
1519 *
1520 *
1521 *
1522 *
1523 * todo? isn't unison or scott-mcpeak-lib-in-cil handles that kind of
1524 * stuff better ? That is the need to localize command line argument
1525 * while still being able to gathering them. Same for logging.
1526 * Similiar to the type prof = PALL | PNONE | PSOME of string list.
1527 * Same spirit of fine grain config in log4j ?
1528 *
1529 * todo? how mercurial/cvs/git manage command line options ? because they
1530 * all have a kind of DSL around arguments with some common options,
1531 * specific options, conventions, etc.
1532 *
1533 *
1534 * todo? generate the corresponding noxxx options ?
1535 * todo? generate list of options and show their value ?
1536 *
1537 * todo? make it possible to set this value via a config file ?
1538 *
1539 *
1540 *)
1541
1542 type arg_spec_full = Arg.key * Arg.spec * Arg.doc
1543 type cmdline_options = arg_spec_full list
1544
1545 (* the format is a list of triples:
1546 * (title of section * (optional) explanation of sections * options)
1547 *)
1548 type options_with_title = string * string * arg_spec_full list
1549 type cmdline_sections = options_with_title list
1550
1551
1552 (* ---------------------------------------------------------------------- *)
1553
1554 (* now I use argv as I like at the call sites to show that
1555 * this function internally use argv.
1556 *)
1557 let parse_options options usage_msg argv =
1558 let args = ref [] in
1559 (try
1560 Arg.parse_argv argv options (fun file -> args := file::!args) usage_msg;
1561 args := List.rev !args;
1562 !args
1563 with
1564 | Arg.Bad msg -> eprintf "%s" msg; exit 2
1565 | Arg.Help msg -> printf "%s" msg; exit 0
1566 )
1567
1568
1569
1570
1571 let usage usage_msg options =
1572 Arg.usage (Arg.align options) usage_msg
1573
1574
1575 (* for coccinelle *)
1576
1577 (* If you don't want the -help and --help that are appended by Arg.align *)
1578 let arg_align2 xs =
1579 Arg.align xs +> List.rev +> drop 2 +> List.rev
1580
1581
1582 let short_usage usage_msg ~short_opt =
1583 usage usage_msg short_opt
1584
1585 let long_usage usage_msg ~short_opt ~long_opt =
1586 pr usage_msg;
1587 pr "";
1588 let all_options_with_title =
1589 (("main options", "", short_opt)::long_opt) in
1590 all_options_with_title +> List.iter
1591 (fun (title, explanations, xs) ->
1592 pr title;
1593 pr_xxxxxxxxxxxxxxxxx();
1594 if explanations <> ""
1595 then begin pr explanations; pr "" end;
1596 arg_align2 xs +> List.iter (fun (key,action,s) ->
1597 pr (" " ^ key ^ s)
1598 );
1599 pr "";
1600 );
1601 ()
1602
1603
1604 (* copy paste of Arg.parse. Don't want the default -help msg *)
1605 let arg_parse2 l msg short_usage_fun =
1606 let args = ref [] in
1607 let f = (fun file -> args := file::!args) in
1608 let l = Arg.align l in
1609 (try begin
1610 Arg.parse_argv Sys.argv l f msg;
1611 args := List.rev !args;
1612 !args
1613 end
1614 with
1615 | Arg.Bad msg -> (* eprintf "%s" msg; exit 2; *)
1616 let xs = lines msg in
1617 (* take only head, it's where the error msg is *)
1618 pr2 (List.hd xs);
1619 short_usage_fun();
1620 raise (UnixExit (2))
1621 | Arg.Help msg -> (* printf "%s" msg; exit 0; *)
1622 raise Impossible (* -help is specified in speclist *)
1623 )
1624
1625
1626 (* ---------------------------------------------------------------------- *)
1627 (* kind of unit testing framework, or toplevel like functionnality
1628 * at shell command line. I realize than in fact It follows a current trend
1629 * to have a main cmdline program where can then select different actions,
1630 * as in cvs/hg/git where do hg <action> <arguments>, and the shell even
1631 * use a curried syntax :)
1632 *
1633 *
1634 * Not-perfect-but-basic-feels-right: an action
1635 * spec looks like this:
1636 *
1637 * let actions () = [
1638 * "-parse_taxo", " <file>",
1639 * Common.mk_action_1_arg test_parse_taxo;
1640 * ...
1641 * ]
1642 *
1643 * Not-perfect-but-basic-feels-right because for such functionality we
1644 * need a way to transform a string into a caml function and pass arguments
1645 * and the preceding design does exactly that, even if then the
1646 * functions that use this design are not so convenient to use (there
1647 * are 2 places where we need to pass those data, in the options and in the
1648 * main dispatcher).
1649 *
1650 * Also it's not too much intrusive. Still have an
1651 * action ref variable in the main.ml and can still use the previous
1652 * simpler way to do where the match args with in main.ml do the
1653 * dispatch.
1654 *
1655 * Use like this at option place:
1656 * (Common.options_of_actions actionref (Test_parsing_c.actions())) ++
1657 * Use like this at dispatch action place:
1658 * | xs when List.mem !action (Common.action_list all_actions) ->
1659 * Common.do_action !action xs all_actions
1660 *
1661 *)
1662
1663 type flag_spec = Arg.key * Arg.spec * Arg.doc
1664 type action_spec = Arg.key * Arg.doc * action_func
1665 and action_func = (string list -> unit)
1666
1667 type cmdline_actions = action_spec list
1668 exception WrongNumberOfArguments
1669
1670 let options_of_actions action_ref actions =
1671 actions +> List.map (fun (key, doc, _func) ->
1672 (key, (Arg.Unit (fun () -> action_ref := key)), doc)
1673 )
1674
1675 let (action_list: cmdline_actions -> Arg.key list) = fun xs ->
1676 List.map (fun (a,b,c) -> a) xs
1677
1678 let (do_action: Arg.key -> string list (* args *) -> cmdline_actions -> unit) =
1679 fun key args xs ->
1680 let assoc = xs +> List.map (fun (a,b,c) -> (a,c)) in
1681 let action_func = List.assoc key assoc in
1682 action_func args
1683
1684
1685 (* todo? if have a function with default argument ? would like a
1686 * mk_action_0_or_1_arg ?
1687 *)
1688
1689 let mk_action_0_arg f =
1690 (function
1691 | [] -> f ()
1692 | _ -> raise WrongNumberOfArguments
1693 )
1694
1695 let mk_action_1_arg f =
1696 (function
1697 | [file] -> f file
1698 | _ -> raise WrongNumberOfArguments
1699 )
1700
1701 let mk_action_2_arg f =
1702 (function
1703 | [file1;file2] -> f file1 file2
1704 | _ -> raise WrongNumberOfArguments
1705 )
1706
1707 let mk_action_3_arg f =
1708 (function
1709 | [file1;file2;file3] -> f file1 file2 file3
1710 | _ -> raise WrongNumberOfArguments
1711 )
1712
1713 let mk_action_n_arg f = f
1714
1715
1716 (*****************************************************************************)
1717 (* Equality *)
1718 (*****************************************************************************)
1719
1720 (* Using the generic (=) is tempting, but it backfires, so better avoid it *)
1721
1722 (* To infer all the code that use an equal, and that should be
1723 * transformed, is not that easy, because (=) is used by many
1724 * functions, such as List.find, List.mem, and so on. So the strategy
1725 * is to turn what you were previously using into a function, because
1726 * (=) return an exception when applied to a function. Then you simply
1727 * use ocamldebug to infer where the code has to be transformed.
1728 *)
1729
1730 (* src: caml mailing list ? *)
1731 let (=|=) : int -> int -> bool = (=)
1732 let (=<=) : char -> char -> bool = (=)
1733 let (=$=) : string -> string -> bool = (=)
1734 let (=:=) : bool -> bool -> bool = (=)
1735
1736 (* the evil generic (=). I define another symbol to more easily detect
1737 * it, cos the '=' sign is syntaxically overloaded in caml. It is also
1738 * used to define function.
1739 *)
1740 let (=*=) = (=)
1741
1742 (* if really want to forbid to use '='
1743 let (=) = (=|=)
1744 *)
1745 let (=) () () = false
1746
1747
1748
1749
1750
1751
1752
1753
1754 (*###########################################################################*)
1755 (* And now basic types *)
1756 (*###########################################################################*)
1757
1758
1759
1760 (*****************************************************************************)
1761 (* Bool *)
1762 (*****************************************************************************)
1763 let (==>) b1 b2 = if b1 then b2 else true (* could use too => *)
1764
1765 (* superseded by another <=> below
1766 let (<=>) a b = if a =*= b then 0 else if a < b then -1 else 1
1767 *)
1768
1769 let xor a b = not (a =*= b)
1770
1771
1772 (*****************************************************************************)
1773 (* Char *)
1774 (*****************************************************************************)
1775
1776 let string_of_char c = String.make 1 c
1777
1778 let is_single = String.contains ",;()[]{}_`"
1779 let is_symbol = String.contains "!@#$%&*+./<=>?\\^|:-~"
1780 let is_space = String.contains "\n\t "
1781 let cbetween min max c =
1782 (int_of_char c) <= (int_of_char max) &&
1783 (int_of_char c) >= (int_of_char min)
1784 let is_upper = cbetween 'A' 'Z'
1785 let is_lower = cbetween 'a' 'z'
1786 let is_alpha c = is_upper c || is_lower c
1787 let is_digit = cbetween '0' '9'
1788
1789 let string_of_chars cs = cs +> List.map (String.make 1) +> String.concat ""
1790
1791
1792
1793 (*****************************************************************************)
1794 (* Num *)
1795 (*****************************************************************************)
1796
1797 (* since 3.08, div by 0 raise Div_by_rezo, and not anymore a hardware trap :)*)
1798 let (/!) x y = if y =|= 0 then (log "common.ml: div by 0"; 0) else x / y
1799
1800 (* now in prelude
1801 * let rec (do_n: int -> (unit -> unit) -> unit) = fun i f ->
1802 * if i = 0 then () else (f(); do_n (i-1) f)
1803 *)
1804
1805 (* now in prelude
1806 * let rec (foldn: ('a -> int -> 'a) -> 'a -> int -> 'a) = fun f acc i ->
1807 * if i = 0 then acc else foldn f (f acc i) (i-1)
1808 *)
1809
1810 let sum_float = List.fold_left (+.) 0.0
1811 let sum_int = List.fold_left (+) 0
1812
1813 let pi = 3.14159265358979323846
1814 let pi2 = pi /. 2.0
1815 let pi4 = pi /. 4.0
1816
1817 (* 180 = pi *)
1818 let (deg_to_rad: float -> float) = fun deg ->
1819 (deg *. pi) /. 180.0
1820
1821 let clampf = function
1822 | n when n < 0.0 -> 0.0
1823 | n when n > 1.0 -> 1.0
1824 | n -> n
1825
1826 let square x = x *. x
1827
1828 let rec power x n = if n =|= 0 then 1 else x * power x (n-1)
1829
1830 let between i min max = i > min && i < max
1831
1832 let (between_strict: int -> int -> int -> bool) = fun a b c ->
1833 a < b && b < c
1834
1835
1836 let bitrange x p = let v = power 2 p in between x (-v) v
1837
1838 (* descendant *)
1839 let (prime1: int -> int option) = fun x ->
1840 let rec prime1_aux n =
1841 if n =|= 1 then None
1842 else
1843 if (x / n) * n =|= x then Some n else prime1_aux (n-1)
1844 in if x =|= 1 then None else if x < 0 then failwith "negative" else prime1_aux (x-1)
1845
1846 (* montant, better *)
1847 let (prime: int -> int option) = fun x ->
1848 let rec prime_aux n =
1849 if n =|= x then None
1850 else
1851 if (x / n) * n =|= x then Some n else prime_aux (n+1)
1852 in if x =|= 1 then None else if x < 0 then failwith "negative" else prime_aux 2
1853
1854 let sum xs = List.fold_left (+) 0 xs
1855 let product = List.fold_left ( * ) 1
1856
1857
1858 let decompose x =
1859 let rec decompose x =
1860 if x =|= 1 then []
1861 else
1862 (match prime x with
1863 | None -> [x]
1864 | Some n -> n::decompose (x / n)
1865 )
1866 in assert (product (decompose x) =|= x); decompose x
1867
1868 let mysquare x = x * x
1869 let sqr a = a *. a
1870
1871
1872 type compare = Equal | Inf | Sup
1873 let (<=>) a b = if a =*= b then Equal else if a < b then Inf else Sup
1874 let (<==>) a b = if a =*= b then 0 else if a < b then -1 else 1
1875
1876 type uint = int
1877
1878
1879 let int_of_stringchar s =
1880 fold_left_with_index (fun acc e i -> acc + (Char.code e*(power 8 i))) 0 (List.rev (list_of_string s))
1881
1882 let int_of_base s base =
1883 fold_left_with_index (fun acc e i ->
1884 let j = Char.code e - Char.code '0' in
1885 if j >= base then failwith "not in good base"
1886 else acc + (j*(power base i))
1887 )
1888 0 (List.rev (list_of_string s))
1889
1890 let int_of_stringbits s = int_of_base s 2
1891 let _ = example (int_of_stringbits "1011" =|= 1*8 + 1*2 + 1*1)
1892
1893 let int_of_octal s = int_of_base s 8
1894 let _ = example (int_of_octal "017" =|= 15)
1895
1896 (* let int_of_hex s = int_of_base s 16, NONONONO cos 'A' - '0' does not give 10 !! *)
1897
1898 let int_of_all s =
1899 if String.length s >= 2 && (String.get s 0 =<= '0') && is_digit (String.get s 1)
1900 then int_of_octal s else int_of_string s
1901
1902
1903 let (+=) ref v = ref := !ref + v
1904 let (-=) ref v = ref := !ref - v
1905
1906 let pourcent x total =
1907 (x * 100) / total
1908 let pourcent_float x total =
1909 ((float_of_int x) *. 100.0) /. (float_of_int total)
1910
1911 let pourcent_float_of_floats x total =
1912 (x *. 100.0) /. total
1913
1914
1915 let pourcent_good_bad good bad =
1916 (good * 100) / (good + bad)
1917
1918 let pourcent_good_bad_float good bad =
1919 (float_of_int good *. 100.0) /. (float_of_int good +. float_of_int bad)
1920
1921 type 'a max_with_elem = int ref * 'a ref
1922 let update_max_with_elem (aref, aelem) ~is_better (newv, newelem) =
1923 if is_better newv aref
1924 then begin
1925 aref := newv;
1926 aelem := newelem;
1927 end
1928
1929 (*****************************************************************************)
1930 (* Numeric/overloading *)
1931 (*****************************************************************************)
1932
1933 type 'a numdict =
1934 NumDict of (('a-> 'a -> 'a) *
1935 ('a-> 'a -> 'a) *
1936 ('a-> 'a -> 'a) *
1937 ('a -> 'a));;
1938
1939 let add (NumDict(a, m, d, n)) = a;;
1940 let mul (NumDict(a, m, d, n)) = m;;
1941 let div (NumDict(a, m, d, n)) = d;;
1942 let neg (NumDict(a, m, d, n)) = n;;
1943
1944 let numd_int = NumDict(( + ),( * ),( / ),( ~- ));;
1945 let numd_float = NumDict(( +. ),( *. ), ( /. ),( ~-. ));;
1946 let testd dict n =
1947 let ( * ) x y = mul dict x y in
1948 let ( / ) x y = div dict x y in
1949 let ( + ) x y = add dict x y in
1950 (* Now you can define all sorts of things in terms of *, /, + *)
1951 let f num = (num * num) / (num + num) in
1952 f n;;
1953
1954
1955
1956 module ArithFloatInfix = struct
1957 let (+..) = (+)
1958 let (-..) = (-)
1959 let (/..) = (/)
1960 let ( *.. ) = ( * )
1961
1962
1963 let (+) = (+.)
1964 let (-) = (-.)
1965 let (/) = (/.)
1966 let ( * ) = ( *. )
1967
1968 let (+=) ref v = ref := !ref + v
1969 let (-=) ref v = ref := !ref - v
1970
1971 end
1972
1973
1974
1975 (*****************************************************************************)
1976 (* Tuples *)
1977 (*****************************************************************************)
1978
1979 type 'a pair = 'a * 'a
1980 type 'a triple = 'a * 'a * 'a
1981
1982 let fst3 (x,_,_) = x
1983 let snd3 (_,y,_) = y
1984 let thd3 (_,_,z) = z
1985
1986 let sndthd (a,b,c) = (b,c)
1987
1988 let map_fst f (x, y) = f x, y
1989 let map_snd f (x, y) = x, f y
1990
1991 let pair f (x,y) = (f x, f y)
1992
1993 (* for my ocamlbeautify script *)
1994 let snd = snd
1995 let fst = fst
1996
1997 let double a = a,a
1998 let swap (x,y) = (y,x)
1999
2000
2001 let tuple_of_list1 = function [a] -> a | _ -> failwith "tuple_of_list1"
2002 let tuple_of_list2 = function [a;b] -> a,b | _ -> failwith "tuple_of_list2"
2003 let tuple_of_list3 = function [a;b;c] -> a,b,c | _ -> failwith "tuple_of_list3"
2004 let tuple_of_list4 = function [a;b;c;d] -> a,b,c,d | _ -> failwith "tuple_of_list4"
2005 let tuple_of_list5 = function [a;b;c;d;e] -> a,b,c,d,e | _ -> failwith "tuple_of_list5"
2006 let tuple_of_list6 = function [a;b;c;d;e;f] -> a,b,c,d,e,f | _ -> failwith "tuple_of_list6"
2007
2008
2009 (*****************************************************************************)
2010 (* Maybe *)
2011 (*****************************************************************************)
2012
2013 (* type 'a maybe = Just of 'a | None *)
2014
2015 type ('a,'b) either = Left of 'a | Right of 'b
2016 (* with sexp *)
2017 type ('a, 'b, 'c) either3 = Left3 of 'a | Middle3 of 'b | Right3 of 'c
2018 (* with sexp *)
2019
2020 let just = function
2021 | (Some x) -> x
2022 | _ -> failwith "just: pb"
2023
2024 let some = just
2025
2026
2027 let fmap f = function
2028 | None -> None
2029 | Some x -> Some (f x)
2030 let map_option = fmap
2031
2032 let do_option f = function
2033 | None -> ()
2034 | Some x -> f x
2035
2036 let optionise f =
2037 try Some (f ()) with Not_found -> None
2038
2039
2040
2041 (* pixel *)
2042 let some_or = function
2043 | None -> id
2044 | Some e -> fun _ -> e
2045
2046
2047 let partition_either f l =
2048 let rec part_either left right = function
2049 | [] -> (List.rev left, List.rev right)
2050 | x :: l ->
2051 (match f x with
2052 | Left e -> part_either (e :: left) right l
2053 | Right e -> part_either left (e :: right) l) in
2054 part_either [] [] l
2055
2056
2057 (* pixel *)
2058 let rec filter_some = function
2059 | [] -> []
2060 | None :: l -> filter_some l
2061 | Some e :: l -> e :: filter_some l
2062
2063 let map_filter f xs = xs +> List.map f +> filter_some
2064
2065 let rec find_some p = function
2066 | [] -> raise Not_found
2067 | x :: l ->
2068 match p x with
2069 | Some v -> v
2070 | None -> find_some p l
2071
2072 (* same
2073 let map_find f xs =
2074 xs +> List.map f +> List.find (function Some x -> true | None -> false)
2075 +> (function Some x -> x | None -> raise Impossible)
2076 *)
2077
2078
2079 let list_to_single_or_exn xs =
2080 match xs with
2081 | [] -> raise Not_found
2082 | x::y::zs -> raise Multi_found
2083 | [x] -> x
2084
2085 (*****************************************************************************)
2086 (* TriBool *)
2087 (*****************************************************************************)
2088
2089 type bool3 = True3 | False3 | TrueFalsePb3 of string
2090
2091
2092
2093 (*****************************************************************************)
2094 (* Regexp, can also use PCRE *)
2095 (*****************************************************************************)
2096
2097 (* Note: OCaml Str regexps are different from Perl regexp:
2098 * - The OCaml regexp must match the entire way.
2099 * So "testBee" =~ "Bee" is wrong
2100 * but "testBee" =~ ".*Bee" is right
2101 * Can have the perl behavior if use Str.search_forward instead of
2102 * Str.string_match.
2103 * - Must add some additional \ in front of some special char. So use
2104 * \\( \\| and also \\b
2105 * - It does not always handle newlines very well.
2106 * - \\b does consider _ but not numbers in indentifiers.
2107 *
2108 * Note: PCRE regexps are then different from Str regexps ...
2109 * - just use '(' ')' for grouping, not '\\)'
2110 * - still need \\b for word boundary, but this time it works ...
2111 * so can match some word that have some digits in them.
2112 *
2113 *)
2114
2115 (* put before String section because String section use some =~ *)
2116
2117 (* let gsubst = global_replace *)
2118
2119
2120 let (==~) s re = Str.string_match re s 0
2121
2122 let _memo_compiled_regexp = Hashtbl.create 101
2123 let candidate_match_func s re =
2124 (* old: Str.string_match (Str.regexp re) s 0 *)
2125 let compile_re =
2126 memoized _memo_compiled_regexp re (fun () -> Str.regexp re)
2127 in
2128 Str.string_match compile_re s 0
2129
2130 let match_func s re =
2131 profile_code "Common.=~" (fun () -> candidate_match_func s re)
2132
2133 let (=~) s re =
2134 match_func s re
2135
2136
2137
2138
2139
2140 let string_match_substring re s =
2141 try let _i = Str.search_forward re s 0 in true
2142 with Not_found -> false
2143
2144 let _ =
2145 example(string_match_substring (Str.regexp "foo") "a foo b")
2146 let _ =
2147 example(string_match_substring (Str.regexp "\\bfoo\\b") "a foo b")
2148 let _ =
2149 example(string_match_substring (Str.regexp "\\bfoo\\b") "a\n\nfoo b")
2150 let _ =
2151 example(string_match_substring (Str.regexp "\\bfoo_bar\\b") "a\n\nfoo_bar b")
2152 (* does not work :(
2153 let _ =
2154 example(string_match_substring (Str.regexp "\\bfoo_bar2\\b") "a\n\nfoo_bar2 b")
2155 *)
2156
2157
2158
2159 let (regexp_match: string -> string -> string) = fun s re ->
2160 assert(s =~ re);
2161 Str.matched_group 1 s
2162
2163 (* beurk, side effect code, but hey, it is convenient *)
2164 (* now in prelude
2165 * let (matched: int -> string -> string) = fun i s ->
2166 * Str.matched_group i s
2167 *
2168 * let matched1 = fun s -> matched 1 s
2169 * let matched2 = fun s -> (matched 1 s, matched 2 s)
2170 * let matched3 = fun s -> (matched 1 s, matched 2 s, matched 3 s)
2171 * let matched4 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s)
2172 * let matched5 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s)
2173 * let matched6 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s, matched 6 s)
2174 *)
2175
2176
2177
2178 let split sep s = Str.split (Str.regexp sep) s
2179 let _ = example (split "/" "" =*= [])
2180 let join sep xs = String.concat sep xs
2181 let _ = example (join "/" ["toto"; "titi"; "tata"] =$= "toto/titi/tata")
2182 (*
2183 let rec join str = function
2184 | [] -> ""
2185 | [x] -> x
2186 | x::xs -> x ^ str ^ (join str xs)
2187 *)
2188
2189
2190 let (split_list_regexp: string -> string list -> (string * string list) list) =
2191 fun re xs ->
2192 let rec split_lr_aux (heading, accu) = function
2193 | [] -> [(heading, List.rev accu)]
2194 | x::xs ->
2195 if x =~ re
2196 then (heading, List.rev accu)::split_lr_aux (x, []) xs
2197 else split_lr_aux (heading, x::accu) xs
2198 in
2199 split_lr_aux ("__noheading__", []) xs
2200 +> (fun xs -> if (List.hd xs) =*= ("__noheading__",[]) then List.tl xs else xs)
2201
2202
2203
2204 let regexp_alpha = Str.regexp
2205 "^[a-zA-Z_][A-Za-z_0-9]*$"
2206
2207
2208 let all_match re s =
2209 let regexp = Str.regexp re in
2210 let res = ref [] in
2211 let _ = Str.global_substitute regexp (fun _s ->
2212 let substr = Str.matched_string s in
2213 assert(substr ==~ regexp); (* @Effect: also use it's side effect *)
2214 let paren_matched = matched1 substr in
2215 push2 paren_matched res;
2216 "" (* @Dummy *)
2217 ) s in
2218 List.rev !res
2219
2220 let _ = example (all_match "\\(@[A-Za-z]+\\)" "ca va @Et toi @Comment"
2221 =*= ["@Et";"@Comment"])
2222
2223
2224 let global_replace_regexp re f_on_substr s =
2225 let regexp = Str.regexp re in
2226 Str.global_substitute regexp (fun _wholestr ->
2227
2228 let substr = Str.matched_string s in
2229 f_on_substr substr
2230 ) s
2231
2232
2233 let regexp_word_str =
2234 "\\([a-zA-Z_][A-Za-z_0-9]*\\)"
2235 let regexp_word = Str.regexp regexp_word_str
2236
2237 let regular_words s =
2238 all_match regexp_word_str s
2239
2240 let contain_regular_word s =
2241 let xs = regular_words s in
2242 List.length xs >= 1
2243
2244
2245
2246 (*****************************************************************************)
2247 (* Strings *)
2248 (*****************************************************************************)
2249
2250 let slength = String.length
2251 let concat = String.concat
2252
2253 (* ruby *)
2254 let i_to_s = string_of_int
2255 let s_to_i = int_of_string
2256
2257
2258 (* strings take space in memory. Better when can share the space used by
2259 similar strings *)
2260 let _shareds = Hashtbl.create 100
2261 let (shared_string: string -> string) = fun s ->
2262 try Hashtbl.find _shareds s
2263 with Not_found -> (Hashtbl.add _shareds s s; s)
2264
2265 let chop = function
2266 | "" -> ""
2267 | s -> String.sub s 0 (String.length s - 1)
2268
2269
2270 let chop_dirsymbol = function
2271 | s when s =~ "\\(.*\\)/$" -> matched1 s
2272 | s -> s
2273
2274
2275 let (<!!>) s (i,j) =
2276 String.sub s i (if j < 0 then String.length s - i + j + 1 else j - i)
2277 (* let _ = example ( "tototati"<!!>(3,-2) = "otat" ) *)
2278
2279 let (<!>) s i = String.get s i
2280
2281 (* pixel *)
2282 let rec split_on_char c s =
2283 try
2284 let sp = String.index s c in
2285 String.sub s 0 sp ::
2286 split_on_char c (String.sub s (sp+1) (String.length s - sp - 1))
2287 with Not_found -> [s]
2288
2289
2290 let lowercase = String.lowercase
2291
2292 let quote s = "\"" ^ s ^ "\""
2293
2294 (* easier to have this to be passed as hof, because ocaml dont have
2295 * haskell "section" operators
2296 *)
2297 let null_string s =
2298 s =$= ""
2299
2300 let is_blank_string s =
2301 s =~ "^\\([ \t]\\)*$"
2302
2303 (* src: lablgtk2/examples/entrycompletion.ml *)
2304 let is_string_prefix s1 s2 =
2305 (String.length s1 <= String.length s2) &&
2306 (String.sub s2 0 (String.length s1) =$= s1)
2307
2308 let plural i s =
2309 if i =|= 1
2310 then Printf.sprintf "%d %s" i s
2311 else Printf.sprintf "%d %ss" i s
2312
2313 let showCodeHex xs = List.iter (fun i -> printf "%02x" i) xs
2314
2315 let take_string n s =
2316 String.sub s 0 (n-1)
2317
2318 let take_string_safe n s =
2319 if n > String.length s
2320 then s
2321 else take_string n s
2322
2323
2324
2325 (* used by LFS *)
2326 let size_mo_ko i =
2327 let ko = (i / 1024) mod 1024 in
2328 let mo = (i / 1024) / 1024 in
2329 (if mo > 0
2330 then sprintf "%dMo%dKo" mo ko
2331 else sprintf "%dKo" ko
2332 )
2333
2334 let size_ko i =
2335 let ko = i / 1024 in
2336 sprintf "%dKo" ko
2337
2338
2339
2340
2341
2342
2343 (* done in summer 2007 for julia
2344 * Reference: P216 of gusfeld book
2345 * For two strings S1 and S2, D(i,j) is defined to be the edit distance of S1[1..i] to S2[1..j]
2346 * So edit distance of S1 (of length n) and S2 (of length m) is D(n,m)
2347 *
2348 * Dynamic programming technique
2349 * base:
2350 * D(i,0) = i for all i (cos to go from S1[1..i] to 0 characteres of S2 you have to delete all characters from S1[1..i]
2351 * D(0,j) = j for all j (cos j characters must be inserted)
2352 * recurrence:
2353 * D(i,j) = min([D(i-1, j)+1, D(i, j - 1 + 1), D(i-1, j-1) + t(i,j)])
2354 * where t(i,j) is equal to 1 if S1(i) != S2(j) and 0 if equal
2355 * intuition = there is 4 possible action = deletion, insertion, substitution, or match
2356 * so Lemma =
2357 *
2358 * D(i,j) must be one of the three
2359 * D(i, j-1) + 1
2360 * D(i-1, j)+1
2361 * D(i-1, j-1) +
2362 * t(i,j)
2363 *
2364 *
2365 *)
2366 let matrix_distance s1 s2 =
2367 let n = (String.length s1) in
2368 let m = (String.length s2) in
2369 let mat = Array.make_matrix (n+1) (m+1) 0 in
2370 let t i j =
2371 if String.get s1 (i-1) =<= String.get s2 (j-1)
2372 then 0
2373 else 1
2374 in
2375 let min3 a b c = min (min a b) c in
2376
2377 begin
2378 for i = 0 to n do
2379 mat.(i).(0) <- i
2380 done;
2381 for j = 0 to m do
2382 mat.(0).(j) <- j;
2383 done;
2384 for i = 1 to n do
2385 for j = 1 to m do
2386 mat.(i).(j) <-
2387 min3 (mat.(i).(j-1) + 1) (mat.(i-1).(j) + 1) (mat.(i-1).(j-1) + t i j)
2388 done
2389 done;
2390 mat
2391 end
2392 let edit_distance s1 s2 =
2393 (matrix_distance s1 s2).(String.length s1).(String.length s2)
2394
2395
2396 let test = edit_distance "vintner" "writers"
2397 let _ = assert (edit_distance "winter" "winter" =|= 0)
2398 let _ = assert (edit_distance "vintner" "writers" =|= 5)
2399
2400
2401 (*****************************************************************************)
2402 (* Filenames *)
2403 (*****************************************************************************)
2404
2405 let dirname = Filename.dirname
2406 let basename = Filename.basename
2407
2408 type filename = string (* TODO could check that exist :) type sux *)
2409 (* with sexp *)
2410 type dirname = string (* TODO could check that exist :) type sux *)
2411 (* with sexp *)
2412
2413 module BasicType = struct
2414 type filename = string
2415 end
2416
2417
2418 let (filesuffix: filename -> string) = fun s ->
2419 (try regexp_match s ".+\\.\\([a-zA-Z0-9_]+\\)$" with _ -> "NOEXT")
2420 let (fileprefix: filename -> string) = fun s ->
2421 (try regexp_match s "\\(.+\\)\\.\\([a-zA-Z0-9_]+\\)?$" with _ -> s)
2422
2423 let _ = example (filesuffix "toto.c" =$= "c")
2424 let _ = example (fileprefix "toto.c" =$= "toto")
2425
2426 (*
2427 assert (s = fileprefix s ^ filesuffix s)
2428
2429 let withoutExtension s = global_replace (regexp "\\..*$") "" s
2430 let () = example "without"
2431 (withoutExtension "toto.s.toto" = "toto")
2432 *)
2433
2434 let adjust_ext_if_needed filename ext =
2435 if String.get ext 0 <> '.'
2436 then failwith "I need an extension such as .c not just c";
2437
2438 if not (filename =~ (".*\\" ^ ext))
2439 then filename ^ ext
2440 else filename
2441
2442
2443
2444 let db_of_filename file =
2445 dirname file, basename file
2446
2447 let filename_of_db (basedir, file) =
2448 Filename.concat basedir file
2449
2450
2451
2452 let dbe_of_filename file =
2453 (* raise Invalid_argument if no ext, so safe to use later the unsafe
2454 * fileprefix and filesuffix functions.
2455 *)
2456 ignore(Filename.chop_extension file);
2457 Filename.dirname file,
2458 Filename.basename file +> fileprefix,
2459 Filename.basename file +> filesuffix
2460
2461 let filename_of_dbe (dir, base, ext) =
2462 Filename.concat dir (base ^ "." ^ ext)
2463
2464
2465 let dbe_of_filename_safe file =
2466 try Left (dbe_of_filename file)
2467 with Invalid_argument _ ->
2468 Right (Filename.dirname file, Filename.basename file)
2469
2470
2471 let dbe_of_filename_nodot file =
2472 let (d,b,e) = dbe_of_filename file in
2473 let d = if d =$= "." then "" else d in
2474 d,b,e
2475
2476
2477
2478
2479
2480 let replace_ext file oldext newext =
2481 let (d,b,e) = dbe_of_filename file in
2482 assert(e =$= oldext);
2483 filename_of_dbe (d,b,newext)
2484
2485
2486 let normalize_path file =
2487 let (dir, filename) = Filename.dirname file, Filename.basename file in
2488 let xs = split "/" dir in
2489 let rec aux acc = function
2490 | [] -> List.rev acc
2491 | x::xs ->
2492 (match x with
2493 | "." -> aux acc xs
2494 | ".." -> aux (List.tl acc) xs
2495 | x -> aux (x::acc) xs
2496 )
2497 in
2498 let xs' = aux [] xs in
2499 Filename.concat (join "/" xs') filename
2500
2501
2502
2503 (*
2504 let relative_to_absolute s =
2505 if Filename.is_relative s
2506 then
2507 begin
2508 let old = Sys.getcwd () in
2509 Sys.chdir s;
2510 let current = Sys.getcwd () in
2511 Sys.chdir old;
2512 s
2513 end
2514 else s
2515 *)
2516
2517 let relative_to_absolute s =
2518 if Filename.is_relative s
2519 then Sys.getcwd () ^ "/" ^ s
2520 else s
2521
2522 let is_relative s = Filename.is_relative s
2523 let is_absolute s = not (is_relative s)
2524
2525
2526 (* @Pre: prj_path must not contain regexp symbol *)
2527 let filename_without_leading_path prj_path s =
2528 let prj_path = chop_dirsymbol prj_path in
2529 if s =~ ("^" ^ prj_path ^ "/\\(.*\\)$")
2530 then matched1 s
2531 else
2532 failwith
2533 (spf "cant find filename_without_project_path: %s %s" prj_path s)
2534
2535
2536 (*****************************************************************************)
2537 (* i18n *)
2538 (*****************************************************************************)
2539 type langage =
2540 | English
2541 | Francais
2542 | Deutsch
2543
2544 (* gettext ? *)
2545
2546
2547 (*****************************************************************************)
2548 (* Dates *)
2549 (*****************************************************************************)
2550
2551 (* maybe I should use ocamlcalendar, but I don't like all those functors ... *)
2552
2553 type month =
2554 | Jan | Feb | Mar | Apr | May | Jun
2555 | Jul | Aug | Sep | Oct | Nov | Dec
2556 type year = Year of int
2557 type day = Day of int
2558 type wday = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday
2559
2560 type date_dmy = DMY of day * month * year
2561
2562 type hour = Hour of int
2563 type minute = Min of int
2564 type second = Sec of int
2565
2566 type time_hms = HMS of hour * minute * second
2567
2568 type full_date = date_dmy * time_hms
2569
2570
2571 (* intervalle *)
2572 type days = Days of int
2573
2574 type time_dmy = TimeDMY of day * month * year
2575
2576
2577 type float_time = float
2578
2579
2580
2581 let check_date_dmy (DMY (day, month, year)) =
2582 raise Todo
2583
2584 let check_time_dmy (TimeDMY (day, month, year)) =
2585 raise Todo
2586
2587 let check_time_hms (HMS (x,y,a)) =
2588 raise Todo
2589
2590
2591
2592 (* ---------------------------------------------------------------------- *)
2593
2594 (* older code *)
2595 let int_to_month i =
2596 assert (i <= 12 && i >= 1);
2597 match i with
2598
2599 | 1 -> "Jan"
2600 | 2 -> "Feb"
2601 | 3 -> "Mar"
2602 | 4 -> "Apr"
2603 | 5 -> "May"
2604 | 6 -> "Jun"
2605 | 7 -> "Jul"
2606 | 8 -> "Aug"
2607 | 9 -> "Sep"
2608 | 10 -> "Oct"
2609 | 11 -> "Nov"
2610 | 12 -> "Dec"
2611 (*
2612 | 1 -> "January"
2613 | 2 -> "February"
2614 | 3 -> "March"
2615 | 4 -> "April"
2616 | 5 -> "May"
2617 | 6 -> "June"
2618 | 7 -> "July"
2619 | 8 -> "August"
2620 | 9 -> "September"
2621 | 10 -> "October"
2622 | 11 -> "November"
2623 | 12 -> "December"
2624 *)
2625 | _ -> raise Impossible
2626
2627
2628 let month_info = [
2629 1 , Jan, "Jan", "January", 31;
2630 2 , Feb, "Feb", "February", 28;
2631 3 , Mar, "Mar", "March", 31;
2632 4 , Apr, "Apr", "April", 30;
2633 5 , May, "May", "May", 31;
2634 6 , Jun, "Jun", "June", 30;
2635 7 , Jul, "Jul", "July", 31;
2636 8 , Aug, "Aug", "August", 31;
2637 9 , Sep, "Sep", "September", 30;
2638 10 , Oct, "Oct", "October", 31;
2639 11 , Nov, "Nov", "November", 30;
2640 12 , Dec, "Dec", "December", 31;
2641 ]
2642
2643 let week_day_info = [
2644 0 , Sunday , "Sun" , "Dim" , "Sunday";
2645 1 , Monday , "Mon" , "Lun" , "Monday";
2646 2 , Tuesday , "Tue" , "Mar" , "Tuesday";
2647 3 , Wednesday , "Wed" , "Mer" , "Wednesday";
2648 4 , Thursday , "Thu" ,"Jeu" ,"Thursday";
2649 5 , Friday , "Fri" , "Ven" , "Friday";
2650 6 , Saturday , "Sat" ,"Sam" , "Saturday";
2651 ]
2652
2653 let i_to_month_h =
2654 month_info +> List.map (fun (i,month,monthstr,mlong,days) -> i, month)
2655 let s_to_month_h =
2656 month_info +> List.map (fun (i,month,monthstr,mlong,days) -> monthstr, month)
2657 let slong_to_month_h =
2658 month_info +> List.map (fun (i,month,monthstr,mlong,days) -> mlong, month)
2659 let month_to_s_h =
2660 month_info +> List.map (fun (i,month,monthstr,mlong,days) -> month, monthstr)
2661 let month_to_i_h =
2662 month_info +> List.map (fun (i,month,monthstr,mlong,days) -> month, i)
2663
2664 let i_to_wday_h =
2665 week_day_info +> List.map (fun (i,day,dayen,dayfr,daylong) -> i, day)
2666 let wday_to_en_h =
2667 week_day_info +> List.map (fun (i,day,dayen,dayfr,daylong) -> day, dayen)
2668 let wday_to_fr_h =
2669 week_day_info +> List.map (fun (i,day,dayen,dayfr,daylong) -> day, dayfr)
2670
2671 let month_of_string s =
2672 List.assoc s s_to_month_h
2673
2674 let month_of_string_long s =
2675 List.assoc s slong_to_month_h
2676
2677 let string_of_month s =
2678 List.assoc s month_to_s_h
2679
2680 let month_of_int i =
2681 List.assoc i i_to_month_h
2682
2683 let int_of_month m =
2684 List.assoc m month_to_i_h
2685
2686
2687 let wday_of_int i =
2688 List.assoc i i_to_wday_h
2689
2690 let string_en_of_wday wday =
2691 List.assoc wday wday_to_en_h
2692 let string_fr_of_wday wday =
2693 List.assoc wday wday_to_fr_h
2694
2695 (* ---------------------------------------------------------------------- *)
2696
2697 let wday_str_of_int ~langage i =
2698 let wday = wday_of_int i in
2699 match langage with
2700 | English -> string_en_of_wday wday
2701 | Francais -> string_fr_of_wday wday
2702 | Deutsch -> raise Todo
2703
2704
2705
2706 let string_of_date_dmy (DMY (Day n, month, Year y)) =
2707 (spf "%02d-%s-%d" n (string_of_month month) y)
2708
2709
2710 let string_of_unix_time ?(langage=English) tm =
2711 let y = tm.Unix.tm_year + 1900 in
2712 let mon = string_of_month (month_of_int (tm.Unix.tm_mon + 1)) in
2713 let d = tm.Unix.tm_mday in
2714 let h = tm.Unix.tm_hour in
2715 let min = tm.Unix.tm_min in
2716 let s = tm.Unix.tm_sec in
2717
2718 let wday = wday_str_of_int ~langage tm.Unix.tm_wday in
2719
2720 spf "%02d/%03s/%04d (%s) %02d:%02d:%02d" d mon y wday h min s
2721
2722 (* ex: 21/Jul/2008 (Lun) 21:25:12 *)
2723 let unix_time_of_string s =
2724 if s =~
2725 ("\\([0-9][0-9]\\)/\\(...\\)/\\([0-9][0-9][0-9][0-9]\\) " ^
2726 "\\(.*\\) \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)")
2727 then
2728 let (sday, smonth, syear, _sday, shour, smin, ssec) = matched7 s in
2729
2730 let y = s_to_i syear - 1900 in
2731 let mon =
2732 smonth +> month_of_string +> int_of_month +> (fun i -> i -1)
2733 in
2734
2735 let tm = Unix.localtime (Unix.time ()) in
2736 { tm with
2737 Unix.tm_year = y;
2738 Unix.tm_mon = mon;
2739 Unix.tm_mday = s_to_i sday;
2740 Unix.tm_hour = s_to_i shour;
2741 Unix.tm_min = s_to_i smin;
2742 Unix.tm_sec = s_to_i ssec;
2743 }
2744 else failwith ("unix_time_of_string: " ^ s)
2745
2746
2747
2748 let short_string_of_unix_time ?(langage=English) tm =
2749 let y = tm.Unix.tm_year + 1900 in
2750 let mon = string_of_month (month_of_int (tm.Unix.tm_mon + 1)) in
2751 let d = tm.Unix.tm_mday in
2752 let _h = tm.Unix.tm_hour in
2753 let _min = tm.Unix.tm_min in
2754 let _s = tm.Unix.tm_sec in
2755
2756 let wday = wday_str_of_int ~langage tm.Unix.tm_wday in
2757
2758 spf "%02d/%03s/%04d (%s)" d mon y wday
2759
2760
2761 let string_of_unix_time_lfs time =
2762 spf "%02d--%s--%d"
2763 time.Unix.tm_mday
2764 (int_to_month (time.Unix.tm_mon + 1))
2765 (time.Unix.tm_year + 1900)
2766
2767
2768 (* ---------------------------------------------------------------------- *)
2769 let string_of_floattime ?langage i =
2770 let tm = Unix.localtime i in
2771 string_of_unix_time ?langage tm
2772
2773 let short_string_of_floattime ?langage i =
2774 let tm = Unix.localtime i in
2775 short_string_of_unix_time ?langage tm
2776
2777 let floattime_of_string s =
2778 let tm = unix_time_of_string s in
2779 let (sec,_tm) = Unix.mktime tm in
2780 sec
2781
2782
2783 (* ---------------------------------------------------------------------- *)
2784 let days_in_week_of_day day =
2785 let tm = Unix.localtime day in
2786
2787 let wday = tm.Unix.tm_wday in
2788 let wday = if wday =|= 0 then 6 else wday -1 in
2789
2790 let mday = tm.Unix.tm_mday in
2791
2792 let start_d = mday - wday in
2793 let end_d = mday + (6 - wday) in
2794
2795 enum start_d end_d +> List.map (fun mday ->
2796 Unix.mktime {tm with Unix.tm_mday = mday} +> fst
2797 )
2798
2799 let first_day_in_week_of_day day =
2800 List.hd (days_in_week_of_day day)
2801
2802 let last_day_in_week_of_day day =
2803 last (days_in_week_of_day day)
2804
2805
2806 (* ---------------------------------------------------------------------- *)
2807
2808 (* (modified) copy paste from ocamlcalendar/src/date.ml *)
2809 let days_month =
2810 [| 0; 31; 59; 90; 120; 151; 181; 212; 243; 273; 304; 334(*; 365*) |]
2811
2812
2813 let rough_days_since_jesus (DMY (Day nday, month, Year year)) =
2814 let n =
2815 nday +
2816 (days_month.(int_of_month month -1)) +
2817 year * 365
2818 in
2819 Days n
2820
2821
2822
2823 let is_more_recent d1 d2 =
2824 let (Days n1) = rough_days_since_jesus d1 in
2825 let (Days n2) = rough_days_since_jesus d2 in
2826 (n1 > n2)
2827
2828
2829 let max_dmy d1 d2 =
2830 if is_more_recent d1 d2
2831 then d1
2832 else d2
2833
2834 let min_dmy d1 d2 =
2835 if is_more_recent d1 d2
2836 then d2
2837 else d1
2838
2839
2840 let maximum_dmy ds =
2841 foldl1 max_dmy ds
2842
2843 let minimum_dmy ds =
2844 foldl1 min_dmy ds
2845
2846
2847
2848 let rough_days_between_dates d1 d2 =
2849 let (Days n1) = rough_days_since_jesus d1 in
2850 let (Days n2) = rough_days_since_jesus d2 in
2851 Days (n2 - n1)
2852
2853 let _ = example
2854 (rough_days_between_dates
2855 (DMY (Day 7, Jan, Year 1977))
2856 (DMY (Day 13, Jan, Year 1977)) =*= Days 6)
2857
2858 (* because of rough days, it is a bit buggy, here it should return 1 *)
2859 (*
2860 let _ = assert_equal
2861 (rough_days_between_dates
2862 (DMY (Day 29, Feb, Year 1977))
2863 (DMY (Day 1, Mar , Year 1977)))
2864 (Days 1)
2865 *)
2866
2867
2868 (* from julia, in gitsort.ml *)
2869
2870 (*
2871 let antimonths =
2872 [(1,31);(2,28);(3,31);(4,30);(5,31); (6,6);(7,7);(8,31);(9,30);(10,31);
2873 (11,30);(12,31);(0,31)]
2874
2875 let normalize (year,month,day,hour,minute,second) =
2876 if hour < 0
2877 then
2878 let (day,hour) = (day - 1,hour + 24) in
2879 if day = 0
2880 then
2881 let month = month - 1 in
2882 let day = List.assoc month antimonths in
2883 let day =
2884 if month = 2 && year / 4 * 4 = year && not (year / 100 * 100 = year)
2885 then 29
2886 else day in
2887 if month = 0
2888 then (year-1,12,day,hour,minute,second)
2889 else (year,month,day,hour,minute,second)
2890 else (year,month,day,hour,minute,second)
2891 else (year,month,day,hour,minute,second)
2892
2893 *)
2894
2895
2896 let mk_date_dmy day month year =
2897 let date = DMY (Day day, month_of_int month, Year year) in
2898 (* check_date_dmy date *)
2899 date
2900
2901
2902 (* ---------------------------------------------------------------------- *)
2903 (* conversion to unix.tm *)
2904
2905 let dmy_to_unixtime (DMY (Day n, month, Year year)) =
2906 let tm = {
2907 Unix.tm_sec = 0; (** Seconds 0..60 *)
2908 tm_min = 0; (** Minutes 0..59 *)
2909 tm_hour = 12; (** Hours 0..23 *)
2910 tm_mday = n; (** Day of month 1..31 *)
2911 tm_mon = (int_of_month month -1); (** Month of year 0..11 *)
2912 tm_year = year - 1900; (** Year - 1900 *)
2913 tm_wday = 0; (** Day of week (Sunday is 0) *)
2914 tm_yday = 0; (** Day of year 0..365 *)
2915 tm_isdst = false; (** Daylight time savings in effect *)
2916 } in
2917 Unix.mktime tm
2918
2919 let unixtime_to_dmy tm =
2920 let n = tm.Unix.tm_mday in
2921 let month = month_of_int (tm.Unix.tm_mon + 1) in
2922 let year = tm.Unix.tm_year + 1900 in
2923
2924 DMY (Day n, month, Year year)
2925
2926
2927 let unixtime_to_floattime tm =
2928 Unix.mktime tm +> fst
2929
2930 let floattime_to_unixtime sec =
2931 Unix.localtime sec
2932
2933
2934 let sec_to_days sec =
2935 let minfactor = 60 in
2936 let hourfactor = 60 * 60 in
2937 let dayfactor = 60 * 60 * 24 in
2938
2939 let days = sec / dayfactor in
2940 let hours = (sec mod dayfactor) / hourfactor in
2941 let mins = (sec mod hourfactor) / minfactor in
2942 let sec = (sec mod 60) in
2943 (* old: Printf.sprintf "%d days, %d hours, %d minutes" days hours mins *)
2944 (if days > 0 then plural days "day" ^ " " else "") ^
2945 (if hours > 0 then plural hours "hour" ^ " " else "") ^
2946 (if mins > 0 then plural mins "min" ^ " " else "") ^
2947 (spf "%dsec" sec)
2948
2949 let sec_to_hours sec =
2950 let minfactor = 60 in
2951 let hourfactor = 60 * 60 in
2952
2953 let hours = sec / hourfactor in
2954 let mins = (sec mod hourfactor) / minfactor in
2955 let sec = (sec mod 60) in
2956 (* old: Printf.sprintf "%d days, %d hours, %d minutes" days hours mins *)
2957 (if hours > 0 then plural hours "hour" ^ " " else "") ^
2958 (if mins > 0 then plural mins "min" ^ " " else "") ^
2959 (spf "%dsec" sec)
2960
2961
2962
2963 let test_date_1 () =
2964 let date = DMY (Day 17, Sep, Year 1991) in
2965 let float, tm = dmy_to_unixtime date in
2966 pr2 (spf "date: %.0f" float);
2967 ()
2968
2969
2970 (* src: ferre in logfun/.../date.ml *)
2971
2972 let day_secs : float = 86400.
2973
2974 let today : unit -> float = fun () -> (Unix.time () )
2975 let yesterday : unit -> float = fun () -> (Unix.time () -. day_secs)
2976 let tomorrow : unit -> float = fun () -> (Unix.time () +. day_secs)
2977
2978 let lastweek : unit -> float = fun () -> (Unix.time () -. (7.0 *. day_secs))
2979 let lastmonth : unit -> float = fun () -> (Unix.time () -. (30.0 *. day_secs))
2980
2981
2982 let week_before : float_time -> float_time = fun d ->
2983 (d -. (7.0 *. day_secs))
2984 let month_before : float_time -> float_time = fun d ->
2985 (d -. (30.0 *. day_secs))
2986
2987 let week_after : float_time -> float_time = fun d ->
2988 (d +. (7.0 *. day_secs))
2989
2990
2991
2992 (*****************************************************************************)
2993 (* Lines/words/strings *)
2994 (*****************************************************************************)
2995
2996 (* now in prelude:
2997 * let (list_of_string: string -> char list) = fun s ->
2998 * (enum 0 ((String.length s) - 1) +> List.map (String.get s))
2999 *)
3000
3001 let _ = example (list_of_string "abcd" =*= ['a';'b';'c';'d'])
3002
3003 (*
3004 let rec (list_of_stream: ('a Stream.t) -> 'a list) =
3005 parser
3006 | [< 'c ; stream >] -> c :: list_of_stream stream
3007 | [<>] -> []
3008
3009 let (list_of_string: string -> char list) =
3010 Stream.of_string $ list_of_stream
3011 *)
3012
3013 (* now in prelude:
3014 * let (lines: string -> string list) = fun s -> ...
3015 *)
3016
3017 let (lines_with_nl: string -> string list) = fun s ->
3018 let rec lines_aux = function
3019 | [] -> []
3020 | [x] -> if x =$= "" then [] else [x ^ "\n"] (* old: [x] *)
3021 | x::xs ->
3022 let e = x ^ "\n" in
3023 e::lines_aux xs
3024 in
3025 (time_func (fun () -> Str.split_delim (Str.regexp "\n") s)) +> lines_aux
3026
3027 (* in fact better make it return always complete lines, simplify *)
3028 (* Str.split, but lines "\n1\n2\n" dont return the \n and forget the first \n => split_delim better than split *)
3029 (* +> List.map (fun s -> s ^ "\n") but add an \n even at the end => lines_aux *)
3030 (* old: slow
3031 let chars = list_of_string s in
3032 chars +> List.fold_left (fun (acc, lines) char ->
3033 let newacc = acc ^ (String.make 1 char) in
3034 if char = '\n'
3035 then ("", newacc::lines)
3036 else (newacc, lines)
3037 ) ("", [])
3038 +> (fun (s, lines) -> List.rev (s::lines))
3039 *)
3040
3041 (* CHECK: unlines (lines x) = x *)
3042 let (unlines: string list -> string) = fun s ->
3043 (String.concat "\n" s) ^ "\n"
3044 let (words: string -> string list) = fun s ->
3045 Str.split (Str.regexp "[ \t()\";]+") s
3046 let (unwords: string list -> string) = fun s ->
3047 String.concat "" s
3048
3049 let (split_space: string -> string list) = fun s ->
3050 Str.split (Str.regexp "[ \t\n]+") s
3051
3052
3053 (* todo opti ? *)
3054 let nblines s =
3055 lines s +> List.length
3056 let _ = example (nblines "" =|= 0)
3057 let _ = example (nblines "toto" =|= 1)
3058 let _ = example (nblines "toto\n" =|= 1)
3059 let _ = example (nblines "toto\ntata" =|= 2)
3060 let _ = example (nblines "toto\ntata\n" =|= 2)
3061
3062 (*****************************************************************************)
3063 (* Process/Files *)
3064 (*****************************************************************************)
3065 let cat_orig file =
3066 let chan = open_in file in
3067 let rec cat_orig_aux () =
3068 try
3069 (* cant do input_line chan::aux() cos ocaml eval from right to left ! *)
3070 let l = input_line chan in
3071 l :: cat_orig_aux ()
3072 with End_of_file -> [] in
3073 cat_orig_aux()
3074
3075 (* tail recursive efficient version *)
3076 let cat file =
3077 let chan = open_in file in
3078 let rec cat_aux acc () =
3079 (* cant do input_line chan::aux() cos ocaml eval from right to left ! *)
3080 let (b, l) = try (true, input_line chan) with End_of_file -> (false, "") in
3081 if b
3082 then cat_aux (l::acc) ()
3083 else acc
3084 in
3085 cat_aux [] () +> List.rev +> (fun x -> close_in chan; x)
3086
3087 let cat_array file =
3088 (""::cat file) +> Array.of_list
3089
3090
3091 let interpolate str =
3092 begin
3093 command2 ("printf \"%s\\n\" " ^ str ^ ">/tmp/caml");
3094 cat "/tmp/caml"
3095 end
3096
3097 (* could do a print_string but printf dont like print_string *)
3098 let echo s = printf "%s" s; flush stdout; s
3099
3100 let usleep s = for i = 1 to s do () done
3101
3102 let sleep_little () =
3103 (*old: *)
3104 Unix.sleep 1
3105 (*ignore(Sys.command ("usleep " ^ !_sleep_time))*)
3106
3107
3108 (* now in prelude:
3109 * let command2 s = ignore(Sys.command s)
3110 *)
3111
3112 let do_in_fork f =
3113 let pid = Unix.fork () in
3114 if pid =|= 0
3115 then
3116 begin
3117 (* Unix.setsid(); *)
3118 Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ ->
3119 pr2 "being killed";
3120 Unix.kill 0 Sys.sigkill;
3121 ));
3122 f();
3123 exit 0;
3124 end
3125 else pid
3126
3127
3128 let process_output_to_list2 = fun command ->
3129 let chan = Unix.open_process_in command in
3130 let res = ref ([] : string list) in
3131 let rec process_otl_aux () =
3132 let e = input_line chan in
3133 res := e::!res;
3134 process_otl_aux() in
3135 try process_otl_aux ()
3136 with End_of_file ->
3137 let stat = Unix.close_process_in chan in (List.rev !res,stat)
3138 let cmd_to_list command =
3139 let (l,_) = process_output_to_list2 command in l
3140 let process_output_to_list = cmd_to_list
3141 let cmd_to_list_and_status = process_output_to_list2
3142
3143 (* now in prelude:
3144 * let command2 s = ignore(Sys.command s)
3145 *)
3146
3147
3148 let _batch_mode = ref false
3149 let command2_y_or_no cmd =
3150 if !_batch_mode then begin command2 cmd; true end
3151 else begin
3152
3153 pr2 (cmd ^ " [y/n] ?");
3154 match read_line () with
3155 | "y" | "yes" | "Y" -> command2 cmd; true
3156 | "n" | "no" | "N" -> false
3157 | _ -> failwith "answer by yes or no"
3158 end
3159
3160 let command2_y_or_no_exit_if_no cmd =
3161 let res = command2_y_or_no cmd in
3162 if res
3163 then ()
3164 else raise (UnixExit (1))
3165
3166
3167
3168
3169 let mkdir ?(mode=0o770) file =
3170 Unix.mkdir file mode
3171
3172 let read_file_orig file = cat file +> unlines
3173 let read_file file =
3174 let ic = open_in file in
3175 let size = in_channel_length ic in
3176 let buf = String.create size in
3177 really_input ic buf 0 size;
3178 close_in ic;
3179 buf
3180
3181
3182 let write_file ~file s =
3183 let chan = open_out file in
3184 (output_string chan s; close_out chan)
3185
3186 let filesize file =
3187 (Unix.stat file).Unix.st_size
3188
3189 let filemtime file =
3190 (Unix.stat file).Unix.st_mtime
3191
3192 (* opti? use wc -l ? *)
3193 let nblines_file file =
3194 cat file +> List.length
3195
3196 let lfile_exists filename =
3197 try
3198 (match (Unix.lstat filename).Unix.st_kind with
3199 | (Unix.S_REG | Unix.S_LNK) -> true
3200 | _ -> false
3201 )
3202 with Unix.Unix_error (Unix.ENOENT, _, _) -> false
3203
3204 let is_directory file =
3205 (Unix.stat file).Unix.st_kind =*= Unix.S_DIR
3206
3207
3208 (* src: from chailloux et al book *)
3209 let capsule_unix f args =
3210 try (f args)
3211 with Unix.Unix_error (e, fm, argm) ->
3212 log (Printf.sprintf "exn Unix_error: %s %s %s\n" (Unix.error_message e) fm argm)
3213
3214
3215 let (readdir_to_kind_list: string -> Unix.file_kind -> string list) =
3216 fun path kind ->
3217 Sys.readdir path
3218 +> Array.to_list
3219 +> List.filter (fun s ->
3220 try
3221 let stat = Unix.lstat (path ^ "/" ^ s) in
3222 stat.Unix.st_kind =*= kind
3223 with e ->
3224 pr2 ("EXN pb stating file: " ^ s);
3225 false
3226 )
3227
3228 let (readdir_to_dir_list: string -> string list) = fun path ->
3229 readdir_to_kind_list path Unix.S_DIR
3230
3231 let (readdir_to_file_list: string -> string list) = fun path ->
3232 readdir_to_kind_list path Unix.S_REG
3233
3234 let (readdir_to_link_list: string -> string list) = fun path ->
3235 readdir_to_kind_list path Unix.S_LNK
3236
3237
3238 let (readdir_to_dir_size_list: string -> (string * int) list) = fun path ->
3239 Sys.readdir path
3240 +> Array.to_list
3241 +> map_filter (fun s ->
3242 let stat = Unix.lstat (path ^ "/" ^ s) in
3243 if stat.Unix.st_kind =*= Unix.S_DIR
3244 then Some (s, stat.Unix.st_size)
3245 else None
3246 )
3247
3248 (* could be in control section too *)
3249
3250 (* Why a use_cache argument ? because sometimes want disable it but dont
3251 * want put the cache_computation funcall in comment, so just easier to
3252 * pass this extra option.
3253 *)
3254 let cache_computation2 ?(verbose=false) ?(use_cache=true) file ext_cache f =
3255 if not use_cache
3256 then f ()
3257 else begin
3258 if not (Sys.file_exists file)
3259 then failwith ("can't find: " ^ file);
3260 let file_cache = (file ^ ext_cache) in
3261 if Sys.file_exists file_cache &&
3262 filemtime file_cache >= filemtime file
3263 then begin
3264 if verbose then pr2 ("using cache: " ^ file_cache);
3265 get_value file_cache
3266 end
3267 else begin
3268 let res = f () in
3269 write_value res file_cache;
3270 res
3271 end
3272 end
3273 let cache_computation ?verbose ?use_cache a b c =
3274 profile_code "Common.cache_computation" (fun () ->
3275 cache_computation2 ?verbose ?use_cache a b c)
3276
3277
3278 let cache_computation_robust2
3279 file ext_cache
3280 (need_no_changed_files, need_no_changed_variables) ext_depend
3281 f =
3282 if not (Sys.file_exists file)
3283 then failwith ("can't find: " ^ file);
3284
3285 let file_cache = (file ^ ext_cache) in
3286 let dependencies_cache = (file ^ ext_depend) in
3287
3288 let dependencies =
3289 (* could do md5sum too *)
3290 ((file::need_no_changed_files) +> List.map (fun f -> f, filemtime f),
3291 need_no_changed_variables)
3292 in
3293
3294 if Sys.file_exists dependencies_cache &&
3295 get_value dependencies_cache =*= dependencies
3296 then get_value file_cache
3297 else begin
3298 pr2 ("cache computation recompute " ^ file);
3299 let res = f () in
3300 write_value dependencies dependencies_cache;
3301 write_value res file_cache;
3302 res
3303 end
3304
3305 let cache_computation_robust a b c d e =
3306 profile_code "Common.cache_computation_robust" (fun () ->
3307 cache_computation_robust2 a b c d e)
3308
3309
3310
3311
3312 (* dont forget that cmd_to_list call bash and so pattern may contain
3313 * '*' symbols that will be expanded, so can do glob "*.c"
3314 *)
3315 let glob pattern =
3316 cmd_to_list ("ls -1 " ^ pattern)
3317
3318
3319 (* update: have added the -type f, so normally need less the sanity_check_xxx
3320 * function below *)
3321 let files_of_dir_or_files ext xs =
3322 xs +> List.map (fun x ->
3323 if is_directory x
3324 then cmd_to_list ("find " ^ x ^" -noleaf -type f -name \"*." ^ext^"\"")
3325 else [x]
3326 ) +> List.concat
3327
3328
3329 let files_of_dir_or_files_no_vcs ext xs =
3330 xs +> List.map (fun x ->
3331 if is_directory x
3332 then
3333 cmd_to_list
3334 ("find " ^ x ^" -noleaf -type f -name \"*." ^ext^"\"" ^
3335 "| grep -v /.hg/ |grep -v /CVS/ | grep -v /.git/ |grep -v /_darcs/"
3336 )
3337 else [x]
3338 ) +> List.concat
3339
3340
3341 let files_of_dir_or_files_no_vcs_post_filter regex xs =
3342 xs +> List.map (fun x ->
3343 if is_directory x
3344 then
3345 cmd_to_list
3346 ("find " ^ x ^
3347 " -noleaf -type f | grep -v /.hg/ |grep -v /CVS/ | grep -v /.git/ |grep -v /_darcs/"
3348 )
3349 +> List.filter (fun s -> s =~ regex)
3350 else [x]
3351 ) +> List.concat
3352
3353
3354 let sanity_check_files_and_adjust ext files =
3355 let files = files +> List.filter (fun file ->
3356 if not (file =~ (".*\\."^ext))
3357 then begin
3358 pr2 ("warning: seems not a ."^ext^" file");
3359 false
3360 end
3361 else
3362 if is_directory file
3363 then begin
3364 pr2 (spf "warning: %s is a directory" file);
3365 false
3366 end
3367 else true
3368 ) in
3369 files
3370
3371
3372
3373
3374 (* taken from mlfuse, the predecessor of ocamlfuse *)
3375 type rwx = [`R|`W|`X] list
3376 let file_perm_of : u:rwx -> g:rwx -> o:rwx -> Unix.file_perm =
3377 fun ~u ~g ~o ->
3378 let to_oct l =
3379 List.fold_left (fun acc p -> acc lor ((function `R -> 4 | `W -> 2 | `X -> 1) p)) 0 l in
3380 let perm =
3381 ((to_oct u) lsl 6) lor
3382 ((to_oct g) lsl 3) lor
3383 (to_oct o)
3384 in
3385 perm
3386
3387
3388 (* pixel *)
3389 let has_env var =
3390 try
3391 let _ = Sys.getenv var in true
3392 with Not_found -> false
3393
3394 (* emacs/lisp inspiration (eric cooper and yaron minsky use that too) *)
3395 let (with_open_outfile: filename -> (((string -> unit) * out_channel) -> 'a) -> 'a) =
3396 fun file f ->
3397 let chan = open_out file in
3398 let pr s = output_string chan s in
3399 unwind_protect (fun () ->
3400 let res = f (pr, chan) in
3401 close_out chan;
3402 res)
3403 (fun e -> close_out chan)
3404
3405 let (with_open_infile: filename -> ((in_channel) -> 'a) -> 'a) = fun file f ->
3406 let chan = open_in file in
3407 unwind_protect (fun () ->
3408 let res = f chan in
3409 close_in chan;
3410 res)
3411 (fun e -> close_in chan)
3412
3413
3414 let (with_open_outfile_append: filename -> (((string -> unit) * out_channel) -> 'a) -> 'a) =
3415 fun file f ->
3416 let chan = open_out_gen [Open_creat;Open_append] 0o666 file in
3417 let pr s = output_string chan s in
3418 unwind_protect (fun () ->
3419 let res = f (pr, chan) in
3420 close_out chan;
3421 res)
3422 (fun e -> close_out chan)
3423
3424
3425 (* now in prelude:
3426 * exception Timeout
3427 *)
3428
3429 (* it seems that the toplevel block such signals, even with this explicit
3430 * command :(
3431 * let _ = Unix.sigprocmask Unix.SIG_UNBLOCK [Sys.sigalrm]
3432 *)
3433
3434 (* could be in Control section *)
3435
3436 (* subtil: have to make sure that timeout is not intercepted before here, so
3437 * avoid exn handle such as try (...) with _ -> cos timeout will not bubble up
3438 * enough. In such case, add a case before such as
3439 * with Timeout -> raise Timeout | _ -> ...
3440 *
3441 * question: can we have a signal and so exn when in a exn handler ?
3442 *)
3443 let timeout_function timeoutval = fun f ->
3444 try
3445 begin
3446 Sys.set_signal Sys.sigalrm (Sys.Signal_handle (fun _ -> raise Timeout ));
3447 ignore(Unix.alarm timeoutval);
3448 let x = f() in
3449 ignore(Unix.alarm 0);
3450 x
3451 end
3452 with Timeout ->
3453 begin
3454 log "timeout (we abort)";
3455 raise Timeout;
3456 end
3457 | e ->
3458 (* subtil: important to disable the alarm before relaunching the exn,
3459 * otherwise the alarm is still running.
3460 *
3461 * robust?: and if alarm launched after the log (...) ?
3462 * Maybe signals are disabled when process an exception handler ?
3463 *)
3464 begin
3465 ignore(Unix.alarm 0);
3466 (* log ("exn while in transaction (we abort too, even if ...) = " ^
3467 Printexc.to_string e);
3468 *)
3469 log "exn while in timeout_function";
3470 raise e
3471 end
3472
3473 let timeout_function_opt timeoutvalopt f =
3474 match timeoutvalopt with
3475 | None -> f()
3476 | Some x -> timeout_function x f
3477
3478
3479
3480 (* creation of tmp files, a la gcc *)
3481
3482 let _temp_files_created = ref []
3483
3484 (* ex: new_temp_file "cocci" ".c" will give "/tmp/cocci-3252-434465.c" *)
3485 let new_temp_file prefix suffix =
3486 let processid = i_to_s (Unix.getpid ()) in
3487 let tmp_file = Filename.temp_file (prefix ^ "-" ^ processid ^ "-") suffix in
3488 push2 tmp_file _temp_files_created;
3489 tmp_file
3490
3491
3492 let save_tmp_files = ref false
3493 let erase_temp_files () =
3494 if not !save_tmp_files then begin
3495 !_temp_files_created +> List.iter (fun s ->
3496 (* pr2 ("erasing: " ^ s); *)
3497 command2 ("rm -f " ^ s)
3498 );
3499 _temp_files_created := []
3500 end
3501
3502 (* now in prelude: exception UnixExit of int *)
3503 let exn_to_real_unixexit f =
3504 try f()
3505 with UnixExit x -> exit x
3506
3507
3508
3509
3510 let uncat xs file =
3511 with_open_outfile file (fun (pr,_chan) ->
3512 xs +> List.iter (fun s -> pr s; pr "\n");
3513
3514 )
3515
3516
3517
3518
3519
3520
3521 (*****************************************************************************)
3522 (* List *)
3523 (*****************************************************************************)
3524
3525 (* pixel *)
3526 let uncons l = (List.hd l, List.tl l)
3527
3528 (* pixel *)
3529 let safe_tl l = try List.tl l with _ -> []
3530
3531 let push l v =
3532 l := v :: !l
3533
3534 let rec zip xs ys =
3535 match (xs,ys) with
3536 | ([],[]) -> []
3537 | ([],_) -> failwith "zip: not same length"
3538 | (_,[]) -> failwith "zip: not same length"
3539 | (x::xs,y::ys) -> (x,y)::zip xs ys
3540
3541 let rec zip_safe xs ys =
3542 match (xs,ys) with
3543 | ([],_) -> []
3544 | (_,[]) -> []
3545 | (x::xs,y::ys) -> (x,y)::zip_safe xs ys
3546
3547 let rec unzip zs =
3548 List.fold_right (fun e (xs, ys) ->
3549 (fst e::xs), (snd e::ys)) zs ([],[])
3550
3551
3552 let map_withkeep f xs =
3553 xs +> List.map (fun x -> f x, x)
3554
3555 (* now in prelude
3556 * let rec take n xs =
3557 * match (n,xs) with
3558 * | (0,_) -> []
3559 * | (_,[]) -> failwith "take: not enough"
3560 * | (n,x::xs) -> x::take (n-1) xs
3561 *)
3562
3563 let rec take_safe n xs =
3564 match (n,xs) with
3565 | (0,_) -> []
3566 | (_,[]) -> []
3567 | (n,x::xs) -> x::take_safe (n-1) xs
3568
3569 let rec take_until p = function
3570 | [] -> []
3571 | x::xs -> if p x then [] else x::(take_until p xs)
3572
3573 let take_while p = take_until (p $ not)
3574
3575
3576 (* now in prelude: let rec drop n xs = ... *)
3577 let _ = example (drop 3 [1;2;3;4] =*= [4])
3578
3579 let rec drop_while p = function
3580 | [] -> []
3581 | x::xs -> if p x then drop_while p xs else x::xs
3582
3583
3584 let rec drop_until p xs =
3585 drop_while (fun x -> not (p x)) xs
3586 let _ = example (drop_until (fun x -> x =|= 3) [1;2;3;4;5] =*= [3;4;5])
3587
3588
3589 let span p xs = (take_while p xs, drop_while p xs)
3590
3591
3592 let rec (span: ('a -> bool) -> 'a list -> 'a list * 'a list) =
3593 fun p -> function
3594 | [] -> ([], [])
3595 | x::xs ->
3596 if p x then
3597 let (l1, l2) = span p xs in
3598 (x::l1, l2)
3599 else ([], x::xs)
3600 let _ = example ((span (fun x -> x <= 3) [1;2;3;4;1;2] =*= ([1;2;3],[4;1;2])))
3601
3602 let rec groupBy eq l =
3603 match l with
3604 | [] -> []
3605 | x::xs ->
3606 let (xs1,xs2) = List.partition (fun x' -> eq x x') xs in
3607 (x::xs1)::(groupBy eq xs2)
3608
3609 let rec group_by_mapped_key fkey l =
3610 match l with
3611 | [] -> []
3612 | x::xs ->
3613 let k = fkey x in
3614 let (xs1,xs2) = List.partition (fun x' -> let k2 = fkey x' in k=*=k2) xs
3615 in
3616 (k, (x::xs1))::(group_by_mapped_key fkey xs2)
3617
3618
3619
3620
3621 let (exclude_but_keep_attached: ('a -> bool) -> 'a list -> ('a * 'a list) list)=
3622 fun f xs ->
3623 let rec aux_filter acc = function
3624 | [] -> [] (* drop what was accumulated because nothing to attach to *)
3625 | x::xs ->
3626 if f x
3627 then aux_filter (x::acc) xs
3628 else (x, List.rev acc)::aux_filter [] xs
3629 in
3630 aux_filter [] xs
3631 let _ = example
3632 (exclude_but_keep_attached (fun x -> x =|= 3) [3;3;1;3;2;3;3;3] =*=
3633 [(1,[3;3]);(2,[3])])
3634
3635 let (group_by_post: ('a -> bool) -> 'a list -> ('a list * 'a) list * 'a list)=
3636 fun f xs ->
3637 let rec aux_filter grouped_acc acc = function
3638 | [] ->
3639 List.rev grouped_acc, List.rev acc
3640 | x::xs ->
3641 if f x
3642 then
3643 aux_filter ((List.rev acc,x)::grouped_acc) [] xs
3644 else
3645 aux_filter grouped_acc (x::acc) xs
3646 in
3647 aux_filter [] [] xs
3648
3649 let _ = example
3650 (group_by_post (fun x -> x =|= 3) [1;1;3;2;3;4;5;3;6;6;6] =*=
3651 ([([1;1],3);([2],3);[4;5],3], [6;6;6]))
3652
3653 let (group_by_pre: ('a -> bool) -> 'a list -> 'a list * ('a * 'a list) list)=
3654 fun f xs ->
3655 let xs' = List.rev xs in
3656 let (ys, unclassified) = group_by_post f xs' in
3657 List.rev unclassified,
3658 ys +> List.rev +> List.map (fun (xs, x) -> x, List.rev xs )
3659
3660 let _ = example
3661 (group_by_pre (fun x -> x =|= 3) [1;1;3;2;3;4;5;3;6;6;6] =*=
3662 ([1;1], [(3,[2]); (3,[4;5]); (3,[6;6;6])]))
3663
3664
3665 let rec (split_when: ('a -> bool) -> 'a list -> 'a list * 'a * 'a list) =
3666 fun p -> function
3667 | [] -> raise Not_found
3668 | x::xs ->
3669 if p x then
3670 [], x, xs
3671 else
3672 let (l1, a, l2) = split_when p xs in
3673 (x::l1, a, l2)
3674 let _ = example (split_when (fun x -> x =|= 3)
3675 [1;2;3;4;1;2] =*= ([1;2],3,[4;1;2]))
3676
3677
3678 (* not so easy to come up with ... used in aComment for split_paragraph *)
3679 let rec split_gen_when_aux f acc xs =
3680 match xs with
3681 | [] ->
3682 if null acc
3683 then []
3684 else [List.rev acc]
3685 | (x::xs) ->
3686 (match f (x::xs) with
3687 | None ->
3688 split_gen_when_aux f (x::acc) xs
3689 | Some (rest) ->
3690 let before = List.rev acc in
3691 if null before
3692 then split_gen_when_aux f [] rest
3693 else before::split_gen_when_aux f [] rest
3694 )
3695 (* could avoid introduce extra aux function by using ?(acc = []) *)
3696 let split_gen_when f xs =
3697 split_gen_when_aux f [] xs
3698
3699
3700
3701 (* generate exception (Failure "tl") if there is no element satisfying p *)
3702 let rec (skip_until: ('a list -> bool) -> 'a list -> 'a list) = fun p xs ->
3703 if p xs then xs else skip_until p (List.tl xs)
3704 let _ = example
3705 (skip_until (function 1::2::xs -> true | _ -> false)
3706 [1;3;4;1;2;4;5] =*= [1;2;4;5])
3707
3708 let rec skipfirst e = function
3709 | [] -> []
3710 | e'::l when e =*= e' -> skipfirst e l
3711 | l -> l
3712
3713
3714 (* now in prelude:
3715 * let rec enum x n = ...
3716 *)
3717
3718
3719 let index_list xs =
3720 if null xs then [] (* enum 0 (-1) generate an exception *)
3721 else zip xs (enum 0 ((List.length xs) -1))
3722
3723 let index_list_and_total xs =
3724 let total = List.length xs in
3725 if null xs then [] (* enum 0 (-1) generate an exception *)
3726 else zip xs (enum 0 ((List.length xs) -1))
3727 +> List.map (fun (a,b) -> (a,b,total))
3728
3729 let index_list_1 xs =
3730 xs +> index_list +> List.map (fun (x,i) -> x, i+1)
3731
3732 let or_list = List.fold_left (||) false
3733 let and_list = List.fold_left (&&) true
3734
3735 let avg_list xs =
3736 let sum = sum_int xs in
3737 (float_of_int sum) /. (float_of_int (List.length xs))
3738
3739 let snoc x xs = xs @ [x]
3740 let cons x xs = x::xs
3741
3742 let head_middle_tail xs =
3743 match xs with
3744 | x::y::xs ->
3745 let head = x in
3746 let reversed = List.rev (y::xs) in
3747 let tail = List.hd reversed in
3748 let middle = List.rev (List.tl reversed) in
3749 head, middle, tail
3750 | _ -> failwith "head_middle_tail, too small list"
3751
3752 let _ = assert_equal (head_middle_tail [1;2;3]) (1, [2], 3)
3753 let _ = assert_equal (head_middle_tail [1;3]) (1, [], 3)
3754
3755 (* now in prelude
3756 * let (++) = (@)
3757 *)
3758
3759 (* let (++) = (@), could do that, but if load many times the common, then pb *)
3760 (* let (++) l1 l2 = List.fold_right (fun x acc -> x::acc) l1 l2 *)
3761
3762 let remove x xs =
3763 let newxs = List.filter (fun y -> y <> x) xs in
3764 assert (List.length newxs =|= List.length xs - 1);
3765 newxs
3766
3767
3768 let exclude p xs =
3769 List.filter (fun x -> not (p x)) xs
3770
3771 (* now in prelude
3772 *)
3773
3774 let fold_k f lastk acc xs =
3775 let rec fold_k_aux acc = function
3776 | [] -> lastk acc
3777 | x::xs ->
3778 f acc x (fun acc -> fold_k_aux acc xs)
3779 in
3780 fold_k_aux acc xs
3781
3782
3783 let rec list_init = function
3784 | [] -> raise Not_found
3785 | [x] -> []
3786 | x::y::xs -> x::(list_init (y::xs))
3787
3788 let rec list_last = function
3789 | [] -> raise Not_found
3790 | [x] -> x
3791 | x::y::xs -> list_last (y::xs)
3792
3793 (* pixel *)
3794 (* now in prelude
3795 * let last_n n l = List.rev (take n (List.rev l))
3796 * let last l = List.hd (last_n 1 l)
3797 *)
3798
3799 let rec join_gen a = function
3800 | [] -> []
3801 | [x] -> [x]
3802 | x::xs -> x::a::(join_gen a xs)
3803
3804
3805 (* todo: foldl, foldr (a more consistent foldr) *)
3806
3807 (* start pixel *)
3808 let iter_index f l =
3809 let rec iter_ n = function
3810 | [] -> ()
3811 | e::l -> f e n ; iter_ (n+1) l
3812 in iter_ 0 l
3813
3814 let map_index f l =
3815 let rec map_ n = function
3816 | [] -> []
3817 | e::l -> f e n :: map_ (n+1) l
3818 in map_ 0 l
3819
3820
3821 (* pixel *)
3822 let filter_index f l =
3823 let rec filt i = function
3824 | [] -> []
3825 | e::l -> if f i e then e :: filt (i+1) l else filt (i+1) l
3826 in
3827 filt 0 l
3828
3829 (* pixel *)
3830 let do_withenv doit f env l =
3831 let r_env = ref env in
3832 let l' = doit (fun e ->
3833 let e', env' = f !r_env e in
3834 r_env := env' ; e'
3835 ) l in
3836 l', !r_env
3837
3838 (* now in prelude:
3839 * let fold_left_with_index f acc = ...
3840 *)
3841
3842 let map_withenv f env e = do_withenv List.map f env e
3843
3844 let rec collect_accu f accu = function
3845 | [] -> accu
3846 | e::l -> collect_accu f (List.rev_append (f e) accu) l
3847
3848 let collect f l = List.rev (collect_accu f [] l)
3849
3850 (* cf also List.partition *)
3851
3852 let rec fpartition p l =
3853 let rec part yes no = function
3854 | [] -> (List.rev yes, List.rev no)
3855 | x :: l ->
3856 (match p x with
3857 | None -> part yes (x :: no) l
3858 | Some v -> part (v :: yes) no l) in
3859 part [] [] l
3860
3861 (* end pixel *)
3862
3863 let rec removelast = function
3864 | [] -> failwith "removelast"
3865 | [_] -> []
3866 | e::l -> e :: removelast l
3867
3868 let remove x = List.filter (fun y -> y != x)
3869 let empty list = null list
3870
3871
3872 let rec inits = function
3873 | [] -> [[]]
3874 | e::l -> [] :: List.map (fun l -> e::l) (inits l)
3875
3876 let rec tails = function
3877 | [] -> [[]]
3878 | (_::xs) as xxs -> xxs :: tails xs
3879
3880
3881 let reverse = List.rev
3882 let rev = List.rev
3883
3884 let nth = List.nth
3885 let fold_left = List.fold_left
3886 let rev_map = List.rev_map
3887
3888 (* pixel *)
3889 let rec fold_right1 f = function
3890 | [] -> failwith "fold_right1"
3891 | [e] -> e
3892 | e::l -> f e (fold_right1 f l)
3893
3894 let maximum l = foldl1 max l
3895 let minimum l = foldl1 min l
3896
3897 (* do a map tail recursive, and result is reversed, it is a tail recursive map => efficient *)
3898 let map_eff_rev = fun f l ->
3899 let rec map_eff_aux acc =
3900 function
3901 | [] -> acc
3902 | x::xs -> map_eff_aux ((f x)::acc) xs
3903 in
3904 map_eff_aux [] l
3905
3906 let acc_map f l =
3907 let rec loop acc = function
3908 [] -> List.rev acc
3909 | x::xs -> loop ((f x)::acc) xs in
3910 loop [] l
3911
3912
3913 let rec (generate: int -> 'a -> 'a list) = fun i el ->
3914 if i =|= 0 then []
3915 else el::(generate (i-1) el)
3916
3917 let rec uniq = function
3918 | [] -> []
3919 | e::l -> if List.mem e l then uniq l else e :: uniq l
3920
3921 let has_no_duplicate xs =
3922 List.length xs =|= List.length (uniq xs)
3923 let is_set_as_list = has_no_duplicate
3924
3925
3926 let rec get_duplicates xs =
3927 match xs with
3928 | [] -> []
3929 | x::xs ->
3930 if List.mem x xs
3931 then x::get_duplicates xs (* todo? could x from xs to avoid double dups?*)
3932 else get_duplicates xs
3933
3934 let rec all_assoc e = function
3935 | [] -> []
3936 | (e',v) :: l when e=*=e' -> v :: all_assoc e l
3937 | _ :: l -> all_assoc e l
3938
3939 let prepare_want_all_assoc l =
3940 List.map (fun n -> n, uniq (all_assoc n l)) (uniq (List.map fst l))
3941
3942 let rotate list = List.tl list ++ [(List.hd list)]
3943
3944 let or_list = List.fold_left (||) false
3945 let and_list = List.fold_left (&&) true
3946
3947 let rec (return_when: ('a -> 'b option) -> 'a list -> 'b) = fun p -> function
3948 | [] -> raise Not_found
3949 | x::xs -> (match p x with None -> return_when p xs | Some b -> b)
3950
3951 let rec splitAt n xs =
3952 if n =|= 0 then ([],xs)
3953 else
3954 (match xs with
3955 | [] -> ([],[])
3956 | (x::xs) -> let (a,b) = splitAt (n-1) xs in (x::a, b)
3957 )
3958
3959 let pack n xs =
3960 let rec pack_aux l i = function
3961 | [] -> failwith "not on a boundary"
3962 | [x] -> if i =|= n then [l++[x]] else failwith "not on a boundary"
3963 | x::xs ->
3964 if i =|= n
3965 then (l++[x])::(pack_aux [] 1 xs)
3966 else pack_aux (l++[x]) (i+1) xs
3967 in
3968 pack_aux [] 1 xs
3969
3970 let min_with f = function
3971 | [] -> raise Not_found
3972 | e :: l ->
3973 let rec min_with_ min_val min_elt = function
3974 | [] -> min_elt
3975 | e::l ->
3976 let val_ = f e in
3977 if val_ < min_val
3978 then min_with_ val_ e l
3979 else min_with_ min_val min_elt l
3980 in min_with_ (f e) e l
3981
3982 let two_mins_with f = function
3983 | e1 :: e2 :: l ->
3984 let rec min_with_ min_val min_elt min_val2 min_elt2 = function
3985 | [] -> min_elt, min_elt2
3986 | e::l ->
3987 let val_ = f e in
3988 if val_ < min_val2
3989 then
3990 if val_ < min_val
3991 then min_with_ val_ e min_val min_elt l
3992 else min_with_ min_val min_elt val_ e l
3993 else min_with_ min_val min_elt min_val2 min_elt2 l
3994 in
3995 let v1 = f e1 in
3996 let v2 = f e2 in
3997 if v1 < v2 then min_with_ v1 e1 v2 e2 l else min_with_ v2 e2 v1 e1 l
3998 | _ -> raise Not_found
3999
4000 let grep_with_previous f = function
4001 | [] -> []
4002 | e::l ->
4003 let rec grep_with_previous_ previous = function
4004 | [] -> []
4005 | e::l -> if f previous e then e :: grep_with_previous_ e l else grep_with_previous_ previous l
4006 in e :: grep_with_previous_ e l
4007
4008 let iter_with_previous f = function
4009 | [] -> ()
4010 | e::l ->
4011 let rec iter_with_previous_ previous = function
4012 | [] -> ()
4013 | e::l -> f previous e ; iter_with_previous_ e l
4014 in iter_with_previous_ e l
4015
4016
4017 let iter_with_before_after f xs =
4018 let rec aux before_rev after =
4019 match after with
4020 | [] -> ()
4021 | x::xs ->
4022 f before_rev x xs;
4023 aux (x::before_rev) xs
4024 in
4025 aux [] xs
4026
4027
4028
4029 (* kind of cartesian product of x*x *)
4030 let rec (get_pair: ('a list) -> (('a * 'a) list)) = function
4031 | [] -> []
4032 | x::xs -> (List.map (fun y -> (x,y)) xs) ++ (get_pair xs)
4033
4034
4035 (* retourne le rang dans une liste d'un element *)
4036 let rang elem liste =
4037 let rec rang_rec elem accu = function
4038 | [] -> raise Not_found
4039 | a::l -> if a =*= elem then accu
4040 else rang_rec elem (accu+1) l in
4041 rang_rec elem 1 liste
4042
4043 (* retourne vrai si une liste contient des doubles *)
4044 let rec doublon = function
4045 | [] -> false
4046 | a::l -> if List.mem a l then true
4047 else doublon l
4048
4049 let rec (insert_in: 'a -> 'a list -> 'a list list) = fun x -> function
4050 | [] -> [[x]]
4051 | y::ys -> (x::y::ys) :: (List.map (fun xs -> y::xs) (insert_in x ys))
4052 (* insert_in 3 [1;2] = [[3; 1; 2]; [1; 3; 2]; [1; 2; 3]] *)
4053
4054 let rec (permutation: 'a list -> 'a list list) = function
4055 | [] -> []
4056 | [x] -> [[x]]
4057 | x::xs -> List.flatten (List.map (insert_in x) (permutation xs))
4058 (* permutation [1;2;3] =
4059 * [[1; 2; 3]; [2; 1; 3]; [2; 3; 1]; [1; 3; 2]; [3; 1; 2]; [3; 2; 1]]
4060 *)
4061
4062
4063 let rec remove_elem_pos pos xs =
4064 match (pos, xs) with
4065 | _, [] -> failwith "remove_elem_pos"
4066 | 0, x::xs -> xs
4067 | n, x::xs -> x::(remove_elem_pos (n-1) xs)
4068
4069 let rec insert_elem_pos (e, pos) xs =
4070 match (pos, xs) with
4071 | 0, xs -> e::xs
4072 | n, x::xs -> x::(insert_elem_pos (e, (n-1)) xs)
4073 | n, [] -> failwith "insert_elem_pos"
4074
4075 let rec uncons_permut xs =
4076 let indexed = index_list xs in
4077 indexed +> List.map (fun (x, pos) -> (x, pos), remove_elem_pos pos xs)
4078 let _ =
4079 example
4080 (uncons_permut ['a';'b';'c'] =*=
4081 [('a', 0), ['b';'c'];
4082 ('b', 1), ['a';'c'];
4083 ('c', 2), ['a';'b']
4084 ])
4085
4086 let rec uncons_permut_lazy xs =
4087 let indexed = index_list xs in
4088 indexed +> List.map (fun (x, pos) ->
4089 (x, pos),
4090 lazy (remove_elem_pos pos xs)
4091 )
4092
4093
4094
4095
4096 (* pixel *)
4097 let rec map_flatten f l =
4098 let rec map_flatten_aux accu = function
4099 | [] -> accu
4100 | e :: l -> map_flatten_aux (List.rev (f e) ++ accu) l
4101 in List.rev (map_flatten_aux [] l)
4102
4103
4104 let rec repeat e n =
4105 let rec repeat_aux acc = function
4106 | 0 -> acc
4107 | n when n < 0 -> failwith "repeat"
4108 | n -> repeat_aux (e::acc) (n-1) in
4109 repeat_aux [] n
4110
4111 let rec map2 f = function
4112 | [] -> []
4113 | x::xs -> let r = f x in r::map2 f xs
4114
4115 let rec map3 f l =
4116 let rec map3_aux acc = function
4117 | [] -> acc
4118 | x::xs -> map3_aux (f x::acc) xs in
4119 map3_aux [] l
4120
4121 (*
4122 let tails2 xs = map rev (inits (rev xs))
4123 let res = tails2 [1;2;3;4]
4124 let res = tails [1;2;3;4]
4125 let id x = x
4126 *)
4127
4128 let pack_sorted same xs =
4129 let rec pack_s_aux acc xs =
4130 match (acc,xs) with
4131 | ((cur,rest),[]) -> cur::rest
4132 | ((cur,rest), y::ys) ->
4133 if same (List.hd cur) y then pack_s_aux (y::cur, rest) ys
4134 else pack_s_aux ([y], cur::rest) ys
4135 in pack_s_aux ([List.hd xs],[]) (List.tl xs) +> List.rev
4136 let test = pack_sorted (=*=) [1;1;1;2;2;3;4]
4137
4138
4139 let rec keep_best f =
4140 let rec partition e = function
4141 | [] -> e, []
4142 | e' :: l ->
4143 match f(e,e') with
4144 | None -> let (e'', l') = partition e l in e'', e' :: l'
4145 | Some e'' -> partition e'' l
4146 in function
4147 | [] -> []
4148 | e::l ->
4149 let (e', l') = partition e l in
4150 e' :: keep_best f l'
4151
4152 let rec sorted_keep_best f = function
4153 | [] -> []
4154 | [a] -> [a]
4155 | a :: b :: l ->
4156 match f a b with
4157 | None -> a :: sorted_keep_best f (b :: l)
4158 | Some e -> sorted_keep_best f (e :: l)
4159
4160
4161
4162 let (cartesian_product: 'a list -> 'b list -> ('a * 'b) list) = fun xs ys ->
4163 xs +> List.map (fun x -> ys +> List.map (fun y -> (x,y)))
4164 +> List.flatten
4165
4166 let _ = assert_equal
4167 (cartesian_product [1;2] ["3";"4";"5"])
4168 [1,"3";1,"4";1,"5"; 2,"3";2,"4";2,"5"]
4169
4170 let sort_prof a b =
4171 profile_code "Common.sort_by_xxx" (fun () -> List.sort a b)
4172
4173 let sort_by_val_highfirst xs =
4174 sort_prof (fun (k1,v1) (k2,v2) -> compare v2 v1) xs
4175 let sort_by_val_lowfirst xs =
4176 sort_prof (fun (k1,v1) (k2,v2) -> compare v1 v2) xs
4177
4178 let sort_by_key_highfirst xs =
4179 sort_prof (fun (k1,v1) (k2,v2) -> compare k2 k1) xs
4180 let sort_by_key_lowfirst xs =
4181 sort_prof (fun (k1,v1) (k2,v2) -> compare k1 k2) xs
4182
4183 let _ = example (sort_by_key_lowfirst [4, (); 7,()] =*= [4,(); 7,()])
4184 let _ = example (sort_by_key_highfirst [4,(); 7,()] =*= [7,(); 4,()])
4185
4186
4187 let sortgen_by_key_highfirst xs =
4188 sort_prof (fun (k1,v1) (k2,v2) -> compare k2 k1) xs
4189 let sortgen_by_key_lowfirst xs =
4190 sort_prof (fun (k1,v1) (k2,v2) -> compare k1 k2) xs
4191
4192 (*----------------------------------*)
4193
4194 (* sur surEnsemble [p1;p2] [[p1;p2;p3] [p1;p2] ....] -> [[p1;p2;p3] ... *)
4195 (* mais pas p2;p3 *)
4196 (* (aop) *)
4197 let surEnsemble liste_el liste_liste_el =
4198 List.filter
4199 (function liste_elbis ->
4200 List.for_all (function el -> List.mem el liste_elbis) liste_el
4201 ) liste_liste_el;;
4202
4203
4204
4205 (*----------------------------------*)
4206 (* combinaison/product/.... (aop) *)
4207 (* 123 -> 123 12 13 23 1 2 3 *)
4208 let rec realCombinaison = function
4209 | [] -> []
4210 | [a] -> [[a]]
4211 | a::l ->
4212 let res = realCombinaison l in
4213 let res2 = List.map (function x -> a::x) res in
4214 res2 ++ res ++ [[a]]
4215
4216 (* genere toutes les combinaisons possible de paire *)
4217 (* par exemple combinaison [1;2;4] -> [1, 2; 1, 4; 2, 4] *)
4218 let rec combinaison = function
4219 | [] -> []
4220 | [a] -> []
4221 | [a;b] -> [(a, b)]
4222 | a::b::l -> (List.map (function elem -> (a, elem)) (b::l)) ++
4223 (combinaison (b::l))
4224
4225 (*----------------------------------*)
4226
4227 (* list of list(aop) *)
4228 (* insere elem dans la liste de liste (si elem est deja present dans une de *)
4229 (* ces listes, on ne fait rien *)
4230 let rec insere elem = function
4231 | [] -> [[elem]]
4232 | a::l ->
4233 if (List.mem elem a) then a::l
4234 else a::(insere elem l)
4235
4236 let rec insereListeContenant lis el = function
4237 | [] -> [el::lis]
4238 | a::l ->
4239 if List.mem el a then
4240 (List.append lis a)::l
4241 else a::(insereListeContenant lis el l)
4242
4243 (* fusionne les listes contenant et1 et et2 dans la liste de liste*)
4244 let rec fusionneListeContenant (et1, et2) = function
4245 | [] -> [[et1; et2]]
4246 | a::l ->
4247 (* si les deux sont deja dedans alors rien faire *)
4248 if List.mem et1 a then
4249 if List.mem et2 a then a::l
4250 else
4251 insereListeContenant a et2 l
4252 else if List.mem et2 a then
4253 insereListeContenant a et1 l
4254 else a::(fusionneListeContenant (et1, et2) l)
4255
4256 (*****************************************************************************)
4257 (* Arrays *)
4258 (*****************************************************************************)
4259
4260 (* do bound checking ? *)
4261 let array_find_index f a =
4262 let rec array_find_index_ i =
4263 if f i then i else array_find_index_ (i+1)
4264 in
4265 try array_find_index_ 0 with _ -> raise Not_found
4266
4267 let array_find_index_via_elem f a =
4268 let rec array_find_index_ i =
4269 if f a.(i) then i else array_find_index_ (i+1)
4270 in
4271 try array_find_index_ 0 with _ -> raise Not_found
4272
4273
4274
4275 type idx = Idx of int
4276 let next_idx (Idx i) = (Idx (i+1))
4277 let int_of_idx (Idx i) = i
4278
4279 let array_find_index_typed f a =
4280 let rec array_find_index_ i =
4281 if f i then i else array_find_index_ (next_idx i)
4282 in
4283 try array_find_index_ (Idx 0) with _ -> raise Not_found
4284
4285
4286
4287 (*****************************************************************************)
4288 (* Matrix *)
4289 (*****************************************************************************)
4290
4291 type 'a matrix = 'a array array
4292
4293 let map_matrix f mat =
4294 mat +> Array.map (fun arr -> arr +> Array.map f)
4295
4296 let (make_matrix_init:
4297 nrow:int -> ncolumn:int -> (int -> int -> 'a) -> 'a matrix) =
4298 fun ~nrow ~ncolumn f ->
4299 Array.init nrow (fun i ->
4300 Array.init ncolumn (fun j ->
4301 f i j
4302 )
4303 )
4304
4305 let iter_matrix f m =
4306 Array.iteri (fun i e ->
4307 Array.iteri (fun j x ->
4308 f i j x
4309 ) e
4310 ) m
4311
4312 let nb_rows_matrix m =
4313 Array.length m
4314
4315 let nb_columns_matrix m =
4316 assert(Array.length m > 0);
4317 Array.length m.(0)
4318
4319 (* check all nested arrays have the same size *)
4320 let invariant_matrix m =
4321 raise Todo
4322
4323 let (rows_of_matrix: 'a matrix -> 'a list list) = fun m ->
4324 Array.to_list m +> List.map Array.to_list
4325
4326 let (columns_of_matrix: 'a matrix -> 'a list list) = fun m ->
4327 let nbcols = nb_columns_matrix m in
4328 let nbrows = nb_rows_matrix m in
4329 (enum 0 (nbcols -1)) +> List.map (fun j ->
4330 (enum 0 (nbrows -1)) +> List.map (fun i ->
4331 m.(i).(j)
4332 ))
4333
4334
4335 let all_elems_matrix_by_row m =
4336 rows_of_matrix m +> List.flatten
4337
4338
4339 let ex_matrix1 =
4340 [|
4341 [|0;1;2|];
4342 [|3;4;5|];
4343 [|6;7;8|];
4344 |]
4345 let ex_rows1 =
4346 [
4347 [0;1;2];
4348 [3;4;5];
4349 [6;7;8];
4350 ]
4351 let ex_columns1 =
4352 [
4353 [0;3;6];
4354 [1;4;7];
4355 [2;5;8];
4356 ]
4357 let _ = example (rows_of_matrix ex_matrix1 =*= ex_rows1)
4358 let _ = example (columns_of_matrix ex_matrix1 =*= ex_columns1)
4359
4360
4361 (*****************************************************************************)
4362 (* Fast array *)
4363 (*****************************************************************************)
4364 (*
4365 module B_Array = Bigarray.Array2
4366 *)
4367
4368 (*
4369 open B_Array
4370 open Bigarray
4371 *)
4372
4373
4374 (* for the string_of auto generation of camlp4
4375 val b_array_string_of_t : 'a -> 'b -> string
4376 val bigarray_string_of_int16_unsigned_elt : 'a -> string
4377 val bigarray_string_of_c_layout : 'a -> string
4378 let b_array_string_of_t f a = "<>"
4379 let bigarray_string_of_int16_unsigned_elt a = "<>"
4380 let bigarray_string_of_c_layout a = "<>"
4381
4382 *)
4383
4384
4385 (*****************************************************************************)
4386 (* Set. Have a look too at set*.mli *)
4387 (*****************************************************************************)
4388 type 'a set = 'a list
4389 (* with sexp *)
4390
4391 let (empty_set: 'a set) = []
4392 let (insert_set: 'a -> 'a set -> 'a set) = fun x xs ->
4393 if List.mem x xs
4394 then (* let _ = print_string "warning insert: already exist" in *)
4395 xs
4396 else x::xs
4397
4398 let is_set xs =
4399 has_no_duplicate xs
4400
4401 let (single_set: 'a -> 'a set) = fun x -> insert_set x empty_set
4402 let (set: 'a list -> 'a set) = fun xs ->
4403 xs +> List.fold_left (flip insert_set) empty_set
4404
4405 let (exists_set: ('a -> bool) -> 'a set -> bool) = List.exists
4406 let (forall_set: ('a -> bool) -> 'a set -> bool) = List.for_all
4407 let (filter_set: ('a -> bool) -> 'a set -> 'a set) = List.filter
4408 let (fold_set: ('a -> 'b -> 'a) -> 'a -> 'b set -> 'a) = List.fold_left
4409 let (map_set: ('a -> 'b) -> 'a set -> 'b set) = List.map
4410 let (member_set: 'a -> 'a set -> bool) = List.mem
4411
4412 let find_set = List.find
4413 let sort_set = List.sort
4414 let iter_set = List.iter
4415
4416 let (top_set: 'a set -> 'a) = List.hd
4417
4418 let (inter_set: 'a set -> 'a set -> 'a set) = fun s1 s2 ->
4419 s1 +> fold_set (fun acc x -> if member_set x s2 then insert_set x acc else acc) empty_set
4420 let (union_set: 'a set -> 'a set -> 'a set) = fun s1 s2 ->
4421 s2 +> fold_set (fun acc x -> if member_set x s1 then acc else insert_set x acc) s1
4422 let (minus_set: 'a set -> 'a set -> 'a set) = fun s1 s2 ->
4423 s1 +> filter_set (fun x -> not (member_set x s2))
4424
4425
4426 let union_all l = List.fold_left union_set [] l
4427
4428 let big_union_set f xs = xs +> map_set f +> fold_set union_set empty_set
4429
4430 let (card_set: 'a set -> int) = List.length
4431
4432 let (include_set: 'a set -> 'a set -> bool) = fun s1 s2 ->
4433 (s1 +> forall_set (fun p -> member_set p s2))
4434
4435 let equal_set s1 s2 = include_set s1 s2 && include_set s2 s1
4436
4437 let (include_set_strict: 'a set -> 'a set -> bool) = fun s1 s2 ->
4438 (card_set s1 < card_set s2) && (include_set s1 s2)
4439
4440 let ($*$) = inter_set
4441 let ($+$) = union_set
4442 let ($-$) = minus_set
4443 let ($?$) a b = profile_code "$?$" (fun () -> member_set a b)
4444 let ($<$) = include_set_strict
4445 let ($<=$) = include_set
4446 let ($=$) = equal_set
4447
4448 (* as $+$ but do not check for memberness, allow to have set of func *)
4449 let ($@$) = fun a b -> a @ b
4450
4451 let rec nub = function
4452 [] -> []
4453 | x::xs -> if List.mem x xs then nub xs else x::(nub xs)
4454
4455 (*****************************************************************************)
4456 (* Set as normal list *)
4457 (*****************************************************************************)
4458 (*
4459 let (union: 'a list -> 'a list -> 'a list) = fun l1 l2 ->
4460 List.fold_left (fun acc x -> if List.mem x l1 then acc else x::acc) l1 l2
4461
4462 let insert_normal x xs = union xs [x]
4463
4464 (* retourne lis1 - lis2 *)
4465 let minus l1 l2 = List.filter (fun x -> not (List.mem x l2)) l1
4466
4467 let inter l1 l2 = List.fold_left (fun acc x -> if List.mem x l2 then x::acc else acc) [] l1
4468
4469 let union_list = List.fold_left union []
4470
4471 let uniq lis =
4472 List.fold_left (function acc -> function el -> union [el] acc) [] lis
4473
4474 (* pixel *)
4475 let rec non_uniq = function
4476 | [] -> []
4477 | e::l -> if mem e l then e :: non_uniq l else non_uniq l
4478
4479 let rec inclu lis1 lis2 =
4480 List.for_all (function el -> List.mem el lis2) lis1
4481
4482 let equivalent lis1 lis2 =
4483 (inclu lis1 lis2) && (inclu lis2 lis1)
4484
4485 *)
4486
4487
4488 (*****************************************************************************)
4489 (* Set as sorted list *)
4490 (*****************************************************************************)
4491 (* liste trie, cos we need to do intersection, and insertion (it is a set
4492 cos when introduce has, if we create a new has => must do a recurse_rep
4493 and another categ can have to this has => must do an union
4494 *)
4495 (*
4496 let rec insert x = function
4497 | [] -> [x]
4498 | y::ys ->
4499 if x = y then y::ys
4500 else (if x < y then x::y::ys else y::(insert x ys))
4501
4502 (* same, suppose sorted list *)
4503 let rec intersect x y =
4504 match(x,y) with
4505 | [], y -> []
4506 | x, [] -> []
4507 | x::xs, y::ys ->
4508 if x = y then x::(intersect xs ys)
4509 else
4510 (if x < y then intersect xs (y::ys)
4511 else intersect (x::xs) ys
4512 )
4513 (* intersect [1;3;7] [2;3;4;7;8];; *)
4514 *)
4515
4516 (*****************************************************************************)
4517 (* Assoc *)
4518 (*****************************************************************************)
4519 type ('a,'b) assoc = ('a * 'b) list
4520 (* with sexp *)
4521
4522
4523 let (assoc_to_function: ('a, 'b) assoc -> ('a -> 'b)) = fun xs ->
4524 xs +> List.fold_left (fun acc (k, v) ->
4525 (fun k' ->
4526 if k =*= k' then v else acc k'
4527 )) (fun k -> failwith "no key in this assoc")
4528 (* simpler:
4529 let (assoc_to_function: ('a, 'b) assoc -> ('a -> 'b)) = fun xs ->
4530 fun k -> List.assoc k xs
4531 *)
4532
4533 let (empty_assoc: ('a, 'b) assoc) = []
4534 let fold_assoc = List.fold_left
4535 let insert_assoc = fun x xs -> x::xs
4536 let map_assoc = List.map
4537 let filter_assoc = List.filter
4538
4539 let assoc = List.assoc
4540 let keys xs = List.map fst xs
4541
4542 let lookup = assoc
4543
4544 (* assert unique key ?*)
4545 let del_assoc key xs = xs +> List.filter (fun (k,v) -> k <> key)
4546 let replace_assoc (key, v) xs = insert_assoc (key, v) (del_assoc key xs)
4547
4548 let apply_assoc key f xs =
4549 let old = assoc key xs in
4550 replace_assoc (key, f old) xs
4551
4552 let big_union_assoc f xs = xs +> map_assoc f +> fold_assoc union_set empty_set
4553
4554 (* todo: pb normally can suppr fun l -> .... l but if do that, then strange type _a
4555 => assoc_map is strange too => equal dont work
4556 *)
4557 let (assoc_reverse: (('a * 'b) list) -> (('b * 'a) list)) = fun l ->
4558 List.map (fun(x,y) -> (y,x)) l
4559
4560 let (assoc_map: (('a * 'b) list) -> (('a * 'b) list) -> (('a * 'a) list)) =
4561 fun l1 l2 ->
4562 let (l1bis, l2bis) = (assoc_reverse l1, assoc_reverse l2) in
4563 List.map (fun (x,y) -> (y, List.assoc x l2bis )) l1bis
4564
4565 let rec (lookup_list: 'a -> ('a , 'b) assoc list -> 'b) = fun el -> function
4566 | [] -> raise Not_found
4567 | (xs::xxs) -> try List.assoc el xs with Not_found -> lookup_list el xxs
4568
4569 let (lookup_list2: 'a -> ('a , 'b) assoc list -> ('b * int)) = fun el xxs ->
4570 let rec lookup_l_aux i = function
4571 | [] -> raise Not_found
4572 | (xs::xxs) ->
4573 try let res = List.assoc el xs in (res,i)
4574 with Not_found -> lookup_l_aux (i+1) xxs
4575 in lookup_l_aux 0 xxs
4576
4577 let _ = example
4578 (lookup_list2 "c" [["a",1;"b",2];["a",1;"b",3];["a",1;"c",7]] =*= (7,2))
4579
4580
4581 let assoc_option k l =
4582 optionise (fun () -> List.assoc k l)
4583
4584 let assoc_with_err_msg k l =
4585 try List.assoc k l
4586 with Not_found ->
4587 pr2 (spf "pb assoc_with_err_msg: %s" (dump k));
4588 raise Not_found
4589
4590 (*****************************************************************************)
4591 (* Assoc int -> xxx with binary tree. Have a look too at Mapb.mli *)
4592 (*****************************************************************************)
4593
4594 (* ex: type robot_list = robot_info IntMap.t *)
4595 module IntMap = Map.Make
4596 (struct
4597 type t = int
4598 let compare = compare
4599 end)
4600 let intmap_to_list m = IntMap.fold (fun id v acc -> (id, v) :: acc) m []
4601 let intmap_string_of_t f a = "<Not Yet>"
4602
4603 module IntIntMap = Map.Make
4604 (struct
4605 type t = int * int
4606 let compare = compare
4607 end)
4608
4609 let intintmap_to_list m = IntIntMap.fold (fun id v acc -> (id, v) :: acc) m []
4610 let intintmap_string_of_t f a = "<Not Yet>"
4611
4612
4613 (*****************************************************************************)
4614 (* Hash *)
4615 (*****************************************************************************)
4616
4617 (* il parait que better when choose a prime *)
4618 let hcreate () = Hashtbl.create 401
4619 let hadd (k,v) h = Hashtbl.add h k v
4620 let hmem k h = Hashtbl.mem h k
4621 let hfind k h = Hashtbl.find h k
4622 let hreplace (k,v) h = Hashtbl.replace h k v
4623 let hiter = Hashtbl.iter
4624 let hfold = Hashtbl.fold
4625 let hremove k h = Hashtbl.remove h k
4626
4627
4628 let hash_to_list h =
4629 Hashtbl.fold (fun k v acc -> (k,v)::acc) h []
4630 +> List.sort compare
4631
4632 let hash_to_list_unsorted h =
4633 Hashtbl.fold (fun k v acc -> (k,v)::acc) h []
4634
4635 let hash_of_list xs =
4636 let h = Hashtbl.create 101 in
4637 begin
4638 xs +> List.iter (fun (k, v) -> Hashtbl.add h k v);
4639 h
4640 end
4641
4642 let _ =
4643 let h = Hashtbl.create 101 in
4644 Hashtbl.add h "toto" 1;
4645 Hashtbl.add h "toto" 1;
4646 assert(hash_to_list h =*= ["toto",1; "toto",1])
4647
4648
4649 let hfind_default key value_if_not_found h =
4650 try Hashtbl.find h key
4651 with Not_found ->
4652 (Hashtbl.add h key (value_if_not_found ()); Hashtbl.find h key)
4653
4654 (* not as easy as Perl $h->{key}++; but still possible *)
4655 let hupdate_default key op value_if_not_found h =
4656 let old = hfind_default key value_if_not_found h in
4657 Hashtbl.replace h key (op old)
4658
4659
4660 let hfind_option key h =
4661 optionise (fun () -> Hashtbl.find h key)
4662
4663
4664 (* see below: let hkeys h = ... *)
4665
4666
4667 (*****************************************************************************)
4668 (* Hash sets *)
4669 (*****************************************************************************)
4670
4671 type 'a hashset = ('a, bool) Hashtbl.t
4672 (* with sexp *)
4673
4674
4675 let hash_hashset_add k e h =
4676 match optionise (fun () -> Hashtbl.find h k) with
4677 | Some hset -> Hashtbl.replace hset e true
4678 | None ->
4679 let hset = Hashtbl.create 11 in
4680 begin
4681 Hashtbl.add h k hset;
4682 Hashtbl.replace hset e true;
4683 end
4684
4685 let hashset_to_set baseset h =
4686 h +> hash_to_list +> List.map fst +> (fun xs -> baseset#fromlist xs)
4687
4688 let hashset_to_list h = hash_to_list h +> List.map fst
4689
4690 let hashset_of_list xs =
4691 xs +> List.map (fun x -> x, true) +> hash_of_list
4692
4693
4694
4695 let hkeys h =
4696 let hkey = Hashtbl.create 101 in
4697 h +> Hashtbl.iter (fun k v -> Hashtbl.replace hkey k true);
4698 hashset_to_list hkey
4699
4700
4701
4702 let group_assoc_bykey_eff2 xs =
4703 let h = Hashtbl.create 101 in
4704 xs +> List.iter (fun (k, v) -> Hashtbl.add h k v);
4705 let keys = hkeys h in
4706 keys +> List.map (fun k -> k, Hashtbl.find_all h k)
4707
4708 let group_assoc_bykey_eff xs =
4709 profile_code2 "Common.group_assoc_bykey_eff" (fun () ->
4710 group_assoc_bykey_eff2 xs)
4711
4712
4713 let test_group_assoc () =
4714 let xs = enum 0 10000 +> List.map (fun i -> i_to_s i, i) in
4715 let xs = ("0", 2)::xs in
4716 (* let _ys = xs +> Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) *)
4717 let ys = xs +> group_assoc_bykey_eff
4718 in
4719 pr2_gen ys
4720
4721
4722 let uniq_eff xs =
4723 let h = Hashtbl.create 101 in
4724 xs +> List.iter (fun k ->
4725 Hashtbl.add h k true
4726 );
4727 hkeys h
4728
4729
4730
4731 let diff_two_say_set_eff xs1 xs2 =
4732 let h1 = hashset_of_list xs1 in
4733 let h2 = hashset_of_list xs2 in
4734
4735 let hcommon = Hashtbl.create 101 in
4736 let honly_in_h1 = Hashtbl.create 101 in
4737 let honly_in_h2 = Hashtbl.create 101 in
4738
4739 h1 +> Hashtbl.iter (fun k _ ->
4740 if Hashtbl.mem h2 k
4741 then Hashtbl.replace hcommon k true
4742 else Hashtbl.add honly_in_h1 k true
4743 );
4744 h2 +> Hashtbl.iter (fun k _ ->
4745 if Hashtbl.mem h1 k
4746 then Hashtbl.replace hcommon k true
4747 else Hashtbl.add honly_in_h2 k true
4748 );
4749 hashset_to_list hcommon,
4750 hashset_to_list honly_in_h1,
4751 hashset_to_list honly_in_h2
4752
4753
4754 (*****************************************************************************)
4755 (* Stack *)
4756 (*****************************************************************************)
4757 type 'a stack = 'a list
4758 (* with sexp *)
4759
4760 let (empty_stack: 'a stack) = []
4761 let (push: 'a -> 'a stack -> 'a stack) = fun x xs -> x::xs
4762 let (top: 'a stack -> 'a) = List.hd
4763 let (pop: 'a stack -> 'a stack) = List.tl
4764
4765 let top_option = function
4766 | [] -> None
4767 | x::xs -> Some x
4768
4769
4770
4771
4772 (* now in prelude:
4773 * let push2 v l = l := v :: !l
4774 *)
4775
4776 let pop2 l =
4777 let v = List.hd !l in
4778 begin
4779 l := List.tl !l;
4780 v
4781 end
4782
4783
4784 (*****************************************************************************)
4785 (* Undoable Stack *)
4786 (*****************************************************************************)
4787
4788 (* Okasaki use such structure also for having efficient data structure
4789 * supporting fast append.
4790 *)
4791
4792 type 'a undo_stack = 'a list * 'a list (* redo *)
4793
4794 let (empty_undo_stack: 'a undo_stack) =
4795 [], []
4796
4797 (* push erase the possible redo *)
4798 let (push_undo: 'a -> 'a undo_stack -> 'a undo_stack) = fun x (undo,redo) ->
4799 x::undo, []
4800
4801 let (top_undo: 'a undo_stack -> 'a) = fun (undo, redo) ->
4802 List.hd undo
4803
4804 let (pop_undo: 'a undo_stack -> 'a undo_stack) = fun (undo, redo) ->
4805 match undo with
4806 | [] -> failwith "empty undo stack"
4807 | x::xs ->
4808 xs, x::redo
4809
4810 let (undo_pop: 'a undo_stack -> 'a undo_stack) = fun (undo, redo) ->
4811 match redo with
4812 | [] -> failwith "empty redo, nothing to redo"
4813 | x::xs ->
4814 x::undo, xs
4815
4816 let redo_undo x = undo_pop x
4817
4818
4819 let top_undo_option = fun (undo, redo) ->
4820 match undo with
4821 | [] -> None
4822 | x::xs -> Some x
4823
4824 (*****************************************************************************)
4825 (* Binary tree *)
4826 (*****************************************************************************)
4827 type 'a bintree = Leaf of 'a | Branch of ('a bintree * 'a bintree)
4828
4829
4830 (*****************************************************************************)
4831 (* N-ary tree *)
4832 (*****************************************************************************)
4833
4834 (* no empty tree, must have one root at list *)
4835 type 'a tree = Tree of 'a * ('a tree) list
4836
4837 let rec (tree_iter: ('a -> unit) -> 'a tree -> unit) = fun f tree ->
4838 match tree with
4839 | Tree (node, xs) ->
4840 f node;
4841 xs +> List.iter (tree_iter f)
4842
4843
4844 (*****************************************************************************)
4845 (* N-ary tree with updatable childrens *)
4846 (*****************************************************************************)
4847
4848 (* no empty tree, must have one root at list *)
4849
4850 type 'a treeref =
4851 | NodeRef of 'a * 'a treeref list ref
4852
4853 let treeref_children_ref tree =
4854 match tree with
4855 | NodeRef (n, x) -> x
4856
4857
4858
4859 let rec (treeref_node_iter:
4860 (* (('a * ('a, 'b) treeref list ref) -> unit) ->
4861 ('a, 'b) treeref -> unit
4862 *) 'a)
4863 =
4864 fun f tree ->
4865 match tree with
4866 (* | LeafRef _ -> ()*)
4867 | NodeRef (n, xs) ->
4868 f (n, xs);
4869 !xs +> List.iter (treeref_node_iter f)
4870
4871
4872 let find_treeref f tree =
4873 let res = ref [] in
4874
4875 tree +> treeref_node_iter (fun (n, xs) ->
4876 if f (n,xs)
4877 then push2 (n, xs) res;
4878 );
4879 match !res with
4880 | [n,xs] -> NodeRef (n, xs)
4881 | [] -> raise Not_found
4882 | x::y::zs -> raise Multi_found
4883
4884 let rec (treeref_node_iter_with_parents:
4885 (* (('a * ('a, 'b) treeref list ref) -> ('a list) -> unit) ->
4886 ('a, 'b) treeref -> unit)
4887 *) 'a)
4888 =
4889 fun f tree ->
4890 let rec aux acc tree =
4891 match tree with
4892 (* | LeafRef _ -> ()*)
4893 | NodeRef (n, xs) ->
4894 f (n, xs) acc ;
4895 !xs +> List.iter (aux (n::acc))
4896 in
4897 aux [] tree
4898
4899
4900 (* ---------------------------------------------------------------------- *)
4901 (* Leaf can seem redundant, but sometimes want to directly see if
4902 * a children is a leaf without looking if the list is empty.
4903 *)
4904 type ('a, 'b) treeref2 =
4905 | NodeRef2 of 'a * ('a, 'b) treeref2 list ref
4906 | LeafRef2 of 'b
4907
4908
4909 let treeref2_children_ref tree =
4910 match tree with
4911 | LeafRef2 _ -> failwith "treeref_tail: leaf"
4912 | NodeRef2 (n, x) -> x
4913
4914
4915
4916 let rec (treeref_node_iter2:
4917 (('a * ('a, 'b) treeref2 list ref) -> unit) ->
4918 ('a, 'b) treeref2 -> unit) =
4919 fun f tree ->
4920 match tree with
4921 | LeafRef2 _ -> ()
4922 | NodeRef2 (n, xs) ->
4923 f (n, xs);
4924 !xs +> List.iter (treeref_node_iter2 f)
4925
4926
4927 let find_treeref2 f tree =
4928 let res = ref [] in
4929
4930 tree +> treeref_node_iter2 (fun (n, xs) ->
4931 if f (n,xs)
4932 then push2 (n, xs) res;
4933 );
4934 match !res with
4935 | [n,xs] -> NodeRef2 (n, xs)
4936 | [] -> raise Not_found
4937 | x::y::zs -> raise Multi_found
4938
4939
4940
4941
4942 let rec (treeref_node_iter_with_parents2:
4943 (('a * ('a, 'b) treeref2 list ref) -> ('a list) -> unit) ->
4944 ('a, 'b) treeref2 -> unit) =
4945 fun f tree ->
4946 let rec aux acc tree =
4947 match tree with
4948 | LeafRef2 _ -> ()
4949 | NodeRef2 (n, xs) ->
4950 f (n, xs) acc ;
4951 !xs +> List.iter (aux (n::acc))
4952 in
4953 aux [] tree
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967 let find_treeref_with_parents_some f tree =
4968 let res = ref [] in
4969
4970 tree +> treeref_node_iter_with_parents (fun (n, xs) parents ->
4971 match f (n,xs) parents with
4972 | Some v -> push2 v res;
4973 | None -> ()
4974 );
4975 match !res with
4976 | [v] -> v
4977 | [] -> raise Not_found
4978 | x::y::zs -> raise Multi_found
4979
4980 let find_multi_treeref_with_parents_some f tree =
4981 let res = ref [] in
4982
4983 tree +> treeref_node_iter_with_parents (fun (n, xs) parents ->
4984 match f (n,xs) parents with
4985 | Some v -> push2 v res;
4986 | None -> ()
4987 );
4988 match !res with
4989 | [v] -> !res
4990 | [] -> raise Not_found
4991 | x::y::zs -> !res
4992
4993
4994 (*****************************************************************************)
4995 (* Graph. Have a look too at Ograph_*.mli *)
4996 (*****************************************************************************)
4997 (* todo: generalise to put in common (need 'edge (and 'c ?),
4998 * and take in param a display func, cos caml sux, no overloading of show :(
4999 * Simple impelemntation. Can do also matrix, or adjacent list, or pointer(ref)
5000 * todo: do some check (dont exist already, ...)
5001 *)
5002
5003 type 'node graph = ('node set) * (('node * 'node) set)
5004
5005 let (add_node: 'a -> 'a graph -> 'a graph) = fun node (nodes, arcs) ->
5006 (node::nodes, arcs)
5007
5008 let (del_node: 'a -> 'a graph -> 'a graph) = fun node (nodes, arcs) ->
5009 (nodes $-$ set [node], arcs)
5010 (* could do more job:
5011 let _ = assert (successors node (nodes, arcs) = empty) in
5012 +> List.filter (fun (src, dst) -> dst != node))
5013 *)
5014 let (add_arc: ('a * 'a) -> 'a graph -> 'a graph) = fun arc (nodes, arcs) ->
5015 (nodes, set [arc] $+$ arcs)
5016
5017 let (del_arc: ('a * 'a) -> 'a graph -> 'a graph) = fun arc (nodes, arcs) ->
5018 (nodes, arcs +> List.filter (fun a -> not (arc =*= a)))
5019
5020 let (successors: 'a -> 'a graph -> 'a set) = fun x (nodes, arcs) ->
5021 arcs +> List.filter (fun (src, dst) -> src =*= x) +> List.map snd
5022
5023 let (predecessors: 'a -> 'a graph -> 'a set) = fun x (nodes, arcs) ->
5024 arcs +> List.filter (fun (src, dst) -> dst =*= x) +> List.map fst
5025
5026 let (nodes: 'a graph -> 'a set) = fun (nodes, arcs) -> nodes
5027
5028 (* pre: no cycle *)
5029 let rec (fold_upward: ('b -> 'a -> 'b) -> 'a set -> 'b -> 'a graph -> 'b) =
5030 fun f xs acc graph ->
5031 match xs with
5032 | [] -> acc
5033 | x::xs -> (f acc x)
5034 +> (fun newacc -> fold_upward f (graph +> predecessors x) newacc graph)
5035 +> (fun newacc -> fold_upward f xs newacc graph)
5036 (* TODO avoid already visited *)
5037
5038 let empty_graph = ([], [])
5039
5040
5041
5042 (*
5043 let (add_arcs_toward: int -> (int list) -> 'a graph -> 'a graph) = fun i xs ->
5044 function
5045 (nodes, arcs) -> (nodes, (List.map (fun j -> (j,i) ) xs)++arcs)
5046 let (del_arcs_toward: int -> (int list) -> 'a graph -> 'a graph)= fun i xs g ->
5047 List.fold_left (fun acc el -> del_arc (el, i) acc) g xs
5048 let (add_arcs_from: int -> (int list) -> 'a graph -> 'a graph) = fun i xs ->
5049 function
5050 (nodes, arcs) -> (nodes, (List.map (fun j -> (i,j) ) xs)++arcs)
5051
5052
5053 let (del_node: (int * 'node) -> 'node graph -> 'node graph) = fun node ->
5054 function (nodes, arcs) ->
5055 let newnodes = List.filter (fun a -> not (node = a)) nodes in
5056 if newnodes = nodes then (raise Not_found) else (newnodes, arcs)
5057 let (replace_node: int -> 'node -> 'node graph -> 'node graph) = fun i n ->
5058 function (nodes, arcs) ->
5059 let newnodes = List.filter (fun (j,_) -> not (i = j)) nodes in
5060 ((i,n)::newnodes, arcs)
5061 let (get_node: int -> 'node graph -> 'node) = fun i -> function
5062 (nodes, arcs) -> List.assoc i nodes
5063
5064 let (get_free: 'a graph -> int) = function
5065 (nodes, arcs) -> (maximum (List.map fst nodes))+1
5066 (* require no cycle !!
5067 TODO if cycle check that we have already visited a node *)
5068 let rec (succ_all: int -> 'a graph -> (int list)) = fun i -> function
5069 (nodes, arcs) as g ->
5070 let direct = succ i g in
5071 union direct (union_list (List.map (fun i -> succ_all i g) direct))
5072 let rec (pred_all: int -> 'a graph -> (int list)) = fun i -> function
5073 (nodes, arcs) as g ->
5074 let direct = pred i g in
5075 union direct (union_list (List.map (fun i -> pred_all i g) direct))
5076 (* require that the nodes are different !! *)
5077 let rec (equal: 'a graph -> 'a graph -> bool) = fun g1 g2 ->
5078 let ((nodes1, arcs1),(nodes2, arcs2)) = (g1,g2) in
5079 try
5080 (* do 2 things, check same length and to assoc *)
5081 let conv = assoc_map nodes1 nodes2 in
5082 List.for_all (fun (i1,i2) ->
5083 List.mem (List.assoc i1 conv, List.assoc i2 conv) arcs2)
5084 arcs1
5085 && (List.length arcs1 = List.length arcs2)
5086 (* could think that only forall is needed, but need check same lenth too*)
5087 with _ -> false
5088
5089 let (display: 'a graph -> ('a -> unit) -> unit) = fun g display_func ->
5090 let rec aux depth i =
5091 print_n depth " ";
5092 print_int i; print_string "->"; display_func (get_node i g);
5093 print_string "\n";
5094 List.iter (aux (depth+2)) (succ i g)
5095 in aux 0 1
5096
5097 let (display_dot: 'a graph -> ('a -> string) -> unit)= fun (nodes,arcs) func ->
5098 let file = open_out "test.dot" in
5099 output_string file "digraph misc {\n" ;
5100 List.iter (fun (n, node) ->
5101 output_int file n; output_string file " [label=\"";
5102 output_string file (func node); output_string file " \"];\n"; ) nodes;
5103 List.iter (fun (i1,i2) -> output_int file i1 ; output_string file " -> " ;
5104 output_int file i2 ; output_string file " ;\n"; ) arcs;
5105 output_string file "}\n" ;
5106 close_out file;
5107 let status = Unix.system "viewdot test.dot" in
5108 ()
5109 (* todo: faire = graphe (int can change !!! => cant make simply =)
5110 reassign number first !!
5111 *)
5112
5113 (* todo: mettre diff(modulo = !!) en rouge *)
5114 let (display_dot2: 'a graph -> 'a graph -> ('a -> string) -> unit) =
5115 fun (nodes1, arcs1) (nodes2, arcs2) func ->
5116 let file = open_out "test.dot" in
5117 output_string file "digraph misc {\n" ;
5118 output_string file "rotate = 90;\n";
5119 List.iter (fun (n, node) ->
5120 output_string file "100"; output_int file n;
5121 output_string file " [label=\"";
5122 output_string file (func node); output_string file " \"];\n"; ) nodes1;
5123 List.iter (fun (n, node) ->
5124 output_string file "200"; output_int file n;
5125 output_string file " [label=\"";
5126 output_string file (func node); output_string file " \"];\n"; ) nodes2;
5127 List.iter (fun (i1,i2) ->
5128 output_string file "100"; output_int file i1 ; output_string file " -> " ;
5129 output_string file "100"; output_int file i2 ; output_string file " ;\n";
5130 )
5131 arcs1;
5132 List.iter (fun (i1,i2) ->
5133 output_string file "200"; output_int file i1 ; output_string file " -> " ;
5134 output_string file "200"; output_int file i2 ; output_string file " ;\n"; )
5135 arcs2;
5136 (* output_string file "500 -> 1001; 500 -> 2001}\n" ; *)
5137 output_string file "}\n" ;
5138 close_out file;
5139 let status = Unix.system "viewdot test.dot" in
5140 ()
5141
5142
5143 *)
5144 (*****************************************************************************)
5145 (* Generic op *)
5146 (*****************************************************************************)
5147 (* overloading *)
5148
5149 let map = List.map (* note: really really slow, use rev_map if possible *)
5150 let filter = List.filter
5151 let fold = List.fold_left
5152 let member = List.mem
5153 let iter = List.iter
5154 let find = List.find
5155 let exists = List.exists
5156 let forall = List.for_all
5157 let big_union f xs = xs +> map f +> fold union_set empty_set
5158 (* let empty = [] *)
5159 let empty_list = []
5160 let sort = List.sort
5161 let length = List.length
5162 (* in prelude now: let null xs = match xs with [] -> true | _ -> false *)
5163 let head = List.hd
5164 let tail = List.tl
5165 let is_singleton = fun xs -> List.length xs =|= 1
5166
5167 (*****************************************************************************)
5168 (* Geometry (raytracer) *)
5169 (*****************************************************************************)
5170
5171 type vector = (float * float * float)
5172 type point = vector
5173 type color = vector (* color(0-1) *)
5174
5175 (* todo: factorise *)
5176 let (dotproduct: vector * vector -> float) =
5177 fun ((x1,y1,z1),(x2,y2,z2)) -> (x1*.x2 +. y1*.y2 +. z1*.z2)
5178 let (vector_length: vector -> float) =
5179 fun (x,y,z) -> sqrt (square x +. square y +. square z)
5180 let (minus_point: point * point -> vector) =
5181 fun ((x1,y1,z1),(x2,y2,z2)) -> ((x1 -. x2),(y1 -. y2),(z1 -. z2))
5182 let (distance: point * point -> float) =
5183 fun (x1, x2) -> vector_length (minus_point (x2,x1))
5184 let (normalise: vector -> vector) =
5185 fun (x,y,z) ->
5186 let len = vector_length (x,y,z) in (x /. len, y /. len, z /. len)
5187 let (mult_coeff: vector -> float -> vector) =
5188 fun (x,y,z) c -> (x *. c, y *. c, z *. c)
5189 let (add_vector: vector -> vector -> vector) =
5190 fun v1 v2 -> let ((x1,y1,z1),(x2,y2,z2)) = (v1,v2) in
5191 (x1+.x2, y1+.y2, z1+.z2)
5192 let (mult_vector: vector -> vector -> vector) =
5193 fun v1 v2 -> let ((x1,y1,z1),(x2,y2,z2)) = (v1,v2) in
5194 (x1*.x2, y1*.y2, z1*.z2)
5195 let sum_vector = List.fold_left add_vector (0.0,0.0,0.0)
5196
5197 (*****************************************************************************)
5198 (* Pics (raytracer) *)
5199 (*****************************************************************************)
5200
5201 type pixel = (int * int * int) (* RGB *)
5202
5203 (* required pixel list in row major order, line after line *)
5204 let (write_ppm: int -> int -> (pixel list) -> string -> unit) = fun
5205 width height xs filename ->
5206 let chan = open_out filename in
5207 begin
5208 output_string chan "P6\n";
5209 output_string chan ((string_of_int width) ^ "\n");
5210 output_string chan ((string_of_int height) ^ "\n");
5211 output_string chan "255\n";
5212 List.iter (fun (r,g,b) ->
5213 List.iter (fun byt -> output_byte chan byt) [r;g;b]
5214 ) xs;
5215 close_out chan
5216 end
5217
5218 let test_ppm1 () = write_ppm 100 100
5219 ((generate (50*100) (1,45,100)) ++ (generate (50*100) (1,1,100)))
5220 "img.ppm"
5221
5222 (*****************************************************************************)
5223 (* Diff (lfs) *)
5224 (*****************************************************************************)
5225 type diff = Match | BnotinA | AnotinB
5226
5227 let (diff: (int -> int -> diff -> unit)-> (string list * string list) -> unit)=
5228 fun f (xs,ys) ->
5229 let file1 = "/tmp/diff1-" ^ (string_of_int (Unix.getuid ())) in
5230 let file2 = "/tmp/diff2-" ^ (string_of_int (Unix.getuid ())) in
5231 let fileresult = "/tmp/diffresult-" ^ (string_of_int (Unix.getuid ())) in
5232 write_file file1 (unwords xs);
5233 write_file file2 (unwords ys);
5234 command2
5235 ("diff --side-by-side -W 1 " ^ file1 ^ " " ^ file2 ^ " > " ^ fileresult);
5236 let res = cat fileresult in
5237 let a = ref 0 in
5238 let b = ref 0 in
5239 res +> List.iter (fun s ->
5240 match s with
5241 | ("" | " ") -> f !a !b Match; incr a; incr b;
5242 | ">" -> f !a !b BnotinA; incr b;
5243 | ("|" | "/" | "\\" ) ->
5244 f !a !b BnotinA; f !a !b AnotinB; incr a; incr b;
5245 | "<" -> f !a !b AnotinB; incr a;
5246 | _ -> raise Impossible
5247 )
5248 (*
5249 let _ =
5250 diff
5251 ["0";"a";"b";"c";"d"; "f";"g";"h";"j";"q"; "z"]
5252 [ "a";"b";"c";"d";"e";"f";"g";"i";"j";"k";"r";"x";"y";"z"]
5253 (fun x y -> pr "match")
5254 (fun x y -> pr "a_not_in_b")
5255 (fun x y -> pr "b_not_in_a")
5256 *)
5257
5258 let (diff2: (int -> int -> diff -> unit) -> (string * string) -> unit) =
5259 fun f (xstr,ystr) ->
5260 write_file "/tmp/diff1" xstr;
5261 write_file "/tmp/diff2" ystr;
5262 command2
5263 ("diff --side-by-side --left-column -W 1 " ^
5264 "/tmp/diff1 /tmp/diff2 > /tmp/diffresult");
5265 let res = cat "/tmp/diffresult" in
5266 let a = ref 0 in
5267 let b = ref 0 in
5268 res +> List.iter (fun s ->
5269 match s with
5270 | "(" -> f !a !b Match; incr a; incr b;
5271 | ">" -> f !a !b BnotinA; incr b;
5272 | "|" -> f !a !b BnotinA; f !a !b AnotinB; incr a; incr b;
5273 | "<" -> f !a !b AnotinB; incr a;
5274 | _ -> raise Impossible
5275 )
5276
5277
5278 (*****************************************************************************)
5279 (* Parsers (aop-colcombet) *)
5280 (*****************************************************************************)
5281
5282 let parserCommon lexbuf parserer lexer =
5283 try
5284 let result = parserer lexer lexbuf in
5285 result
5286 with Parsing.Parse_error ->
5287 print_string "buf: "; print_string lexbuf.Lexing.lex_buffer;
5288 print_string "\n";
5289 print_string "current: "; print_int lexbuf.Lexing.lex_curr_pos;
5290 print_string "\n";
5291 raise Parsing.Parse_error
5292
5293
5294 (* marche pas ca neuneu *)
5295 (*
5296 let getDoubleParser parserer lexer string =
5297 let lexbuf1 = Lexing.from_string string in
5298 let chan = open_in string in
5299 let lexbuf2 = Lexing.from_channel chan in
5300 (parserCommon lexbuf1 parserer lexer , parserCommon lexbuf2 parserer lexer )
5301 *)
5302
5303 let getDoubleParser parserer lexer =
5304 (
5305 (function string ->
5306 let lexbuf1 = Lexing.from_string string in
5307 parserCommon lexbuf1 parserer lexer
5308 ),
5309 (function string ->
5310 let chan = open_in string in
5311 let lexbuf2 = Lexing.from_channel chan in
5312 parserCommon lexbuf2 parserer lexer
5313 ))
5314
5315
5316 (*****************************************************************************)
5317 (* parser combinators *)
5318 (*****************************************************************************)
5319
5320 (* cf parser_combinators.ml
5321 *
5322 * Could also use ocaml stream. but not backtrack and forced to do LL,
5323 * so combinators are better.
5324 *
5325 *)
5326
5327
5328 (*****************************************************************************)
5329 (* Parser related (cocci) *)
5330 (*****************************************************************************)
5331
5332 type parse_info = {
5333 str: string;
5334 charpos: int;
5335
5336 line: int;
5337 column: int;
5338 file: filename;
5339 }
5340 (* with sexp *)
5341
5342 let fake_parse_info = {
5343 charpos = -1; str = "";
5344 line = -1; column = -1; file = "";
5345 }
5346
5347 let string_of_parse_info x =
5348 spf "%s at %s:%d:%d" x.str x.file x.line x.column
5349 let string_of_parse_info_bis x =
5350 spf "%s:%d:%d" x.file x.line x.column
5351
5352 let (info_from_charpos2: int -> filename -> (int * int * string)) =
5353 fun charpos filename ->
5354
5355 (* Currently lexing.ml does not handle the line number position.
5356 * Even if there is some fields in the lexing structure, they are not
5357 * maintained by the lexing engine :( So the following code does not work:
5358 * let pos = Lexing.lexeme_end_p lexbuf in
5359 * sprintf "at file %s, line %d, char %d" pos.pos_fname pos.pos_lnum
5360 * (pos.pos_cnum - pos.pos_bol) in
5361 * Hence this function to overcome the previous limitation.
5362 *)
5363 let chan = open_in filename in
5364 let linen = ref 0 in
5365 let posl = ref 0 in
5366 let rec charpos_to_pos_aux last_valid =
5367 let s =
5368 try Some (input_line chan)
5369 with End_of_file when charpos =|= last_valid -> None in
5370 incr linen;
5371 match s with
5372 Some s ->
5373 let s = s ^ "\n" in
5374 if (!posl + slength s > charpos)
5375 then begin
5376 close_in chan;
5377 (!linen, charpos - !posl, s)
5378 end
5379 else begin
5380 posl := !posl + slength s;
5381 charpos_to_pos_aux !posl;
5382 end
5383 | None -> (!linen, charpos - !posl, "\n")
5384 in
5385 let res = charpos_to_pos_aux 0 in
5386 close_in chan;
5387 res
5388
5389 let info_from_charpos a b =
5390 profile_code "Common.info_from_charpos" (fun () -> info_from_charpos2 a b)
5391
5392
5393
5394 let full_charpos_to_pos2 = fun filename ->
5395
5396 let size = (filesize filename + 2) in
5397
5398 let arr = Array.create size (0,0) in
5399
5400 let chan = open_in filename in
5401
5402 let charpos = ref 0 in
5403 let line = ref 0 in
5404
5405 let rec full_charpos_to_pos_aux () =
5406 try
5407 let s = (input_line chan) in
5408 incr line;
5409
5410 (* '... +1 do' cos input_line dont return the trailing \n *)
5411 for i = 0 to (slength s - 1) + 1 do
5412 arr.(!charpos + i) <- (!line, i);
5413 done;
5414 charpos := !charpos + slength s + 1;
5415 full_charpos_to_pos_aux();
5416
5417 with End_of_file ->
5418 for i = !charpos to Array.length arr - 1 do
5419 arr.(i) <- (!line, 0);
5420 done;
5421 ();
5422 in
5423 begin
5424 full_charpos_to_pos_aux ();
5425 close_in chan;
5426 arr
5427 end
5428 let full_charpos_to_pos a =
5429 profile_code "Common.full_charpos_to_pos" (fun () -> full_charpos_to_pos2 a)
5430
5431 let test_charpos file =
5432 full_charpos_to_pos file +> dump +> pr2
5433
5434
5435
5436 let complete_parse_info filename table x =
5437 { x with
5438 file = filename;
5439 line = fst (table.(x.charpos));
5440 column = snd (table.(x.charpos));
5441 }
5442
5443
5444
5445 let full_charpos_to_pos_large2 = fun filename ->
5446
5447 let size = (filesize filename + 2) in
5448
5449 (* old: let arr = Array.create size (0,0) in *)
5450 let arr1 = Bigarray.Array1.create
5451 Bigarray.int Bigarray.c_layout size in
5452 let arr2 = Bigarray.Array1.create
5453 Bigarray.int Bigarray.c_layout size in
5454 Bigarray.Array1.fill arr1 0;
5455 Bigarray.Array1.fill arr2 0;
5456
5457 let chan = open_in filename in
5458
5459 let charpos = ref 0 in
5460 let line = ref 0 in
5461
5462 let rec full_charpos_to_pos_aux () =
5463 try
5464 let s = (input_line chan) in
5465 incr line;
5466
5467 (* '... +1 do' cos input_line dont return the trailing \n *)
5468 for i = 0 to (slength s - 1) + 1 do
5469 (* old: arr.(!charpos + i) <- (!line, i); *)
5470 arr1.{!charpos + i} <- (!line);
5471 arr2.{!charpos + i} <- i;
5472 done;
5473 charpos := !charpos + slength s + 1;
5474 full_charpos_to_pos_aux();
5475
5476 with End_of_file ->
5477 for i = !charpos to (* old: Array.length arr *)
5478 Bigarray.Array1.dim arr1 - 1 do
5479 (* old: arr.(i) <- (!line, 0); *)
5480 arr1.{i} <- !line;
5481 arr2.{i} <- 0;
5482 done;
5483 ();
5484 in
5485 begin
5486 full_charpos_to_pos_aux ();
5487 close_in chan;
5488 (fun i -> arr1.{i}, arr2.{i})
5489 end
5490 let full_charpos_to_pos_large a =
5491 profile_code "Common.full_charpos_to_pos_large"
5492 (fun () -> full_charpos_to_pos_large2 a)
5493
5494
5495 let complete_parse_info_large filename table x =
5496 { x with
5497 file = filename;
5498 line = fst (table (x.charpos));
5499 column = snd (table (x.charpos));
5500 }
5501
5502 (*---------------------------------------------------------------------------*)
5503 (* Decalage is here to handle stuff such as cpp which include file and who
5504 * can make shift.
5505 *)
5506 let (error_messagebis: filename -> (string * int) -> int -> string)=
5507 fun filename (lexeme, lexstart) decalage ->
5508
5509 let charpos = lexstart + decalage in
5510 let tok = lexeme in
5511 let (line, pos, linecontent) = info_from_charpos charpos filename in
5512 sprintf "File \"%s\", line %d, column %d, charpos = %d
5513 around = '%s', whole content = %s"
5514 filename line pos charpos tok (chop linecontent)
5515
5516 let error_message = fun filename (lexeme, lexstart) ->
5517 try error_messagebis filename (lexeme, lexstart) 0
5518 with
5519 End_of_file ->
5520 ("PB in Common.error_message, position " ^ i_to_s lexstart ^
5521 " given out of file:" ^ filename)
5522
5523
5524
5525 let error_message_short = fun filename (lexeme, lexstart) ->
5526 try
5527 let charpos = lexstart in
5528 let (line, pos, linecontent) = info_from_charpos charpos filename in
5529 sprintf "File \"%s\", line %d" filename line
5530
5531 with End_of_file ->
5532 begin
5533 ("PB in Common.error_message, position " ^ i_to_s lexstart ^
5534 " given out of file:" ^ filename);
5535 end
5536
5537
5538
5539 (*****************************************************************************)
5540 (* Regression testing bis (cocci) *)
5541 (*****************************************************************************)
5542
5543 (* todo: keep also size of file, compute md5sum ? cos maybe the file
5544 * has changed!.
5545 *
5546 * todo: could also compute the date, or some version info of the program,
5547 * can record the first date when was found a OK, the last date where
5548 * was ok, and then first date when found fail. So the
5549 * Common.Ok would have more information that would be passed
5550 * to the Common.Pb of date * date * date * string peut etre.
5551 *
5552 * todo? maybe use plain text file instead of marshalling.
5553 *)
5554
5555 type score_result = Ok | Pb of string
5556 (* with sexp *)
5557 type score = (string (* usually a filename *), score_result) Hashtbl.t
5558 (* with sexp *)
5559 type score_list = (string (* usually a filename *) * score_result) list
5560 (* with sexp *)
5561
5562 let empty_score () = (Hashtbl.create 101 : score)
5563
5564
5565
5566 let regression_testing_vs newscore bestscore =
5567
5568 let newbestscore = empty_score () in
5569
5570 let allres =
5571 (hash_to_list newscore +> List.map fst)
5572 $+$
5573 (hash_to_list bestscore +> List.map fst)
5574 in
5575 begin
5576 allres +> List.iter (fun res ->
5577 match
5578 optionise (fun () -> Hashtbl.find newscore res),
5579 optionise (fun () -> Hashtbl.find bestscore res)
5580 with
5581 | None, None -> raise Impossible
5582 | Some x, None ->
5583 Printf.printf "new test file appeared: %s\n" res;
5584 Hashtbl.add newbestscore res x;
5585 | None, Some x ->
5586 Printf.printf "old test file disappeared: %s\n" res;
5587 | Some newone, Some bestone ->
5588 (match newone, bestone with
5589 | Ok, Ok ->
5590 Hashtbl.add newbestscore res Ok
5591 | Pb x, Ok ->
5592 Printf.printf
5593 "PBBBBBBBB: a test file does not work anymore!!! : %s\n" res;
5594 Printf.printf "Error : %s\n" x;
5595 Hashtbl.add newbestscore res Ok
5596 | Ok, Pb x ->
5597 Printf.printf "Great: a test file now works: %s\n" res;
5598 Hashtbl.add newbestscore res Ok
5599 | Pb x, Pb y ->
5600 Hashtbl.add newbestscore res (Pb x);
5601 if not (x =$= y)
5602 then begin
5603 Printf.printf
5604 "Semipb: still error but not same error : %s\n" res;
5605 Printf.printf "%s\n" (chop ("Old error: " ^ y));
5606 Printf.printf "New error: %s\n" x;
5607 end
5608 )
5609 );
5610 flush stdout; flush stderr;
5611 newbestscore
5612 end
5613
5614 let regression_testing newscore best_score_file =
5615
5616 pr2 ("regression file: "^ best_score_file);
5617 let (bestscore : score) =
5618 if not (Sys.file_exists best_score_file)
5619 then write_value (empty_score()) best_score_file;
5620 get_value best_score_file
5621 in
5622 let newbestscore = regression_testing_vs newscore bestscore in
5623 write_value newbestscore (best_score_file ^ ".old");
5624 write_value newbestscore best_score_file;
5625 ()
5626
5627
5628
5629
5630 let string_of_score_result v =
5631 match v with
5632 | Ok -> "Ok"
5633 | Pb s -> "Pb: " ^ s
5634
5635 let total_scores score =
5636 let total = hash_to_list score +> List.length in
5637 let good = hash_to_list score +> List.filter
5638 (fun (s, v) -> v =*= Ok) +> List.length in
5639 good, total
5640
5641
5642 let print_total_score score =
5643 pr2 "--------------------------------";
5644 pr2 "total score";
5645 pr2 "--------------------------------";
5646 let (good, total) = total_scores score in
5647 pr2 (sprintf "good = %d/%d" good total)
5648
5649 let print_score score =
5650 score +> hash_to_list +> List.iter (fun (k, v) ->
5651 pr2 (sprintf "% s --> %s" k (string_of_score_result v))
5652 );
5653 print_total_score score;
5654 ()
5655
5656
5657 (*****************************************************************************)
5658 (* Scope managment (cocci) *)
5659 (*****************************************************************************)
5660
5661 (* could also make a function Common.make_scope_functions that return
5662 * the new_scope, del_scope, do_in_scope, add_env. Kind of functor :)
5663 *)
5664
5665 type ('a, 'b) scoped_env = ('a, 'b) assoc list
5666
5667 (*
5668 let rec lookup_env f env =
5669 match env with
5670 | [] -> raise Not_found
5671 | []::zs -> lookup_env f zs
5672 | (x::xs)::zs ->
5673 match f x with
5674 | None -> lookup_env f (xs::zs)
5675 | Some y -> y
5676
5677 let member_env_key k env =
5678 try
5679 let _ = lookup_env (fun (k',v) -> if k = k' then Some v else None) env in
5680 true
5681 with Not_found -> false
5682
5683 *)
5684
5685 let rec lookup_env k env =
5686 match env with
5687 | [] -> raise Not_found
5688 | []::zs -> lookup_env k zs
5689 | ((k',v)::xs)::zs ->
5690 if k =*= k'
5691 then v
5692 else lookup_env k (xs::zs)
5693
5694 let member_env_key k env =
5695 match optionise (fun () -> lookup_env k env) with
5696 | None -> false
5697 | Some _ -> true
5698
5699
5700 let new_scope scoped_env = scoped_env := []::!scoped_env
5701 let del_scope scoped_env = scoped_env := List.tl !scoped_env
5702
5703 let do_in_new_scope scoped_env f =
5704 begin
5705 new_scope scoped_env;
5706 let res = f() in
5707 del_scope scoped_env;
5708 res
5709 end
5710
5711 let add_in_scope scoped_env def =
5712 let (current, older) = uncons !scoped_env in
5713 scoped_env := (def::current)::older
5714
5715
5716
5717
5718
5719 (* note that ocaml hashtbl store also old value of a binding when add
5720 * add a newbinding; that's why del_scope works
5721 *)
5722
5723 type ('a, 'b) scoped_h_env = {
5724 scoped_h : ('a, 'b) Hashtbl.t;
5725 scoped_list : ('a, 'b) assoc list;
5726 }
5727
5728 let empty_scoped_h_env () = {
5729 scoped_h = Hashtbl.create 101;
5730 scoped_list = [[]];
5731 }
5732 let clone_scoped_h_env x =
5733 { scoped_h = Hashtbl.copy x.scoped_h;
5734 scoped_list = x.scoped_list;
5735 }
5736
5737 let rec lookup_h_env k env =
5738 Hashtbl.find env.scoped_h k
5739
5740 let member_h_env_key k env =
5741 match optionise (fun () -> lookup_h_env k env) with
5742 | None -> false
5743 | Some _ -> true
5744
5745
5746 let new_scope_h scoped_env =
5747 scoped_env := {!scoped_env with scoped_list = []::!scoped_env.scoped_list}
5748 let del_scope_h scoped_env =
5749 begin
5750 List.hd !scoped_env.scoped_list +> List.iter (fun (k, v) ->
5751 Hashtbl.remove !scoped_env.scoped_h k
5752 );
5753 scoped_env := {!scoped_env with scoped_list =
5754 List.tl !scoped_env.scoped_list
5755 }
5756 end
5757
5758 let do_in_new_scope_h scoped_env f =
5759 begin
5760 new_scope_h scoped_env;
5761 let res = f() in
5762 del_scope_h scoped_env;
5763 res
5764 end
5765
5766 (*
5767 let add_in_scope scoped_env def =
5768 let (current, older) = uncons !scoped_env in
5769 scoped_env := (def::current)::older
5770 *)
5771
5772 let add_in_scope_h x (k,v) =
5773 begin
5774 Hashtbl.add !x.scoped_h k v;
5775 x := { !x with scoped_list =
5776 ((k,v)::(List.hd !x.scoped_list))::(List.tl !x.scoped_list);
5777 };
5778 end
5779
5780 (*****************************************************************************)
5781 (* Terminal *)
5782 (*****************************************************************************)
5783
5784 (* let ansi_terminal = ref true *)
5785
5786 let (_execute_and_show_progress_func: (int (* length *) -> ((unit -> unit) -> unit) -> unit) ref)
5787 = ref
5788 (fun a b ->
5789 failwith "no execute yet, have you included common_extra.cmo?"
5790 )
5791
5792
5793
5794 let execute_and_show_progress len f =
5795 !_execute_and_show_progress_func len f
5796
5797
5798 (* now in common_extra.ml:
5799 * let execute_and_show_progress len f = ...
5800 *)
5801
5802 (*****************************************************************************)
5803 (* Random *)
5804 (*****************************************************************************)
5805
5806 let _init_random = Random.self_init ()
5807 (*
5808 let random_insert i l =
5809 let p = Random.int (length l +1)
5810 in let rec insert i p l =
5811 if (p = 0) then i::l else (hd l)::insert i (p-1) (tl l)
5812 in insert i p l
5813
5814 let rec randomize_list = function
5815 [] -> []
5816 | a::l -> random_insert a (randomize_list l)
5817 *)
5818 let random_list xs =
5819 List.nth xs (Random.int (length xs))
5820
5821 (* todo_opti: use fisher/yates algorithm.
5822 * ref: http://en.wikipedia.org/wiki/Knuth_shuffle
5823 *
5824 * public static void shuffle (int[] array)
5825 * {
5826 * Random rng = new Random ();
5827 * int n = array.length;
5828 * while (--n > 0)
5829 * {
5830 * int k = rng.nextInt(n + 1); // 0 <= k <= n (!)
5831 * int temp = array[n];
5832 * array[n] = array[k];
5833 * array[k] = temp;
5834 * }
5835 * }
5836
5837 *)
5838 let randomize_list xs =
5839 let permut = permutation xs in
5840 random_list permut
5841
5842
5843
5844 let random_subset_of_list num xs =
5845 let array = Array.of_list xs in
5846 let len = Array.length array in
5847
5848 let h = Hashtbl.create 101 in
5849 let cnt = ref num in
5850 while !cnt > 0 do
5851 let x = Random.int len in
5852 if not (Hashtbl.mem h (array.(x))) (* bugfix2: not just x :) *)
5853 then begin
5854 Hashtbl.add h (array.(x)) true; (* bugfix1: not just x :) *)
5855 decr cnt;
5856 end
5857 done;
5858 let objs = hash_to_list h +> List.map fst in
5859 objs
5860
5861
5862
5863 (*****************************************************************************)
5864 (* Flags and actions *)
5865 (*****************************************************************************)
5866
5867 (* I put it inside a func as it can help to give a chance to
5868 * change the globals before getting the options as some
5869 * options sometimes may want to show the default value.
5870 *)
5871 let cmdline_flags_devel () =
5872 [
5873 "-debugger", Arg.Set debugger ,
5874 " option to set if launched inside ocamldebug";
5875 "-profile", Arg.Unit (fun () -> profile := PALL),
5876 " gather timing information about important functions";
5877 ]
5878 let cmdline_flags_verbose () =
5879 [
5880 "-verbose_level", Arg.Set_int verbose_level,
5881 " <int> guess what";
5882 "-disable_pr2_once", Arg.Set disable_pr2_once,
5883 " to print more messages";
5884 "-show_trace_profile", Arg.Set show_trace_profile,
5885 " show trace";
5886 ]
5887
5888 let cmdline_flags_other () =
5889 [
5890 "-nocheck_stack", Arg.Clear check_stack,
5891 " ";
5892 "-batch_mode", Arg.Set _batch_mode,
5893 " no interactivity"
5894 ]
5895
5896 (* potentially other common options but not yet integrated:
5897
5898 "-timeout", Arg.Set_int timeout,
5899 " <sec> interrupt LFS or buggy external plugins";
5900
5901 (* can't be factorized because of the $ cvs stuff, we want the date
5902 * of the main.ml file, not common.ml
5903 *)
5904 "-version", Arg.Unit (fun () ->
5905 pr2 "version: _dollar_Date: 2008/06/14 00:54:22 _dollar_";
5906 raise (Common.UnixExit 0)
5907 ),
5908 " guess what";
5909
5910 "-shorthelp", Arg.Unit (fun () ->
5911 !short_usage_func();
5912 raise (Common.UnixExit 0)
5913 ),
5914 " see short list of options";
5915 "-longhelp", Arg.Unit (fun () ->
5916 !long_usage_func();
5917 raise (Common.UnixExit 0)
5918 ),
5919 "-help", Arg.Unit (fun () ->
5920 !long_usage_func();
5921 raise (Common.UnixExit 0)
5922 ),
5923 " ";
5924 "--help", Arg.Unit (fun () ->
5925 !long_usage_func();
5926 raise (Common.UnixExit 0)
5927 ),
5928 " ";
5929
5930 *)
5931
5932 let cmdline_actions () =
5933 [
5934 "-test_check_stack", " <limit>",
5935 mk_action_1_arg test_check_stack_size;
5936 ]
5937
5938
5939 (*****************************************************************************)
5940 (* Postlude *)
5941 (*****************************************************************************)
5942 (* stuff put here cos of of forward definition limitation of ocaml *)
5943
5944
5945 (* Infix trick, seen in jane street lib and harrop's code, and maybe in GMP *)
5946 module Infix = struct
5947 let (+>) = (+>)
5948 let (==~) = (==~)
5949 let (=~) = (=~)
5950 end
5951
5952
5953 let main_boilerplate f =
5954 if not (!Sys.interactive) then
5955 exn_to_real_unixexit (fun () ->
5956
5957 Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ ->
5958 pr2 "C-c intercepted, will do some cleaning before exiting";
5959 (* But if do some try ... with e -> and if do not reraise the exn,
5960 * the bubble never goes at top and so I cant really C-c.
5961 *
5962 * A solution would be to not raise, but do the erase_temp_file in the
5963 * syshandler, here, and then exit.
5964 * The current solution is to not do some wild try ... with e
5965 * by having in the exn handler a case: UnixExit x -> raise ... | e ->
5966 *)
5967 Sys.set_signal Sys.sigint Sys.Signal_default;
5968 raise (UnixExit (-1))
5969 ));
5970
5971 (* The finalize below makes it tedious to go back to exn when use
5972 * 'back' in the debugger. Hence this special case. But the
5973 * Common.debugger will be set in main(), so too late, so
5974 * have to be quicker
5975 *)
5976 if Sys.argv +> Array.to_list +> List.exists (fun x -> x =$= "-debugger")
5977 then debugger := true;
5978
5979 finalize (fun ()->
5980 pp_do_in_zero_box (fun () ->
5981 f(); (* <---- here it is *)
5982 ))
5983 (fun()->
5984 if !profile <> PNONE
5985 then pr2 (profile_diagnostic ());
5986 erase_temp_files ();
5987 )
5988 )
5989 (* let _ = if not !Sys.interactive then (main ()) *)
5990
5991
5992 (* based on code found in cameleon from maxence guesdon *)
5993 let md5sum_of_string s =
5994 let com = spf "echo %s | md5sum | cut -d\" \" -f 1"
5995 (Filename.quote s)
5996 in
5997 match cmd_to_list com with
5998 | [s] ->
5999 (*pr2 s;*)
6000 s
6001 | _ -> failwith "md5sum_of_string wrong output"
6002
6003
6004
6005 let with_pr2_to_string f =
6006 let file = new_temp_file "pr2" "out" in
6007 redirect_stdout_stderr file f;
6008 cat file
6009
6010 (* julia: convert something printed using format to print into a string *)
6011 let format_to_string f =
6012 let (nm,o) = Filename.open_temp_file "format_to_s" ".out" in
6013 Format.set_formatter_out_channel o;
6014 let _ = f() in
6015 Format.print_newline();
6016 Format.print_flush();
6017 Format.set_formatter_out_channel stdout;
6018 close_out o;
6019 let i = open_in nm in
6020 let lines = ref [] in
6021 let rec loop _ =
6022 let cur = input_line i in
6023 lines := cur :: !lines;
6024 loop() in
6025 (try loop() with End_of_file -> ());
6026 close_in i;
6027 command2 ("rm -f " ^ nm);
6028 String.concat "\n" (List.rev !lines)
6029
6030
6031
6032 (*****************************************************************************)
6033 (* Misc/test *)
6034 (*****************************************************************************)
6035
6036 let (generic_print: 'a -> string -> string) = fun v typ ->
6037 write_value v "/tmp/generic_print";
6038 command2
6039 ("printf 'let (v:" ^ typ ^ ")= Common.get_value \"/tmp/generic_print\" " ^
6040 " in v;;' " ^
6041 " | calc.top > /tmp/result_generic_print");
6042 cat "/tmp/result_generic_print"
6043 +> drop_while (fun e -> not (e =~ "^#.*")) +> tail
6044 +> unlines
6045 +> (fun s ->
6046 if (s =~ ".*= \\(.+\\)")
6047 then matched1 s
6048 else "error in generic_print, not good format:" ^ s)
6049
6050 (* let main () = pr (generic_print [1;2;3;4] "int list") *)
6051
6052 class ['a] olist (ys: 'a list) =
6053 object(o)
6054 val xs = ys
6055 method view = xs
6056 (* method fold f a = List.fold_left f a xs *)
6057 method fold : 'b. ('b -> 'a -> 'b) -> 'b -> 'b =
6058 fun f accu -> List.fold_left f accu xs
6059 end
6060
6061
6062 (* let _ = write_value ((new setb[])#add 1) "/tmp/test" *)
6063 let typing_sux_test () =
6064 let x = Obj.magic [1;2;3] in
6065 let f1 xs = List.iter print_int xs in
6066 let f2 xs = List.iter print_string xs in
6067 (f1 x; f2 x)
6068
6069 (* let (test: 'a osetb -> 'a ocollection) = fun o -> (o :> 'a ocollection) *)
6070 (* let _ = test (new osetb (Setb.empty)) *)