(* * Zack's own OCaml library -- set of "helpers" function for the OCaml language * * Copyright (C) 2003: * Stefano Zacchiroli * * This module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this module; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. *) open Printf ;; exception Not_implemented ;; let (newline, newline_len) = let default_newline = "\n" in let newline = match Sys.os_type with | "Unix" -> "\n" | "Win32" | "Cygwin" -> "\r\n" | "MacOS" -> "\r" | _ -> default_newline in (newline, String.length newline) ;; module ZLogic = struct let non pred x = not (pred x) ;; let conj p1 p2 x = (p1 x) && (p2 x) ;; let disj p1 p2 x = (p1 x) || (p2 x) ;; let imply p1 p2 x = (non p1) x || p2 x ;; let (&&&) = conj ;; let (|||) = disj ;; let (=>) = imply ;; end ;; module ZArray = struct exception Found of int;; (** return the index of first element in ary on which pred is true *) let index pred ary = try Array.iteri (fun idx e -> if pred e then raise (Found idx)) ary; raise Not_found with Found idx -> idx ;; (** as index but return the element itself instead of the index *) let find pred ary = ary.(index pred ary) ;; (** check if at least one element in ary satisfies pred *) let exists pred ary = try ignore (find pred ary); true with Not_found -> false ;; (** check if all elements in ary satisfy pred *) let for_all pred ary = not (exists (ZLogic.non pred) ary) ;; (** return a fresh array containing all elements of ary that satisfy pred *) let filter pred ary = let indexes = (* indexes of element on which pred is satisfied *) let (_, indexes) = Array.fold_left (fun (i, acc) e -> if pred e then (i+1, i::acc) else (i+1, acc)) (0, []) ary in List.rev indexes in let size = List.length indexes in let newary = Array.make size ary.(0) in let rec fill i = function | [] -> () | idx::tl -> newary.(i) <- ary.(idx); fill (i+1) tl in fill 0 indexes; newary ;; let lrotate () = raise Not_implemented; () ;; let rrotate () = raise Not_implemented; () ;; end module ZDbm = struct (** fold on dbm key and values, processing order is not specified *) let fold f init dbm = let res = ref init in Dbm.iter (fun key value -> res := f !res key value) dbm; !res ;; end module ZHashtbl = struct let keys tbl = Hashtbl.fold (fun key _ acc -> key :: acc) tbl [] ;; let values tbl = Hashtbl.fold (fun _ valu acc -> valu :: acc) tbl [] ;; let remove_all tbl key = for i = 1 to List.length (Hashtbl.find_all tbl key) do Hashtbl.remove tbl key done ;; end module ZList = struct (** tail recursive version of List.map *) let map' f l = let rec aux acc = function | [] -> List.rev acc | hd :: tl -> aux (f hd :: acc) tl in aux [] l ;; (** guarded map on lists. List.length output <= List.length input. Not tail recursive *) let rec map_if f pred = function | [] -> [] | hd::tl when pred hd -> f hd :: map_if f pred tl | hd::tl -> map_if f pred tl ;; (** tail recursive version of map_if *) let map_if' f pred l = let rec aux acc = function | [] -> List.rev acc | hd::tl when pred hd -> aux (f hd :: acc) tl | hd::tl -> aux acc tl in aux [] l ;; (** low level to implement assoc_all and assq_all *) let assoc_all_gen eq key list = let rec aux acc = function | [] -> acc | (k, v)::tl when (eq k key) -> aux (v :: acc) tl | _::tl -> aux acc tl in List.rev (aux [] list) ;; (** return all binding of k in association list l in the order they appear in l. Uses structural equality *) let assoc_all k l = assoc_all_gen (=) k l ;; (** as assoc_all but uses physical equality *) let assq_all k l = assoc_all_gen (==) k l ;; let lrotate = function | [] -> raise (Invalid_argument "Zack.List.lrotate") | hd::tl -> tl @ [hd] ;; let rrotate l = match List.rev l with | [] -> raise (Invalid_argument "Zack.List.rrotate") | hd::tl -> hd :: List.rev tl ;; end module ZSys = struct let copy () = raise Not_implemented; () ;; end module ZUnix = struct let mkdir () = raise Not_implemented; () ;; let get_stats follow_symlink = if follow_symlink then Unix.stat else Unix.lstat ;; (* low level for is_* predicates *) let is_file_kind follow_symlink kind fname = (get_stats follow_symlink fname).Unix.st_kind = kind ;; let is_regular ?(follow_symlink = true) = is_file_kind follow_symlink Unix.S_REG ;; let is_directory ?(follow_symlink = true) = is_file_kind follow_symlink Unix.S_DIR ;; let is_chardev ?(follow_symlink = true) = is_file_kind follow_symlink Unix.S_CHR ;; let is_blockdev ?(follow_symlink = true) = is_file_kind follow_symlink Unix.S_BLK ;; let is_symlink ?(follow_symlink = false) = is_file_kind follow_symlink Unix.S_LNK ;; let is_fifo ?(follow_symlink = true) = is_file_kind follow_symlink Unix.S_FIFO ;; let is_socket ?(follow_symlink = true) = is_file_kind follow_symlink Unix.S_SOCK ;; let size ?(follow_symlink = true) fname = (get_stats follow_symlink fname).Unix.st_size ;; (** return a list of all entries contained in a directory. Return order is not specified *) let ls dirname = let dir = Unix.opendir dirname in let rec aux acc = match (try Some (Unix.readdir dir) with End_of_file -> None) with | Some entry -> aux (entry :: acc) | None -> acc in let res = aux [] in Unix.closedir dir; res ;; end module ZString = struct (** string -> char list *) let explode s = let chars = ref [] in for i = String.length s - 1 downto 0 do chars := s.[i] :: !chars done; !chars ;; (** char list -> string *) let implode l = let buf = Buffer.create (List.length l) in let rec implode' = function | [] -> Buffer.contents buf | hd::tl -> Buffer.add_char buf hd; implode' tl in implode' l ;; (** perl's chomp, remove once trailing "\n", if any *) let chomp s = let len = String.length s in let diff = len - newline_len in if String.sub s diff newline_len = newline then (* trailing newline *) String.sub s 0 diff else s ;; (** map on string *) let map f s = for i = 0 to String.length s do s.[i] <- f s.[i] done ;; (** fold_left on string *) let fold_left f init s = let len = String.length s in let rec fold_left' idx acc = if idx = len then acc else (* idx < len *) fold_left' (idx + 1) (f acc s.[idx]) in fold_left' 0 init ;; (* TODO Non funge *) let fold_right f s init = let len = String.length s in let rec fold_right' idx acc = if idx < 0 then acc else (* idx >= 0 *) fold_right' (idx - 1) (f s.[idx] acc) in fold_right' len (init - 1) ;; (** iter on string *) let iter (f: char -> unit) = fold_left (fun _ c -> f c) () ;; (* let string_iter (f: char -> unit) s = for i = 0 to String.length s do f s.[i] done ;; *) let filter () = raise Not_implemented; () ;; (** create a string of length len and sets each char of them to the result of applying f to the char's index *) let init len f = let str = String.create len in for i = 0 to len - 1 do str.[i] <- f i done; str ;; end module ZRandom = struct type ranges = (int * int) list let digit_range = [48, 57] ;; let alpha_upper_range = [65, 90] ;; let alpha_lower_range = [97, 122] ;; let alpha_range = alpha_upper_range @ alpha_lower_range ;; let alphanum_range = digit_range @ alpha_range ;; let word_range = alphanum_range @ [95, 95] ;; (* alphanum + '_' *) let rec ranges_are_sane = function | (min, max) :: tl -> if min > max || min < 0 || max > 255 then failwith (sprintf "ZRandom: invalid range %d .. %d" min max); ranges_are_sane tl | [] -> () ;; let size_of_ranges = (* assumption: ranges are sane *) let rec aux acc = function | [] -> acc | ((min, max) as range) :: tl -> aux (acc + (max - min + 1)) tl in aux 0 ;; let nth_in_ranges idx ranges = (* assumption: ranges are sane *) if ranges = [] then failwith "ZRandom: no range provided"; let rec aux idx = function | [] -> assert false | (min, max) :: tl -> let nth = min + idx in if nth <= max then nth else aux (nth - max - 1) tl in aux idx ranges ;; (* low level for char and string *) let char' ranges = let int = Random.int (size_of_ranges ranges) in Char.chr (nth_in_ranges int ranges) ;; (** generate a random char inside provided ranges. Ranges are provided as a list of int pairs. Each pair represent an inclusive interval of possible character codes. Default range is [0, 255] *) let char ?(ranges = [0,255]) () = ranges_are_sane ranges; char' ranges ;; (** generate a string of random characters inside provided range *) let string ?(ranges = [0,255]) len = ranges_are_sane ranges; ZString.init len (fun _ -> char' ranges) ;; end module ZStream = struct (** map on streams. Beware that this function build stream using Stream.from. That kind of stream can't be mixed with ordinary streams *) let map f stream = Stream.from (fun _ -> try Some (f (Stream.next stream)) with Stream.Failure -> None) ;; (** fold on streams. Beware that this function build stream using Stream.from. That kind of stream can't be mixed with ordinary streams *) let rec fold f init stream = match (try Some (Stream.next stream) with Stream.Failure -> None) with | Some item -> fold f (f init item) stream | None -> init ;; (** given an input channel return the stream of its lines (without trailing new line) *) let of_inchan ic = Stream.from (fun _ -> try Some (input_line ic) with End_of_file -> None) ;; end (** fold_left on input channel lines *) let rec fold_in f init ic = match (try Some (input_line ic) with End_of_file -> None) with | Some l -> fold_in f (f init l) ic | None -> init ;; (** iter on input channel lines *) let iter_in f = fold_in (fun _ line -> f line) () ;; (** map on input channel lines *) let map_in f ic = List.rev (fold_in (fun acc line -> f line :: acc) [] ic) ;; (** return list of lines read from an input channel *) let input_lines ic = List.rev (fold_in (fun acc line -> line :: acc) [] ic) ;; (** read all data available on an input channel and return them as a string *) let input_all = let strlen = 8192 in let buflen = 8192 * 2 in let str = String.create strlen in fun ic -> let buf = Buffer.create buflen in let rec input' () = let bytes = input ic str 0 strlen in if bytes = 0 then (* EOF *) Buffer.contents buf else begin Buffer.add_substring buf str 0 bytes; input' () end in input' () ;; (** write a list of lines to an output channel. Newline is added at the end of each line *) let rec output_lines lines oc = match lines with | [] -> () | hd::tl -> output_string oc (hd ^ newline); output_lines tl oc ;; (** read_lines on stdin *) let read_lines () = input_lines stdin ;; (** read_all on stdin *) let read_all () = input_all stdin ;; (** Some constructor inverse *) let unsome = function | Some x -> x | None -> raise (Invalid_argument "Zack.unsome") ;; module Array = ZArray ;; module Dbm = ZDbm ;; module Hashtbl = ZHashtbl ;; module List = ZList ;; module Logic = ZLogic ;; module Random = ZRandom ;; module Stream = ZStream ;; module String = ZString ;; module Sys = ZSys ;; module Unix = ZUnix ;;