123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288openStdlet{Logger.log}=Logger.for_section"Mreader_recover"moduleMake(Parser:MenhirLib.IncrementalEngine.EVERYTHING)(Recovery:sigvaldefault_value:Location.t->'aParser.symbol->'atypeaction=|Abort|Rofint|S:'aParser.symbol->action|Subofactionlisttypedecision=|Nothing|Oneofactionlist|Selectof(int->actionlist)valdepth:intarrayvalrecover:int->decisionvalguide:'aParser.symbol->boolvaltoken_of_terminal:'aParser.terminal->'a->Parser.tokenvalnullable:'aParser.nonterminal->boolend)(Dump:sigvalsymbol:unit->Parser.xsymbol->stringend)=structtype'acandidate={line:int;min_col:int;max_col:int;env:'aParser.env}type'acandidates={popped:Parser.xsymbollist;shifted:Parser.xsymboloption;final:'aoption;candidates:'acandidatelist}moduleT=struct(* FIXME: this is a bit ugly. We should ask for the type to be exported
publicly by MenhirLib. *)[@@@ocaml.warning"-37"]type'acheckpoint=|InputNeededof'aParser.env|Shiftingof'aParser.env*'aParser.env*bool|AboutToReduceof'aParser.env*Parser.production|HandlingErrorof'aParser.env|Acceptedof'a|Rejectedexternalinj:'acheckpoint->'aParser.checkpoint="%identity"end(*let env_state env =
match Parser.top env with
| None -> -1
| Some (Parser.Element (state, _, _, _)) ->
Parser.number state*)letfeed_token~allow_reductiontokenenv=letrecauxallow_reduction=function|Parser.HandlingError_|Parser.Rejected->`Fail|Parser.AboutToReduce_whennotallow_reduction->`Fail|Parser.Acceptedv->`Acceptv|(Parser.Shifting_|Parser.AboutToReduce_)ascheckpoint->auxtrue(Parser.resumecheckpoint)|Parser.InputNeededenvascheckpoint->`Recovered(checkpoint,env)inauxallow_reduction(Parser.offer(T.inj(T.InputNeededenv))token)letrecfollow_guidecolenv=matchParser.topenvwith|None->col|Some(Parser.Element(state,_,pos,_))->ifRecovery.guide(Parser.incoming_symbolstate)thenmatchParser.popenvwith|None->col|Someenv->follow_guide(snd(Lexing.split_pospos))envelsecolletcandidateenv=letline,min_col,max_col=matchParser.topenvwith|None->(1,0,0)|Some(Parser.Element(state,_,pos,_))->letdepth=Recovery.depth.(Parser.numberstate)inletline,col=Lexing.split_posposinifdepth=0then(line,col,col)elseletcol'=matchParser.pop_manydepthenvwith|None->max_int|Someenv->(matchParser.topenvwith|None->max_int|Some(Parser.Element(_,_,pos,_))->follow_guide(snd(Lexing.split_pospos))env)in(line,mincolcol',maxcolcol')in{line;min_col;max_col;env}letattemptrtoken=let_,startp,_=tokeninletline,col=Lexing.split_posstartpinletmore_indentedcandidate=line<>candidate.line&&candidate.min_col>colinletrecoveries=List.drop_while~f:more_indentedr.candidatesinletsame_indentedcandidate=line=candidate.line||(candidate.min_col<=col&&col<=candidate.max_col)inletrecoveries=List.take_while~f:same_indentedrecoveriesinletrecaux=function|[]->`Fail|x::xs->(matchfeed_token~allow_reduction:truetokenx.envwith|`Fail->(*if not (is_closed k) then
printf k "Couldn't resume %d with %S.\n"
(env_state x.env) (let (t,_,_) = token in Dump.token t);*)auxxs|`Recovered(checkpoint,_)->`Ok(checkpoint,x.env)|`Acceptv->beginmatchauxxswith|`Fail->`Acceptv|x->xend)inauxrecoveriesletdecideenv=letrecnth_stateenvn=ifn=0thenmatchParser.topenvwith|None->-1(*allow giving up recovery on empty files*)|Some(Parser.Element(state,_,_,_))->Parser.numberstateelsematchParser.popenvwith|None->assert(n=1);-1|Someenv->nth_stateenv(n-1)inletst=nth_stateenv0inmatchRecovery.recoverstwith|Recovery.Nothing->[]|Recovery.Oneactions->actions|Recovery.Selectf->f(nth_stateenvRecovery.depth.(st))letgenerate(typea)(env:aParser.env)=letmoduleE=structexceptionResultofaendinletshifted=refNoneinletrecauxaccenv=matchParser.topenvwith|None->(None,acc)|Some(Parser.Element(state,_,_startp,endp))->((*Dump.element k elt;*)log~title:"decide state""%d"(Parser.numberstate);letactions=decideenvinletcandidate0=candidateenvinletreceval(env:aParser.env):Recovery.action->aParser.env=function|Recovery.Abort->log~title:"eval Abort""";raiseNot_found|Recovery.Rprod->log~title:"eval Reduce""";letprod=Parser.find_productionprodinParser.force_reductionprodenv|Recovery.S(Parser.Nnassym)->letxsym=Parser.Xsyminif!shifted=None&¬(Recovery.nullablen)thenshifted:=Somexsym;log~title:"eval Shift N""%a"Dump.symbolxsym;(* FIXME: if this is correct remove the fixme, otherwise use
[startp] *)letloc={Location.loc_start=endp;loc_end=endp;loc_ghost=true}inletv=Recovery.default_valuelocsyminParser.feedsymendpvendpenv|Recovery.S(Parser.Ttassym)->letxsym=Parser.Xsyminif!shifted=Nonethenshifted:=Somexsym;log~title:"eval Shift T""%a"Dump.symbolxsym;letloc={Location.loc_start=endp;loc_end=endp;loc_ghost=true}inletv=Recovery.default_valuelocsyminlettoken=(Recovery.token_of_terminaltv,endp,endp)inbeginmatchfeed_token~allow_reduction:truetokenenvwith|`Fail->assertfalse|`Acceptv->raise(E.Resultv)|`Recovered(_,env)->envend|Recovery.Subactions->log~title:"enter Sub""";letenv=List.fold_left~f:eval~init:envactionsinlog~title:"leave Sub""";envinmatchList.rev_scan_left[]~f:eval~init:envactions|>List.map~f:(funenv->{candidate0withenv})with|exceptionNot_found->(None,acc)|exceptionE.Resultv->(Somev,acc)|[]->(None,acc)|candidate::_ascandidates->aux(candidates@acc)candidate.env)inletpopped=ref[]in(*let should_pop stack =
let Parser.Element (state, _, _, _) = Parser.stack_element stack in
match Parser.incoming_symbol state with
| (Parser.T term) as t1 when Recovery.can_pop term ->
log "Pop" "pop %s"
(Dump.symbol (Parser.X t1));
begin match Parser.stack_next stack with
| None -> false
| Some stack' ->
let rec check_next = function
| Recovery.S (Parser.T term' as t2) :: _
when Parser.X t1 = Parser.X t2 ->
false
| Recovery.S sym :: _ ->
log "Pop" "then push %s"
(Dump.symbol (Parser.X sym));
popped := Parser.X t1 :: !popped;
true
| Recovery.Sub xs :: _ ->
check_next xs
| _ ->
popped := Parser.X t1 :: !popped;
true
in
check_next (decide stack')
end
| _ -> false
in*)letfinal,candidates=aux[]envin(List.rev!popped,!shifted,final,candidates)letgenerateenv=letpopped,shifted,final,candidates=generateenvinletcandidates=List.rev_filtercandidates~f:(funt->not(Parser.env_has_default_reductiont.env))in{popped;shifted;final;candidates=candidateenv::candidates}(*let dump {Nav. nav; body; _} ~wrong:(t,s,_ as token) ~rest:tokens env =
if not (is_closed body) then (
let l, c = Lexing.split_pos s in
printf body "Unexpected %S at %d:%d, " (Dump.token t) l c;
link body "see recoveries"
(fun _ -> Nav.push nav "Recoveries" @@ fun {Nav. body; _} ->
let r = generate body env in
let rec aux = function
| [] -> ()
| token :: tokens ->
match attempt body r token with
| `Fail -> aux tokens
| `Accept _ ->
text body "\nCouldn't resume, generated final AST.\n"
| `Ok (_, recovered_from) ->
printf body "\nResumed with %S from:\n"
(let (t,_,_) = token in Dump.token t);
Dump.env body recovered_from
in
aux (token :: tokens)
);
text body ".\n";
Dump.env body env;
text body "\n"
)*)end