123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223open!Coreopen!Import(* This univ-map is basically a set of all the type-ids that are free inside
the produced closure. *)moduleFree_variables=structincludeUniv_map.Make(Univ_map.Type_id_key)(structtype'at=unit[@@derivingsexp_of]end)letmergeab=List.fold(to_alistb)~init:a~f:(funacc(T(id,()))->setacc~key:id~data:());;end(* When executing the generated function, this Env is used to pass down any
variables that were bound in a let%sub. *)moduleEnv=Univ_map.Make(Univ_map.Type_id_key)(structtype'at='a[@@derivingsexp_of]end)(* This is a tri-state option, where the third state is a function that permits
the generation of the contained value, provided that all of the free
variables that were found, are bound inside the environment. *)moduleOption_or_miss=structtype'at=|None|Someof'a|Missof{free:Free_variables.t;gen:Env.t->'a}(* compresses a [Miss] when the set of free variables is empty. *)letsquash=function|None->None|Somea->Somea|Miss{free;gen}whenFree_variables.is_emptyfree->Some(genEnv.empty)|other->other;;letmapa~f=matchawith|None->None|Somea->Some(fa)|Miss{free;gen}->Miss{free;gen=(funm->f(genm))};;letbothab=matcha,bwith|None,_|_,None->None|Somea,Someb->Some(a,b)|Somea,Miss{free;gen}->Miss{free;gen=(funm->a,genm)}|Miss{free;gen},Someb->Miss{free;gen=(funm->genm,b)}|Miss{free=free_a;gen=gen_a},Miss{free=free_b;gen=gen_b}->letfree=Free_variables.mergefree_afree_binMiss{free;gen=(funenv->gen_aenv,gen_benv)};;moduleLet_syntax=structletmap=mapletboth=bothendend(* value_to_function takes a Value.t and attempts to translate it into a function
which has access to a "key" and "data" (named so because this function
is only used to simplify an [assoc], which provides both of those values. *)letrecvalue_to_function:typekeydataresult.resultValue.t->keyType_equal.Id.t->dataType_equal.Id.t->(key->data->result)Option_or_miss.t=funvaluekey_iddata_id->letopenOption_or_missinmatchvalue.valuewith|Constant(r,_)->Some(fun_key_data->r)|Incr_->None|Namedname->letsame_name=Type_equal.Id.same_witnessin(matchsame_namenamekey_id,same_namenamedata_idwith|SomeT,_->Some(funkey_data->key)|_,SomeT->Some(fun_keydata->data)|None,None->Miss{free=Free_variables.(add_exnempty~key:name~data:());gen=(funenv__->Env.find_exnenvname)})|Cutoff_->None|Both(a,b)->let%mapa=value_to_functionakey_iddata_idandb=value_to_functionbkey_iddata_idinfunkeydata->akeydata,bkeydata|Map{t;f}->let%mapa=value_to_functiontkey_iddata_idinfunkeydata->f(akeydata)|Map2{t1;t2;f}->let%mapt1=value_to_functiont1key_iddata_idandt2=value_to_functiont2key_iddata_idinfunkeydata->f(t1keydata)(t2keydata)|Map3{t1;t2;t3;f}->let%mapt1=value_to_functiont1key_iddata_idandt2=value_to_functiont2key_iddata_idandt3=value_to_functiont3key_iddata_idinfunkeydata->f(t1keydata)(t2keydata)(t3keydata)|Map4{t1;t2;t3;t4;f}->let%mapt1=value_to_functiont1key_iddata_idandt2=value_to_functiont2key_iddata_idandt3=value_to_functiont3key_iddata_idandt4=value_to_functiont4key_iddata_idinfunkeydata->f(t1keydata)(t2keydata)(t3keydata)(t4keydata)|Map5{t1;t2;t3;t4;t5;f}->let%mapt1=value_to_functiont1key_iddata_idandt2=value_to_functiont2key_iddata_idandt3=value_to_functiont3key_iddata_idandt4=value_to_functiont4key_iddata_idandt5=value_to_functiont5key_iddata_idinfunkeydata->f(t1keydata)(t2keydata)(t3keydata)(t4keydata)(t5keydata)|Map6{t1;t2;t3;t4;t5;t6;f}->let%mapt1=value_to_functiont1key_iddata_idandt2=value_to_functiont2key_iddata_idandt3=value_to_functiont3key_iddata_idandt4=value_to_functiont4key_iddata_idandt5=value_to_functiont5key_iddata_idandt6=value_to_functiont6key_iddata_idinfunkeydata->f(t1keydata)(t2keydata)(t3keydata)(t4keydata)(t5keydata)(t6keydata)|Map7{t1;t2;t3;t4;t5;t6;t7;f}->let%mapt1=value_to_functiont1key_iddata_idandt2=value_to_functiont2key_iddata_idandt3=value_to_functiont3key_iddata_idandt4=value_to_functiont4key_iddata_idandt5=value_to_functiont5key_iddata_idandt6=value_to_functiont6key_iddata_idandt7=value_to_functiont7key_iddata_idinfunkeydata->f(t1keydata)(t2keydata)(t3keydata)(t4keydata)(t5keydata)(t6keydata)(t7keydata);;letreccomputation_to_function:typekeydatamodeldynamic_actionstatic_actionresult.(model,dynamic_action,static_action,result)Computation.t->key_id:keyType_equal.Id.t->data_id:dataType_equal.Id.t->(Path.t->key->data->result)Option_or_miss.t=funcomputation~key_id~data_id->letrecurse=computation_to_function~key_id~data_idinlethandle_subst(typem1da1sa1r1m2da2sa2r2)~(from:(m1,da1,sa1,r1)Computation.t)~via~(into:(m2,da2,sa2,r2)Computation.t):(Path.t->key->data->r2)Option_or_miss.t=matchrecursefrom,recurseintowith(* This first ignored pattern is _spooky_. It basically means
that any computations that aren't depended on just aren't counted.
So you could have a Bonsai.state, but if it's unused, then we just
drop it and consider the rest. *)|_,Somer->Somer|None,_|_,None->None|Somefrom,Miss{free;gen}->letfree=Free_variables.removefreeviainletgenenvpathkeydata=letfrom_path=Path.(appendpathElem.Subst_from)inletinto_path=Path.(appendpathElem.Subst_into)inletenv=Env.add_exnenv~key:via~data:(fromfrom_pathkeydata)ingenenvinto_pathkeydatainOption_or_miss.squash(Miss{free;gen})|Miss{free=free_a;gen=gen_a},Miss{free=free_b;gen=gen_b}->letfree_b=Free_variables.removefree_bviainletfree=Free_variables.mergefree_afree_binletgenenvpathkeydata=letfrom_path=Path.(appendpathElem.Subst_from)inletinto_path=Path.(appendpathElem.Subst_into)inletenv=Env.add_exnenv~key:via~data:(gen_aenvfrom_pathkeydata)ingen_benvinto_pathkeydatainOption_or_miss.squash(Miss{free;gen})inmatchcomputationwith|Returnvalue->Option_or_miss.map(value_to_functionvaluekey_iddata_id)~f:(funf_path->f)|Subst{from;via;into;here=_}->handle_subst~from~via~into|Subst_stateless_from{from;via;into;here=_}->handle_subst~from~via~into|Subst_stateless_into{from;via;into;here=_}->handle_subst~from~via~into|Path->Some(funpath__->path)|_->None;;letcomputation_to_functiont~key_compare~key_id~data_id=letmake_path_element=Path.Elem.keyed~compare:key_comparekey_id|>unstageinmatchcomputation_to_functiont~key_id~data_id|>Option_or_miss.squashwith|Somef->Option.some(funpathkeydata->letpath=Path.appendpath(Assoc(make_path_elementkey))infpathkeydata)|None|Miss_->None;;