123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208(* This file is free software, part of dolmen. See file "LICENSE" for more information *)exceptionSigint=Sys.BreakexceptionOut_of_time=Alarm.Out_of_timeexceptionOut_of_space=Alarm.Out_of_spacemoduleMake(State:State.S)=struct(* Setup *)(* ************************************************************************ *)(* We want to catch user interruptions *)let()=matchSys.os_typewith|"Win32"|"Cygwin"->Sys.catch_breaktrue|"Unix"->Sys.set_signalSys.sigint(Sys.Signal_handle(fun_->raiseSigint))|_->()(* Pipeline and execution *)(* ************************************************************************ *)type'stmerge='st->'st->'sttype('a,'b)cont=[`Doneof'a|`Continueof'b]type('st,'a)fix=[`Ok|`Genof'stmerge*('st->'st*'aoption)]type'stk_exn={k:'a.'st->Printexc.raw_backtrace->exn->'a;}type('st,'a,'b)op={name:string;f:'st->'a->'st*'b;}(* Type for pipelines, i.e a series of transformations to
apply to the input. An ('st, 'a, 'b) t is a pipeline that
takes an input of type ['a] and returns a value of type
['b]. *)type(_,_,_)t=(* The end of the pipeline, the identity/reflexive constructor *)|End:('st,'a,'a)t(* Apply a single function and then proceed with the rest of the pipeline *)|Map:('st,'a,'c)op*('st,'c,'b)t->('st,'a,'b)t(* Allow early exiting from the loop *)|Cont:('st,'a,('b,'c)cont)op*('st,'c,'b)t->('st,'a,'b)t(* Concat two pipeline. Not tail recursive. *)|Concat:('st,'a,'b)t*('st,'b,'c)t->('st,'a,'c)t(* Fixpoint expansion *)|Fix:('st,'a,('st,'a)fix)op*('st,'a,unit)t->('st,'a,unit)t(* Creating operators. *)letop?(name="")f={name;f;}letapply?namef=op?name(funstx->st,fx)letiter_?namef=op?name(funstx->fx;st,x)letf_map?name?(test=(fun__->true))f=op?name(funstx->ifteststxthenbeginletst',y=fstxinst',`Continueyendelsest,`Donex)(* Creating pipelines. *)let_end=Endlet(@>>>)opt=Map(op,t)let(@>|>)opt=Cont(op,t)let(@|||)tt'=Concat(t,t')letfixopt=Fix(op,t)(* Eval an operator *)leteval_op~exnopstx=tryop.fstxwithe->letbt=Printexc.get_raw_backtrace()inexn.kstbte(* Eval a pipeline into the corresponding function *)letreceval:typestab.exn:stk_exn->(st,a,b)t->st->a->st*b=fun~exnpipestx->matchpipewith|End->st,x|Map(op,t)->letst',y=eval_op~exnopstxineval~exntst'y|Cont(op,t)->letst',y=eval_op~exnopstxinbeginmatchywith|`Continueres->eval~exntst'res|`Doneres->st',resend|Concat(t,t')->letst',y=eval~exntstxineval~exnt'st'y|Fix(op,t)->letst',y=eval_op~exnopstxinbeginmatchywith|`Ok->eval~exntst'x|`Gen(merge,g)->letst''=eval_gen_fold~exnpipest'ginletst'''=mergestst''inst''',()endandeval_gen_fold:typesta.exn:stk_exn->(st,a,unit)t->st->(st->st*aoption)->st=fun~exnpipestg->matchgstwith|st,None->st|st,Somex->letst',()=eval~exnpipestxineval_gen_fold~exnpipest'g|exceptione->letbt=Printexc.get_raw_backtrace()inexn.kstbte(* Aux function to eval a pipeline on the current value of a generator. *)letrun_aux~exnpipegst=matchgstwith|st,None->`Donest|st,Somex->`Continue(eval~exnpipestx)|exceptione->letbt=Printexc.get_raw_backtrace()inexn.kstbte(* Effectively run a pipeline on all values that come from a generator.
Time/size limits apply for the complete evaluation of each input
(so all expanded values count toward the same limit). *)letrecrun:typea.finally:(State.t->(Printexc.raw_backtrace*exn)option->State.t)->?alarm:Alarm.t->(State.t->State.t*aoption)->State.t->(State.t,a,unit)t->State.t=fun~finally?(alarm=Alarm.default)gstpipe->letexceptionExnofState.t*Printexc.raw_backtrace*exninletmoduleA=(valalarm)inlettime=State.getState.time_limitstinletsize=State.getState.size_limitstinletal=A.setup~time~sizeinletexn={k=funstbte->(* delete alarm as soon as possible *)let()=A.deletealin(* go the the correct handler *)raise(Exn(st,bt,e));}inbeginmatchrun_aux~exnpipegstwith(* End of the run, yay ! *)|`Donest->let()=A.deletealinst(* Regular case, we finished running the pipeline on one input
value, let's get to the next one. *)|`Continue(st',())->let()=A.deletealinletst''=tryfinallyst'Nonewith_->st'inrun~finally~alarmgst''pipe(* "Normal" exception case: the exn was raised by an operator, and caught
then re-raised by the {exn} cotinuation passed to run_aux *)|exceptionExn(st,bt,e)->(* delete alarm *)let()=A.deletealin(* Flush stdout and print a newline in case the exn was
raised in the middle of printing *)Format.pp_print_flushFormat.std_formatter();Format.pp_print_flushFormat.err_formatter();(* Go on running the rest of the pipeline. *)letst'=finallyst(Some(bt,e))inrun~finally~alarmgst'pipe(* Exception case for exceptions, that can realisically occur for all
asynchronous exceptions, or if some operator was not properly wrapped.
In this error case, we might use a rather old and outdate state, but
this should not happen often, and should not matter for asynchronous
exceptions. *)|exceptione->letbt=Printexc.get_raw_backtrace()in(* delete alarm *)let()=A.deletealin(* Flush stdout and print a newline in case the exn was
raised in the middle of printing *)Format.pp_print_flushFormat.std_formatter();Format.pp_print_flushFormat.err_formatter();(* Go on running the rest of the pipeline. *)letst'=finallyst(Some(bt,e))inrun~finally~alarmgst'pipeendend