(** Extensions to the system String module *) let iteri f s = let i = ref 0 in String.iter (fun c -> f !i c; i := !i + 1) s let riteri f s = let n = String.length s - 1 in for i = n downto 0 do f i s.[i] done let mapi f s = let s' = (String.create (String.length s)) in iteri (fun i c -> s'.[i] <- f i s.[i]) s; s' let map f s = mapi (fun i c -> f c) s let smap (f : char -> string) (s : string) : string = let lst = ref [] in String.iter (fun c -> lst := f c :: !lst) s; String.concat "" (List.rev !lst) (** take "abcde" 3 -> "abc" *) let take s n = String.sub s 0 n (** drop "abcde" 3 -> "de" *) let drop s n = String.sub s n ((String.length s) - n) (** split "abcde" 3 -> ("abc", "de") *) let split s n = (take s n, drop s n) (** [has_prefix s1 s2] - Return length of common prefix *) let prefix_length s1 s2 = let rec loop n = try if s1.[n] = s2.[n] then loop (n+1) else n with _ -> n in loop 0 (** [has_prefix pre str] - Return true if str starts with pre *) let has_prefix pre str = prefix_length pre str = String.length pre (** Return the (possibly empty) common prefix of two strings *) let prefix s1 s2 = take s1 (prefix_length s1 s2) (** [drop_prefix pre str] - Drop the prefix pre from the string str, or raise Not_found if it doesn't have it. *) let drop_prefix pre str = if has_prefix pre str then drop str (String.length pre) else raise Not_found (** Return length of common suffix *) let suffix_length s1 s2 = let rec loop l n1 n2 = if n1 < 0 || n2 < 0 then l else if s1.[n1] <> s2.[n2] then l else loop (l+1) (n1-1) (n2-1) in loop 0 (String.length s1 - 1) (String.length s2 - 1) (** Return common suffix *) let suffix s1 s2 = let l = suffix_length s1 s2 in let p = String.length s1 - l in String.sub s1 p l (** [has_suffix suf str] - Does {i str} end with the suffix {i suf}? *) let has_suffix suf str = suffix_length suf str = String.length suf (** [drop_suffix suf s] - Drop suffix suf it it is at end of s, otherwise raise Not_found *) let drop_suffix suf str = if has_suffix suf str then String.sub str 0 ((String.length str) - (String.length suf)) else raise Not_found (** Return the tail of a string from first occurrance of a substring. [strstr str pat] @param str the string to operate on @param pat the substring to search for *) let strstr str pat = let l = String.length pat in try let rec loop n = let sub = String.sub str n l in if sub = pat then Some (drop str n) else loop (n+1) in loop 0 with Invalid_argument "String.sub" -> None (** {2 Regular Expression Fragments} *) (** white - (nonwhite with embedded white) - white *) let wnwre = Pcre2.xnx " \t" (** white - (optional nonwhite with embedded white) - white *) let wonwre = Pcre2.xonx " \t" (** {2 Libc equivalents and derivatives} *) exception Result of int let strspn s accept = try iteri (fun i c -> if not (String.contains accept s.[i]) then raise (Result i)) s; String.length s; with Result n -> n let strcspn s accept = try iteri (fun i c -> if String.contains accept s.[i] then raise (Result i)) s; String.length s with Result n -> n let concat2 s lst = String.concat s lst ^ s