123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173openBaseopenPpxlibopenAst_builder.DefaultmodulePart=structtypet=|Stringofstringloc|Exprof{expr:stringloc;converter:stringlocoption}end(** [relative_position ~base s ~offset] computes the absolute position of [offset] in the
string [s] assuming the beginning of the string is at position [base] *)letrelative_position~base:poss~offset=String.foldis~init:pos~f:(funi(pos:position)cur->ifi>=offsetthenposelse(matchcurwith|'\n'->{poswithpos_lnum=pos.pos_lnum+1;pos_bol=pos.pos_cnum+1;pos_cnum=pos.pos_cnum+1}|_->{poswithpos_cnum=pos.pos_cnum+1}));;(** [relative_location ~base ~start ~end_] compute the absolute location of the relative
location \[start end_\) in the string [base.txt] *)letrelative_location~base:{loc;txt}~start~end_={locwithloc_start=relative_position~base:loc.loc_starttxt~offset:start;loc_end=relative_position~base:loc.loc_starttxt~offset:end_};;letto_parts(s:stringloc)=letstringstartend_=Part.String{txt=String.subs.txt~pos:start~len:(end_-start);loc=relative_location~base:s~start~end_}inletrecloopaccpos=matchString.substr_index~poss.txt~pattern:"%{"with|None->stringpos(String.lengths.txt)::acc|Somestart->letacc=stringposstart::accinletpos=start+2inletend_=matchString.index_froms.txtpos'}'with|None->Location.raise_errorf~loc:s.loc"unterminated %%{"|Someend_->end_inletstring_expr=String.subs.txt~pos~len:(end_-pos)inletacc=matchString.rsplit2~on:'#'string_exprwith|None->letloc=relative_location~base:s~start:pos~end_inPart.Expr{expr={txt=string_expr;loc};converter=None}::acc|Some(string_expr,conversion_module)->letconv={txt=conversion_module^".to_string";loc=relative_location~base:s~start:(pos+String.lengthstring_expr+1)~end_}inPart.Expr{expr={txt=string_expr;loc=relative_location~base:s~start:pos~end_:(pos+String.lengthstring_expr)};converter=Someconv}::accinloopacc(end_+1)inList.rev(loop[]0);;letto_parts{loc;txt=s}=ifString.equalloc.loc_start.pos_fnameloc.loc_end.pos_fname&&Caml.Sys.file_existsloc.loc_start.pos_fnamethen(matchStdio.In_channel.with_fileloc.loc_start.pos_fname~f:(funic->Stdio.In_channel.seekic(Int64.of_intloc.loc_start.pos_cnum);letbuf_len=loc.loc_end.pos_cnum-loc.loc_start.pos_cnuminletbuf=Bytes.createbuf_leninStdio.In_channel.really_input_exnic~buf~pos:0~len:buf_len;Bytes.to_stringbuf)with|s_from_file->letfrom_ast=to_parts{loc;txt=s}inletfrom_file=to_parts{loc;txt=s_from_file}in(* If we have access to the original file, we extract location from it. *)(* Ideally, one should check that [from_file] and [from_ast] are equal (modulo
encoding in strings). Note that we only check the general shapes are equal.
The worse that can happen here is an error message with slightly incorrect
locations. *)List.zip_exnfrom_astfrom_file|>List.map~f:(fun((x,y):Part.t*Part.t)->matchx,ywith|String{txt;_},String{txt=_raw_string;loc}->Part.String{txt;loc}|Expr{expr=e1;converter=c1},Expr{expr=e2;converter=c2}->assert(Bool.equal(Option.is_somec1)(Option.is_somec2));Part.Expr{expr={e1withloc=e2.loc};converter=Option.map2c1c2~f:(func1c2->{c1withloc=c2.loc})}|String_,Expr_|Expr_,String_->assertfalse)|exception_->to_parts{loc;txt=s})elseto_parts{loc;txt=s};;letparse(tokens:Part.tlist)=letparse_expression~locstring=letlexbuf=Lexing.from_stringstringinlexbuf.lex_abs_pos<-loc.loc_start.pos_cnum;lexbuf.lex_curr_p<-loc.loc_start;Ppxlib.Parse.expressionlexbufinList.filter_maptokens~f:(function|Part.String{txt="";_}->None|String{txt;loc}->Some(estring~loctxt)|Expr{expr;converter}->lete=parse_expression~loc:expr.locexpr.txtin(matchconverterwith|None->Somee|Someconv->letconversion_module=parse_expression~loc:conv.locconv.txtinSome(eapply~loc:{expr.locwithloc_end=conv.loc.loc_end}conversion_module[e])));;letconcat~loc=function|[]->estring~loc""|[x]->x|_::_::_asl->eapply~loc(evar~loc"Stdlib.String.concat")[estring~loc"";elist~locl];;let()=Ppxlib.Driver.register_transformation"ppx_string"~extensions:[Extension.declare"ppx_string.string"Extension.Context.expressionAst_pattern.(pstr(pstr_eval(estring__')nil^::nil))(fun~loc:_~path:_sym->lettokens=to_partssyminletexprs=parsetokensinMerlin_helpers.hide_expression(concat~loc:sym.locexprs))];;