123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372openBatteriesopenAstopenPassopenLangmoduleA=Ast_helper(* general helpers --------------------------------------------------------- *)moduleEnum=structincludeEnum(** collects each [x, y] in this enumeration into respective lists [xs, ys]. **)letcollect2e=letxs,ys=Enum.fold(fun(xs,ys)(x,y)->x::xs,y::ys)([],[])einList.revxs,List.revys(** collects each [x, y, z] in this enumeration into respective lists [xs, ys, zs]. **)letcollect3e=letxs,ys,zs=Enum.fold(fun(xs,ys,zs)(x,y,z)->x::xs,y::ys,z::zs)([],[],[])einList.revxs,List.revys,List.revzsend(** [compose_all [f; g; h] x] = [f (g (h x))] **)letcompose_all=function|[]->identity|[f]->f|fs->funx->List.fold_right(funfy->fy)fsx(* ocaml ast helpers --------------------------------------------------------- *)(** convert [string loc] into [Longident.t loc] as just a [Lident]. **)letlident_of_id(id:stringloc)=Location.mkloc(Longident.Lidentid.txt)id.loc(** convert [string loc] into [expr] that is just a [Pexp_ident]. **)letexp_of_id(id:stringloc)=A.Exp.ident~loc:id.loc(lident_of_idid)(** generates simple [let x = e1 in e2] expression **)letsimple_let?(recflag=Asttypes.Nonrecursive)xe1e2=letloc=x.Asttypes.locinletvb=A.Vb.mk~loc(A.Pat.var~locx)e1inA.Exp.let_~locrecflag[vb]e2(** generates simple [let p = e1 in e2] expression **)letsimple_pat_let?(recflag=Asttypes.Nonrecursive)pe1e2=letloc=p.ppat_locinletvb=A.Vb.mk~locpe1inA.Exp.let_~locrecflag[vb]e2(** generate fresh [string loc] using the given [int ref]. *)letfresh~next_id~loc=leti=!next_idinnext_id:=i+1;({txt=Printf.sprintf"np tmp_id%d"i;loc}:stringloc)(* nanopass ast helpers --------------------------------------------------------- *)(** finds all the variables mentioned in the given pattern. returns
the [string loc]s in reverse order. *)letvars_of_pattern=letrectravvrs=function|NPpat_any_->vrs|NPpat_varid->id::vrs|NPpat_alias(pat,id)->trav(id::vrs)pat|NPpat_tuple(pats,_)->List.fold_lefttravvrspats|NPpat_variant(_,None,_)->vrs|NPpat_variant(_,Somepat,_)->travvrspat|NPpat_mappat->travvrspat|NPpat_cata(pat,_)->travvrspatintrav[](* library --------------------------------------------------------- *)moduleLib_ast=structopenLongidentletfold_lid=Ldot(Lident"List","fold_right")letmap_lid=Ldot(Lident"List","map")(** generates expression of the form [fold l z0 (fun x z -> e)]. **)letfold_exp~loclist_expinit_expelem_patacc_patbody_exp=A.Exp.apply~loc(A.Exp.ident~loc{txt=fold_lid;loc})[Nolabel,A.Exp.fun_~locNolabelNoneelem_pat(A.Exp.fun_~locNolabelNoneacc_patbody_exp);Nolabel,list_exp;Nolabel,init_exp](** generates expression of the form [map l (fun p -> e)]. **)letmap_exp~loclist_expelem_patbody_exp=A.Exp.apply~loc(A.Exp.ident~loc{txt=map_lid;loc})[Nolabel,A.Exp.fun_~locNolabelNoneelem_patbody_exp;Nolabel,list_exp](** generates a function to zip n lists *)letzipper_exp~next_id~locnamelist_expsbody_exp=letcons_lid=Asttypes.{txt=Lident"::";loc}inletcons_pats=List.map(fun_->lethd=fresh~next_id~locandtl=fresh~next_id~locin((hd,tl),A.Pat.construct~loccons_lid(Some(A.Pat.tuple~loc[A.Pat.var~lochd;A.Pat.var~loctl]))))list_expsinletrecurse=A.Exp.apply~loc(A.Exp.ident~loc(lident_of_idname))[(Nolabel,A.Exp.tuple~loc(List.map(fun((_,tl),_)->A.Exp.ident~loc(lident_of_idtl))cons_pats))]andtuple=A.Exp.tuple~loc(List.map(fun((hd,_),_)->A.Exp.ident~loc(lident_of_idhd))cons_pats)inletfn_body=A.Exp.function_~loc[A.Exp.case(A.Pat.tuple~loc(List.map(fun(_,pat)->pat)cons_pats))(A.Exp.construct~loccons_lid(Some(A.Exp.tuple~loc[tuple;recurse])));A.Exp.case(A.Pat.any~loc())(A.Exp.construct~loc{txt=Lident"[]";loc}None)]inA.Exp.let_~locRecursive[A.Vb.mk~loc(A.Pat.var~locname)fn_body]body_expend(* codegen begins here --------------------------------------------------------- *)(** given an unconditional pattern, converts it to an equivalent parsetree pattern. *)letrecgen_simple_pat=function|NPpat_anyloc->A.Pat.any~loc()|NPpat_varid->A.Pat.var~loc:id.locid|NPpat_alias(pat,id)->A.Pat.alias~loc:id.loc(gen_simple_patpat)id|NPpat_tuple(pats,loc)->A.Pat.tuple~loc(List.mapgen_simple_patpats)|pat->failwith"gen_simple_pat called on non-simple pat"(** given an [np_pat], returns [ppat, intro], where [ppat] is the generated
pattern, and [intro] is a function on expressions which introduces
let bindings around the given expression.
TODO: maybe represent [intro] as a list of value bindings instead
of a function?
[~next_id] is a [ref int] used to generate fresh identifies
if [~bind_as] is [Some <string loc>], the given string will be
bound to the result of the pattern.
*)letrecgen_pattern~next_id~bind_aspat=letloc=loc_of_patpatinmatchpatwith|NPpat_any_->letppat=matchbind_aswith|None->A.Pat.any~loc()|Someid->A.Pat.var~locid(* [_ as x] becomes just [x] *)inppat,identity|NPpat_varid->letppat=A.Pat.var~loc:id.locidinletppat=matchbind_aswith|None->ppat|Someid'->A.Pat.alias~loc:id.locppatid'(* [x as y] = [x as y] *)inppat,identity|NPpat_alias(pat,id)->beginmatchbind_aswith|None->gen_pattern~next_id~bind_as:(Someid)pat|Someouter_id->(* BEFORE: (p as x) as y -> e
AFTER: p as x -> let y = x in e *)letppat,intro=gen_pattern~next_id~bind_as:(Someid)patinppat,intro%simple_letouter_id(exp_of_idid)end|NPpat_tuple(pats,_)->letppats,intro=matchbind_aswith|None->letppats,intros=List.enumpats|>Enum.map(gen_pattern~next_id~bind_as)|>Enum.collect2inppats,compose_allintros|Someid->(* BEFORE: (p,q) as x -> e
AFTER: (p as t0, q as t1) -> let x = t0, t1 in e *)letppats,intros,binds=List.enumpats|>Enum.map(funpat->letbind=fresh~next_id~locinletp,f=gen_pattern~next_id~bind_as:(Somebind)patinp,f,bind)|>Enum.collect3inlettuple_exp=A.Exp.tuple~loc(List.mapexp_of_idbinds)inppats,compose_allintros%simple_letidtuple_expinA.Pat.tuple~locppats,intro|NPpat_variant(lbl,opt_pat,_)->(* TODO: this may be refactor-able, but i'm not sure. *)beginmatchopt_pat,bind_aswith|None,None->A.Pat.variant~loclblNone,identity|None,Someid->(* note: we can't just do [`Var as x] because that may cause type errors
if we're expecting the reinterpret the variant. *)A.Pat.variant~loclblNone,simple_letid(A.Exp.variant~loclblNone)|Somepat,None->letppat,intro=gen_pattern~next_id~bind_as:NonepatinA.Pat.variant~loclbl(Someppat),intro|Somepat,Someid->letbind=fresh~next_id~locinletppat,intro=gen_pattern~next_id~bind_as:(Somebind)patinA.Pat.variant~loclbl(Someppat),intro%simple_letid(A.Exp.variant~loclbl(Some(exp_of_idbind)))end(* this should never be the case after typeck, but
in case it is, just ignore the missing catamorphism. *)|NPpat_cata(pat,None)->gen_pattern~next_id~bind_aspat|NPpat_cata(pat,Somecata_exp)->(* BEFORE: (p [@r cata]) -> e
AFTER: t0 -> let p = cata t0 in e *)letppat=gen_simple_patpatinletcata_tmp=fresh~next_id~locinA.Pat.var~loccata_tmp,simple_pat_letppat(A.Exp.apply~loccata_exp[Nolabel,exp_of_idcata_tmp])|NPpat_mappat->letpat=matchbind_aswithNone->pat|Someid->NPpat_alias(pat,id)inletlist_tmp=fresh~next_id~locinA.Pat.var~loclist_tmp,simple_pat_let(gen_l_lhs~locpat)(gen_l_rhs~next_idpatlist_tmp)(** generate the LHS pattern for a [@l] pattern (for binding the
results of the list comprehension). *)andgen_l_lhs~locpat=matchvars_of_patternpatwith|[]->A.Pat.construct~loc{txt=Lident"()";loc}None|[x]->A.Pat.var~locx|xs->A.Pat.tuple~loc(List.map(A.Pat.var~loc)xs)(** generate the RHS expression for a [@l] pattern (the expression
that performs the list comprehension). *)andgen_l_rhs~next_idpatlist_tmp=letloc=loc_of_patpatinletppat,intro=gen_pattern~next_id~bind_as:Nonepatinmatchvars_of_patternpatwith|[]->(* TODO: generate List.iter in case any catas have side effects *)A.Exp.construct~loc{txt=Lident"()";loc}None|[x]->Lib_ast.map_exp~loc(exp_of_idlist_tmp)ppat(intro(exp_of_idx))|xs->letempty=A.Exp.construct~loc{txt=Lident"[]";loc}Noneinletconsxy=letarg=A.Exp.tuple~loc[exp_of_idx;exp_of_idy]inA.Exp.construct~loc{txt=Lident"::";loc}(Somearg)inletacc_tmps=List.map(fun{Asttypes.loc}->fresh~next_id~loc)xsinLib_ast.fold_exp~loc(exp_of_idlist_tmp)(A.Exp.tuple~loc(List.map(constempty)xs))ppat(A.Pat.tuple~loc(List.map(A.Pat.var~loc)acc_tmps))(intro(A.Exp.tuple~loc(List.map2consxsacc_tmps)))(** generate type expression from language and nonterm **)lettyp_of_nonterm~loclangnt=A.Typ.constr~loc{txt=Ldot(Lidentlang.npl_name,nt.npnt_name);loc}[]letgen_zipper_exps~next_id~loc=letmapper=letopenAst_mapperin{default_mapperwithexpr=funmapperexpr->matchexprwith|{pexp_desc=Pexp_tuplees;pexp_loc=loc;pexp_attributes=[{txt="l"},_]}->letname=fresh~next_id~locandes=List.map(default_mapper.exprmapper)esinletapply_zipper=A.Exp.apply~loc(A.Exp.ident~loc(lident_of_idname))[(Nolabel,A.Exp.tuple~loces)]inLib_ast.zipper_exp~next_id~locnameesapply_zipper|expr->default_mapper.exprmapperexpr}inmapper.exprmapper(** generate [value_binding] from [np_processor]. **)letgen_processor_vbl0l1proc=letloc=proc.npc_locandnext_id=ref0in(* generate pattern/exprs for clauses *)letclause_lhs,clause_rhs=List.enumproc.npc_clauses|>Enum.map(fun(pat,rhs_exp)->letp_lhs,intro=gen_pattern~next_id~bind_as:Nonepatinp_lhs,introrhs_exp)|>Enum.collect2in(* generate domain/co-domain type *)letdom_typ=typ_of_nonterm~locl0proc.npc_dominletopt_cod_typ=Option.map(typ_of_nonterm~locl1)proc.npc_codin(* generate [match arg0 with clause -> rhs ...] *)letarg_id:stringloc={txt="np proc_arg";loc}inletmatch_expr=A.Exp.match_~loc(exp_of_idarg_id)(List.map2(funlhsrhs->{pc_lhs=lhs;pc_guard=None;pc_rhs=gen_zipper_exps~next_id~locrhs})clause_lhsclause_rhs)in(* annotate match expr if co-domain is present *)letmatch_expr=matchopt_cod_typwith|None->match_expr|Sometyp->A.Exp.constraint_~locmatch_exprtypin(* generate [fun arg0 -> match arg0 with ...] *)letclauses_fn_expr=A.Exp.fun_~loc:proc.npc_clauses_locNolabelNone(A.Pat.constraint_~loc(A.Pat.var~locarg_id)dom_typ)(* annotate domain type *)match_exprin(* [let proc arg ... = function ...] *)A.Vb.mk~loc(A.Pat.var~loc{txt=proc.npc_name;loc})(List.fold_right(fun(lbl,dflt,p)body_exp->A.Exp.fun_~loc:p.ppat_loclbldfltpbody_exp)proc.npc_argsclauses_fn_expr)(** generate [value_binding] from [np_pass]. **)letgen_pass_vbpass=letloc=pass.npp_locinletl0=pass.npp_inputinletl1=pass.npp_outputinletpre_introducer=pass.npp_preinletproc_vbs=List.map(gen_processor_vbl0l1)pass.npp_procsinA.Vb.mk~loc(A.Pat.var~loc{txt=pass.npp_name;loc})(pre_introducer(A.Exp.let_~locRecursiveproc_vbspass.npp_post))