123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302(* This file is free software, part of Logtk. See file "license" for more details. *)(** {1 Utils related to TPTP parsing} *)openLogtkmoduleT=TypedSTermmodulePT=STermmoduleF=T.FormmoduleA=Ast_tptpmoduleLoc=ParseLocationmoduleErr=CCResulttype'aor_error=('a,string)CCResult.ttypetyped=T.ttypeuntyped=PT.texceptionErrorofstringleterrormsg=raise(Errormsg)leterrorfmsg=CCFormat.ksprintfmsg~f:errorletsection=Util.Section.rootletstat_def_as_rw=Util.mk_stat"tptp.def_as_rewrite"(** {2 Printing/Parsing} *)typeparse_cache=(string,unit)Hashtbl.tletcreate_parse_cache()=Hashtbl.create16letfind_filenamedir=(* search in [dir], and its parents recursively *)letrecsearchdir=letcur_name=Filename.concatdirnameinUtil.debugf2~section"search `%s`@ as `%s`"(funk->knamecur_name);ifSys.file_existscur_namethenSomecur_nameelseletdir'=Filename.dirnamedirinifdir=dir'thenNoneelsesearchdir'inletsearch_env()=tryletdir'=Sys.getenv"TPTP"insearchdir'withNot_found->NoneinifFilename.is_relativename(* search by relative path, in parent dirs *)thenmatchsearchdirwith|None->search_env()|Some_asres->reselseifSys.file_existsnamethenSomename(* found *)elseNoneletparse_lexbuf?namesbuf=try(* parse declarations from file *)letdecls=Parse_tptp.parse_declarationsLex_tptp.tokenbufinletq=Queue.create()inList.iter(fundecl->matchdecl,nameswith|(A.CNF_|A.FOF_|A.TFF_|A.THF_|A.TypeDecl_|A.NewType_),None->Queue.pushdeclq|(A.CNF_|A.FOF_|A.TFF_|A.THF_|A.TypeDecl_|A.NewType_),Somenames->ifList.mem(A.get_namedecl)namesthenQueue.pushdeclqelse()(* not included *)|(A.Include_|A.IncludeOnly_),_->Queue.pushdeclq)decls;Err.return(Iter.of_queueq)with|Errormsg|Sys_errormsg->Err.failmsg|e->Err.fail(Printexc.to_stringe)(* find file *)let_find_and_openfilenamedir=matchfilenamewith|"stdin"->stdin|_->matchfind_filefilenamedirwith|Somefilename->begintryopen_infilenamewithSys_errormsg->errorf"error when opening file `%s`: %s"filenamemsgend|None->errorf"could not find file `%s`"filenameletparse_file?cache~recursivef=letparse_cache=lazy(ifrecursivethenCCOpt.get_lazycreate_parse_cachecacheelseassertfalse)inletdir=Filename.dirnamefinletresult_decls=Queue.create()in(* function that parses one file *)letrecparse_this_file?namesfilename=(* find and open file *)letinput=_find_and_openfilenamedirintryletbuf=Lexing.from_channelinputinLoc.set_filebuffilename;(* parse declarations from file *)letdecls=Parse_tptp.parse_declarationsLex_tptp.tokenbufinList.iter(fundecl->matchdecl,nameswith|(A.CNF_|A.FOF_|A.TFF_|A.THF_|A.TypeDecl_|A.NewType_),None->Queue.pushdeclresult_decls|(A.CNF_|A.FOF_|A.TFF_|A.THF_|A.TypeDecl_|A.NewType_),Somenames->ifList.mem(A.get_namedecl)namesthenQueue.pushdeclresult_declselse()(* not included *)|A.Includef,_whenrecursive->ifHashtbl.mem(Lazy.forceparse_cache)fthenUtil.debugf2"@[<2>ignore include of `%s`, already parsed@]"(funk->kf)else((* be sure not to include the file twice *)Hashtbl.add(Lazy.forceparse_cache)f();parse_this_file?names:Nonef)|A.IncludeOnly(f,names'),_whenrecursive->ifHashtbl.mem(Lazy.forceparse_cache)fthenUtil.debugf2"@[<2>ignore include of `%s`, already parsed@]"(funk->kf)else(Util.debugf2"@[<2>caution: partial include of `%s` will not be cached"(funk->kf);parse_this_file~names:names'f)|(A.Include_|A.IncludeOnly_),_->Queue.pushdeclresult_decls)decls;close_ininputwithe->close_ininput;raiseeintryparse_this_file?names:Nonef;Err.return(Iter.of_queueresult_decls)with|Errormsg|Sys_errormsg->Err.fail(Util.err_spf"in parse_tptp: %s"msg)|e->Err.fail(Printexc.to_stringe)letfpf=Format.fprintfletprint_intopptocdecls=fpfoc"@[<v>%a@]@?"(Util.pp_iter~sep:""(A.ppppt))declsletprint_into_filepptfiledecls=CCIO.with_outfile(funoc->letout=Format.formatter_of_out_channelocinprint_intopptoutdecls;Format.pp_print_flushout())lethas_includesdecls=Iter.exists(function|A.Include_|A.IncludeOnly_->true|A.FOF_|A.CNF_|A.TFF_|A.THF_|A.NewType_|A.TypeDecl_->false)decls(** {2 Bridge to UntypedAST} *)moduleUA=UntypedAST(* do we turn "definition" statements into rewrite rules? *)letenable_def_as_rewrite=reffalseletattr_of_info=function|A.GString"ac"|A.GString"AC"|A.GNode("ac",[])|A.GNode("AC",[])->[UA.attr_ac]|A.GNode("infix",[A.GStringn])->[UA.attr_infixn]|A.GNode("prefix",[A.GStringn])->[UA.attr_prefixn]|g->Util.warnf"unknown attribute `%a`, ignore"A.pp_generalg;[](* does this formula look like a proper definition? *)letreclooks_like_deff=matchPT.viewfwith|PT.Bind(Binder.Forall,_,f')->looks_like_deff'|PT.AppBuiltin((Builtin.Equiv|Builtin.Eq),([_;lhs;rhs]|[lhs;rhs]))->(* LHS is an atom/literal? *)letas_atomt=matchPT.viewtwith|PT.Constid|PT.App({PT.term=PT.Constid;_},_)->Someid|_->Nonein(* check that [lhs] is atomic and its head does not occur in [rhs] *)beginmatchas_atomlhswith|None->false|Someid->PT.Seq.symbolsrhs|>Iter.for_all(funid'->id<>id')end|_->falseletto_astst=letconv_formnamerolef=letname=A.string_of_namenameinletattrs=[UA.attr_namename]inmatchrolewith|A.R_question|A.R_conjecture->UA.goal~attrsf|A.R_negated_conjecture->UA.goal~attrs(PT.not_f)|A.R_lemma->UA.lemma~attrsf|A.R_definitionwhen!enable_def_as_rewrite&&looks_like_deff->(* conversion into def *)Util.debugf~section3"(@[tptp.def_as_rewrite@ %a@])"(funk->kPT.ppf);Util.incr_statstat_def_as_rw;UA.rewrite~attrsf|A.R_definition|A.R_axiom|A.R_hypothesis|A.R_assumption|A.R_theorem|A.R_plain|A.R_finite_|A.R_type|A.R_unknown->UA.assert_~attrsfandconv_declnamestyinfo=letname=A.string_of_namenameinletattr_name=UA.attr_namenameinletattrs'=CCList.flat_map(function|A.GNode("attrs",l)->CCList.flat_mapattr_of_infol|_->[])infoinUA.declsty~attrs:(attr_name::attrs')inbeginmatchstwith|A.Include_|A.IncludeOnly_->error"cannot convert `Include` to UntypedAST"|A.TypeDecl(name,s,ty,info)|A.NewType(name,s,ty,info)->conv_declnamestyinfo|A.TFF(name,role,f,_)|A.THF(name,role,f,_)|A.FOF(name,role,f,_)->conv_formnamerolef|A.CNF(name,role,f,_)->assert(not(CCList.is_emptyf));(* to get correct typing. *)letf=ifList.lengthf==1thenList.hdfelsePT.or_finconv_formnamerolefend(* default function for giving a name to the declaration of a symbol *)letname_sym_sy=letstr=CCFormat.sprintf"'ty_decl_%s'"syinA.NameStringstrletof_astst=letname=matchUA.namestwith|None->A.NameString"no_name"|Somes->A.NameStringsinmatchst.UA.stmtwith|UA.Includes->A.Includes|UA.Decl(s,ty)->letname=name_sym_sin(* XXX we should look if [ty] returns tType or not *)A.TypeDecl(name,s,ty,[])|UA.Def_->error"cannot convert `def` statement into TPTP"|UA.Rewrite_->error"cannot convert `rewrite` statement into TPTP"|UA.Data_->error"cannot convert `data` statement into TPTP"|UA.Goalf->A.TFF(name,A.R_conjecture,f,[])|UA.Assertf->A.TFF(name,A.R_axiom,f,[])|UA.Lemmaf->A.TFF(name,A.R_lemma,f,[])let()=Options.add_opts["--tptp-def-as-rewrite",Arg.Setenable_def_as_rewrite," in TPTP, definitions as rewrite rules";"--no-tptp-def-as-rewrite",Arg.Clearenable_def_as_rewrite," disable definition->rewrite in TPTP";]