123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141openCoreopenAngstromopenLanguagetypet={match_template:string;rule:Rule.toption;rewrite_template:stringoption}let(|>>)pf=p>>=funx->return(fx)letalphanum=satisfy(function|'a'..'z'|'A'..'Z'|'0'..'9'->true|_->false)letblank=choice[char' ';char'\t']letidentifier_parser()=many(alphanum<|>char'_')|>>String.of_char_listletsingle_hole_parser()=string":[["*>identifier_parser()<*string"]]"|>>fun_->Noneleteverything_hole_parser()=string":["*>identifier_parser()<*string"]"|>>fun_->Noneletexpression_hole_parser()=string":["*>identifier_parser()<*string":e"<*string"]"|>>fun_->Noneletnon_space_hole_parser()=string":["*>identifier_parser()<*string".]"|>>fun_->Noneletline_hole_parser()=string":["*>identifier_parser()<*string"\\n]"|>>fun_->Noneletblank_hole_parser()=string":["*>many1blank*>identifier_parser()<*string"]"|>>fun_->Noneletany_char_except~reserved=List.foldreserved~init:(return`OK)~f:(funaccreserved_sequence->option`End_of_input(peek_string(String.lengthreserved_sequence)>>=funs->ifString.equalsreserved_sequencethenreturn`Reserved_sequenceelseacc))>>=function|`OK->any_char|`End_of_input->any_char|`Reserved_sequence->fail"reserved sequence hit"letregex_body()=fix(funexpr->(choice[((char'['*>(many1expr)<*char']')|>>funchar_class->Format.sprintf"[%s]"@@String.concatchar_class);(char'\\'*>any_char|>>func->(Format.sprintf"\\%c"c));((any_char_except~reserved:["]"]))|>>Char.to_string]))letregex_hole_parser()=string":["*>identifier_parser()*>char'~'*>(many1@@regex_body())>>=funregex->string"]">>=fun_->return(Some(String.concatregex))typeextracted=|Regexofstring|Constantofstringletextract:extractedlistAngstrom.t=lethole=choice[single_hole_parser();everything_hole_parser();expression_hole_parser();non_space_hole_parser();line_hole_parser();blank_hole_parser();regex_hole_parser()]inmany@@choice[(hole>>=funv->return(Option.mapv~f:(funv->Regexv)));((many1@@any_char_except~reserved:[":["]))>>=func->return(Some(Constant(String.of_char_listc)))]>>=funresult->return(List.filter_optresult)letescapes=letrecauxchars=matchcharswith|[]->[]|x::xs->matchxwith|'\\'|'.'|'+'|'*'|'?'|'('|')'|'|'|'['|']'|'{'|'}'|'^'|'$'asc->'\\'::c::(auxxs)|c->c::(auxxs)inaux(String.to_lists)|>String.of_char_listletto_regex{match_template;_}=letstate=Buffered.parseextractinletstate=Buffered.feedstate(`Stringmatch_template)inletextracted=matchBuffered.feedstate`Eofwith|Buffered.Done(_,result)->result|_->failwith"Could not parse template for ripgrep"in(* Escape regex metachars *)letextracted=List.mapextracted~f:(function|Constants->escapes|Regexs->s)in(* Replace contiguous spaces with the regex \s+ *)letmatch_spaces=Str.regexp"[ \t\r\n]+"inletextracted=List.mapextracted~f:(funpart->Str.global_replacematch_spaces{|\s+|}part)in(* ?s is modifier metasyntax where . matches all chars including newlines. See
regular-expressions.info/modifier.html *)Format.sprintf"(%s)"@@String.concatextracted~sep:")(?s:.)*?("letcreate?rewrite_template?rule~match_template()={match_template;rule;rewrite_template}