123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226openTypesopenErrorsopenUtilopenTypecheckmoduleT=ANSITerminal(** Boolean Primitives *)letbool_binop(x,y)(op:bool->bool->bool)=leta=unpack_boolxandb=unpack_boolyinEvtBool(opab)letbool_unopx(op:bool->bool)=leta=unpack_boolxinEvtBool(opa)letuniqueorfaill=ifDict.dup_existsltheniraise(DictError"Duplicate key in dictionary")elsel(** Evaluate an expression in an environment *)letreceval(e:expr)(state:evalstate):evt=letstate={statewithstack=push_stackstate.stacke}inifstate.verbosity>=2thenprint_message~color:T.Blue~loc:Nowhere"Reduction at depth"(Printf.sprintf"%d\nExpression:\n%s"(depth_of_stackstate.stack)(show_expre))else();letevaluated=matchewith|Unit->EvtUnit|Purity(allowed,ee)->evalee{statewithpurity=allowed}|NumIntn->EvtIntn|NumFloatn->EvtFloatn|NumComplexn->EvtComplexn|Booleanb->EvtBoolb|Strings->EvtStrings|Symbolx->lookupxstate|Listx->EvtList(List.map(funx->evalxstate)x)|Binop(kind,e1,e2)->eval_binopkinde1e2state(* Dictionaries and operations *)|Dictl->letel=uniqueorfail(List.map(eval_assignmentstate)l)inEvtDictel|Notx->bool_unop(evalxstate)not|IfThenElse(guard,first,alt)->letg=unpack_bool(evalguardstate)inifgthenevalfirststateelseevalaltstate|Let(assignments,body)->evalbody(eval_assignment_listassignmentsstate)|Lambda(param,body)->Closure(None,param,body,state.env)(* Function Application *)|Apply(f,arg)->letclosure=evalfstateinletearg=evalargstateinapplyfunclosureeargstate|ApplyPrimitive((name,_,_),args)->leteargs=List.map(funx->evalxstate)argsinletprim=get_primitive_function(match(Dict.getnamePrimitives.ocaml_table)with|None->iraise(Fatal"Unbound primitive. This should never happen")|Somep->p)in(tryprimeargswithInternalError(loc,err,_)->raise(InternalError(loc,err,state.stack)))(* Eval a sequence of expressions but return the last *)|Sequence(e1,e2)->let_=evale1stateinevale2stateinifstate.verbosity>=2thenprint_message~color:T.Cyan~loc:Nowhere"Evaluates to at depth"(Printf.sprintf"%d\n%s\n"(depth_of_stackstate.stack)(show_evtevaluated))else();evaluatedandeval_binop(k:binop)(x:expr)(y:expr)state=matchkwith|Getkey->letkey=(matchywithSymbolz->z|_->iraise(Fatal"Dictionary access"))anded=unpack_dict(evalxstate)in(matchDict.getkeyedwith|None->iraise(DictError"key not found")|Some(LazyExpressionz)->evalzstate|Somez->z)|Cons->letls=unpack_list(evalystate)in(matchlswith|[]->EvtList[evalxstate]|lss->EvtList(evalxstate::lss))|Concat->letev1=evalxstateandev2=evalystateinlett1=typeofev1andt2=typeofev2in(match(t1,t2)with|TString,TString->EvtString((unpack_stringev1)^(unpack_stringev2))|TList,TList->EvtList((unpack_listev1)@(unpack_listev2))|_->iraises(TypeError(Printf.sprintf"Cannot concatenate a two values of type %s and %s"(show_tinfot1)(show_tinfot2)))state.stack)|Compose->letef1=evalystateandef2=evalxstateinstcheck(typeofef1)TLambda;stcheck(typeofef2)TLambda;letparams1=findevtparamsef1inletappl1=apply_from_exprlist(symbols_from_stringsparams1)yineval(lambda_from_paramlistparams1(Apply(x,appl1)))state|Plus->Numericalp.add[(evalxstate);(evalystate)]|Sub->Numericalp.sub[(evalxstate);(evalystate)]|Div->Numericalp.div[(evalxstate);(evalystate)]|Mult->Numericalp.mult[(evalxstate);(evalystate)]|And->bool_binop(evalxstate,evalystate)(&&)|Or->bool_binop(evalxstate,evalystate)(||)|Eq->EvtBool(compare_evt(evalxstate)(evalystate)=0)|Gt->EvtBool(compare_evt(evalxstate)(evalystate)>0)|Lt->EvtBool(compare_evt(evalxstate)(evalystate)<0)|Ge->EvtBool(compare_evt(evalxstate)(evalystate)>=0)|Le->EvtBool(compare_evt(evalxstate)(evalystate)<=0)(* Search for a value in the primitives table and environment *)andlookup(ident:ide)(state:evalstate):evt=match(Dict.getidentPrimitives.table)with|None->(match(Dict.getidentstate.env)with|None->iraises(UnboundVariableident)state.stack|Some(LazyExpressione)->evalestate|Somee->e)|Some(LazyExpressione)->evalestate|Somee->eandapplyfun(closure:evt)(arg:evt)(state:evalstate):evt=(* Evaluate the argument and unpack the evt encapsuled in them *)matchclosurewith|Closure(name,param,body,decenv)->(* Create a recursion environment if the function is recursive *)letself_env=(matchnamewith|None->decenv|Somex->Dict.insertdecenvxclosure)inletappl_env=Dict.insertself_envparamarginevalbody{statewithenv=appl_env}|_->traise"Cannot apply a non functional value"andeval_assignmentstate(islazy,name,value)=ifislazythen(name,LazyExpressionvalue)else(matchvaluewith|Lambda(param,fbody)->letrec_env=Dict.insertstate.envname(Closure(Somename,param,fbody,state.env))inname,evalvalue{statewithenv=rec_env}|_->name,evalvaluestate)andeval_assignment_listassignment_liststate:evalstate=matchassignment_listwith|[]->state|(islazy,name,value)::xs->let_,nval=eval_assignmentstate(islazy,name,value)in(eval_assignment_listxs{statewithenv=(Dict.insertstate.envnamenval)})andeval_commandcommandstatedirscope=ifstate.verbosity>=1thenprint_message~loc:(Nowhere)~color:T.Yellow"AST equivalent"(Printf.sprintf"\n%s"(show_commandcommand))else();matchcommandwith|Directivedir->eval_directivedirstatedirscope|Expre->(* Infer the expression purity and evaluate if appropriate to the current state *)letexprpurity=Puritycheck.inferestateinif(state.purity=Pure||state.purity=Numerical)&&exprpurity=Impuretheniraises(PurityError("This expression contains a "^(show_puretexprpurity)^" expression but it is in "^(show_puretstate.purity)^" state!"))state.stackelse();ifstate.verbosity>=1thenPrintf.eprintf"Has purity: %s\n%!"(show_puretexprpurity)else();(* Normalize the expression *)letoptimized_ast=Optimizer.iterate_optimizerein(* If the expression is NOT already in normal state, print the optimized one if verbosity is enough *)ifoptimized_ast=ethen()elseifstate.verbosity>=1thenprint_message~loc:(Nowhere)~color:T.Yellow"After AST optimization"(Printf.sprintf"\n%s"(show_exproptimized_ast))else();(* Evaluate the expression *)letevaluated=evaloptimized_aststatein(* Print it in its raw form if verbosity is enabled *)ifstate.verbosity>=1thenprint_message~color:T.Green~loc:(Nowhere)"Result"(Printf.sprintf"\t%s"(show_evtevaluated))else();(* Print the fancy result if state.printresult is true *)ifstate.printresultthenPrintf.eprintf"result: %s - %s\n%!"(show_unpacked_evtevaluated)(show_tinfo(Typecheck.typeofevaluated))else();(evaluated,state)|Defdl->let(islazyl,idel,vall)=unzip3dlin(* Infer the values purity and evaluate if appropriate to the current state *)letnew_purity_state=Puritycheck.infer_assignment_listdlstateinletovall=(List.map(Optimizer.iterate_optimizer)vall)inletodl=zip3islazylidelovallin(* Print the definitions if verbosity is enough and they were optimized *)ifovall=vallthen()elseifstate.verbosity>=1thenprint_message~loc:(Nowhere)~color:T.Yellow"After AST optimization"(Printf.sprintf"\n%s"(show_command(Defodl)))else();letnewstate=eval_assignment_listodlnew_purity_statein(EvtUnit,newstate)andeval_command_listcmdlststatedirscope=letmstate=refstateinList.iter(funx->mstate:=snd(eval_commandx!mstatedirscope))cmdlst;(EvtUnit,!mstate)andeval_directivedirstatedirscope=matchdirwith|Dumpenv->Printf.eprintf"<env>: %s\n%!"(show_env_typestate.env);(EvtUnit,state)|Dumppurityenv->Printf.eprintf"<purity_env>: %s\n%!"(show_purityenv_typestate.purityenv);(EvtUnit,state)|Includefileasmodule(f,m)->letmodulename=(matchmwith|Somem->m|None->Filename.remove_extensionf|>Filename.basename|>String.capitalize_ascii)inletfile_in_scope=ifnot(Filename.is_relativef)thenfelseFilename.concat(dirscope)finlet_,resulting_state=eval_command_list(Parsedriver.read_filefile_in_scope){statewithenv=[];purityenv=[]}dirscopeinletnewmodule=EvtDictresulting_state.envin(EvtUnit,{statewithenv=(Dict.insertstate.envmodulenamenewmodule)})|Includefilef->letfile_in_scope=ifnot(Filename.is_relativef)thenfelseFilename.concat(dirscope)fin(* Eval the file contents *)eval_command_list(Parsedriver.read_filefile_in_scope)statedirscope|Setpurityp->ifstate.verbosity>=1thenPrintf.eprintf"%s%!"(show_puretstate.purity)else();(EvtUnit,{statewithpurity=p})|Setverbosev->(EvtUnit,{statewithverbosity=v})