dlvhex
2.5.0
|
00001 /* dlvhex -- Answer-Set Programming with external interfaces. 00002 * Copyright (C) 2005-2007 Roman Schindlauer 00003 * Copyright (C) 2006-2015 Thomas Krennwallner 00004 * Copyright (C) 2009-2016 Peter Schüller 00005 * Copyright (C) 2011-2016 Christoph Redl 00006 * Copyright (C) 2015-2016 Tobias Kaminski 00007 * Copyright (C) 2015-2016 Antonius Weinzierl 00008 * 00009 * This file is part of dlvhex. 00010 * 00011 * dlvhex is free software; you can redistribute it and/or modify it 00012 * under the terms of the GNU Lesser General Public License as 00013 * published by the Free Software Foundation; either version 2.1 of 00014 * the License, or (at your option) any later version. 00015 * 00016 * dlvhex is distributed in the hope that it will be useful, but 00017 * WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with dlvhex; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00024 * 02110-1301 USA. 00025 */ 00026 00034 #ifdef HAVE_CONFIG_H 00035 #include "config.h" 00036 #endif // HAVE_CONFIG_H 00037 00038 //#define BOOST_SPIRIT_DEBUG 00039 00040 #include "dlvhex2/StrongNegationPlugin.h" 00041 #include "dlvhex2/PlatformDefinitions.h" 00042 #include "dlvhex2/ProgramCtx.h" 00043 #include "dlvhex2/Registry.h" 00044 #include "dlvhex2/Printer.h" 00045 #include "dlvhex2/Printhelpers.h" 00046 #include "dlvhex2/PredicateMask.h" 00047 #include "dlvhex2/Logger.h" 00048 #include "dlvhex2/HexParser.h" 00049 #include "dlvhex2/HexParserModule.h" 00050 #include "dlvhex2/HexGrammar.h" 00051 00052 #include <boost/algorithm/string/predicate.hpp> 00053 #include <boost/lexical_cast.hpp> 00054 00055 DLVHEX_NAMESPACE_BEGIN 00056 00057 namespace spirit = boost::spirit; 00058 namespace qi = boost::spirit::qi; 00059 00060 StrongNegationPlugin::CtxData::CtxData(): 00061 enabled(false), 00062 negPredicateArities() 00063 { 00064 } 00065 00066 00067 StrongNegationPlugin::StrongNegationPlugin(): 00068 PluginInterface() 00069 { 00070 setNameVersion("dlvhex-strongnegationplugin[internal]", 2, 0, 0); 00071 } 00072 00073 00074 StrongNegationPlugin::~StrongNegationPlugin() 00075 { 00076 } 00077 00078 00079 // output help message for this plugin 00080 void StrongNegationPlugin::printUsage(std::ostream& o) const 00081 { 00082 // 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789- 00083 o << " --strongnegation-enable[=true,false]" << std::endl 00084 << " Enable or disable strong negation plugin (default is enabled)." << std::endl; 00085 } 00086 00087 00088 // accepted options: --strongnegation-enable 00089 // 00090 // processes options for this plugin, and removes recognized options from pluginOptions 00091 // (do not free the pointers, the const char* directly come from argv) 00092 void StrongNegationPlugin::processOptions( 00093 std::list<const char*>& pluginOptions, 00094 ProgramCtx& ctx) 00095 { 00096 StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>(); 00097 ctxdata.enabled = true; 00098 00099 typedef std::list<const char*>::iterator Iterator; 00100 Iterator it; 00101 WARNING("create (or reuse, maybe from potassco?) cmdline option processing facility") 00102 it = pluginOptions.begin(); 00103 while( it != pluginOptions.end() ) { 00104 bool processed = false; 00105 const std::string str(*it); 00106 if( boost::starts_with(str, "--strongnegation-enable" ) ) { 00107 std::string m = str.substr(std::string("--strongnegation-enable").length()); 00108 if (m == "" || m == "=true") { 00109 ctxdata.enabled = true; 00110 } 00111 else if (m == "=false") { 00112 ctxdata.enabled = false; 00113 } 00114 else { 00115 std::stringstream ss; 00116 ss << "Unknown --strongnegation-enable option: " << m; 00117 throw PluginError(ss.str()); 00118 } 00119 processed = true; 00120 } 00121 00122 if( processed ) { 00123 // return value of erase: element after it, maybe end() 00124 DBGLOG(DBG,"StrongNegationPlugin successfully processed option " << str); 00125 it = pluginOptions.erase(it); 00126 } 00127 else { 00128 it++; 00129 } 00130 } 00131 } 00132 00133 00134 class StrongNegationParserModuleSemantics: 00135 public HexGrammarSemantics 00136 { 00137 public: 00138 StrongNegationPlugin::CtxData& ctxdata; 00139 00140 public: 00141 StrongNegationParserModuleSemantics(ProgramCtx& ctx): 00142 HexGrammarSemantics(ctx), 00143 ctxdata(ctx.getPluginData<StrongNegationPlugin>()) { 00144 } 00145 00146 // use SemanticActionBase to redirect semantic action call into globally 00147 // specializable sem<T> struct space 00148 struct stronglyNegatedPrefixAtom: 00149 SemanticActionBase<StrongNegationParserModuleSemantics, ID, stronglyNegatedPrefixAtom> 00150 { 00151 stronglyNegatedPrefixAtom(StrongNegationParserModuleSemantics& mgr): 00152 stronglyNegatedPrefixAtom::base_type(mgr) { 00153 } 00154 }; 00155 }; 00156 00157 // create semantic handler for above semantic action 00158 // (needs to be in globally specializable struct space) 00159 template<> 00160 struct sem<StrongNegationParserModuleSemantics::stronglyNegatedPrefixAtom> 00161 { 00162 void createAtom(RegistryPtr reg, OrdinaryAtom& atom, ID& target) { 00163 // groundness 00164 DBGLOG(DBG,"checking groundness of tuple " << printrange(atom.tuple)); 00165 IDKind kind = 0; 00166 BOOST_FOREACH(const ID& id, atom.tuple) { 00167 kind |= id.kind; 00168 // make this sure to make the groundness check work 00169 // (if we add "builtin constant terms" like #supremum we might have to change the above statement) 00170 assert((id.kind & ID::SUBKIND_MASK) != ID::SUBKIND_TERM_BUILTIN); 00171 } 00172 const bool ground = !(kind & ID::SUBKIND_TERM_VARIABLE); 00173 if( ground ) { 00174 atom.kind |= ID::SUBKIND_ATOM_ORDINARYG; 00175 target = reg->storeOrdinaryGAtom(atom); 00176 } 00177 else { 00178 atom.kind |= ID::SUBKIND_ATOM_ORDINARYN; 00179 target = reg->storeOrdinaryNAtom(atom); 00180 } 00181 DBGLOG(DBG,"stored atom " << atom << " which got id " << target); 00182 } 00183 00184 void operator()( 00185 StrongNegationParserModuleSemantics& mgr, 00186 const boost::fusion::vector2< 00187 dlvhex::ID, 00188 boost::optional<boost::optional<std::vector<dlvhex::ID> > > 00189 >& source, 00190 ID& target) { 00191 typedef StrongNegationPlugin::CtxData::PredicateArityMap PredicateArityMap; 00192 00193 RegistryPtr reg = mgr.ctx.registry(); 00194 00195 // strong negation is always present here! 00196 00197 // predicate 00198 const ID& idpred = boost::fusion::at_c<0>(source); 00199 00200 // create/get aux constant for idpred 00201 const ID idnegpred = reg->getAuxiliaryConstantSymbol('s', idpred); 00202 00203 // build atom with auxiliary (SUBKIND is initialized by createAtom()) 00204 OrdinaryAtom atom(ID::MAINKIND_ATOM | ID::PROPERTY_AUX); 00205 atom.tuple.push_back(idnegpred); 00206 00207 // arguments 00208 if( (!!boost::fusion::at_c<1>(source)) && 00209 (!!(boost::fusion::at_c<1>(source).get())) ) { 00210 const Tuple& tuple = boost::fusion::at_c<1>(source).get().get(); 00211 atom.tuple.insert(atom.tuple.end(), tuple.begin(), tuple.end()); 00212 } 00213 00214 // store predicate with arity (ensure each predicate is used with only one arity) 00215 PredicateArityMap::const_iterator it = 00216 mgr.ctxdata.negPredicateArities.find(idpred); 00217 if( it == mgr.ctxdata.negPredicateArities.end() ) { 00218 // store as new 00219 mgr.ctxdata.negToPos[idnegpred] = idpred; 00220 } 00221 DBGLOG(DBG,"got strongly negated predicate " << 00222 printToString<RawPrinter>(idpred, reg) << "/" << 00223 idpred << " with arity " << atom.tuple.size() - 1); 00224 mgr.ctxdata.negPredicateArities[idpred].insert(atom.tuple.size() - 1); 00225 00226 // create atom 00227 createAtom(reg, atom, target); 00228 } 00229 }; 00230 00231 namespace 00232 { 00233 00234 template<typename Iterator, typename Skipper> 00235 struct StrongNegationParserModuleGrammarBase: 00236 // we derive from the original hex grammar 00237 // -> we can reuse its rules 00238 public HexGrammarBase<Iterator, Skipper> 00239 { 00240 typedef HexGrammarBase<Iterator, Skipper> Base; 00241 00242 StrongNegationParserModuleSemantics& sem; 00243 00244 StrongNegationParserModuleGrammarBase(StrongNegationParserModuleSemantics& sem): 00245 Base(sem), 00246 sem(sem) { 00247 typedef StrongNegationParserModuleSemantics Sem; 00248 stronglyNegatedPrefixAtom 00249 = ( 00250 qi::lit('-') >> Base::classicalAtomPredicate >> 00251 -(qi::lit('(') > -Base::terms >> qi::lit(')')) > qi::eps 00252 ) [ Sem::stronglyNegatedPrefixAtom(sem) ]; 00253 00254 #ifdef BOOST_SPIRIT_DEBUG 00255 BOOST_SPIRIT_DEBUG_NODE(stronglyNegatedPrefixAtom); 00256 #endif 00257 } 00258 00259 qi::rule<Iterator, ID(), Skipper> stronglyNegatedPrefixAtom; 00260 }; 00261 00262 struct StrongNegationParserModuleGrammar: 00263 StrongNegationParserModuleGrammarBase<HexParserIterator, HexParserSkipper>, 00264 // required for interface 00265 // note: HexParserModuleGrammar = 00266 // boost::spirit::qi::grammar<HexParserIterator, HexParserSkipper> 00267 HexParserModuleGrammar 00268 { 00269 typedef StrongNegationParserModuleGrammarBase<HexParserIterator, HexParserSkipper> GrammarBase; 00270 typedef HexParserModuleGrammar QiBase; 00271 00272 StrongNegationParserModuleGrammar(StrongNegationParserModuleSemantics& sem): 00273 GrammarBase(sem), 00274 QiBase(GrammarBase::stronglyNegatedPrefixAtom) { 00275 } 00276 }; 00277 typedef boost::shared_ptr<StrongNegationParserModuleGrammar> 00278 StrongNegationParserModuleGrammarPtr; 00279 00280 // moduletype = HexParserModule::BODYATOM 00281 // moduletype = HexParserModule::HEADATOM 00282 template<enum HexParserModule::Type moduletype> 00283 class StrongNegationParserModule: 00284 public HexParserModule 00285 { 00286 public: 00287 // the semantics manager is stored/owned by this module! 00288 StrongNegationParserModuleSemantics sem; 00289 // we also keep a shared ptr to the grammar module here 00290 StrongNegationParserModuleGrammarPtr grammarModule; 00291 00292 StrongNegationParserModule(ProgramCtx& ctx): 00293 HexParserModule(moduletype), 00294 sem(ctx) { 00295 LOG(INFO,"constructed StrongNegationParserModule"); 00296 } 00297 00298 virtual HexParserModuleGrammarPtr createGrammarModule() { 00299 assert(!grammarModule && "for simplicity (storing only one grammarModule pointer) we currently assume this will be called only once .. should be no problem to extend"); 00300 grammarModule.reset(new StrongNegationParserModuleGrammar(sem)); 00301 LOG(INFO,"created StrongNegationParserModuleGrammar"); 00302 return grammarModule; 00303 } 00304 }; 00305 00306 } // anonymous namespace 00307 00308 00309 // create parser modules that extend and the basic hex grammar 00310 // this parser also stores the query information into the plugin 00311 std::vector<HexParserModulePtr> 00312 StrongNegationPlugin::createParserModules(ProgramCtx& ctx) 00313 { 00314 DBGLOG(DBG,"StrongNegationPlugin::createParserModules()"); 00315 std::vector<HexParserModulePtr> ret; 00316 00317 StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>(); 00318 if( ctxdata.enabled ) { 00319 ret.push_back(HexParserModulePtr( 00320 new StrongNegationParserModule<HexParserModule::BODYATOM>(ctx))); 00321 ret.push_back(HexParserModulePtr( 00322 new StrongNegationParserModule<HexParserModule::HEADATOM>(ctx))); 00323 } 00324 00325 return ret; 00326 } 00327 00328 00329 namespace 00330 { 00331 00332 typedef StrongNegationPlugin::CtxData CtxData; 00333 00334 class StrongNegationConstraintAdder: 00335 public PluginRewriter 00336 { 00337 public: 00338 StrongNegationConstraintAdder() {} 00339 virtual ~StrongNegationConstraintAdder() {} 00340 00341 virtual void rewrite(ProgramCtx& ctx); 00342 }; 00343 00344 void StrongNegationConstraintAdder::rewrite(ProgramCtx& ctx) { 00345 typedef StrongNegationPlugin::CtxData::PredicateArityMap PredicateArityMap; 00346 00347 DBGLOG_SCOPE(DBG,"neg_rewr",false); 00348 DBGLOG(DBG,"= StrongNegationConstraintAdder::rewrite"); 00349 00350 StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>(); 00351 assert(ctxdata.enabled && "this rewriter should only be used " 00352 "if the plugin is enabled"); 00353 00354 RegistryPtr reg = ctx.registry(); 00355 assert(reg); 00356 PredicateArityMap::const_iterator it; 00357 for(it = ctxdata.negPredicateArities.begin(); 00358 it != ctxdata.negPredicateArities.end(); ++it) { 00359 // for predicate foo of arity k create constraint 00360 // :- foo(X1,X2,...,Xk), foo_neg_aux(X1,X2,...,Xk). 00361 00362 // create atoms 00363 const ID idpred = it->first; 00364 BOOST_FOREACH (unsigned arity, it->second) { 00365 DBGLOG(DBG,"processing predicate '" << 00366 printToString<RawPrinter>(idpred, reg) << "'/" << idpred << 00367 " with arity " << arity); 00368 00369 const ID idnegpred = reg->getAuxiliaryConstantSymbol('s', idpred); 00370 ID idatom; 00371 ID idnegatom; 00372 if( arity == 0 ) { 00373 // ground atoms 00374 OrdinaryAtom predAtom( 00375 ID::MAINKIND_ATOM | 00376 ID::SUBKIND_ATOM_ORDINARYG); 00377 predAtom.tuple.push_back(idpred); 00378 OrdinaryAtom negpredAtom( 00379 ID::MAINKIND_ATOM | 00380 ID::SUBKIND_ATOM_ORDINARYG | 00381 ID::PROPERTY_AUX); 00382 negpredAtom.tuple.push_back(idnegpred); 00383 idatom = reg->storeOrdinaryGAtom(predAtom); 00384 idnegatom = reg->storeOrdinaryGAtom(negpredAtom); 00385 } 00386 else { 00387 // nonground atoms 00388 OrdinaryAtom predAtom( 00389 ID::MAINKIND_ATOM | 00390 ID::SUBKIND_ATOM_ORDINARYN); 00391 predAtom.tuple.push_back(idpred); 00392 OrdinaryAtom negpredAtom( 00393 ID::MAINKIND_ATOM | 00394 ID::SUBKIND_ATOM_ORDINARYN | 00395 ID::PROPERTY_AUX); 00396 negpredAtom.tuple.push_back(idnegpred); 00397 00398 // add variables 00399 for(unsigned i = 0; i < arity; ++i) { 00400 // create variable 00401 std::ostringstream s; 00402 s << "X" << i; 00403 Term var( 00404 ID::MAINKIND_TERM | 00405 ID::SUBKIND_TERM_VARIABLE | 00406 ID::PROPERTY_AUX, 00407 s.str()); 00408 const ID idvar = reg->storeConstOrVarTerm(var); 00409 predAtom.tuple.push_back(idvar); 00410 negpredAtom.tuple.push_back(idvar); 00411 } 00412 00413 DBGLOG(DBG,"storing auxiliary atom " << predAtom); 00414 idatom = reg->storeOrdinaryNAtom(predAtom); 00415 DBGLOG(DBG,"storing auxiliary negative atom " << negpredAtom); 00416 idnegatom = reg->storeOrdinaryNAtom(negpredAtom); 00417 } 00418 00419 // create constraint 00420 Rule r( 00421 ID::MAINKIND_RULE | 00422 ID::SUBKIND_RULE_CONSTRAINT | 00423 ID::PROPERTY_AUX); 00424 00425 r.body.push_back(ID::posLiteralFromAtom(idatom)); 00426 r.body.push_back(ID::posLiteralFromAtom(idnegatom)); 00427 00428 ID idcon = reg->storeRule(r); 00429 ctx.idb.push_back(idcon); 00430 DBGLOG(DBG,"created aux constraint '" << 00431 printToString<RawPrinter>(idcon, reg) << "'"); 00432 } 00433 } 00434 } 00435 00436 } // anonymous namespace 00437 00438 00439 // rewrite program by adding auxiliary query rules 00440 PluginRewriterPtr StrongNegationPlugin::createRewriter(ProgramCtx& ctx) 00441 { 00442 StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>(); 00443 if( !ctxdata.enabled ) 00444 return PluginRewriterPtr(); 00445 00446 return PluginRewriterPtr(new StrongNegationConstraintAdder); 00447 } 00448 00449 00450 namespace 00451 { 00452 00453 class NegAuxPrinter: 00454 public AuxPrinter 00455 { 00456 public: 00457 typedef StrongNegationPlugin::CtxData::NegToPosMap NegToPosMap; 00458 public: 00459 NegAuxPrinter( 00460 RegistryPtr reg, 00461 PredicateMask& negAuxMask, 00462 const NegToPosMap& ntpm): 00463 reg(reg), mask(negAuxMask), ntpm(ntpm) { 00464 } 00465 00466 // print an ID and return true, 00467 // or do not print it and return false 00468 virtual bool print(std::ostream& out, ID id, const std::string& prefix) const 00469 { 00470 assert(id.isAuxiliary()); 00471 mask.updateMask(); 00472 DBGLOG(DBG,"mask is " << *mask.mask()); 00473 if( mask.mask()->getFact(id.address) ) { 00474 // we cannot use any stored text to print this, we have to assemble it from pieces 00475 DBGLOG(DBG,"printing auxiliary for strong negation: " << id); 00476 00477 // get replacement atom details 00478 const OrdinaryAtom& r_atom = reg->ogatoms.getByAddress(id.address); 00479 00480 // find positive version of predicate 00481 assert(!r_atom.tuple.empty()); 00482 const NegToPosMap::const_iterator itpred = ntpm.find(r_atom.tuple.front()); 00483 assert(itpred != ntpm.end()); 00484 const ID idpred = itpred->second; 00485 00486 // print strong negation 00487 out << prefix << '-'; 00488 00489 // print tuple 00490 RawPrinter printer(out, reg); 00491 // predicate 00492 printer.print(idpred); 00493 if( r_atom.tuple.size() > 1 ) { 00494 Tuple t(r_atom.tuple.begin()+1, r_atom.tuple.end()); 00495 out << "("; 00496 printer.printmany(t,","); 00497 out << ")"; 00498 } 00499 00500 return true; 00501 } 00502 return false; 00503 } 00504 00505 protected: 00506 RegistryPtr reg; 00507 PredicateMask& mask; 00508 const NegToPosMap& ntpm; 00509 }; 00510 00511 } // anonymous namespace 00512 00513 00514 // register auxiliary printer for strong negation auxiliaries 00515 void StrongNegationPlugin::setupProgramCtx(ProgramCtx& ctx) 00516 { 00517 StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>(); 00518 if( !ctxdata.enabled ) 00519 return; 00520 00521 RegistryPtr reg = ctx.registry(); 00522 00523 // init predicate mask 00524 ctxdata.myAuxiliaryPredicateMask.setRegistry(reg); 00525 00526 // add all auxiliaries to mask (here we should already have parsed all of them) 00527 typedef CtxData::NegToPosMap NegToPosMap; 00528 NegToPosMap::const_iterator it; 00529 for(it = ctxdata.negToPos.begin(); 00530 it != ctxdata.negToPos.end(); ++it) { 00531 ctxdata.myAuxiliaryPredicateMask.addPredicate(it->first); 00532 } 00533 00534 // update predicate mask 00535 ctxdata.myAuxiliaryPredicateMask.updateMask(); 00536 00537 // create auxiliary printer using mask 00538 AuxPrinterPtr negAuxPrinter(new NegAuxPrinter( 00539 reg, ctxdata.myAuxiliaryPredicateMask, ctxdata.negToPos)); 00540 reg->registerUserAuxPrinter(negAuxPrinter); 00541 } 00542 00543 00544 DLVHEX_NAMESPACE_END 00545 00546 // this would be the code to use this plugin as a "real" plugin in a .so file 00547 // but we directly use it in dlvhex.cpp 00548 #if 0 00549 StrongNegationPlugin theStrongNegationPlugin; 00550 00551 // return plain C type s.t. all compilers and linkers will like this code 00552 extern "C" 00553 void * PLUGINIMPORTFUNCTION() 00554 { 00555 return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theStrongNegationPlugin); 00556 } 00557 #endif 00558 00559 00560 // vim:expandtab:ts=4:sw=4: 00561 // mode: C++ 00562 // End: