move string-related utils into String_utils
[hiphop-php.git] / hphp / hack / src / utils / string_utils.ml
blob2022481c3d09b9bbd71e3f4fff1087ccbc203090
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
11 let soi = string_of_int
12 let string_of_char = String.make 1
14 let string_before s n = String.sub s 0 n
15 let string_after s n = String.sub s n (String.length s - n)
17 let string_starts_with long short =
18 try
19 let long = String.sub long 0 (String.length short) in
20 long = short
21 with Invalid_argument _ ->
22 false
24 let string_ends_with long short =
25 try
26 let len = String.length short in
27 let long = String.sub long (String.length long - len) len in
28 long = short
29 with Invalid_argument _ ->
30 false
32 (* Returns the index of the first occurrence of string `needle` in string
33 `haystack`. If not found, returns -1.
35 An implementation of the Knuth-Morris-Pratt (KMP) algorithm. *)
36 let substring_index needle =
37 (* see Wikipedia pseudocode *)
38 let needle_len = String.length needle in
39 if needle_len = 0 then raise (Invalid_argument needle);
40 let table = Array.make needle_len 0 in
41 table.(0) <- (-1);
42 let pos = ref 2 and cnd = ref 0 in
43 while !pos < needle_len do
44 if needle.[!pos - 1] = needle.[!cnd] then
45 (table.(!pos) <- !cnd + 1; incr pos; incr cnd)
46 else if !cnd > 0 then
47 cnd := table.(!cnd)
48 else
49 (table.(!pos) <- 0; incr pos)
50 done;
51 fun haystack ->
52 let len = String.length haystack in
53 let p = ref 0 in
54 let q = ref 0 in
55 while !p < len && !q < needle_len do
56 if haystack.[!p] = needle.[!q] then (incr p; incr q)
57 else if !q = 0 then incr p
58 else q := table.(!q)
59 done;
60 if !q >= needle_len then !p - needle_len
61 else -1
63 let is_substring needle =
64 let substring_index_memo = substring_index needle in
65 fun haystack -> (substring_index_memo haystack) >= 0
67 (* Return a copy of the string with prefixing string removed.
68 * The function is a no-op if it s does not start with prefix.
69 * Modeled after Python's string.lstrip.
71 let lstrip s prefix =
72 let prefix_length = String.length prefix in
73 if string_starts_with s prefix
74 then String.sub s prefix_length (String.length s - prefix_length)
75 else s
77 let rpartition s c =
78 let sep_idx = String.rindex s c in
79 let first = String.sub s 0 sep_idx in
80 let second =
81 String.sub s (sep_idx + 1) (String.length s - sep_idx - 1) in
82 first, second
84 let is_lowercase_char =
85 let a_code, z_code = Char.code 'a', Char.code 'z' in
86 fun chr ->
87 let code = Char.code chr in
88 a_code <= code && code <= z_code
90 let rec is_not_lowercase str i j =
91 if is_lowercase_char str.[i] then false
92 else if i = j then true
93 else is_not_lowercase str (i + 1) j