123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149(*
* Copyright (c) 2022 Frédéric Bour <frederic.bour@lakaban.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)(* Generic code to reveal sharing in a cyclic graph *)type'agraph='aFastdom.graph={memoize:'b.('a->'b)->('a->'b);successors:'b.('b->'a->'b)->'b->'a->'b;}type('term,'var)binding_structure={(* Rewrite subterms of a term with a custom function *)map_subterms:('term->'term)->'term->'term;(* Produce a fresh variable for a term *)name_term:'term->'var;(* Injection from variable to terms *)var_term:'var->'term;(* [introduce_let ~recursive bindings body] create a possibly recursive
let-binder term that binds the names in [bindings] in the scope of [body]
*)introduce_let:recursive:bool->('var*'term)list->'term->'term;}typeoccurrence={mutablemin_scope:int;cursor:intref;}letexplicit_sharing(typeab)(gr:aFastdom.graph)(bs:(a,b)binding_structure)t=letpostorder,dominance=Fastdom.dominancegrtinletcount=Array.lengthpostorderinletbindings=Array.makecount[]inletvar_name=Array.makecountNoneinletsharetag=matchFastdom.predecessorstagwith|[]->false|[_]->Fastdom.nodetag==t|_::_::_->trueinfori=count-1downto0dolettag=postorder.(i)inifsharetagthenbeginletnode=Fastdom.nodetaginletvar=bs.name_termnodeinvar_name.(i)<-Some(bs.var_termvar);letdominator=Fastdom.dominatortaginletindex=Fastdom.postorder_indexdominatorinbindings.(index)<-(var,tag)::bindings.(index)enddone;letnull_occurrence={min_scope=0;cursor=ref0}inletrec_occurrences=Array.makecountnull_occurrenceinletrectraverse~is_bindingt=letcursor=refmax_intinletbindings,t=lettag=dominancetinletid=Fastdom.postorder_indextaginifid=-1then([],t)elsematchvar_name.(id)with|Somenamewhennotis_binding->letocc=rec_occurrences.(id)inif!(occ.cursor)<occ.min_scopethenocc.min_scope<-!(occ.cursor);([],name)|_->matchbindings.(id)with|[]->([],t)|bindings'->bindings.(id)<-[];letinit_occurrence(_,tag)=rec_occurrences.(Fastdom.postorder_indextag)<-{min_scope=max_int;cursor;}inList.iterinit_occurrencebindings';(bindings',t)inlett=bs.map_subtermstraverse_childtinmatchList.mapitraverse_bindingbindingswith|[]->t|bindings->letnormalize_scope(_,occ,_)min_scope=ifmin_scope<occ.min_scopethen(occ.min_scope<-min_scope;min_scope)elseocc.min_scopeinignore(List.fold_rightnormalize_scopebindingsmax_int:int);letlet_~recursivegroupbody=matchgroupwith|[]->body|bindings->bs.introduce_let~recursive(ifrecursivethenbindingselseList.revbindings)bodyinletrecnonrec_bindingsgroup~scope_limit~index=function|[]->let_~recursive:falsegroupt|(var,occ,t')::bindingswhenocc.min_scope>index->ifindex>=scope_limitthen(let_~recursive:falsegroup(nonrec_bindings[var,t']~scope_limit:occ.min_scope~index:(index+1)bindings))elsenonrec_bindings((var,t')::group)~scope_limit:(minocc.min_scopescope_limit)~index:(index+1)bindings|bindings->let_~recursive:falsegroup(rec_bindings[]indexbindings)andrec_bindingsgroupindex=function|(var,occ,t')::bindingswhenocc.min_scope<=index->rec_bindings((var,t')::group)(index+1)bindings|bindings->let_~recursive:truegroup(nonrec_bindings[]~scope_limit:max_int~indexbindings)innonrec_bindings[]~scope_limit:max_int~index:0bindingsandtraverse_childt=traverse~is_binding:falsetandtraverse_bindingindex(var,tag)=letocc=rec_occurrences.(Fastdom.postorder_indextag)inocc.cursor:=index;(var,occ,traverse~is_binding:true(Fastdom.nodetag))intraverse~is_binding:truet