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