123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081openTypesopenUtil(** "level out" the purity of two values *)letlevel_purityab=match(a,b)with|Impure,_|_,Impure->Impure|Pure,Numerical|Numerical,Pure->Pure|Numerical,Numerical->Numerical|Uncertain,_|_,Uncertain->Uncertain|Pure,Pure->Pure(** Infer the purity of an expression. Note: this is a naive approach.
This function is an abstract interpretation of expressions over primitives and environments.
@param pt The primitives table
@param paraml If inside a lambda, the list of parameters
@param env An environment, needed to infer whether lazy expressions are pure or not *)letrecinferestate:puret=letstate={statewithstack=(push_stackstate.stacke)}inletinferpx=inferxstateinletinferp2ab=level_purity(inferpa)(inferpb)inletinferplls=letapl=List.mapinferplsinList.fold_leftlevel_purityNumericalaplinmatchewith|NumInt_|NumFloat_|NumComplex_|Unit->Numerical|Boolean_|String_->Pure|Nota->inferpa(* Expressions with lists of expressions *)|Listl|Sequencel->inferpll(* Dictionaries contain key value pairs, level them out *)|Dict(l)->let_,vl=unziplininferplvl|Purity(allowed,body)->ifisstrictlypurestate.purity&&isimpureallowedtheniraise(PurityError("Cannot enter an "^(show_puretallowed)^" context from a "^(show_puretstate.purity)^" one!"))elseinferbody{statewithpurity=allowed}(* Infer from all the binary operators *)|Compose(a,b)|Plus(a,b)|Sub(a,b)|Cons(a,b)|Concat(a,b)|Eq(a,b)|Lt(a,b)|Ge(a,b)|Le(a,b)|Gt(a,b)|And(a,b)|Or(a,b)|Mult(a,b)|Div(a,b)->inferp2ab|Lambda(_,b)->inferbstate|IfThenElse(g,t,f)->letpg=infergstateandpt=infertstateandpf=inferfstateinlevel_puritypg(level_purityptpf)|Let(assignments,body)->letnewstate=infer_assignment_listassignmentsstateininferbodynewstate|Symbols->lookupsstate|Apply(f,arg)->letfp=inferpfandargp=inferparginifispurestate.purity&&isimpurefptheniraises(PurityError(Printf.sprintf"Tried to apply a %s value in a %s state"(show_puretfp)(show_puretstate.purity)))state.stackelselevel_purityfpargp|ApplyPrimitive((name,numparams,purity),args)->ifList.lengthargs!=numparamsthen(iraise(Fatal"Primitive Application Error"))elseifispurestate.purity&&isimpurepuritytheniraises(PurityError("Tried to apply an impure primitive in a pure block: "^name))state.stackelsepurityandlookup(name:ide)(state:evalstate):puret=matchDict.getnamePrimitives.purity_tablewith|None->(matchDict.getnamestate.purityenvwith|None->Numerical(* Value may be unbound or in parameters *)|Somepurity->purity)|Somepurity->purityandinfer_assignmentstate(_,name,value):evalstate=(* Return a new state, updating the purityenv with the new binding, infered from the value (where Impure contexts are allowed) *){statewithpurityenv=(Dict.insertstate.purityenvname(infervalue{statewithpurity=Impure}))}andinfer_assignment_listassignmentsstate:evalstate=matchassignmentswith|[]->state|(islazy,name,value)::xs->letnewstate=infer_assignmentstate(islazy,name,value)in(infer_assignment_listxsnewstate)