123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574(* Js_of_ocaml compiler
* Copyright (C) 2013 Hugo Heuzard
*)(* Yoann Padioleau
*
* Copyright (C) 2010 Facebook
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
* license.txt for more details.
*)open!StdlibmoduleLexer:sigtypettypeerrorvalof_file:string->tvalof_channel:in_channel->tvalof_string:?report_error:(error->unit)->?pos:Lexing.position->?filename:string->string->tvalprint_error:error->unitvalcurr_pos:t->Lexing.positionvaltoken:t->Js_token.t*Loc.tvallex_as_regexp:t->Js_token.t*Loc.tvalrollback:t->unitvaldummy_pos:Lexing.positionend=structtypeerror=Loc.t*Flow_lexer.Parse_error.ttypet={l:Sedlexing.lexbuf;report_error:error->unit;mutableenv:Flow_lexer.Lex_env.t}letdummy_pos={Lexing.pos_fname="";pos_lnum=0;pos_cnum=0;pos_bol=0}letzero_pos={Lexing.pos_fname="";pos_lnum=1;pos_cnum=0;pos_bol=0}letprint_error(loc,e)=letf=Loc.filenamelocinletloc=Printf.sprintf"%s:%d:%d"f(Loc.lineloc)(Loc.columnloc)inPrintf.eprintf"Lexer error: %s: %s\n"loc(Flow_lexer.Parse_error.to_stringe)letcreate?(report_error=print_error)l={l;env=Flow_lexer.Lex_env.createl;report_error}letof_filefile:t=letic=open_infileinletlexbuf=Sedlexing.Utf8.from_channelicinSedlexing.set_filenamelexbuffile;createlexbufletof_channelci:t=create(Sedlexing.Utf8.from_channelci)letof_string?report_error?(pos=zero_pos)?filenames=letl=Sedlexing.Utf8.from_stringsinletpos=matchfilenamewith|None->pos|Somepos_fname->{poswithpos_fname}inSedlexing.set_positionlpos;Option.iterfilename~f:(Sedlexing.set_filenamel);create?report_errorlletcurr_poslexbuf=Sedlexing.lexing_position_currlexbuf.lletreport_errorstres=matchFlow_lexer.Lex_result.errorsreswith|[]->()|l->List.iterl~f:t.report_errorlettoken(t:t)=letenv,res=Flow_lexer.lext.envint.env<-env;lettok=Flow_lexer.Lex_result.tokenresinletloc=Flow_lexer.Lex_result.locresinreport_errorstres;tok,locletrollbackt=Sedlexing.rollbackt.lletlex_as_regexp(t:t)=Sedlexing.rollbackt.l;letenv,res=Flow_lexer.regexpt.envint.env<-env;lettok=Flow_lexer.Lex_result.tokenresinletloc=Flow_lexer.Lex_result.locresinreport_errorstres;tok,locendexceptionParsing_errorofParse_info.tletis_comment=function|Js_token.TComment_|TAnnot_|TCommentLineDirective_->true|_->falsemoduleState:sigtypetoken=Js_token.t*Loc.tmoduleCursor:sigtype'atvalinsert_token:'at->Js_token.t->Loc.t->'atvalreplace_token:'at->Js_token.t->Loc.t->'atvallast_token:'at->(Js_token.t*Loc.t*'at)optionvalrewind_block:'at->(Js_token.t*Loc.t*'at)optionendtype'atvalsave_checkpoint:'at->'atvalcursor:'at->'aCursor.tvalcheckpoint:'at->'aJs_parser.MenhirInterpreter.checkpointvaloffer:'at->Js_token.t->Loc.t->'atvalfinalize_error:'at->'atvaltry_recover:'aCursor.t->'atvalcreate:'aJs_parser.MenhirInterpreter.checkpoint->'atvalall_tokens:'at->tokenlistend=structtypetoken=Js_token.t*Loc.ttype'acheckpoint='aJs_parser.MenhirInterpreter.checkpointtype'aw=|Startof'acheckpoint|Checkpointof'acheckpoint*'aw|TokenofJs_token.t*Loc.t*'awmoduleCursor=structtype'at='aw*tokenlistletlast_token((h,next):_t):(_*_*_t)option=letrecfindnext=function|Start_->None|Checkpoint(_,t)->findnextt|Token(tok,loc,t)->ifis_commenttokthenfind((tok,loc)::next)telseSome(tok,loc,(t,(tok,loc)::next))infindnexthletreplace_token((h,next):_t)tokloc:_t=matchnextwith|[]->assertfalse|_::next->h,(tok,loc)::nextletinsert_token((h,next):_t)tokloc:_t=h,(tok,loc)::nextletrewind_block:'at->(Js_token.t*Loc.t*'at)option=funh->letrecrewind(stack:Js_token.tlist)(h:_t)=matchlast_tokenhwith|None->None|Some(tok,loc,h)->(matchtok,stackwith|(T_RPAREN|T_RCURLY|T_RBRACKET),_->letstack=tok::stackinrewindstackh|(T_LPAREN|T_LPAREN_ARROW),[T_RPAREN]|T_LBRACKET,[T_RBRACKET]|T_LCURLY,[T_RCURLY]->Some(tok,loc,h)|(T_LPAREN|T_LPAREN_ARROW),T_RPAREN::stack|T_LBRACKET,T_RBRACKET::stack|T_LCURLY,T_RCURLY::stack->rewindstackh|T_LPAREN,_->assertfalse|T_LBRACKET,_->assertfalse|T_LCURLY,_->assertfalse|_,[]->None|_,(_::_asstack)->rewindstackh)inrewind[]hendtype'at={checkpoint:'acheckpoint;history:'aw;next:tokenlist}letsave_checkpoint{checkpoint;history;next}={checkpoint;history=Checkpoint(checkpoint,history);next}letcursor{history;next;_}=history,nextletrecadvancet=match(t:_Js_parser.MenhirInterpreter.checkpoint)with|Shifting_|AboutToReduce_->advance(Js_parser.MenhirInterpreter.resumet)|InputNeeded_|Accepted_|HandlingError_|Rejected->tletcreatecheckpoint={checkpoint;history=Startcheckpoint;next=[]}letcheckpoint{checkpoint;_}=checkpointletoffer{checkpoint;history;next}tokloc:_t=match(checkpoint:_checkpoint)with|Accepted_->assertfalse|Rejected|HandlingError_->{checkpoint;history;next=(tok,loc)::next}|Shifting_|AboutToReduce_->assertfalse|InputNeeded_->(ifis_commenttokthen{checkpoint;history=Token(tok,loc,history);next}elseletnew_checkpoint=advance(Js_parser.MenhirInterpreter.offercheckpoint(tok,Loc.p1loc,Loc.p2loc))inmatch(new_checkpoint:'acheckpoint)with|Shifting_|AboutToReduce_->assertfalse|Rejected|Accepted_|InputNeeded_->lethistory=matchtokwith|T_VIRTUAL_SEMICOLON->letrecinsert=function|Start_asstart->Token(tok,loc,start)|Checkpoint(_,x)->insertx|Token(inner_tok,loc',tail)asx->ifis_commentinner_tokthenToken(inner_tok,loc',inserttail)elseToken(tok,loc,x)ininserthistory|_->Token(tok,loc,history)in{checkpoint=new_checkpoint;history;next}|HandlingError_->{checkpoint=new_checkpoint;history=Token(tok,loc,Checkpoint(checkpoint,history));next})lettry_recover((h,next):_Cursor.t)=letreccompute(t:_w)=matchtwith|Startenv->advanceenv|Checkpoint(env,_)->advanceenv|Token(tok,loc,t)->(ifis_commenttokthencomputetelsematchcomputetwith|InputNeeded_ascheckpoint->advance(Js_parser.MenhirInterpreter.offercheckpoint(tok,Loc.p1loc,Loc.p2loc))|Shifting_|AboutToReduce_->assertfalse|Accepted_|Rejected|HandlingError_->assertfalse)inletcheckpoint=computehinList.fold_leftnext~init:{checkpoint;history=h;next=[]}~f:(funt(tok,loc)->offerttokloc)letfinalize_error{checkpoint;history;next}=letrecloop(t:_Js_parser.MenhirInterpreter.checkpoint)=matchtwith|HandlingError_|Shifting_|AboutToReduce_->loop(Js_parser.MenhirInterpreter.resumet)|Accepted_|InputNeeded_->assertfalse|Rejected->tin{checkpoint=loopcheckpoint;history;next}letall_tokens{history;_}=letreccollectacct=matchtwith|Start_->acc|Checkpoint(_,tail)->collectacctail|Token(tok,loc,tail)->collect((tok,loc)::acc)tailincollect[]historyendletparse_annots=matchString.drop_prefix~prefix:"//"swith|None->None|Somes->(letbuf=Lexing.from_stringsintrySome(Annot_parser.annotAnnot_lexer.mainbuf)with|Not_found->None|_->None)letrecnl_separatedprevloc'=matchState.Cursor.last_tokenprevwith|None->true|Some(T_VIRTUAL_SEMICOLON,_,prev)->nl_separatedprevloc'|Some(_,loc,_)->Loc.lineloc'<>Loc.line_endlocletacceptablecheckpointtoken=letmoduleI=Js_parser.MenhirInterpreterinletcheckpoint=State.checkpointcheckpointinI.acceptablecheckpointtokenLexer.dummy_posletsemicolon=Js_token.T_VIRTUAL_SEMICOLONletdummy_loc=Loc.createLexer.dummy_posLexer.dummy_posletrecoffer_onet(lexbuf:Lexer.t)=lettok,loc=Lexer.tokenlexbufinmatchtokwith|TCommentLineDirective_->lett=State.offerttoklocinoffer_onetlexbuf|TCommentsastok->lettok=matchparse_annotswith|None->tok|Somea->TAnnot(s,a)inlett=State.offerttoklocinoffer_onetlexbuf|_->lett=matchtokwith|T_LPARENwhenacceptabletT_LPAREN_ARROW->State.save_checkpointt|_->tinleth=State.cursortinlettok,loc=(* restricted productions
* 7.9.1 - 3
* When, as the program is parsed from left to right, a token is encountered
* that is allowed by some production of the grammar, but the production
* is a restricted production and the token would be the first token for a
* terminal or nonterminal immediately following the annotation [no LineTerminator here]
* within the restricted production (and therefore such a token is called a restricted token),
* and the restricted token is separated from the previous token by at least
* one LineTerminator, then a semicolon is automatically inserted before the
* restricted token. *)matchState.Cursor.last_tokenh,tokwith|(Some((T_RETURN|T_CONTINUE|T_BREAK|T_THROW|T_YIELD|T_ASYNC),_,_),((T_SEMICOLON|T_VIRTUAL_SEMICOLON)astok))->tok,loc|Some((T_RETURN|T_CONTINUE|T_BREAK|T_THROW|T_YIELD|T_ASYNC),_,_),_whennl_separatedhloc&&acceptabletT_VIRTUAL_SEMICOLON->(* restricted token can also appear as regular identifier such
as in [x.return]. In such case, feeding a virtual semicolon
could trigger a parser error. Here, we first checkpoint
that a virtual semicolon is acceptable. *)Lexer.rollbacklexbuf;semicolon,dummy_loc(* The practical effect of these restricted productions is as follows:
* When a ++ or -- token is encountered where the parser would treat it
* as a postfix operator, and at least one LineTerminator occurred between
* the preceding token and the ++ or -- token, then a semicolon is automatically
* inserted before the ++ or -- token. *)|_,T_DECRwhennot(nl_separatedhloc)->Js_token.T_DECR_NB,loc|_,T_INCRwhennot(nl_separatedhloc)->Js_token.T_INCR_NB,loc|_,((T_DIV|T_DIV_ASSIGN)astok)->ifacceptablettokthentok,locelselett,loc=Lexer.lex_as_regexplexbufint,loc|_->tok,locinState.offerttoklocletdummy_ident=letdummy="<DUMMY>"inJs_token.T_IDENTIFIER(Stdlib.Utf8_string.of_string_exndummy,dummy)lettoken_to_identt=letname=Js_token.to_stringtinJs_token.T_IDENTIFIER(Stdlib.Utf8_string.of_string_exnname,name)letrecovererror_checkpointprevious_checkpoint=(* 7.9.1 - 1 *)(* When, as the program is parsed from left to right, a token (called the offending token)
is encountered that is not allowed by any production of the grammar, then a semicolon
is automatically inserted before the offending token if one or more of the following
conditions is true:
- The offending token is }.
- The offending token is separated from the previous
token by at least one LineTerminator. *)(* 7.9.1 - 2 *)(* When, as the program is parsed from left to right, the end of the input stream of tokens *)(* is encountered and the parser is unable to parse the input token stream as a single *)(* complete ECMAScript Program, then a semicolon is automatically inserted at the end *)matchState.Cursor.last_token(State.cursorerror_checkpoint)with|None->error_checkpoint|Some(offending_token,offending_loc,rest)->(matchState.Cursor.last_tokenrestwith|None->error_checkpoint|Some(last_token,_,_)->(matchoffending_tokenwith|T_VIRTUAL_SEMICOLON->error_checkpoint(* contextually allowed as identifiers, namely await and yield; *)|(T_YIELD|T_AWAIT)whenacceptableprevious_checkpointdummy_ident->State.Cursor.replace_tokenrest(token_to_identoffending_token)offending_loc|>State.try_recover|T_RCURLYwhenacceptableprevious_checkpointJs_token.T_VIRTUAL_SEMICOLON->State.Cursor.insert_tokenrestsemicolondummy_loc|>State.try_recover|T_EOFwhenacceptableprevious_checkpointJs_token.T_VIRTUAL_SEMICOLON->State.Cursor.insert_tokenrestsemicolondummy_loc|>State.try_recover|T_ARROWwhennot(nl_separatedrestoffending_loc)->((* Restart parsing from the openning parens, patching the
token to be T_LPAREN_ARROW to help the parser *)matchlast_tokenwith|T_RPAREN->(matchState.Cursor.rewind_blockrestwith|Some(T_LPAREN,loc,prev)->State.Cursor.replace_tokenprevT_LPAREN_ARROWloc|>State.try_recover|Some_->assertfalse|None->error_checkpoint)|_->error_checkpoint)|_->(matchlast_tokenwith|T_VIRTUAL_SEMICOLON->error_checkpoint|_whennl_separatedrestoffending_loc&&acceptableprevious_checkpointJs_token.T_VIRTUAL_SEMICOLON->State.Cursor.insert_tokenrestsemicolondummy_loc|>State.try_recover|T_RPARENwhenacceptableprevious_checkpointJs_token.T_VIRTUAL_SEMICOLON_DO_WHILE->State.Cursor.insert_tokenrestsemicolondummy_loc|>State.try_recover|T_RCURLYwhenacceptableprevious_checkpointJs_token.T_VIRTUAL_SEMICOLON_EXPORT_DEFAULT->State.Cursor.insert_tokenrestsemicolondummy_loc|>State.try_recover|_->error_checkpoint)))letparse_auxthe_parser(lexbuf:Lexer.t)=letinit=the_parser(Lexer.curr_poslexbuf)inletrecloop_errorcheckpoint=match(State.checkpointcheckpoint:_Js_parser.MenhirInterpreter.checkpoint)with|InputNeeded_|Shifting_|AboutToReduce_|Accepted_->assertfalse|Rejected->`Errorcheckpoint|HandlingError_->loop_errorcheckpointinletrecloopcheckpointprevious_checkpoint=match(State.checkpointcheckpoint:_Js_parser.MenhirInterpreter.checkpoint)with|Shifting_|AboutToReduce_->assertfalse|Acceptedv->`Ok(v,checkpoint)|Rejected->loop_errorcheckpoint|InputNeeded_env->letprevious_checkpoint=checkpointinletnew_checkpoint=offer_onecheckpointlexbufinloopnew_checkpointprevious_checkpoint|HandlingError_env->(leterror_checkpoint=checkpointinletnew_checkpoint=recovererror_checkpointprevious_checkpointinmatchState.checkpointnew_checkpointwith|HandlingError_->(letcheckpoint=State.finalize_errornew_checkpointinmatchState.checkpointcheckpointwith|Rejected->`Errorcheckpoint|_->assertfalse)|_->loopnew_checkpointnew_checkpoint)inletcheckpoint=State.createinitinletres=loopcheckpointcheckpointinmatchreswith|`Okall->all|`Errort->letreclastcursor=matchState.Cursor.last_tokencursorwith|None->assertfalse|Some(T_VIRTUAL_SEMICOLON,_,cursor)->lastcursor|Some(_,loc,_)->Loc.p1locinletp=last(State.cursort)inraise(Parsing_error(Parse_info.t_of_posp))letfail_early=object(m)inheritJs_traverse.iterassupermethodearly_errorp=raise(Parsing_errorp.loc)methodstatements=matchswith|Import(_,loc)->raise(Parsing_errorloc)|Export(_,loc)->raise(Parsing_errorloc)|_->super#statementsmethodprogramp=List.iterp~f:(fun((p:Javascript.statement),_loc)->matchpwith|Import_->super#statementp|Export(e,_)->(matchewith|CoverExportFrome->m#early_errore|_->super#statementp)|_->super#statementp)endletcheck_programp=List.iterp~f:(function_,p->fail_early#program[p])letparse'lex=letp,toks=parse_auxJs_parser.Incremental.programlexincheck_programp;lettoks=State.all_tokenstoksinlettake_annot_before=lettoks_r=reftoksinletrecloopstart_posacc(toks:(Js_token.t*_)list)=matchtokswith|[]->assertfalse|(TAnnota,loc)::xs->loopstart_pos((a,Parse_info.t_of_pos(Loc.p1loc))::acc)xs|((TComment_|TCommentLineDirective_),_)::xs->loopstart_posaccxs|(_,loc)::xs->ifLoc.cnumloc=start_pos.Lexing.pos_cnumthen(toks_r:=toks;List.revacc)elseloopstart_pos[]xsinfunstart_pos->loopstart_pos[]!toks_rinletp=List.mapp~f:(fun(start_pos,s)->take_annot_beforestart_pos,s)inletgroups=List.groupp~f:(funa_pred->matchawith|[],_->true|_::_,_->false)inletp=List.mapgroups~f:(function|[]->assertfalse|(annot,_)::_asl->annot,List.mapl~f:snd)inp,toksletparselex=letp,_=parse_auxJs_parser.Incremental.programlexincheck_programp;List.mapp~f:(fun(_,x)->x)letparse_exprlex=letexpr,_=parse_auxJs_parser.Incremental.standalone_expressionlexinfail_early#expressionexpr;expr