123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154(**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*)letsoi=string_of_intletstring_of_char=String.make1letstring_beforesn=String.subs0nletstring_aftersn=String.subsn(String.lengths-n)letstring_starts_withlongshort=tryletlong=String.sublong0(String.lengthshort)inlong=shortwithInvalid_argument_->falseletstring_ends_withlongshort=tryletlen=String.lengthshortinletlong=String.sublong(String.lengthlong-len)leninlong=shortwithInvalid_argument_->false(* Returns the index of the first occurrence of string `needle` in string
`haystack`. If not found, returns -1.
An implementation of the Knuth-Morris-Pratt (KMP) algorithm. *)letsubstring_indexneedle=(* see Wikipedia pseudocode *)letneedle_len=String.lengthneedleinifneedle_len=0thenraise(Invalid_argumentneedle);lettable=Array.makeneedle_len0intable.(0)<-(-1);letpos=ref2andcnd=ref0inwhile!pos<needle_lendoifneedle.[!pos-1]=needle.[!cnd]then(table.(!pos)<-!cnd+1;incrpos;incrcnd)elseif!cnd>0thencnd:=table.(!cnd)else(table.(!pos)<-0;incrpos)done;funhaystack->letlen=String.lengthhaystackinletp=ref0inletq=ref0inwhile!p<len&&!q<needle_lendoifhaystack.[!p]=needle.[!q]then(incrp;incrq)elseif!q=0thenincrpelseq:=table.(!q)done;if!q>=needle_lenthen!p-needle_lenelse-1letis_substringneedle=letsubstring_index_memo=substring_indexneedleinfunhaystack->(substring_index_memohaystack)>=0(* Return a copy of the string with prefixing string removed.
* The function is a no-op if it s does not start with prefix.
* Modeled after Python's string.lstrip.
*)letlstripsprefix=letprefix_length=String.lengthprefixinifstring_starts_withsprefixthenString.subsprefix_length(String.lengths-prefix_length)elsesletrstripssuffix=letresult_length=String.lengths-String.lengthsuffixinifstring_ends_withssuffixthenString.subs0result_lengthelsesletrpartitionsc=letsep_idx=String.rindexscinletfirst=String.subs0sep_idxinletsecond=String.subs(sep_idx+1)(String.lengths-sep_idx-1)infirst,secondletis_lowercase_char=leta_code,z_code=Char.code'a',Char.code'z'infunchr->letcode=Char.codechrina_code<=code&&code<=z_codeletrecis_not_lowercasestrij=ifis_lowercase_charstr.[i]thenfalseelseifi=jthentrueelseis_not_lowercasestr(i+1)j(* String provides map and iter but not fold. It also is missing a char_list_of
* function. Oh well. You can use fold to simulate anything you need, I suppose
*)letfold_left~f~accstr=letacc=refaccinString.iter(func->acc:=f(!acc)c)str;!accletsplitc=Str.split(Str.regexp@@Char.escapedc)(* Replaces all instances of the needle character with the replacement character
*)letreplace_charneedlereplacement=String.map(func->ifc=needlethenreplacementelsec)(* Splits a string into a list of strings using "\n", "\r" or "\r\n" as
* delimiters. If the string starts or ends with a delimiter, there WILL be an
* empty string at the beginning or end of the list, like Str.split_delim does
*)letsplit_into_linesstr=(* To avoid unnecessary string allocations, we're going to keep a list of
* the start index of each line and how long it is. Then, at the end, we can
* use String.sub to create the actual strings. *)let_,(last_start,lines)=fold_left~f:(fun(idx,(start,lines))c->(* For \r\n, we've already processed the newline *)ifc='\n'&&idx>0&&String.getstr(idx-1)='\r'thenidx+1,(idx+1,lines)elseifc='\n'||c='\r'thenidx+1,(idx+1,(start,idx-start)::lines)elseidx+1,(start,lines))~acc:(0,(0,[]))strin(* Reverses the list of start,len and turns them into strings *)List.fold_left(funlines(start,len)->(String.substrstartlen)::lines)[]((last_start,String.lengthstr-last_start)::lines)(* Splits a string into a list of strings using only "\n" as a delimiter.
* If the string ends with a delimiter, an empty string representing the
* contents after the final delimiter is NOT included (unlike Str.split_delim).
*)letsplit_on_newlinescontent=letre=Str.regexp"[\n]"inletlines=Str.split_delimrecontentin(* don't create a list entry for the line after a trailing newline *)matchList.revlineswith|""::rest->List.revrest|_->lines