123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300(*
CFG - Manipulation of Context-Free Grammars
Copyright (C) 2000-2017 Markus Mottl
email: markus.mottl@gmail.com
WWW: http://www.ocaml.info
This library 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 2.1 of the License, or (at your option) any later version.
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 GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)openCfg_intfmoduleMake(Spec_:SPEC):(CFGwithmoduleSpec=Spec_)=structmoduleMySet=Set.MakemoduleMyMap=Map.MakemoduleSpec=Spec_openSpecletcompare_symss1s2=matchs1,s2with|NTnt1,NTnt2->compare_ntnt1nt2|Tt1,Tt2->compare_tt1t2|NT_,T_->1|T_,NT_->-1letreccompare_listscfl1l2=matchl1,l2with|[],[]->0|_,[]->1|[],_->-1|h1::t1,h2::t2->letc=cfh1h2inifc=0thencompare_listscft1t2elsecletcompare_prod(p1,sl1)(p2,sl2)=letc=compare_prodp1p2inifc=0thencompare_listscompare_symssl1sl2elsecmoduleTSet=MySet(structtypet=Spec.tletcompare=compare_tend)moduleTMap=MyMap(structtypet=Spec.tletcompare=compare_tend)moduleNTSet=MySet(structtypet=ntletcompare=compare_ntend)moduleNTMap=MyMap(structtypet=ntletcompare=compare_ntend)moduleProdSet=MySet(structtypet=prod*symbollistletcompare=compare_prodend)moduleProdMap=MyMap(structtypet=prod*symbollistletcompare=compare_prodend)typegrammar=ProdSet.tNTMap.ttypelive_grammar=(int*intProdMap.t)NTMap.tletempty=NTMap.emptyletmaybe_get_prodsgrnt=tryNTMap.findntgrwithNot_found->ProdSet.emptyletadd_prodgrntprsl=ifsl=[]theninvalid_arg"Cfg.add_prod: symbol list empty!"elseNTMap.addnt(ProdSet.add(pr,sl)(maybe_get_prodsgrnt))grletremove_ntgrnt=NTMap.removentgrletunion_auxkvacc=tryNTMap.addk(ProdSet.union(NTMap.findkacc)v)accwithNot_found->NTMap.addkvaccletuniongr1gr2=NTMap.foldunion_auxgr2gr1letdiff_auxkvacc=tryletprods_diff=ProdSet.diff(NTMap.findkacc)vinifProdSet.is_emptyprods_diffthenNTMap.removekaccelseNTMap.addkprods_diffaccwithNot_found->accletdiffgr1gr2=NTMap.folddiff_auxgr2gr1letinter_auxkv(res,grasacc)=tryletnew_gr=NTMap.removekgrinletk_prods=NTMap.findkgrinletprods_inter=ProdSet.interk_prodsvinifProdSet.is_emptyprods_interthenres,new_grelseNTMap.addkprods_interres,new_grwithNot_found->accletintergr1gr2=fst(NTMap.foldinter_auxgr2(NTMap.empty,gr1))(** PRUNE UNPRODUCTIVE *)letsym_derivablents=function|NTnt->NTMap.memntnts|_->trueletprod_defineddefined_nts(_,syms)=List.for_all(sym_derivabledefined_nts)symsletremove_unproductiventprodsdefined_nts=letproductive_prods=ProdSet.filter(prod_defineddefined_nts)prodsinifProdSet.is_emptyproductive_prodsthenNTMap.removentdefined_ntselseNTMap.addntproductive_prodsdefined_ntsletrecprune_unproductivegr=letremaining_nts=NTMap.foldremove_unproductivegrgrinifNTMap.equalProdSet.equalremaining_ntsgrthengrelseprune_unproductiveremaining_nts(** PRUNE NONLIVE *)exceptionFoundofintletreccalc_syms_derivnlive_nts=function|[]->raise(Foundn)|T_::syms->calc_syms_derivnlive_ntssyms|NTnt::syms->letnew_d=trySome(max(NTMap.findntlive_nts+1)n)withNot_found->Noneinmatchnew_dwith|None->()|Somenew_n->calc_syms_derivnew_nlive_ntssymsletcalc_prod_derivlive_nts(_,syms)=calc_syms_deriv1live_ntssymsletcalc_prods_derivlive_nts=ProdSet.iter(calc_prod_derivlive_nts)letcoll_live_infoold_liventprods(live_nts,nonlive_ntsaslive_info)=trycalc_prods_derivold_liveprods;live_infowithFoundn->NTMap.addntnlive_nts,NTMap.removentnonlive_ntsletrecsplit_live_info(live_nts,nonlive_ntsaslive_info)=letnew_live_nts,new_nonlive_ntsasnew_live_info=NTMap.fold(coll_live_infolive_nts)nonlive_ntslive_infoinifNTMap.equal(=)live_ntsnew_live_nts&&NTMap.equalProdSet.equalnonlive_ntsnew_nonlive_ntsthenlive_infoelsesplit_live_infonew_live_infoletderiv_prodslive_nts(_,symsasprod)prods=trycalc_syms_deriv1live_ntssyms;prodswithFoundn->ProdMap.addprodnprodsletderive_ntsgrlive_ntsntn=letprods=ProdSet.fold(deriv_prodslive_nts)(NTMap.findntgr)ProdMap.emptyinNTMap.addnt(n,prods)letprune_nonlivegr=letlive_nts,_=split_live_info(NTMap.empty,gr)inNTMap.fold(derive_ntsgrlive_nts)live_ntsNTMap.empty(** PRUNE UNREACHABLE *)letcoll_reachable_symgracc=function|T_->acc|NTnt->tryNTMap.addnt(NTMap.findntgr)accwithNot_found->accletcoll_reachable_prodgr(_,syms)acc=List.fold_left(coll_reachable_symgr)accsymsletcoll_reachable_prodsgrprods=ProdSet.fold(coll_reachable_prodgr)prodsNTMap.emptyletcoll_reachable_ntgr_prods=NTMap.foldNTMap.add(coll_reachable_prodsgrprods)letrecget_unreachablegrroot_nts=ifNTMap.is_emptyroot_ntsthengrelseletnew_gr=NTMap.fold(funk_->NTMap.removek)root_ntsgrinletreachable_nts=NTMap.fold(coll_reachable_ntnew_gr)root_ntsNTMap.emptyinget_unreachablenew_grreachable_ntsletprune_unreachablegrstart_sym=lets_prods=NTMap.findstart_symgrinletno_s_gr=NTMap.removestart_symgrindiffgr(get_unreachableno_s_gr(coll_reachable_prodsno_s_grs_prods))(** CONVERSION FUNCTIONS *)letunlive_prodpr_=ProdSet.addprletunlive_prodsprods=ProdMap.foldunlive_prodprodsProdSet.emptyletunlive_ntsnt(_,prods)=NTMap.addnt(unlive_prodsprods)letgrammar_of_livegr=NTMap.foldunlive_ntsgrNTMap.emptyletprune_live_prodsgrntprod_acc=ifProdSet.memprod(NTMap.findntgr)thenaccelseProdMap.removeprodaccletprune_live_ntsgrnt(d,dprods)=tryNTMap.addnt(d,ProdMap.fold(prune_live_prodsgrnt)dprodsdprods)withNot_found->NTMap.removentletprune_unreachable_livelive_grstart_sym=letgr=prune_unreachable(grammar_of_livelive_gr)start_syminNTMap.fold(prune_live_ntsgr)live_grlive_grletmake_livegr=prune_nonlive(prune_unproductivegr)letmake_sanegr=prune_unreachable(grammar_of_live(make_livegr))letmake_sane_livegr=prune_unreachable_live(make_livegr)letgrammar_contentsgr=grletderiv_depth_infolive_gr=live_grletnts_in_symacc=functionNTnt->NTSet.addntacc|_->accletnts_in_prod(_,syms)acc=List.fold_leftnts_in_symaccsymsletnts_in_ntntprodsacc=ProdSet.foldnts_in_prodprods(NTSet.addntacc)letnts_in_grammargr=NTMap.foldnts_in_ntgrNTSet.emptyletts_in_symacc=functionTt->TSet.addtacc|_->accletts_in_prod(_,syms)acc=List.fold_leftts_in_symaccsymsletts_in_nt_=ProdSet.foldts_in_prodletts_in_grammargr=NTMap.foldts_in_ntgrTSet.emptyletprods_in_nt_=ProdSet.unionletprods_in_grammargr=NTMap.foldprods_in_ntgrProdSet.empty(** BOUNDED DERIVATION *)letcollect_sym(ts,nts)=function|Tt->TSet.addtts,nts|NTnt->ts,NTSet.addntntsletcollect_syms(_,syms)acc=List.fold_leftcollect_symaccsymsletbnd_descend_ntgrnt=ProdSet.foldcollect_syms(NTMap.findntgr)letrecbnd_descendlevelsgrreached_ntsn=ifn<=0||NTSet.is_emptyreached_ntsthenlevelselselet_,new_reached_ntsasthis_level=NTSet.fold(bnd_descend_ntgr)reached_nts(TSet.empty,NTSet.empty)inbnd_descend(this_level::levels)grnew_reached_nts(n-1)letbnd_ascend_prodkept_nts(_,symsasprod)prods=ifList.for_all(sym_derivablekept_nts)symsthenprodselseProdSet.removeprodprodsletbnd_ascend_ntgrkept_ntsntnts=letprods=NTMap.findntgrinletkept_prods=ProdSet.fold(bnd_ascend_prodkept_nts)prodsprodsinifProdSet.is_emptykept_prodsthenntselseNTMap.addntkept_prodsntsletcleanup_sym(ts,nts)=ifTSet.is_emptyts&&NTMap.is_emptyntsthenraiseExitelsefunction|Tt->TSet.removetts,nts|NTnt->ts,NTMap.removentntsletcleanup_prod(_,syms)level=List.fold_leftcleanup_symlevelsymsletreccleanup_levelskept_nts=function|[]->assertfalse|(ts,ntsaslevel)::restaslevels->trylet(bad_ts,bad_nts)=NTMap.fold(fun_->ProdSet.foldcleanup_prod)kept_ntslevelinletgood_ts=TSet.difftsbad_tsinletgood_nts=NTMap.fold(funnt_->NTMap.removent)bad_ntsntsinifNTMap.is_emptybad_ntsthenifTSet.is_emptybad_tsthenlevelselse(good_ts,nts)::restelse(good_ts,good_nts)::cleanup_levelsgood_ntsrestwithExit->levelsletbnd_ascendgr(levels,kept_nts)(ts,nts)=letnew_kept_nts=NTSet.fold(bnd_ascend_ntgrkept_nts)ntsNTMap.emptyin(ts,new_kept_nts)::cleanup_levelsnew_kept_ntslevels,new_kept_ntsletbounded_grammargrstartn=matchbnd_descend[]gr(NTSet.singletonstart)nwith|[]->[]|(ts,_last_nts)::levels->letinit=[(ts,NTMap.empty)],NTMap.emptyinfst(List.fold_left(bnd_ascendgr)initlevels)end