123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483openParsetreeopenAsttypesopenLongidentopenAst_helperopenLocationletmkpatvari=Pat.var(mknoloc("c"^(string_of_inti)))letmkidenti=Exp.ident(mknoloc(Lident("c"^(string_of_inti))))(* ============================= [ Base types ] ============================= *)let()=[("bool -> float",[%type:bool],[%type:float],[%exprfunction|false->0.|true->1.]);("bool -> int",[%type:bool],[%type:int],[%exprfunction|false->0|true->1]);("bool -> string",[%type:bool],[%type:string],[%exprstring_of_bool]);("char -> int",[%type:char],[%type:int],[%exprint_of_char]);("char -> string",[%type:char],[%type:string],[%exprString.make1]);("float -> string",[%type:float],[%type:string],[%exprstring_of_float]);("int -> bool",[%type:int],[%type:bool],[%exprfunction|0->false|1->true|_->failwith"madcast: int -> bool"]);("int -> char",[%type:int],[%type:char],[%exprfuni->trychar_of_intiwithFailure_->failwith"madcast: int -> char"]);("int -> float",[%type:int],[%type:float],[%exprfloat_of_int]);("int -> string",[%type:int],[%type:string],[%exprstring_of_int]);("string -> bool",[%type:string],[%type:bool],[%exprfuns->trybool_of_stringswithFailure_->failwith"madcast: string -> bool"]);("string -> char",[%type:string],[%type:char],[%exprfuns->ifString.lengths=1thens.[0]elsefailwith"madcast: string -> char"]);("string -> float",[%type:string],[%type:float],[%exprfuns->tryfloat_of_stringswithFailure_->failwith"madcast: string -> float"]);("string -> int",[%type:string],[%type:int],[%exprfuns->tryint_of_stringswithFailure_->failwith"madcast: string -> int"])]|>List.iter(fun(name,itype,otype,expr)->letmatcher(itype',otype')=ifParsetree_utils.equal_core_typeitypeitype'&&Parsetree_utils.equal_core_typeotypeotype'thenSome[]elseNoneinletbuildercasts=assert(casts=[]);exprinRuleSet.register(Rule.make~name~matcher~builder()))(* ============================== [ Options ] =============================== *)let()=letname="'a option -> 'b option"inletmatcher=function|[%type:[%t?itype]option],[%type:[%t?otype]option]->Some[itype,otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfunction|None->None|Somex->Some([%eList.hdcasts]x)]inRuleSet.register(Rule.make~name~matcher~builder())let()=letname="'a -> 'b option"inletmatcher=function|itype,[%type:[%t?otype]option]->Some[itype,otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfunx->Some([%eList.hdcasts]x)]inRuleSet.(register~applies_after:[lookup"'a option -> 'b option"](Rule.make~name~matcher~builder()))let()=letname="'a option -> 'b"inletmatcher=function|[%type:[%t?itype]option],otype->Some[itype,otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfunction|None->failwith"madcast: 'a option -> 'b"|Somex->[%eList.hdcasts]x]inRuleSet.(register~applies_after:[lookup"'a option -> 'b option"](Rule.make~name~matcher~builder()))(* =============================== [ Arrays ] =============================== *)let()=letname="'a array -> 'b array"inletmatcher=function|[%type:[%t?itype]array],[%type:[%t?otype]array]->Some[itype,otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprArray.map[%eList.hdcasts]]inRuleSet.register(Rule.make~name~matcher~builder())let()=letname="'a -> 'b array"inletmatcher=function|itype,[%type:[%t?otype]array]->Some[itype,otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfunx->[|[%eList.hdcasts]x|]]inRuleSet.(register~applies_after:[lookup"'a array -> 'b array"](Rule.make~name~matcher~builder()))let()=letname="'a array -> 'b"inletmatcher=function|[%type:[%t?itype]array],otype->Some[itype,otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfuna->ifArray.lengtha=1then[%eList.hdcasts]a.(0)elsefailwith"madcast: 'a array -> 'b"]inRuleSet.(register~applies_after:[lookup"'a array -> 'b array";lookup"'a -> 'b array"](Rule.make~name~matcher~builder()))let()=letname="<tuple> -> 'b array"inletmatcher=function|{ptyp_desc=Ptyp_tupleitypes},[%type:[%t?otype]array]->Some(List.map(funitype->(itype,otype))itypes)|_->Noneinletbuildercasts=(* fun (c0,...ck) -> [|cast0 c0; ... castk ck|] *)Exp.fun_NolabelNone(Pat.tuple(List.mapi(funi_->mkpatvari)casts))(Exp.array(List.mapi(funicast->Exp.applycast[Nolabel,mkidenti])casts))inRuleSet.register(Rule.make~name~matcher~builder())let()=letname="'a array -> <tuple>"inletmatcher=function|[%type:[%t?itype]array],{ptyp_desc=Ptyp_tupleotypes}->Some(List.map(funotype->(itype,otype))otypes)|_->Noneinletbuildercasts=(* function
| [|c0;...ck|] -> (cast0 c0, ... castk ck)
| _ -> failwith ... *)Exp.function_[Exp.case(Pat.array(List.mapi(funi_->mkpatvari)casts))(Exp.tuple(List.mapi(funicast->Exp.applycast[Nolabel,mkidenti])casts));Exp.case(Pat.any())[%exprfailwith"madcast: 'a array -> <tuple>"]]inRuleSet.register(Rule.make~name~matcher~builder())let()=letname="<tuple> array -> 'a array"inletmatcher=function|[%type:[%t?{ptyp_desc=Ptyp_tupleitypes}]array],[%type:[%t?otype]array]->Some[Typ.tupleitypes,[%type:[%totype]array]]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfuna->Array.map[%eList.hdcasts]a|>Array.to_list|>Array.concat]inRuleSet.(register~applies_before:[lookup"'a -> 'b array";lookup"'a array -> 'b"](Rule.make~name~matcher~builder()))let()=letname="'a array -> <tuple> array"inletmatcher=function|[%type:[%t?itype]array],[%type:[%t?{ptyp_desc=Ptyp_tupleotypes}]array]->Some(List.map(funotype->(itype,otype))otypes)|_->Noneinletbuildercasts=letl=List.lengthcastsinletexp_intn=Exp.constant(Const.intn)in[%exprfuna->ifArray.lengthamod[%eexp_intl]<>0thenfailwith"madcast: 'a array -> <tuple> array"elseArray.init(Array.lengtha/[%eexp_intl])(funi->[%eExp.tuple(List.mapi(funjcast->[%expr[%ecast]a.([%eexp_intj]+i*[%eexp_intl])])casts)])]inRuleSet.(register~applies_before:[lookup"'a -> 'b array";lookup"'a array -> 'b"]~applies_after:[lookup"<tuple> array -> 'a array"](Rule.make~name~matcher~builder()))(* =============================== [ Lists ] ================================ *)(* using the rules for arrays *)let()=letname="'a list -> 'a array -> 'b"inletmatcher=function|[%type:[%t?itype]list],otype->Some[[%type:[%titype]array],otype]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfunl->Array.of_listl|>[%eList.hdcasts]]inRuleSet.register(Rule.make~name~matcher~builder())let()=letname="'a -> 'b array -> 'b list"inletmatcher=function|itype,[%type:[%t?otype]list]->Some[itype,[%type:[%totype]array]]|_->Noneinletbuildercasts=assert(List.lengthcasts=1);[%exprfunx->[%eList.hdcasts]x|>Array.to_list]inRuleSet.register(Rule.make~name~matcher~builder())(* =============================== [ Tuples ] =============================== *)let()=letname="<tuple> -> <tuple>"inletmatcher=function|{ptyp_desc=Ptyp_tupleitypes},{ptyp_desc=Ptyp_tupleotypes}whenList.lengthitypes=List.lengthotypes->Some(List.combineitypesotypes)|_->Noneinletbuildercasts=(* fun (c0,...ck) -> (cast0 c0, ... castk ck) *)Exp.fun_NolabelNone(Pat.tuple(List.mapi(funi_->mkpatvari)casts))(Exp.tuple(List.mapi(funicast->Exp.applycast[Nolabel,mkidenti])casts))inRuleSet.register(Rule.make~name~matcher~builder())(* ============================= [ Functions ] ============================== *)let()=letname="('a -> 'b) -> ('c -> 'd)"inletmatcher=function|[%type:[%t?iitype]->[%t?iotype]],[%type:[%t?oitype]->[%t?ootype]]->Some[(oitype,iitype);(iotype,ootype)]|_->Noneinletbuilder=function|[icast;ocast]->[%exprfunfx->x|>[%eicast]|>f|>[%eocast]]|_->assertfalseinRuleSet.register(Rule.make~name~matcher~builder())let()=letname="currying"inletmatcher(itype,otype)=matchitypewith|[%type:[%t?{ptyp_desc=Ptyp_tupleiitypes}]->[%t?iotype]]->(letrecmatcher=function|([],ootype)->[(iotype,ootype)](* this is the right order *)|(iitype::iitypes,[%type:[%t?oitype]->[%t?ootype]])->(oitype,iitype)::matcher(iitypes,ootype)|_->failwith"matcher"intrySome(matcher(iitypes,otype))withFailure_->None)|_->Noneinletbuildercasts=letocast=ExtList.ftcastsinleticasts=ExtList.bdcastsin[%exprfunf->[%eExtList.foldi_right(* imbricated functions *)(funi_exp->Exp.fun_NolabelNone(mkpatvari)exp)icasts((* the body of the function *)Exp.applyocast[Nolabel,Exp.apply[%exprf][Nolabel,Exp.tuple(List.mapi(funiicast->Exp.applyicast[Nolabel,mkidenti])icasts)]])]]inRuleSet.register~applies_after:[RuleSet.lookup"('a -> 'b) -> ('c -> 'd)"](Rule.make~name~matcher~builder())let()=letname="uncurrying"inletmatcher(itype,otype)=matchotypewith|[%type:[%t?{ptyp_desc=Ptyp_tupleoitypes}]->[%t?ootype]]->(letrecmatcher=function|(iotype,[])->[(iotype,ootype)](* this is the right order *)|([%type:[%t?iitype]->[%t?iotype]],oitype::ootypes)->(oitype,iitype)::matcher(iotype,ootypes)|_->failwith"matcher"intrySome(matcher(itype,oitypes))withFailure_->None)|_->Noneinletbuildercasts=letocast=ExtList.ftcastsinleticasts=ExtList.bdcastsin[%exprfunf->[%eExp.fun_NolabelNone(Pat.tuple(List.mapi(funi_->mkpatvari)icasts))(Exp.applyocast[Nolabel,(Exp.apply[%exprf](List.mapi(funiicast->(Nolabel,Exp.applyicast[Nolabel,mkidenti]))icasts))])]]inRuleSet.register~applies_after:[RuleSet.lookup"('a -> 'b) -> ('c -> 'd)"](Rule.make~name~matcher~builder())(* ======================= [ And now, the main loop ] ======================= *)letrecreverse_possibles=function(* changes a list of possibilities in possibilities of lists *)|[]->[[]]|possible_heads::tail_of_possibles->List.map(funpossible_tail->List.map(funpossible_head->possible_head::possible_tail)possible_heads)(reverse_possiblestail_of_possibles)|>List.flattenletrecderive(itype,otype):Parsetree.expressionlist=RuleSet.fold_by_priority(funrules->function|[]->(* Empty means that the stronger priorities have found
nothing. We go through all the rules at our priority,
apply them and see which ones did succeed. *)List.fold_left(funcastsrule->matchRule.match_rule(itype,otype)with|None->(* the rule found nothing *)casts|Somepremises->(List.mapderivepremises|>reverse_possibles|>List.map(funpremises->Rule.build_rulepremises))@casts)[]rules|_ascasts->(* Non-empty means that the previous priorities have found
something already, so we let that and do nothing. *)casts)[]letderiveitypeotype=(* We ask derive to derive expressions for itype -> otype. We then
annotate them with that type where type variables are universally
quantified. Since this can syntactically only happen in a let, we
return something like:
let cast : [vars]. [itype -> otype] = [expr] in cast
*)lett=Parsetree_utils.universal_closure_of_core_type[%type:[%titype]->[%totype]]inderive(itype,otype)|>List.map(funexpr->[%exprlet(cast:[%tt])=[%eexpr]incast])