123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395openWax_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)letatom~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))letvaltype(t:valtype)=matchtwith|I32->type_"i32"|I64->type_"i64"|F32->type_"f32"|F64->type_"f64"|V128->type_"v128"|Refty->reftypetyletpackedtypet=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](* Branch-hinting proposal: the [(@metadata.code.branch_hint "\00"|"\01")]
annotation that prefixes a hinted conditional branch. *)letbranch_hint_annotation(likely:bool)=leti,s=escape_string(iflikelythen"\001"else"\000")inlist[atom~style:Annotation"@metadata.code.branch_hint";Atom{loc=None;style=Annotation;len=Some(i+2);s="\""^s^"\"";};](*** The instruction printer ***)letrecinstri=letloc=i.Ast.infoinmatchi.Ast.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';](* Branch-hinting proposal: unfolded hinted branch — the annotation precedes the
wrapped instruction. A block-form [if] keeps its own multi-line layout (the
annotation on its own line above it); an inline [br_if]/[br_on_*] stays on one
line with the annotation. *)|Hinted(h,inner)->(matchinner.Ast.descwith|If_->Vertical_block(Someloc,[branch_hint_annotationh;instrinner])|_->block~loc~transparent:true[branch_hint_annotationh;instrinner])|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]|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)(* Branch-hinting proposal: a folded hinted branch puts the annotation *before*
the folded group — [(@…) (br_if …)] / [(@…) (if …)] — not inside it. The
wrapped instruction is printed as its own folded node. *)|Folded({desc=Hinted(h,inner);info},l)->block~loc~transparent:true[branch_hint_annotationh;instr{Ast.desc=Folded(inner,l);info};]|Folded(i,l)->list~loc[block~transparent:true(instri::List.mapinstrl)]letinstr_list_needs_vertical_layoutl=List.exists(fun(i:_Ast.Text.instr)->matchi.Ast.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|[({Ast.desc=Folded_;_}asi)]->instri|_->list(keywordname::inline_instrse)letfunction_indiceslst=letextracti=matchiwith|[({Ast.desc=RefFuncidx;_}|{Ast.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}->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))::(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(fun(name,id)->list(keyword"item"::(opt_idid@[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^"\"";};]|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 ***)letmodule_?(color=Auto)?out_channel?(tail=[])?collectprinter~trivia(id,fields)=(* [collect] marks the dry trivia-collection traversal; time the real emit
only, so a single "output" timing is reported. *)Wax_utils.Debug.timed_if(collect=None)"output"@@fun()->letuse_color=should_use_color~color~out_channelinlettheme=get_themeuse_colorinletctx={base=Styled.create~printer~theme?collect~trivia();format=Hybrid;indent_level=2;}inletsexp=(* 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)informat_sexpfalse(ifid=Nonethen1else0)falsectxsexp;(* 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_triviactxtailletinstrprinteri=letuse_color=should_use_color~color:Auto~out_channel:(Somestderr)inlettheme=get_themeuse_colorinformat_sexpfalse2false{base=Styled.create~printer~theme~trivia:(Hashtbl.create16)();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=letbuf=Buffer.create64inletfmt=Format.formatter_of_bufferbufinPrinter.runfmt(funprinter->format_sexpfalse2false{base=Styled.create~printer~theme:(get_themefalse)~trivia:(Hashtbl.create16)();format=Compact;indent_level=2;}(subtypet));Format.pp_print_flushfmt();String.trim(Buffer.contentsbuf)