12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553openWax_utils.ColorsmodulePrinter=Wax_utils.PrintermoduleTrivia=Wax_utils.TriviamoduleStyled=Wax_utils.Styled_printermoduleUint32=Wax_utils.Uint32moduleUint64=Wax_utils.Uint64openAst.Text(*** Printer primitives and sexp model ***)letget_themeuse_color=ifuse_colorthenwat_themeelseno_colortypeformat=Compact|Expansive|Hybrid|Adaptivetypectx={base:Styled.t;format:format;indent_level:int}let_=(Compact,Hybrid,Adaptive)(* The styling and trivia plumbing is shared with the Wax printer in
[Wax_utils.Styled_printer]; only [format]/[indent_level] are specific here. *)letprint_styledctxstyle?(len=None)text=Styled.print_styledctx.basestyle~lentextletprint_triviactxlst=Styled.print_triviactx.baselstletget_triviactxloc=Styled.get_triviactx.baselocletatomic_nodectxlocf=Styled.atomic_nodectx.baselocfletstring_nodectxlocstylelens=atomic_nodectxloc@@fun()->print_styledctxstyle~lenstypesexp=|Atomof{loc:Ast.locationoption;len:intoption;style:style;s:string;}|ListofAst.locationoption*sexplist|Blockof{loc:Ast.locationoption;l:sexplist;transparent:bool;bk:[`Box|`Hov|`Hv];}|Vertical_blockofAst.locationoption*sexplist|Structured_blockofAst.locationoption*structurelistandstructure=Delimiterofsexp|Contentsofsexplistletrecneeds_vertical_layout=function|Vertical_block_|Structured_block_->true|Atom_->false|List(_,l)->List.existsneeds_vertical_layoutl|Block{l;_}->List.existsneeds_vertical_layoutlletrecformat_sexpin_blockdepthfirstctxs=letp=ctx.base.printerinmatchswith|Atom{loc;len;style;s}->string_nodectxlocstylelens|List(loc,l)when(ctx.format=Hybrid&&depth>1)||ctx.format=Compact->lettrivia=get_triviactxlocinprint_triviactxtrivia.before;Printer.boxp(fun()->print_styledctxPunctuation"(";Printer.hvboxp~indent:(ctx.indent_level-1)(fun()->List.iteri(funiv->ifi>0thenPrinter.spacep();format_sexpin_blockdepth(i=0)ctxv)l);print_triviactxtrivia.within;print_styledctxPunctuation")";print_triviactxtrivia.after)|List(loc,l)->lettrivia=get_triviactxlocinprint_triviactxtrivia.before;(if((notin_block)&&ctx.format=Expansive)||List.existsneeds_vertical_layoutlthenPrinter.vboxelsePrinter.hvbox)p(fun()->print_styledctxPunctuation"(";Printer.indentpctx.indent_level(fun()->List.iteri(funiv->ifi>0thenPrinter.spacep();format_sexpin_block(depth+1)(i=0)ctxv)l);Printer.cutp();print_triviactxtrivia.within;Printer.boxp(fun()->print_styledctxPunctuation")";print_triviactxtrivia.after))|Block{l;transparent;loc;bk}->(lettrivia=get_triviactxlocinletindent=iffirstthenctx.indent_level-1else0inprint_triviactxtrivia.before;letrender()=List.iteri(funiv->ifi>0thenPrinter.spacep();format_sexp(in_block||nottransparent)depth(first&&i=0)ctxv)l;print_triviactxtrivia.within;print_triviactxtrivia.afterinmatchbkwith|`Hv->Printer.hvboxp~indentrender|`Hov->Printer.hovboxp~indentrender|`Box->(if(notin_block)&&transparent&&ctx.format=ExpansivethenPrinter.vboxelsePrinter.box)p~indentrender)|Vertical_block(loc,l)->lettrivia=get_triviactxlocin(* Printed before the box opens so a comment leading the first element
merges with the line break before the block instead of doubling it
into a blank line. *)print_triviactxtrivia.before;Printer.vboxp(fun()->List.iteri(funiv->ifi>0thenPrinter.spacep();format_sexpin_blockdepthfalsectxv)l;print_triviactxtrivia.within;print_triviactxtrivia.after)|Structured_block(loc,l)->lettrivia=get_triviactxlocinprint_triviactxtrivia.before;Printer.vboxp(fun()->letlen=List.lengthlinList.iteri(funis->matchswith|Delimiterd->ifi>0thenPrinter.newlinep();ifi=len-1thenprint_triviactxtrivia.within;Printer.boxp(fun()->format_sexpin_blockdepthfalsectxd;print_triviactxtrivia.after)|Contents[]->()|Contentsl->Printer.indentpctx.indent_level(fun()->Printer.newlinep();Printer.vboxp(fun()->List.iteri(funiv->ifi>0thenPrinter.newlinep();format_sexpin_blockdepthfalsectxv)l)))l)(* The dry pass that feeds {!Trivia.associate}'s [only] set needs just the set
of locations [format_sexp] would look up. [format_sexp] visits every node of
the [sexp] tree exactly once and probes each node's [loc] via [get_trivia], so
that set is precisely the [loc] fields present in the tree. Walk the tree to
gather them directly, skipping the whole layout engine a discarded
[format_sexp] run would otherwise drive — the dry pass used to be nearly as
costly as the real emit. *)letreccollect_locssets=letadd=functionSomeloc->Trivia.marksetloc|None->()inmatchswith|Atom{loc;_}->addloc|List(loc,l)|Vertical_block(loc,l)->addloc;List.iter(collect_locsset)l|Block{loc;l;_}->addloc;List.iter(collect_locsset)l|Structured_block(loc,l)->addloc;List.iter(function|Delimiterd->collect_locssetd|Contentsl->List.iter(collect_locsset)l)lletatom~style?locs=Atom{loc;len=None;style;s}letkeyword?locs=atom~style:Keyword?locsletinstruction?locs=atom~style:Instruction?locslettype_?locs=atom~style:Type?locsletlist?locl=List(loc,l)letblock?loc?(transparent=false)?(bk=`Box)l=Block{loc;l;transparent;bk}letstructured_block?locl=Structured_block(loc,l)letoptionfx=matchxwithNone->[]|Somex->fxletu32~style?loci=atom~style?loc(Uint32.to_stringi)letu64?loci=atom?loc(Uint64.to_stringi)letescape_string=Wax_utils.Unicode.escape_string(* The source text of a [$id] and its terminal width: a plain identifier is
written [$x], anything else in the quoted [$"…"] form with escaping. *)letid_partsx=ifLexer.is_valid_identifierxthen(Wax_utils.Unicode.terminal_widthx+1,"$"^x)elseleti,s=escape_stringxin(i+3,"$\""^s^"\"")letid_stringx=snd(id_partsx)letid?(style=Identifier)?locx=letlen,s=id_partsxinAtom{loc;style;len=Somelen;s}letopt_id=option(funi->[id~loc:i.Ast.infoi.Ast.desc])letindex?(style=Identifier)x=letloc=x.Ast.infoinmatchx.descwithNumi->u32~style~loci|Ids->id~style~locs(*** Type printing ***)letheaptype(ty:heaptype)=matchheaptype_keywordtywith|Somekw->type_kw|None->(matchtywith|Typet->indext|Exactt->list[type_"exact";indext]|_->assertfalse)letreftype{nullable;typ}=match(nullable,typ)with|true,Func->type_"funcref"|true,NoFunc->type_"nullfuncref"|true,Extern->type_"externref"|true,NoExtern->type_"nullexternref"|true,Any->type_"anyref"|true,Eq->type_"eqref"|true,I31->type_"i31ref"|true,Struct->type_"structref"|true,Array->type_"arrayref"|true,None_->type_"nullref"|true,Exn->type_"exnref"|true,NoExn->type_"nullexnref"|true,Cont->type_"contref"|true,NoCont->type_"nullcontref"|_->letr=[heaptypetyp]inlist(type_"ref"::(ifnullablethentype_"null"::relser))(* A value-type alias use, [(@type $id)]. *)lettype_alias_usea=list[atom~style:Annotation"@type";id~style:Annotation~loc:a.Ast.infoa.Ast.desc;]letvaltype(t:valtype)=matchtwith|I32->type_"i32"|I64->type_"i64"|F32->type_"f32"|F64->type_"f64"|V128->type_"v128"|Refty->reftypety|Aliasa->type_alias_usealetpackedtypet=matchtwithI8->type_"i8"|I16->type_"i16"letmake_list~kind?(always=false)namefl=if(notalways)&&l=[]then[]else[list(kindname::fl)]letvaltype_listnametl=make_list~kind:keywordname(funtl->List.mapvaltypetl)tlletfunctype{params;results}=letparams=Array.to_listparamsinletparams_sexp=ifList.for_all(funp->fstp.Ast.desc=None)paramsthenvaltype_list"param"(List.map(funp->sndp.Ast.desc)params)else(* Anchor each [(param …)] at the parameter's own location (the name is
anchored separately via [opt_id]), so a trailing comment attaches to
the parameter. *)List.map(funp->leti,t=p.Ast.descinlist~loc:p.Ast.info(keyword"param"::(opt_idi@[valtypet])))paramsinparams_sexp@valtype_list"result"(Array.to_listresults)letstoragetypetyp=matchtypwithValuetyp->valtypetyp|Packedtyp->packedtypetypletmut_typef{mut;typ}=ifmutthenlist[keyword"mut";ftyp]elseftypletfieldtypetyp=mut_type(funt->storagetypet)typletglobaltypetyp=mut_type(funt->valtypet)typletcomptype(typ:comptype)=matchtypwith|Functy->list(keyword"func"::functypety)|Structl->list(keyword"struct"::List.map(funfld->letnm,f=fld.Ast.descinlist~loc:fld.Ast.info(keyword"field"::(opt_idnm@[fieldtypef])))(Array.to_listl))|Arrayty->list[keyword"array";fieldtypety]|Contidx->list[keyword"cont";indexidx]lettypeuseidx=[list[keyword"type";indexidx]]lettypeuse'(idx,typ)=optiontypeuseidx@optionfunctypetypletblocktype=option@@funt->matchtwith|Valtypet->[list[keyword"result";valtypet]]|Typeuset->typeuse'tletaddress_typeat=matchatwith`I32->[]|`I64->[keyword"i64"]letlimits{Ast.desc={mi;ma;address_type=at;page_size_log2;shared};_}=address_typeat@(u64~style:Constantmi::option(funi->[u64~style:Constanti])ma)@(ifsharedthen[keyword"shared"]else[])@matchpage_size_log2with|None->[]|Somep->[list[keyword"pagesize";u64~style:Constant(Uint64.of_int64(Int64.shift_left1Lp));];]lettabletype{limits=l;reftype=typ}=limitsl@[reftypetyp](*** Operators, immediates, and SIMD ***)letquoted_strings=letloc=s.Ast.infoinleti,s=escape_strings.Ast.descinAtom{loc=Someloc;style=String;len=Some(i+2);s="\""^s^"\""}letexportsl=List.map(funname->list[keyword"export";quoted_stringname])llettype_prefixopnm=(matchopwith|I32_->"i32."|I64_->"i64."|F32_->"f32."|F64_->"f64.")^nmletsignageop(s:signage)=op^matchswithSigned->"_s"|Unsigned->"_u"letsizesz=matchszwith`F32->"f32"|`F64->"f64"|`I32->"i32"|`I64->"i64"letint_un_opwidthop=matchopwith|Clz->"clz"|Ctz->"ctz"|Popcnt->"popcnt"|Eqz->"eqz"|Trunc(sz,s)->signage("trunc_"^sizesz)s|TruncSat(sz,s)->signage("trunc_sat_"^sizesz)s|Reinterpret->"reinterpret_f"^width|ExtendSsz->(matchszwith|`_8->"extend8_s"|`_16->"extend16_s"|`_32->"extend32_s")letint_bin_op_(op:int_bin_op)=matchopwith|Add->"add"|Sub->"sub"|Mul->"mul"|Divs->signage"div"s|Rems->signage"rem"s|And->"and"|Or->"or"|Xor->"xor"|Shl->"shl"|Shrs->signage"shr"s|Rotl->"rotl"|Rotr->"rotr"|Eq->"eq"|Ne->"ne"|Lts->signage"lt"s|Gts->signage"gt"s|Les->signage"le"s|Ges->signage"ge"sletfloat_un_opszop=matchopwith|Neg->"neg"|Abs->"abs"|Ceil->"ceil"|Floor->"floor"|Trunc->"trunc"|Nearest->"nearest"|Sqrt->"sqrt"|Convert(`I32,s)->signage"convert_i32"s|Convert(`I64,s)->signage"convert_i64"s|Reinterpret->"reinterpret_i"^szletfloat_bin_op_op=matchopwith|Add->"add"|Sub->"sub"|Mul->"mul"|Div->"div"|Min->"min"|Max->"max"|CopySign->"copysign"|Eq->"eq"|Ne->"ne"|Lt->"lt"|Gt->"gt"|Le->"le"|Ge->"ge"letselecti32i64f32f64_op=matchopwith|I32x->i32"32"x|I64x->i64"64"x|F32x->f32"32"x|F64x->f64"64"xletmemidxi=ifi.Ast.desc=NumUint32.zerothen[]else[indexi]letmemargalign'{offset;align}=(ifoffset=Uint64.zerothen[]else[atom~style:Attribute("offset="^Uint64.to_stringoffset)])@ifalign=align'then[]else[atom~style:Attribute(Printf.sprintf"align="^Uint64.to_stringalign)]letvec_shape=function|I8x16->"i8x16"|I16x8->"i16x8"|I32x4->"i32x4"|I64x2->"i64x2"|F32x4->"f32x4"|F64x2->"f64x2"letvec_tern_opop=matchopwith|VecRelaxedMAdd_->"relaxed_madd"|VecRelaxedNMAdd_->"relaxed_nmadd"|VecRelaxedLaneSelect_->"relaxed_laneselect"|VecRelaxedDotAdd->"relaxed_dot_i8x16_i7x16_add_s"letvec_const{Wax_utils.V128.shape;components}=keyword(matchshapewith|I8x16->"i8x16"|I16x8->"i16x8"|I32x4->"i32x4"|I64x2->"i64x2"|F32x4->"f32x4"|F64x2->"f64x2")::List.map(atom~style:Constant)components(* One data-segment element (WAT numeric-values proposal): a quoted byte string,
a typed numeric run [(i16 -1 2)], or a run of [v128] constants. *)letdata_elem_sexpe=matche.Ast.descwith|Strs->quoted_string{ewithAst.desc=s}|Numlist(ty,l)->list(storagetypety::List.map(atom~style:Constant)l)|V128listvs->list(keyword"v128"::List.concat_mapvec_constvs)letvec_load_op_nat_align=function|Load128->16|Load8x8S|Load8x8U|Load16x4S|Load16x4U|Load32x2S|Load32x2U|Load64Zero->8|Load32Zero->4letvec_lane_op_nat_align=function|`I8->1|`I16->2|`I32->4|`I64->8letnum_type_nat_align=functionNumI32|NumF32->4|NumI64|NumF64->8letstorage_type_nat_align=function`I8->1|`I16->2|`I32->4letcatchesl=List.map(func->matchcwith|Catch(x,l)->list[keyword"catch";indexx;indexl]|CatchRef(x,l)->list[keyword"catch_ref";indexx;indexl]|CatchAlll->list[keyword"catch_all";indexl]|CatchAllRefl->list[keyword"catch_all_ref";indexl])lleton_clausesl=List.map(func->matchcwith|OnLabel(x,l)->list[keyword"on";indexx;indexl]|OnSwitchx->list[keyword"on";indexx;keyword"switch"])lletcmp_op_string(op:Ast.cmp_op)=matchopwith|Eq->"="|Ne->"<>"|Lt->"<"|Gt->">"|Le->"<="|Ge->">="letreccond_doc(c:Ast.cond)=matchcwith|Cond_varv->id~style:Annotation~loc:v.Ast.infov.Ast.desc|Cond_strings->leti,str=escape_strings.Ast.descinAtom{loc=Somes.Ast.info;style=Annotation;len=Some(i+2);s="\""^str^"\"";}|Cond_version(a,b,c)->list(List.map(funn->atom~style:Annotation(string_of_intn))[a;b;c])|Cond_andl->list(atom~style:Annotation"and"::List.mapcond_docl)|Cond_orl->list(atom~style:Annotation"or"::List.mapcond_docl)|Cond_note->list[atom~style:Annotation"not";cond_doce]|Cond_cmp(op,a,b)->list[atom~style:Annotation(cmp_op_stringop);cond_doca;cond_docb](* A [metadata.code.*] annotation's raw-byte payload, escaped as a string
literal. *)lethint_payloads=leti,s=escape_stringsinAtom{loc=None;style=Annotation;len=Some(i+2);s="\""^s^"\""}lethint_annotnamepayload=list(atom~style:Annotation("@metadata.code."^name)::payload)(* Branch-hinting proposal: the [(@metadata.code.branch_hint "\00"|"\01")]
annotation that prefixes a hinted conditional branch. *)letbranch_hint_annotation(likely:bool)=hint_annot"branch_hint"[hint_payload(iflikelythen"\001"else"\000")](* Compilation-hints proposal: [(@metadata.code.instr_freq …)]. The reserved
values print as their keywords. Otherwise the byte stands for an
executions-per-call ratio, printed as [(freq n)] when that ratio is a whole
number — the common case, a hot instruction — and as the raw byte otherwise,
because a fractional ratio's shortest decimal need not re-encode to the same
byte and the hint must round-trip exactly. *)letinstr_freq_annotation(b:Hints.freq)=letpayload=ifb=Hints.never_optthen[list[atom~style:Annotation"never_opt"]]elseifb=Hints.always_optthen[list[atom~style:Annotation"always_opt"]]elsematchHints.ratio_of_freqbwith|SomerwhenFloat.is_integerr&&r>=1.->[list[atom~style:Annotation"freq";atom~style:Constant(Printf.sprintf"%.0f"r);];]|_->[hint_payload(Hints.freq_payloadb)]inhint_annot"instr_freq"payload(* Compilation-hints proposal: [(@metadata.code.call_targets (target $f 0.73) …)].
Always the structured form: the raw-byte spelling can only carry a numeric
index, so it could not print a target the source named. *)letcall_targets_annotationtargets=hint_annot"call_targets"(List.map(fun(idx,pct)->list[atom~style:Annotation"target";(* Printed without its source span: an annotation payload must not
be a trivia anchor, or a comment sitting before the annotation
would attach inside this group and split it across lines. *)(matchidx.Ast.descwith|Numi->u32~style:Identifieri|Ids->id~style:Identifiers);atom~style:Constant(Printf.sprintf"%g"(float_of_intpct/.100.));])targets)(* Compilation-hints proposal:
[(@metadata.code.compilation_priority (priority 1) (optimization 10))], the
one hint of the family that belongs to a function rather than an instruction.
Printed inside [(func …)] after the type use, which is the offset-0 position
the section keys it under. Always the structured form; [run_once] prints as its
keyword. *)letcompilation_priority_annotations(p:Hints.priorityoption)=Option.to_list(Option.map(fun({compilation;optimization}:Hints.priority)->hint_annot"compilation_priority"(list[atom~style:Annotation"priority";atom~style:Constant(string_of_intcompilation);]::Option.to_list(Option.map(funo->ifo=Hints.run_oncethenlist[atom~style:Annotation"run_once"]elselist[atom~style:Annotation"optimization";atom~style:Constant(string_of_into);])optimization)))p)(* Branch-hinting / compilation-hints proposals: the [(@metadata.code.…)]
annotations an instruction's hints print as, in section order. *)lethint_annotations(h:_Hints.t)=Option.to_list(Option.map(fun(b:boolHints.hint)->branch_hint_annotationb.value)h.Hints.branch)@Option.to_list(Option.map(fun(f:Hints.freqHints.hint)->instr_freq_annotationf.value)h.Hints.freq)@Option.to_list(Option.map(fun(l:_Hints.hint)->call_targets_annotationl.value)h.Hints.targets)(*** The instruction printer ***)(* A hinted instruction prints its annotations before itself. A block-form [if]
keeps its own multi-line layout, with the annotation on its own line above it;
an inline [br_if]/[br_on_*] stays on one line with the annotation. A folded
group takes its annotation outside the parentheses, which the [Folded] arm of
[instr_unhinted] handles for itself. *)letrecinstri=matchhint_annotationsi.hintswith|[]->instr_unhintedi|annots->(letloc=i.infoinletbare=instr_unhinted{iwithhints=Hints.none}inmatchi.descwith|If_->Vertical_block(Someloc,annots@[bare])|_->block~loc~transparent:true(annots@[bare]))andinstr_unhintedi=letloc=i.infoinmatchi.descwith|ExternConvertAny->instruction~loc"extern.convert_any"|AnyConvertExtern->instruction~loc"any.convert_extern"|Constop->block~loc[instruction(type_prefixop"const");atom~style:Constant(select(fun_i->i)(fun_i->i)(fun_i->i)(fun_i->i)(fun_i->i)op);]|UnOp(I32op)->instruction~loc("i32."^int_un_op"32"op)|UnOp(I64op)->instruction~loc("i64."^int_un_op"64"op)|UnOp(F32op)->instruction~loc("f32."^float_un_op"32"op)|UnOp(F64op)->instruction~loc("f64."^float_un_op"64"op)|BinOp(I32op)->instruction~loc("i32."^int_bin_op"32"op)|BinOp(I64op)->instruction~loc("i64."^int_bin_op"64"op)|BinOp(F32op)->instruction~loc("f32."^float_bin_op"32"op)|BinOp(F64op)->instruction~loc("f64."^float_bin_op"64"op)|Add128->instruction~loc"i64.add128"|Sub128->instruction~loc"i64.sub128"|MulWides->instruction~loc(signage"i64.mul_wide"s)|I32WrapI64->instruction~loc"i32.wrap_i64"|I64ExtendI32s->instruction~loc(signage"i64.extend_i32"s)|F32DemoteF64->instruction~loc"f32.demote_f64"|F64PromoteF32->instruction~loc"f64.promote_f32"|VecConstc->block~loc(instruction"v128.const"::vec_constc)|VecShufflelanes->block~loc(instruction"i8x16.shuffle"::List.init16(funi->atom~style:Constant(Int.to_string(Char.codelanes.[i]))))|VecExtract(op,s,lane)->block~loc[instruction~loc(vec_shapeop^".extract_lane"^matchswithSomes->signage""s|None->"");atom~style:Constant(Int.to_stringlane);]|VecReplace(op,lane)->block[instruction~loc(vec_shapeop^".replace_lane");atom~style:Constant(Int.to_stringlane);]|(VecSplat_|VecUnOp_|VecBinOp_|VecTest_|VecShift_|VecBitmask_)asdesc->(* Plain vector instructions share their WAT mnemonics with the lexer via
the Simd registry. *)instruction~loc(Option.get(Simd.wat_mnemonicdesc))|VecTernOpop->letshape_str=matchopwith|VecRelaxedMAdds|VecRelaxedNMAdds->(matchswith`F32->"f32x4"|`F64->"f64x2")|VecRelaxedLaneSelects->vec_shapes|VecRelaxedDotAdd->"i32x4"ininstruction~loc(shape_str^"."^vec_tern_opop)|VecBitselect->instruction~loc"v128.bitselect"|VecLoad(i,op,m)->block~loc(instruction(matchopwith|Load128->"v128.load"|Load8x8S->"v128.load8x8_s"|Load8x8U->"v128.load8x8_u"|Load16x4S->"v128.load16x4_s"|Load16x4U->"v128.load16x4_u"|Load32x2S->"v128.load32x2_s"|Load32x2U->"v128.load32x2_u"|Load32Zero->"v128.load32_zero"|Load64Zero->"v128.load64_zero")::(memidxi@memarg(Uint64.of_int(vec_load_op_nat_alignop))m))|VecStore(i,m)->block~loc(instruction"v128.store"::(memidxi@memarg(Uint64.of_int16)m))|VecLoadLane(i,op,m,lane)->block~loc(instruction(Printf.sprintf"v128.load%s_lane"(matchopwith|`I8->"8"|`I16->"16"|`I32->"32"|`I64->"64"))::(memidxi@memarg(Uint64.of_int(vec_lane_op_nat_alignop))m@[atom~style:Constant(Int.to_stringlane)]))|VecStoreLane(i,op,m,lane)->block~loc(instruction(Printf.sprintf"v128.store%s_lane"(matchopwith|`I8->"8"|`I16->"16"|`I32->"32"|`I64->"64"))::(memidxi@memarg(Uint64.of_int(vec_lane_op_nat_alignop))m@[atom~style:Constant(Int.to_stringlane)]))|VecLoadSplat(i,op,m)->block~loc(instruction(Printf.sprintf"v128.load%s_splat"(matchopwith|`I8->"8"|`I16->"16"|`I32->"32"|`I64->"64"))::(memidxi@memarg(Uint64.of_int(vec_lane_op_nat_alignop))m))|Load(i,m,sz)->block~loc(instruction(Printf.sprintf"%s.load"(matchszwith|NumI32->"i32"|NumI64->"i64"|NumF32->"f32"|NumF64->"f64"))::(memidxi@memarg(Uint64.of_int(num_type_nat_alignsz))m))|LoadS(i,m,sz,sz',s)->block~loc(instruction(signage(Printf.sprintf"%s.load%s"(matchszwith`I32->"i32"|`I64->"i64")(matchsz'with`I8->"8"|`I16->"16"|`I32->"32"))s)::(memidxi@memarg(Uint64.of_int(storage_type_nat_alignsz'))m))|Store(i,m,sz)->block~loc(instruction(Printf.sprintf"%s.store"(matchszwith|NumI32->"i32"|NumI64->"i64"|NumF32->"f32"|NumF64->"f64"))::(memidxi@memarg(Uint64.of_int(num_type_nat_alignsz))m))|StoreS(i,m,sz,sz')->block~loc(instruction(Printf.sprintf"%s.store%s"(matchszwith`I32->"i32"|`I64->"i64")(matchsz'with`I8->"8"|`I16->"16"|`I32->"32"))::(memidxi@memarg(Uint64.of_int(storage_type_nat_alignsz'))m))|Atomic(i,op,m)->block~loc(instruction(Atomics.nameop)::(memidxi@memarg(Uint64.of_int(1lslAtomics.natural_align_log2op))m))|AtomicFence->instruction~loc"atomic.fence"|MemorySizem->block~loc(instruction"memory.size"::memidxm)|MemoryGrowm->block~loc(instruction"memory.grow"::memidxm)|MemoryFillm->block~loc(instruction"memory.fill"::memidxm)|MemoryCopy(m,m')->block~loc(instruction"memory.copy"::(ifm.desc=NumUint32.zero&&m'.desc=NumUint32.zerothen[]else[indexm;indexm']))|MemoryInit(m,d)->block~loc(instruction"memory.init"::(memidxm@[indexd]))|DataDropd->block~loc[instruction"data.drop";indexd]|TableGetm->block~loc(instruction"table.get"::memidxm)|TableSetm->block~loc(instruction"table.set"::memidxm)|TableSizem->block~loc(instruction"table.size"::memidxm)|TableGrowm->block~loc(instruction"table.grow"::memidxm)|TableFillm->block~loc(instruction"table.fill"::memidxm)|TableCopy(m,m')->block~loc(instruction"table.copy"::(ifm.desc=NumUint32.zero&&m'.desc=NumUint32.zerothen[]else[indexm;indexm']))|TableInit(m,d)->block~loc(instruction"table.init"::(memidxm@[indexd]))|ElemDropd->block~loc[instruction"elem.drop";indexd]|LocalGeti->block~loc[instruction"local.get";indexi]|LocalTeei->block~loc[instruction"local.tee";indexi]|GlobalGeti->block~loc[instruction"global.get";indexi]|CallIndirect(id,typ)->block~loc(instruction"call_indirect"::((ifid.desc=NumUint32.zerothen[]else[indexid])@typeuse'typ))|ReturnCallIndirect(id,typ)->block~loc(instruction"return_call_indirect"::((ifid.desc=NumUint32.zerothen[]else[indexid])@typeuse'typ))|Callf->block~loc[instruction"call";indexf]|Selectt->block~loc(instruction"select"::option(funt->valtype_list"result"t)t)|RefFunci->block~loc[instruction"ref.func";indexi]|RefIsNull->block~loc[instruction"ref.is_null"]|RefAsNonNull->block~loc[instruction"ref.as_non_null"]|CallReft->block~loc[instruction"call_ref";indext]|RefI31->instruction~loc"ref.i31"|I31Gets->instruction~loc(signage"i31.get"s)|ArrayNewt->block~loc[instruction"array.new";indext]|ArrayNewDefaultt->block~loc[instruction"array.new_default";indext]|ArrayNewFixed(t,i)->block~loc[instruction"array.new_fixed";indext;u32~style:Constanti]|ArrayNewElem(i,i')->block~loc[instruction"array.new_elem";indexi;indexi']|ArrayNewData(typ,data)->block~loc[instruction"array.new_data";indextyp;indexdata]|ArrayInitData(i,i')->block~loc[instruction"array.init_data";indexi;indexi']|ArrayInitElem(i,i')->block~loc[instruction"array.init_elem";indexi;indexi']|ArrayGet(None,typ)->block~loc[instruction"array.get";indextyp]|ArrayGet(Somes,typ)->block~loc[instruction(signage"array.get"s);indextyp]|ArrayLen->instruction~loc"array.len"|ArrayCopy(i,i')->block~loc[instruction"array.copy";indexi;indexi']|ArrayFilli->block~loc[instruction"array.fill";indexi]|StructNewtyp->block~loc[instruction"struct.new";indextyp]|StructNewDefaulttyp->block~loc[instruction"struct.new_default";indextyp]|StructNewDesctyp->block~loc[instruction"struct.new_desc";indextyp]|StructNewDefaultDesctyp->block~loc[instruction"struct.new_default_desc";indextyp]|StructGet(None,typ,f)->block~loc[instruction"struct.get";indextyp;indexf]|StructGet(Somes,typ,f)->block~loc[instruction(signage"struct.get"s);indextyp;indexf]|RefCastty->block~loc[instruction"ref.cast";reftypety]|RefCastDescEqty->block~loc[instruction"ref.cast_desc_eq";reftypety]|RefGetDesci->block~loc[instruction"ref.get_desc";indexi]|RefTestty->block~loc[instruction"ref.test";reftypety]|RefEq->instruction~loc"ref.eq"|RefNullty->block~loc[instruction"ref.null";heaptypety]|If{label;typ;if_block;else_block}->structured_block~loc(Delimiter(block(instruction"if"::(opt_idlabel@blocktypetyp)))::Contents(List.mapinstrif_block.desc)::(ifelse_block.desc=[]then[Delimiter(instruction"end")]else[Delimiter(instruction"else");Contents(List.mapinstrelse_block.desc);Delimiter(instruction"end");]))|Drop->instruction~loc"drop"|LocalSeti->block~loc[instruction"local.set";indexi]|GlobalSeti->block~loc[instruction"global.set";indexi]|Block{label;typ;block=b}->structured_block~loc[Delimiter(block(instruction"block"::(opt_idlabel@blocktypetyp)));Contents(List.mapinstrb.desc);Delimiter(instruction"end");]|Loop{label;typ;block=b}->structured_block~loc[Delimiter(block(instruction"loop"::(opt_idlabel@blocktypetyp)));Contents(List.mapinstrb.desc);Delimiter(instruction"end");]|TryTable{label;typ;catches=c;block=b}->structured_block~loc[Delimiter(block(instruction"try_table"::(opt_idlabel@blocktypetyp)));Contents(catchesc@List.mapinstrb.desc);Delimiter(instruction"end");]|Try{label;typ;block=b;catches;catch_all}->structured_block~loc(Delimiter(block(instruction"try"::(opt_idlabel@blocktypetyp)))::Contents(List.mapinstrb.desc)::(List.flatten(List.map(fun(i,l)->[Delimiter(block[instruction"catch";indexi]);Contents(List.mapinstrl.Ast.desc);])catches)@(matchcatch_allwith|None->[]|Somec->[Delimiter(instruction"catch_all");Contents(List.mapinstrc.desc);])@[Delimiter(instruction"end")]))|Br_table(l,i)->block~loc(instruction"br_table"::List.map(funi->indexi)(l@[i]))|Bri->block~loc[instruction"br";indexi]|Br_ifi->block~loc[instruction"br_if";indexi]|Br_on_nulli->block~loc[instruction"br_on_null";indexi]|Br_on_non_nulli->block~loc[instruction"br_on_non_null";indexi]|Br_on_cast(i,ty,ty')->block~loc[instruction"br_on_cast";indexi;reftypety;reftypety']|Br_on_cast_fail(i,ty,ty')->block~loc[instruction"br_on_cast_fail";indexi;reftypety;reftypety']|Br_on_cast_desc_eq(i,ty,ty')->block~loc[instruction"br_on_cast_desc_eq";indexi;reftypety;reftypety']|Br_on_cast_desc_eq_fail(i,ty,ty')->block~loc[instruction"br_on_cast_desc_eq_fail";indexi;reftypety;reftypety';]|Return->instruction~loc"return"|Throwtag->block~loc[instruction"throw";indextag]|ThrowRef->block~loc[instruction"throw_ref"]|ContNewi->block~loc[instruction"cont.new";indexi]|ContBind(i,j)->block~loc[instruction"cont.bind";indexi;indexj]|Suspendi->block~loc[instruction"suspend";indexi]|Resume(i,clauses)->block~loc(instruction"resume"::indexi::on_clausesclauses)|ResumeThrow(i,j,clauses)->block~loc(instruction"resume_throw"::indexi::indexj::on_clausesclauses)|ResumeThrowRef(i,clauses)->block~loc(instruction"resume_throw_ref"::indexi::on_clausesclauses)|Switch(i,j)->block~loc[instruction"switch";indexi;indexj]|Nop->instruction~loc"nop"|Unreachable->instruction~loc"unreachable"|ArraySettyp->block~loc[instruction"array.set";indextyp]|StructSet(typ,i)->block~loc[instruction"struct.set";indextyp;indexi]|ReturnCallf->block~loc[instruction"return_call";indexf]|ReturnCallReftyp->block~loc[instruction"return_call_ref";indextyp](* A folded hinted branch puts the annotation *before* the folded group —
[(@…) (br_if …)] / [(@…) (if …)] — not inside it. The head is reprinted
without its hints so the group itself comes out plain. *)|Folded(head,l)whennot(Hints.is_emptyhead.hints)->block~loc~transparent:true(hint_annotationshead.hints@[instr_unhinted{desc=Folded({headwithhints=Hints.none},l);info=head.info;hints=Hints.none;};])|Folded({desc=If{label;typ;if_block;else_block};_},l)->(* Give each clause the location of its (then ...)/(else ...) group so
a comment trailing the clause attaches to it. *)letclause?(always=false)nameb=if(notalways)&&b.Ast.desc=[]then[]else[list~loc:b.Ast.info(instructionname::List.mapinstrb.Ast.desc);]inlist~loc(block~transparent:true(block(instruction"if"::(opt_idlabel@blocktypetyp))::List.mapinstrl)::(clause~always:true"then"if_block@clause"else"else_block))|Folded({desc=Block{label;typ;block=b};_},l)->assert(l=[]);list~loc(block(instruction"block"::(opt_idlabel@blocktypetyp))::List.mapinstrb.desc)|Folded({desc=Loop{label;typ;block=b};_},l)->assert(l=[]);list~loc(block(instruction"loop"::(opt_idlabel@blocktypetyp))::List.mapinstrb.desc)|Folded({desc=TryTable{label;typ;catches=c;block=b};_},l)->assert(l=[]);list~loc(block(instruction"try_table"::(opt_idlabel@blocktypetyp))::block(catchesc)::List.mapinstrb.desc)|Folded({desc=Try{label;typ;block=b;catches;catch_all};_},l)->assert(l=[]);list~loc(block(instruction"try"::(opt_idlabel@blocktypetyp))::list(instruction"do"::List.mapinstrb.desc)::(List.map(fun(i,l)->list(block[instruction"catch";indexi]::List.mapinstrl.Ast.desc))catches@matchcatch_allwith|None->[]|Somel->[list(instruction"catch_all"::List.mapinstrl.Ast.desc)]))|String(id,s)|Folded({desc=String(id,s);_},[])->list~loc(block(atom~style:Annotation"@string"::option(funid->[index~style:Annotationid])id)::List.map(funs->letloc=Somes.Ast.infoinleti,s=escape_strings.Ast.descinAtom{loc;style=Annotation;len=Some(i+2);s="\""^s^"\"";})s)|Charc|Folded({desc=Charc;_},[])->letn=Uchar.utf_8_byte_lengthcinletb=Bytes.createninignore(Bytes.set_utf_8_ucharb0c);list~loc[atom~style:Annotation"@char";(leti,s=escape_string(Bytes.to_stringb)inAtom{loc=None;style=Annotation;len=Some(i+2);s="\""^s^"\"";});]|If_annotation{cond;then_body;else_body}|Folded({desc=If_annotation{cond;then_body;else_body};_},[])->letclauseheadbody=list~loc:body.Ast.info(atom~style:Annotation("@"^head)::List.mapinstrbody.Ast.desc)inlist~loc(block[atom~style:Annotation"@if";cond_doccond]::clause"then"then_body::option(fune->[clause"else"e])else_body)|Folded(i,l)->list~loc[block~transparent:true(instri::List.mapinstrl)]letinstr_list_needs_vertical_layoutl=List.exists(fun(i:_Ast.Text.instr)->matchi.descwithFolded_->false|_->true)lletinline_instrsl=List.mapinstrlletinstrsl=matchlwith|[]->[]|_->letdocs=List.mapinstrlinifinstr_list_needs_vertical_layoutlthen[Vertical_block(None,docs)]elsedocs(*** Types, declarations, and module fields ***)letsubtype?loct=letid,{typ;supertype;final;descriptor;describes}=t.Ast.descinletloc=matchlocwithSome_->loc|None->Somet.Ast.infoin(* [(describes $o)] / [(descriptor $d)] clauses precede the composite type. *)letclauses=option(funi->[list[keyword"describes";indexi]])describes@option(funi->[list[keyword"descriptor";indexi]])descriptoriniffinal&&Option.is_nonesupertypethenlist?loc(block(keyword"type"::opt_idid)::(clauses@[block[comptypetyp]]))elselist?loc[block(keyword"type"::opt_idid);list[block(block(keyword"sub"::((iffinalthen[keyword"final"]else[])@option(funi->[indexi])supertype))::(clauses@[comptypetyp]));];]letfundecl(idx,typ)=optiontypeuseidx@option(fun{params;results}->ifparams=[||]&&results=[||]then[]else[block((ifArray.for_all(funp->fstp.Ast.desc=None)paramsthenmake_list~kind:keyword"param"(funtl->List.mapvaltypetl)(Array.to_list(Array.map(funp->sndp.Ast.desc)params))elseArray.to_list(Array.map(funp->leti,t=p.Ast.descinlist~loc:p.Ast.info(keyword"param"::(opt_idi@[valtypet])))params))@valtype_list"result"(Array.to_listresults));])typletexprnamee=matchewith|[({desc=Folded_;_}asi)]->instri|_->list(keywordname::inline_instrse)letfunction_indices(lst:_Ast.Text.exprlist)=letextracti=matchiwith|[({desc=RefFuncidx;_}|{desc=Folded({desc=RefFuncidx;_},[]);_});]->Someidx|_->NoneinifList.for_all(funi->extracti<>None)lstthenSome(List.filter_mapextractlst)elseNone(* The [(kind …)] descriptor s-expression shared by a plain import and a compact
group item, e.g. [(func $id (type 0))] / [(global i32)]. *)letimport_desc_blockid(desc:importdesc)=letkind,typ=matchdescwith|Func{exact;typ}->("func",ifexactthen[list(type_"exact"::fundecltyp)]elsefundecltyp)|Globalty->("global",[globaltypety])|Tagtyp->("tag",fundecltyp)|Memoryl->("memory",limitsl)|Tablety->("table",tabletypety)inblock(keywordkind::(opt_idid@typ))letrecmodulefieldf=letloc=f.Ast.infoinmatchf.Ast.descwith(* Carry the field location (which spans the whole type definition) so a
leading comment or blank line attaches before the type rather than to its
inner name — the array element itself is built with a dummy location. *)|Types[|t|]->subtype~loct|Typesl->list~loc(keyword"rec"::List.mapsubtype(Array.to_listl))|Func{id;typ;locals;instrs=i;exports=e;priority}->letlocal_sexpe=letnm,t=e.Ast.descin(* The hovbox lets a comment before any local break correctly while the
locals stay packed. *)list~loc:e.Ast.info(keyword"local"::(opt_idnm@[valtypet]))inletlocals_block=matchlocalswith|[]->[]|_->[block~bk:`Hov(List.maplocal_sexplocals)]inlist~loc(block(keyword"func"::(opt_idid@exportse@fundecltyp))::(compilation_priority_annotationspriority@locals_block@instrsi))|Import{module_;name;id;desc;exports=e}->(matchewith|[]->list~loc[block[keyword"import";quoted_stringmodule_;quoted_stringname];list[import_desc_blockiddesc];]|_->letkind,typ=matchdescwith|Func{exact;typ}->("func",ifexactthen[list(type_"exact"::fundecltyp)]elsefundecltyp)|Globalty->("global",[globaltypety])|Tagtyp->("tag",fundecltyp)|Memoryl->("memory",limitsl)|Tablety->("table",tabletypety)inlist~loc(block(keywordkind::(opt_idid@exportse@[list[keyword"import";quoted_stringmodule_;quoted_stringname;];]))::typ))(* compact-import-section: [(import "m" (item "n" (kind …)) …)] with a type per
item, and [(import "m" (item "n") … (kind …))] with one shared trailing
type on name-only items. *)|Import_group1{module_;items}->list~loc(block[keyword"import";quoted_stringmodule_]::List.map(fun(name,id,desc)->list[keyword"item";quoted_stringname;list[import_desc_blockiddesc];])items)|Import_group2{module_;desc;items}->list~loc(block[keyword"import";quoted_stringmodule_]::List.map(funname->list[keyword"item";quoted_stringname])items@[list[import_desc_blockNonedesc]])|Global{id;typ;init;exports=e}->list~loc(block(keyword"global"::(opt_idid@exportse@[globaltypetyp]))::instrsinit)|Tag{id;typ;exports=e}->list~loc[block(keyword"tag"::(opt_idid@exportse@fundecltyp))]|Data{id;init;mode}->lethead=keyword"data"::(opt_idid@matchmodewith|Passive->[]|Active(i,e)->(ifi.desc=NumUint32.zerothen[]else[list[keyword"memory";indexi]])@[expr"offset"e])inlist~loc(blockhead::List.mapdata_elem_sexpinit)|Startidx->list~loc[keyword"start";indexidx]|Memory{id;limits=l;init;exports=e}->lethead=keyword"memory"::(opt_idid@exportse@matchinitwith|None->limitsl|Some_->address_typel.desc.address_type)inlist~loc(blockhead::(matchinitwith|None->[]|Someinit->[list(keyword"data"::List.mapdata_elem_sexpinit)]))|Table{id;typ;init;exports=e}->list~loc(block(keyword"table"::(opt_idid@exportse@matchinitwith|Init_default|Init_expr_->tabletypetyp|Init_segment_->address_typetyp.limits.desc.address_type@[reftypetyp.reftype]))::(matchinitwith|Init_default->[]|Init_expri->instrsi|Init_segmentseg->[list(keyword"elem"::(matchfunction_indicessegwith|Somelst->List.mapindexlst|None->List.map(fune->expr"item"e)seg));]))|Export{name;kind;index=i}->list~loc[keyword"export";quoted_stringname;list[keyword(matchkindwith|Func->"func"|Memory->"memory"|Table->"table"|Tag->"tag"|Global->"global");indexi;];]|Elem{id;typ;init;mode}->letlst=(* The [func] shorthand denotes element type [(ref func)] (non-null,
matching the [ref.func] elements), not the nullable [funcref]. It
may therefore only be used for a non-nullable [Func] type;
abbreviating a [funcref] segment would silently drop its
nullability. *)matchtypwith|{nullable=false;typ=Func}->function_indicesinit|_->Noneinlist~loc[block(keyword"elem"::(opt_idid@(matchmodewith|Passive->[]|Active(idx,ofs)->(ifidx.desc=NumUint32.zerothen[]else[list[keyword"table";indexidx]])@[expr"offset"ofs]|Declare->[keyword"declare"])@[(iflst=Nonethenreftypetypelsekeyword"func")]));block(matchlstwith|Somelst->List.mapindexlst|_->List.map(fune->expr"item"e)init);]|String_global{id=i;typ;init}->list~loc(block(atom~style:Annotation"@string"::id~style:Annotationi.Ast.desc::option(funid->[index~style:Annotationid])typ)::List.map(funs->leti,s=escape_strings.Ast.descinAtom{loc=None;style=Annotation;len=Some(i+2);s="\""^s^"\"";})init)|Feature_annotationname->leti,s=escape_stringname.Ast.descinlist~loc[atom~style:Annotation"@feature";Atom{loc=None;style=Annotation;len=Some(i+2);s="\""^s^"\"";};]|Type_alias{id=i;typ}->list~loc[atom~style:Annotation"@type";id~style:Annotation~loc:i.Ast.infoi.Ast.desc;valtypetyp;]|Module_if_annotation{cond;then_fields;else_fields}->letclauseheadfields=list~loc:fields.Ast.info(atom~style:Annotation("@"^head)::List.mapmodulefieldfields.Ast.desc)inlist~loc(block[atom~style:Annotation"@if";cond_doccond]::clause"then"then_fields::option(fune->[clause"else"e])else_fields)(*** Entry points ***)(* The pretty-printer's intermediate [sexp] tree, laid out once. Building it for
a large module allocates heavily, so a caller that needs both the dry
trivia-collection pass and the real emit builds it a single time with
{!prepare} and drives both passes off the result — rather than rebuilding it
for each, which used to dominate the WAT output path. [top_depth] is the
initial [format_sexp] depth (1 for an unnamed [(module …)] laid out as a
[Vertical_block], 0 otherwise). *)typedocument={sexp:sexp;top_depth:int}letprepare(id,fields)=Wax_utils.Debug.timed"layout"@@fun()->letsexp=(* Top-level fields are laid out strictly one per line. *)ifid=NonethenVertical_block(None,List.mapmodulefieldfields)elselist(block~transparent:true(keyword"module"::opt_idid)::List.mapmodulefieldfields)in{sexp;top_depth=(ifid=Nonethen1else0)}(* Dry pass: record the looked-up locations without laying anything out. *)letcollect{sexp;_}set=collect_locssetsexpletemit?(color=Auto)?out_channel?(tail=[])printer~trivia{sexp;top_depth}=Wax_utils.Debug.timed"output"@@fun()->letuse_color=should_use_color~color~out_channelinlettheme=get_themeuse_colorinletctx={base=Styled.create~printer~theme~trivia();format=Hybrid;indent_level=2;}informat_sexpfalsetop_depthfalsectxsexp;(* Comments owned by no location (trailing comments, or the whole file for an
empty module) are printed last so they are not dropped. Blank lines at the
very end are dropped (end-of-file whitespace), but blank lines separating
tail comments are kept. *)lettail=Trivia.drop_trailing_blank_linestailinprint_triviactxtailletmodule_?color?out_channel?(tail=[])printer~triviam=emit?color?out_channel~tailprinter~trivia(preparem)letinstrprinteri=letuse_color=should_use_color~color:Auto~out_channel:(Somestderr)inlettheme=get_themeuse_colorinformat_sexpfalse2false{base=Styled.create~printer~theme~trivia:(Wax_utils.Trivia.empty())();format=Compact;indent_level=2;}(instri)(* A type definition rendered to a plain (uncoloured) one-line-ish string, for
the editor to show on hover over a type identifier. *)letsubtype_stringt=String.trim(Printer.run_string(funprinter->format_sexpfalse2false{base=Styled.create~printer~theme:(get_themefalse)~trivia:(Wax_utils.Trivia.empty())();format=Compact;indent_level=2;}(subtypet)))