123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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 *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(**
Read-eval-print-loop interactive frontend.
Borrows the syntax and semantics from universal.
*)openMopsaopenMopsa_universal_parseropenSig.Abstraction.StatelessopenLexingopenUniversal.AstopenUniversal.FrontendopenDebug(** {2 Interpreter state} *)(** ********************* *)typectx={ctx_var:var_context;ctx_fun:fun_context;}letinit_ctx()={ctx_var=MS.empty;ctx_fun=MS.empty;}(** {2 Parsing} *)(** *********** *)letrange_of_string?(org=0)(str:string):range=mk_orig_range(mk_pos"<input>"0org)(mk_pos"<input>"0(org+String.lengthstr))(** Helper to parse a string using a menhir parser entry point. *)letparse_string?(org=0)parserstr=letlex=from_stringstrintrylex.lex_curr_p<-{lex.lex_curr_pwithpos_cnum=org;};parserU_lexer.tokenlexwith|U_parser.Error->letrange=from_lexing_range(Lexing.lexeme_start_plex)(Lexing.lexeme_end_plex)inExceptions.syntax_errorrange"Syntax error"str|Failures->letrange=from_lexing_range(Lexing.lexeme_start_plex)(Lexing.lexeme_end_plex)inExceptions.syntax_errorrange"%s"str(** Parse an expression. *)letparse_expr?org(ctx:ctx)(str:string):expr=letast=parse_string?orgU_parser.expr_eofstrinfrom_exprast(range_of_string?orgstr)ctx.ctx_var(Somectx.ctx_fun)(** Parse a statement. *)letparse_stmt?org(ctx:ctx)(str:string):stmt=letast=parse_string?orgU_parser.stat_eofstrinfrom_stmtast(range_of_string?orgstr)ctx.ctx_var(Somectx.ctx_fun)(** Parse a variable declaration. *)letparse_vardec?org(ctx:ctx)(str:string):ctx*stmtlist*varlist=letast=parse_string?orgU_parser.declaration_eofstrinletrange=range_of_string?orgstrinletvar_ctx,init,gvar=var_ctx_init_of_declaration[ast,range]ctx.ctx_var(Somectx.ctx_fun)Nonein{ctxwithctx_var=var_ctx;},init,gvar(** Parse a function declaration. *)letparse_fundec?org(ctx:ctx)(str:string):ctx*fundec=letast=parse_string?orgU_parser.fundec_eofstrinletrange=range_of_string?orgstrinletfun_ctx,var_ctx_map=fun_ctx_of_global[ast,range]ctx.ctx_varinletf=MS.findast.funnamefun_ctxinletvar_ctx2,init=var_init_of_functionctx.ctx_varvar_ctx_mapfun_ctxastinletbody=from_stmt(fstast.body)(sndast.body)var_ctx2(Somefun_ctx)inf.fun_body<-mk_block(init@[body])range;{ctxwithctx_fun=fun_ctx},f(** Parse a variable. *)letparse_var?org(ctx:ctx)(str:string):var=from_varstr(range_of_string?orgstr)ctx.ctx_vartypeinput_class=|VarDecl|FunDecl|Stmtletvardecl_str=Str.regexp"\\(int\\|real\\|string\\|char\\)[ \t\r\n]+[a-zA-Z0-9]+[ \t\r\n]*[,;=].*"letfundecl_str=Str.regexp"\\(int\\|real\\|string\\|char\\|void\\)[ \t\r\n]+[a-zA-Z0-9]+[ \t\r\n]*(.*"(** Try to guess the nature of the input. *)letclassify_inputstr=ifStr.string_matchvardecl_strstr0thenVarDeclelseifStr.string_matchfundecl_strstr0thenFunDeclelseStmt(** {2 Printing} *)(** ************ *)leteol=Str.regexp"\n\\|\r\\|\r\n"(** Prints a string with some locations highlighted. *)letprint_highlight(str:string)(range:range)=(* coloring *)letreset()=print_string"\027[0m"andhighlight()=print_string"\027[1;41m"(* bright with red background *)in(* range *)letr1=get_range_startrangeandr2=get_range_endrangeinletl1,l2=get_pos_liner1,get_pos_liner2andc1,c2=get_pos_columnr1,get_pos_columnr2in(* cut into lines *)letlines=Str.spliteolstrin(* for each line *)Printf.printf"\027[0m";letrecdoiti=function|[]->()|line::rest->ifi>l1&&i<=l2thenhighlight();forj=0toString.lengthline-1doifi=l1&&j=c1thenhighlight()elseifi=l2&&j=c2+1thenreset();print_charline.[j]done;reset();print_char'\n';doit(i+1)restindoit1lines(** {2 Main loop} *)(** ************* *)letspaces=Str.regexp"[ \t\r\n]+"letrepl_ctx=LineEdit.create_ctx()letprompt="\027[1;32mMOPSA > \027[0m"letcol_reset="\027[0m"letcol_error="\027[1;31m"(* bright red *)letcol_out="\027[1;33m"(* bright yellow *)letpf=Format.printfletprint_usage()=pf"Commands:@.";pf" h[elp]@.";pf" q[uit]@.";pf" p[rint] <variable>@.";pf" <statement>;@.";pf" <declaration>;@.";()letrecrepl_loopctxmanflow=pf"%s@?"prompt;letstr=LineEdit.read_linerepl_ctxinletquit=reffalseinletctx,flow=trymatchStr.splitspacesstrwith|[]|["h"]|["help"]|["?"]->print_usage();ctx,flow|["quit"]|["q"]->quit:=true;ctx,flow|("print"|"p")::vars->Exceptions.panic"print not supported"|_->(* autodetect the command *)matchclassify_inputstrwith|Stmt->letstmt=parse_stmtctxstrinpf"%s@[<v 4>X♯ ≜ 𝕊⟦%a@]⟧ =@]%s@."col_outpp_stmtstmtcol_reset;letflow=man.execstmtflow|>post_to_flowmaninpf"%s@[<v 4>%a@]%s@."col_out(format(Flow.printman.lattice.print))flowcol_reset;ctx,flow|VarDecl->letctx,stmts,vars=parse_vardecctxstrinletflow=List.fold_left(funflowstmt->man.execstmtflow|>post_to_flowman)flowstmtsinList.iter(funv->pf"variable %a : %a declared@."pp_varvpp_typ(vtypv))vars;pf"%s@[<v 4>X♯ ≜%s@."col_outcol_reset;pf"%s%a%s@."col_out(format(Flow.printman.lattice.print))flowcol_reset;ctx,flow|FunDecl->letctx,fdec=parse_fundecctxstrinpf"function %s declared@."fdec.fun_orig_name;ctx,flowwith|Panic(msg,txt)->pf"%sPanic: %s: %s%s@."col_errormsgtxtcol_reset;ctx,flow|PanicAtLocation(r,msg,txt)->pf"%sPanic: %s at %a: %s%s@."col_errormsg(pp_range)rtxtcol_reset;print_highlightstrr;ctx,flow|PanicAtFrame(r,cs,msg,txt)->pf"%sPanic: %s at %a: %s%s@."col_errormsg(pp_range)rtxtcol_reset;print_highlightstrr;ctx,flow|SyntaxError(r,txt)->pf"%sSyntax error at %a: %s%s@."col_error(pp_range)rtxtcol_reset;print_highlightstrr;ctx,flow|e->pf"%sexception %s%s@."col_error(Printexc.to_stringe)col_reset;ctx,flowinif!quitthenflowelserepl_loopctxmanflow(** Main loop. *)letenter_replmanflow=repl_loop(init_ctx())manflow(** {2 Interactive "program"} *)(* ************************* *)typeprog_kind+=|P_REPL(* no contents *)let()=register_program{compare=(funnext->next);print=(fundefaultfmtprog->matchprog.prog_kindwith|P_REPL->Format.fprintffmt"read-eval-print loop"|_->defaultfmtprog);}(** Ignore files and return the constant P_REPL program. *)letparse_programfiles:program=iffiles<>[]thenExceptions.warn"File arguments in REPL are ignored";{prog_kind=P_REPL;prog_range=range_of_string"";}moduleDomain=structincludeGenStatelessDomainId(structletname="universal.repl"end)letdependencies=[]letchecks=[]letinitprogmanflow=Noneletexecstmtmanflow=matchskindstmtwith|S_program({prog_kind=P_REPL},_)->Some(Post.return(enter_replmanflow))|_->Noneletevalexpmanflow=Noneletaskquerymanflow=Noneletprint_exprmanflowprinterexp=()endlet()=register_stateless_domain(moduleDomain)(* Front-end registration *)let()=register_frontend{lang="repl";parse=parse_program;on_panic=fun___->();}