123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256moduleCamlLexer=LexeropenCore_kernelopenPolyopenPpxlibopenExpect_test_commonopenExpect_test_matcherletdeclare_extensionname~kind=Extension.Expert.declarenameExtension.Context.structure_item(Ppx_expect_payload.pattern())(Ppx_expect_payload.make~kind)letexpect=declare_extension"expect"~kind:Normalletexpect_exact=declare_extension"expect_exact"~kind:Exactletexpect_extensions=[expect;expect_exact]letpart_attr=Attribute.Floating.declare"toplevel_expect_test.part"Attribute.Floating.Context.structure_itemAst_pattern.(single_expr_payload(estring__))(funs->s)typechunk={part:stringoption;phrases:toplevel_phraselist;expectation:Fmt.tCst.tExpectation.t;phrases_loc:Location.t}letsplit_chunks~fname~allow_output_patternsphrases=letrecloop~loc_start~partphrasescode_accacc=matchphraseswith|[]->ifcode_acc=[]then(List.revacc,None)else(List.revacc,Some(List.revcode_acc,loc_start,part))|phrase::phrases->matchphrasewith|Ptop_def[]->loopphrasescode_accacc~loc_start~part|Ptop_def[{pstr_desc=Pstr_extension(ext,attrs);pstr_loc=loc}]->beginmatchExtension.Expert.convertexpect_extensionsext~locwith|None->loopphrases(phrase::code_acc)acc~loc_start~part|Somef->assert_no_attributesattrs;lete={phrases=List.revcode_acc;expectation=Expectation.map_pretty(f~extension_id_loc:(fstext).loc)~f:(Lexer.parse_pretty~allow_output_patterns);phrases_loc={loc_start;loc_end=loc.loc_start;loc_ghost=false};part}inloopphrases[](e::acc)~loc_start:loc.loc_end~partend|Ptop_def[{pstr_desc=Pstr_attribute_;pstr_loc=loc}asitem]->beginmatchAttribute.Floating.convert[part_attr]itemwith|None->loopphrases(phrase::code_acc)acc~loc_start~part|Somepart->matchcode_accwith|_::_->Location.raise_errorf~loc"[@@@part ...] cannot appear in the middle of a code block."|[]->loopphrases[]acc~loc_start:loc.loc_end~part:(Somepart)end|_->loopphrases(phrase::code_acc)acc~loc_start~partinloopphrases[][]~part:None~loc_start:{Lexing.pos_fname=fname;pos_bol=0;pos_cnum=0;pos_lnum=1};;(** Extract the subset of the contents of a string, based on an OCaml AST location. *)letextract_by_loccontents(loc:Location.t)=letstart=loc.loc_start.pos_cnuminletstop=loc.loc_end.pos_cnuminString.subcontents~pos:start~len:(stop-start);;letrender_expect_exn:_Cst.tExpectation.Body.t->stringoption=function|Exacts->Somes|Prettycst->Some(Cst.to_stringcst)|Output->None|Unreachable->assertfalse;;letdeclare_org_extensionname=Extension.Expert.declarenameExtension.Context.expressionAst_pattern.(map(single_expr_payload(pexp_loc__(pexp_constant(pconst_string____))))~f:(funflocstag->f(Some(loc,s,tag)))|||map(pstrnil)~f:(funf->fNone))(funpayload->matchpayloadwith|None->""|Some(_,s,_)->s)letorg=declare_org_extension"org"letorg_extensions=[org]typemlt_block=|Orgofstring|Expectofstring|Codeofstring[@@derivingsexp]moduleChunks=struct(* Comments are discarded by the parser that passes phrases to this function, so we must
expand the locations to include top-level comments. *)typeposition=Lexing.positionletsexp_of_position{Lexing.pos_cnum;_}=[%sexp(pos_cnum:int)]typelocation=Location.t={loc_start:position;loc_end:position;loc_ghost:bool}[@@derivingsexp_of]moduleChunk=structtype'at=|Expansiveoflocation|Fixedof{loc:location;value:'a}|Ignoredoflocation[@@derivingsexp_of]letloc=function|Expansiveloc|Fixed{loc;value=_}|Ignoredloc->loc;;endtype'at='aChunk.tQueue.t[@@derivingsexp_of]letexpansive(t:_t)loc=Queue.enqueuet(Expansiveloc)letfixed(t:_t)locvalue=Queue.enqueuet(Fixed{loc;value})letignored(t:_t)loc=Queue.enqueuet(Ignoredloc)letmake_empty_loc~pos_cnum:Location.t=letpos:position={pos_fname="";pos_lnum=0;pos_bol=0;pos_cnum}in{loc_start=pos;loc_end=pos;loc_ghost=false};;letcreate()=Queue.create()letlocs_without_gapst~final_pos_cnum=letnonempty_locloc_startloc_end:Location.t=assert(Int.(<)loc_start.Lexing.pos_cnumloc_end.Lexing.pos_cnum);{loc_start;loc_end;loc_ghost=false}inletmake_filler~(prev:Location.t)~(next:Location.t):'aChunk.toption=letcmp=[%compare:int]prev.loc_end.pos_cnumnext.loc_start.pos_cnuminmatchOrdering.of_intcmpwith|Less->Some(Expansive(nonempty_locprev.loc_endnext.loc_start))|Equal->None|Greater->raise_s[%message"Overlap."(prev:location)(next:location)]inletrecfill_gapsfinal_locchunksacc=matchchunkswith|[]->acc|[chunk]->make_filler~prev:(Chunk.locchunk)~next:final_loc|>Option.fold~init:(chunk::acc)~f:(Fn.flipList.cons)|car::(cadr::_ascdr)->make_filler~prev:(Chunk.loccar)~next:(Chunk.loccadr)|>Option.fold~init:(car::acc)~f:(Fn.flipList.cons)|>fill_gapsfinal_loccdrinletrecmerge_expansive_chunksaccchunks:'aChunk.tlist=matchchunkswith|[]->acc|[chunk]->chunk::acc|car::(cadr::cddrascdr)->matchcar,cadrwith|(Fixed_|Ignored_),_|_,(Fixed_|Ignored_)->merge_expansive_chunks(car::acc)cdr|Expansiveprev,Expansivenext->letloc=(* Flipped because [merge_expansive_chunks] sees the chunks backwards *)assert(Int.(=)next.loc_end.pos_cnumprev.loc_start.pos_cnum);nonempty_locnext.loc_startprev.loc_endinmerge_expansive_chunksacc(Expansiveloc::cddr)inmerge_expansive_chunks[](fill_gaps(make_empty_loc~pos_cnum:final_pos_cnum)(Queue.to_listt)[]);;endletparsephrases~contents=letchunks=Chunks.create()inList.iterphrases~f:(function|Ptop_defstructure_items->List.iterstructure_items~f:(fun({pstr_desc;pstr_loc=loc}asitem)->matchpstr_descwith|Pstr_extension(ext,attrs)->beginmatch(Extension.Expert.convertorg_extensionsext~loc,Extension.Expert.convertexpect_extensionsext~loc)with|(Somebody,None)->Chunks.fixedchunksloc(`Orgbody);|(None,Somef)->assert_no_attributesattrs;letexpectation=Expectation.map_pretty(f~extension_id_loc:(fstext).loc)~f:(Lexer.parse_pretty~allow_output_patterns:false)inOption.iter(render_expect_exnexpectation.body)~f:(funbody->Chunks.fixedchunksloc(`Expectbody))|None,None->()|Some_,Some_->lets=extract_by_loccontentslocinraise_s[%message"Both an org and an expect node."s]end|Pstr_attribute_->beginmatchAttribute.Floating.convert[part_attr]itemwith(* Documentation comments can desugar into a top-level [doc] attribute. *)|None->Chunks.expansivechunksloc|Some_->Chunks.ignoredchunksloc(* Discard [@@@part] declarations. *)end|_->Chunks.expansivechunksloc)|Ptop_dir_->());Chunks.locs_without_gapschunks~final_pos_cnum:(String.lengthcontents)|>List.filter_map~f:(function|Fixed{loc=_;value=`Orgbody}->Some(Orgbody)|Fixed{loc=_;value=`Expectbody}->Some(Expectbody)|Ignored_->None|Expansiveloc->letcode=extract_by_loccontentslocinifString.is_emptycodethenNoneelseSome(Codecode));;