123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214(** Calling a prover using Why3. *)open!LplibopenLplib.ExtraopenTimedopenCommonopenErroropenCoreopenTermopenPrint(** Logging function for external prover calling with Why3. *)letlog_why3=Logger.make'w'"why3""why3 provers"letlog_why3=log_why3.pp(** [default_prover] contains the name of the current prover. Note that it can
be changed by using the "set prover <string>" command. *)letdefault_prover:stringref=ref"Alt-Ergo"(** [timeout] is the current time limit (in seconds) for a Why3 prover to find
a proof. It can be changed with "set prover <int>". *)lettimeout:intref=ref2(** [why3_config] is the Why3 configuration read from the configuration file
(["~/.why3.conf"] usually). For more information, visit the Why3
documentation at http://why3.lri.fr/api/Whyconf.html. *)letwhy3_config:Why3.Whyconf.config=letcfg=Why3.Whyconf.init_configNoneinletprovers=Why3.Whyconf.get_proverscfginConsole.out2"provers available for why3:";letpp_proverp_=Console.out2"%a"Why3.Whyconf.print_proverpinWhy3.Whyconf.Mprover.iterpp_proverprovers;cfg(** [why3_main] is the main section of the Why3 configuration. *)letwhy3_main:Why3.Whyconf.main=Why3.Whyconf.get_mainwhy3_config(** [why3_env] is the initialized Why3 environment. *)letwhy3_env:Why3.Env.env=Why3.Env.create_env(Why3.Whyconf.loadpathwhy3_main)(** Builtin configuration for propositional logic. *)typeconfig={symb_P:sym(** Encoding of propositions. *);symb_T:sym(** Encoding of types. *);symb_or:sym(** Disjunction(∨) symbol. *);symb_and:sym(** Conjunction(∧) symbol. *);symb_imp:sym(** Implication(⇒) symbol. *);symb_bot:sym(** Bot(⊥) symbol. *);symb_top:sym(** Top(⊤) symbol. *);symb_not:sym(** Not(¬) symbol. *)}(** [get_config ss pos] build the configuration using [ss]. *)letget_config:Sig_state.t->Pos.popt->config=funsspos->letbuiltin=Builtin.getssposin{symb_P=builtin"P";symb_T=builtin"T";symb_or=builtin"or";symb_and=builtin"and";symb_imp=builtin"imp";symb_bot=builtin"bot";symb_top=builtin"top";symb_not=builtin"not"}(** A map from lambdapi terms to Why3 constants. *)typecnst_table=(term*Why3.Term.lsymbol)list(** [new_axiom_name ()] generates a fresh axiom name. *)letnew_axiom_name:unit->string=letcounter=ref0infun_->incrcounter;"Why3axiom_"^(string_of_int!counter)(** [translate_term cfg tbl t] translates the given lambdapi term [t] into the
correspondig Why3 term, using the configuration [cfg] and constants in the
association list [tbl]. *)lettranslate_term:config->cnst_table->term->(cnst_table*Why3.Term.term)option=funcfgtblt->letrectranslate_proptblt=matchget_argstwith|(Symb(s),[t1;t2])whens==cfg.symb_and->let(tbl,t1)=translate_proptblt1inlet(tbl,t2)=translate_proptblt2in(tbl,Why3.Term.t_andt1t2)|(Symb(s),[t1;t2])whens==cfg.symb_or->let(tbl,t1)=translate_proptblt1inlet(tbl,t2)=translate_proptblt2in(tbl,Why3.Term.t_ort1t2)|(Symb(s),[t1;t2])whens==cfg.symb_imp->let(tbl,t1)=translate_proptblt1inlet(tbl,t2)=translate_proptblt2in(tbl,Why3.Term.t_impliest1t2)|(Symb(s),[t])whens==cfg.symb_not->let(tbl,t)=translate_proptbltin(tbl,Why3.Term.t_nott)|(Symb(s),[])whens==cfg.symb_bot->(tbl,Why3.Term.t_false)|(Symb(s),[])whens==cfg.symb_top->(tbl,Why3.Term.t_true)|(_,_)->(* If the term [p] is mapped in [tbl] then use it. *)tryletsym=List.assoc_eq(Eval.eq_modulo[])ttblin(tbl,Why3.Term.ps_appsym[])withNot_found->(* Otherwise generate a new constant in why3. *)letsym=Why3.Term.create_psymbol(Why3.Ident.id_fresh"P")[]in((t,sym)::tbl,Why3.Term.ps_appsym[])inmatchget_argstwith|(Symb(s),[t])whens==cfg.symb_P->Some(translate_proptblt)|_->None(** [encode ss pos hs g] translates the hypotheses [hs] and the goal [g]
into Why3 terms, to construct a Why3 task. *)letencode:Sig_state.t->Pos.popt->Env.env->term->Why3.Task.task=funssposhsg->letcfg=get_configssposinlet(constants,hypothesis)=lettranslate_hyp(tbl,map)(name,(_,hyp,_))=matchtranslate_termcfgtbl(Bindlib.unboxhyp)with|Some(tbl,why3_hyp)->(tbl,StrMap.addnamewhy3_hypmap)|None->(tbl,map)inList.fold_lefttranslate_hyp([],StrMap.empty)hsin(* Translate the goal term. *)let(tbl,why3_term)=matchtranslate_termcfgconstantsgwith|Some(tbl,why3_term)->(tbl,why3_term)|None->fatalpos"The goal [%a] is not of the form [P _]"pp_termgin(* Add the declaration of every constant in a task. *)letfntsk(_,t)=Why3.Task.add_param_decltsktinlettsk=List.fold_leftfnNonetblin(* Add the declaration of every hypothesis in the task. *)letfnnamettsk=letaxiom=Why3.Decl.create_prsymbol(Why3.Ident.id_freshname)inWhy3.Task.add_prop_decltskWhy3.Decl.Paxiomaxiomtinlettsk=StrMap.foldfnhypothesistskin(* Add the goal itself. *)letgoal=Why3.Decl.create_prsymbol(Why3.Ident.id_fresh"main_goal")in(* Return the task that contains the encoded lambdapi formula in Why3. *)Why3.Task.add_prop_decltskWhy3.Decl.Pgoalgoalwhy3_term(** [run_task tsk pos prover_name] Run the task [tsk] with the specified
prover named [prover_name] and return the answer of the prover. *)letrun_task:Why3.Task.task->Pos.popt->string->bool=funtskposprover_name->(* Filter the set of why3 provers. *)letfilter=Why3.Whyconf.parse_filter_proverprover_nameinletprovers=Why3.Whyconf.filter_proverswhy3_configfilterin(* Fail if we did not find a matching prover. *)ifWhy3.Whyconf.Mprover.is_emptyproversthenbeginfatal_msg"prover \"%s\" not found.@."prover_name;letprovers=Why3.Whyconf.get_proverswhy3_configinlet_=ifWhy3.Whyconf.Mprover.is_emptyproversthenfatal_msg"There are no available Why3 provers.@."elseletfnp_=fatal_msg" - %a@."Why3.Whyconf.print_proverpinfatal_msg"The available Why3 provers are:@.";Why3.Whyconf.Mprover.iterfnproversinfatal_msg"Why3 configuration read from \"%s\".@."(Why3.Whyconf.get_conf_filewhy3_config);fatal_msg"Your prover might not be installed or detected, ";fatalpos"remember to run [why3 config detect]."end;(* Return the prover configuration and load the driver. *)letprover=snd(Why3.Whyconf.Mprover.max_bindingprovers)inletdriver=tryWhy3.Whyconf.(load_driverwhy3_mainwhy3_envprover)withe->fatalpos"Failed to load driver for \"%s\": %a"prover.prover.prover_nameWhy3.Exn_printer.exn_printerein(* Actually run the prover. *)letlimit={Why3.Call_provers.empty_limitwithlimit_time=!timeout}inletcommand=prover.Why3.Whyconf.commandinletcall=Why3.Driver.prove_task~limit~commanddrivertskinWhy3.Call_provers.((wait_on_callcall).pr_answer=Valid)(** [handle ss pos ps prover_name g] runs the Why3 prover corresponding to the
name [prover_name] (if given or a default one otherwise) on the goal [g].
If the prover succeeded to prove the goal, then this is reflected by a new
axiom that is added to signature [ss]. *)lethandle:Sig_state.t->Pos.popt->stringoption->Proof.goal_typ->term=funssposprover_name{goal_meta=m;goal_hyps=hyps;goal_type=trm}->(* Get the name of the prover. *)letprover_name=Option.get!default_proverprover_nameinifLogger.log_enabled()thenlog_why3"running with prover \"%s\""prover_name;(* Encode the goal in Why3. *)lettsk=encodessposhypstrmin(* Run the task with the prover named [prover_name]. *)ifnot(run_tasktskposprover_name)thenfatalpos"\"%s\" did not find a proof"prover_name;(* Create a new axiom name that represents the proved goal. *)letaxiom_name=new_axiom_name()in(* Add the axiom to the current signature. *)leta=Sign.add_symbolss.signaturePrivatConstEagertrue(Pos.makeposaxiom_name)!(m.meta_type)[]inifLogger.log_enabled()thenlog_why3"axiom %a created"Print.pp_uidaxiom_name;(* Return the variable terms of each item in the context. *)letterms=List.rev_map(fun(_,(x,_,_))->mk_Varix)hypsin(* Apply the instance of the axiom with context. *)add_args(mk_Symba)terms