123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179openStdcompattypet={base:string;off:int;len:int}letof_string?(off=0)base={base;off;len=String.lengthbase-off}letto_string{base;off;len}=String.subbaseofflenletprintppfs=Format.fprintfppf"%S"(to_strings)letget_offset{off;_}=offletlength{len;_}=lenletis_emptys=lengths=0letoffsetn{base;off;len}=ifn<0theninvalid_arg"offset";letrecloopnbaseofflen=ifn=0||len=0then{base;off;len}elsematchbase.[off]with|'\t'->letts=((off+4)/4*4)-offinletb=Buffer.createleninBuffer.add_substringbbase0off;for_=1totsdoBuffer.add_charb' 'done;Buffer.add_substringbbase(off+1)(len-1);loopn(Buffer.contentsb)off(len+ts-1)|_->loop(n-1)base(off+1)(len-1)inloopnbaseofflenletlexbufs=Lexing.from_string(to_strings)letcontainss1{base;off;len}=letrecloopoff=ifoff+String.lengths1>lenthenfalseelses1=String.subbaseoff(String.lengths1)||loop(off+1)inloopofflethead=function|{len=0;_}->None|{base;off;_}->Somebase.[off]letlast=function|{len=0;_}->None|{base;off;len}->Somebase.[off+len-1]lettail=function|{len=0;_}ass->s|{base;off;len}->{base;off=succoff;len=predlen}letunconss=heads|>Option.map(funhd->(hd,tails))lettakens=ifn<0theninvalid_arg"take";letrecloopns=ifn=0||lengths=0then[]elsematchheadswithSomec->c::loop(predn)(tails)|None->[]inloopnslettake_prefixns=ifn<0theninvalid_arg"take_prefix";letlen=minns.lenin{swithlen}letdropns=ifn<0theninvalid_arg"drop";(* len should not be reduced below 0, as strings cannot have a negative length *)letlen=max(s.len-n)0in(* off should not exceed the length of the base string *)letoff=min(s.off+n)(String.lengths.base)in{swithoff;len}letdrop_last=function|{len=0;_}ass->s|{base;off;len}->{base;off;len=predlen}letrecdrop_whilefs=matchunconsswithSome(x,s')whenfx->drop_whilefs'|_->sletrecdrop_last_whilefs=matchlastswith|Somelwhenfl->drop_last_whilef(drop_lasts)|_->sletindexfs=letlen=lengthsinletrest=drop_while(func->not(fc))sinletidx=len-lengthrestinifidx=lenthenNoneelseSomeidx(* Uncomment to test *)(* TODO: rig up method to unit test our utilities *)(* let () = *)(* let index c = index (Char.equal c) in *)(* let s = of_string "abcd" in *)(* assert (index 'a' s = Some 0); *)(* assert (index 'b' s = Some 1); *)(* assert (index 'c' s = Some 2); *)(* assert (index 'z' s = None) *)letsplit_atfs=matchindexfswith|None->(s,offset(lengths)s)|Someidx->({swithlen=idx},offsetidxs)(* Uncomment to test *)(* TODO: rig up method to unit test our utilities *)(* let () = *)(* let f x = x = 'c' in *)(* let before, rest = split_at f (of_string "abcdef") in *)(* assert ("ab" = to_string before); *)(* assert ("cdef" = to_string rest); *)(* let before, rest = split_at f (of_string "cab") in *)(* assert ("" = to_string before); *)(* assert ("cab" = to_string rest); *)(* let before, rest = split_at f (of_string "aaa") in *)(* assert ("aaa" = to_string before); *)(* assert ("" = to_string rest) *)letindex_unescapedseps=letrecloopidxstate=ifidx=s.off+s.lenthenNone(* If we get here and we're inside a verbatim span, what to do? *)elsematch(state,s.base.[idx])with|`normal,'\\'->loop(idx+1)`escape|`normal,'`'->loop(idx+1)(`verbatim_open1)|`normal,cwhenc=sep->Some(idx-s.off)|`normal,_->loop(idx+1)`normal|`escape,_->loop(idx+1)`normal|`verbatim_openn,'`'->loop(idx+1)(`verbatim_open(n+1))|`verbatim_openn,_->loop(idx+1)(`within_verbatimn)|`within_verbatim1,'`'->loop(idx+1)`normal|`within_verbatimn,'`'->loop(idx+1)(`verbatim_close(n,n-1))|`within_verbatimn,_->loop(idx+1)(`within_verbatimn)|`verbatim_close(_,1),'`'->loop(idx+1)`normal|`verbatim_close(n,k),'`'->loop(idx+1)(`verbatim_close(n,k-1))|`verbatim_close(n,_),_->loop(idx+1)(`within_verbatimn)inloops.off`normalletexistsfs=letrecloopsi=ifi>=s.lenthenfalseelseiffs.base.[s.off+i]thentrueelseloops(succi)inloops0letfor_allfs=not(exists(func->not(fc))s)letsub~lens=iflen>s.lentheninvalid_arg"sub";{swithlen}letfold_leftfinits=letrecauxaccrest=matchunconsrestwithNone->acc|Some(x,xs)->aux(fxacc)xsinauxinits(* let () = *)(* let s = of_string "abcde" in *)(* assert (fold_left (fun _ n -> n + 1) 0 s = 5); *)(* assert (fold_left (fun c s -> String.make 2 c ^ s) "" s = "eeddccbbaa") *)lettrims=letis_whitespace=function|' '|'\t'|'\010'..'\013'->true|_->falseindrop_whileis_whitespace(drop_last_whileis_whitespaces)