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/ChoicePlugin.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 #include "dlvhex2/LiberalSafetyChecker.h" 00052 00053 #include <boost/algorithm/string/predicate.hpp> 00054 #include <boost/lexical_cast.hpp> 00055 00056 DLVHEX_NAMESPACE_BEGIN 00057 00058 namespace spirit = boost::spirit; 00059 namespace qi = boost::spirit::qi; 00060 00061 ChoicePlugin::CtxData::CtxData(): 00062 enabled(false) 00063 { 00064 } 00065 00066 00067 ChoicePlugin::ChoicePlugin(): 00068 PluginInterface() 00069 { 00070 setNameVersion("dlvhex-choicePlugin[internal]", 2, 0, 0); 00071 } 00072 00073 00074 ChoicePlugin::~ChoicePlugin() 00075 { 00076 } 00077 00078 00079 // output help message for this plugin 00080 void ChoicePlugin::printUsage(std::ostream& o) const 00081 { 00082 // 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789- 00083 o << " --choice-enable[=true,false]" << std::endl 00084 << " Enable choice rules (default is enabled)." << std::endl; 00085 } 00086 00087 00088 // accepted options: --choice-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 ChoicePlugin::processOptions( 00093 std::list<const char*>& pluginOptions, 00094 ProgramCtx& ctx) 00095 { 00096 ChoicePlugin::CtxData& ctxdata = ctx.getPluginData<ChoicePlugin>(); 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, "--choice-enable" ) ) { 00107 std::string m = str.substr(std::string("--choice-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 --choice-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,"ChoicePlugin successfully processed option " << str); 00125 it = pluginOptions.erase(it); 00126 } 00127 else { 00128 it++; 00129 } 00130 } 00131 } 00132 00133 00134 class ChoiceParserModuleSemantics: 00135 public HexGrammarSemantics 00136 { 00137 public: 00138 ChoicePlugin::CtxData& ctxdata; 00139 00140 public: 00141 ChoiceParserModuleSemantics(ProgramCtx& ctx): 00142 HexGrammarSemantics(ctx), 00143 ctxdata(ctx.getPluginData<ChoicePlugin>()) { 00144 } 00145 00146 // use SemanticActionBase to redirect semantic action call into globally 00147 // specializable sem<T> struct space 00148 struct choiceRule: 00149 SemanticActionBase<ChoiceParserModuleSemantics, ID, choiceRule> 00150 { 00151 choiceRule(ChoiceParserModuleSemantics& mgr): 00152 choiceRule::base_type(mgr) { 00153 } 00154 }; 00155 struct choiceHead: 00156 SemanticActionBase<ChoiceParserModuleSemantics, Tuple, choiceHead> 00157 { 00158 choiceHead(ChoiceParserModuleSemantics& mgr): 00159 choiceHead::base_type(mgr) { 00160 } 00161 }; 00162 struct choiceElement: 00163 SemanticActionBase<ChoiceParserModuleSemantics, ID, choiceElement> 00164 { 00165 choiceElement(ChoiceParserModuleSemantics& mgr): 00166 choiceElement::base_type(mgr) { 00167 } 00168 }; 00169 }; 00170 00171 // create semantic handler for above semantic action 00172 // (needs to be in globally specializable struct space) 00173 template<> 00174 struct sem<ChoiceParserModuleSemantics::choiceRule> 00175 { 00176 void operator()( 00177 ChoiceParserModuleSemantics& mgr, 00178 const boost::fusion::vector2< 00179 dlvhex::Tuple, 00180 boost::optional<std::vector<dlvhex::ID> > 00181 >& source, 00182 ID& target) { 00183 RegistryPtr reg = mgr.ctx.registry(); 00184 00185 // add original rule body to all rewritten rules 00186 Tuple rules = boost::fusion::at_c<0>(source); 00187 if (!!boost::fusion::at_c<1>(source)) { 00188 int i = 0; 00189 BOOST_FOREACH (ID ruleID, rules) { 00190 Rule rule = reg->rules.getByID(ruleID); 00191 rule.body.insert(rule.body.end(), boost::fusion::at_c<1>(source).get().begin(), boost::fusion::at_c<1>(source).get().end()); 00192 // check if the rule contains external atoms 00193 BOOST_FOREACH (ID b, rule.body) { 00194 if (b.isExternalAtom()) { 00195 rule.kind |= ID::PROPERTY_RULE_EXTATOMS; 00196 break; 00197 } 00198 } 00199 rules[i] = reg->storeRule(rule); 00200 i++; 00201 } 00202 } 00203 BOOST_FOREACH (ID ruleID, rules) { 00204 if ( mgr.mlpMode == 0 ) { 00205 mgr.ctx.idb.push_back(ruleID); 00206 } 00207 else { 00208 mgr.ctx.idbList.back().push_back(ruleID); 00209 } 00210 // return ID of last rule 00211 target = ID_FAIL; 00212 } 00213 } 00214 }; 00215 template<> 00216 struct sem<ChoiceParserModuleSemantics::choiceHead> 00217 { 00218 void operator()( 00219 ChoiceParserModuleSemantics& mgr, 00220 const boost::fusion::vector3< 00221 boost::optional<boost::fusion::vector2<ID, ID> >, 00222 boost::optional<std::vector<dlvhex::ID> >, 00223 boost::optional<boost::fusion::vector2<ID, ID> > 00224 >& source, 00225 Tuple& target) { 00226 RegistryPtr reg = mgr.ctx.registry(); 00227 00228 // constraint choice according to bounds 00229 Rule r(ID::MAINKIND_RULE | ID::SUBKIND_RULE_CONSTRAINT); 00230 00231 int varnr = 1; 00232 00233 // Step 1: Create builtins of kind: "not l <= V1" and "not r <= u" 00234 // (where l and u are the lower and upper bounds and <= can also be different comparison operators) 00235 BuiltinAtom bound1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_BUILTIN); 00236 BuiltinAtom bound2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_BUILTIN); 00237 { 00238 // left bound 00239 if (!!boost::fusion::at_c<0>(source)) { 00240 IDAddress adr = boost::fusion::at_c<1>(boost::fusion::at_c<0>(source).get()).address; 00241 bound1.tuple.push_back(ID::termFromBuiltin(static_cast<dlvhex::ID::TermBuiltinAddress>(ID::negateBinaryOperator(adr)))); 00242 bound1.tuple.push_back(boost::fusion::at_c<0>(boost::fusion::at_c<0>(source).get())); 00243 bound1.tuple.push_back(reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr))); 00244 } 00245 00246 // left right bound 00247 if (!!boost::fusion::at_c<2>(source)) { 00248 IDAddress adr = boost::fusion::at_c<0>(boost::fusion::at_c<2>(source).get()).address; 00249 bound2.tuple.push_back(ID::termFromBuiltin(static_cast<dlvhex::ID::TermBuiltinAddress>(ID::negateBinaryOperator(adr)))); 00250 bound2.tuple.push_back(reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr))); 00251 bound2.tuple.push_back(boost::fusion::at_c<1>(boost::fusion::at_c<2>(source).get())); 00252 } 00253 00254 // default 00255 if (!boost::fusion::at_c<0>(source) && !boost::fusion::at_c<1>(source)) { 00256 bound1.tuple.push_back(ID::termFromBuiltin(ID::TERM_BUILTIN_GE)); 00257 bound1.tuple.push_back(reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr))); 00258 bound1.tuple.push_back(ID::termFromInteger(0)); 00259 } 00260 } 00261 00262 // Step 2: compute V1 as the sum of all counts of choice elements 00263 ID cntVarID = reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr)); 00264 DBGLOG(DBG, "Creating aggregate cntVarID=#count{ ChoiceAtom(...) : ChoiceCondition(...) }"); 00265 AggregateAtom cnt(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_AGGREGATE); 00266 cnt.tuple[0] = cntVarID; 00267 cnt.tuple[1] = ID::termFromBuiltin(ID::TERM_BUILTIN_EQ); 00268 cnt.tuple[2] = ID::termFromBuiltin(ID::TERM_BUILTIN_AGGCOUNT); 00269 cnt.tuple[3] = ID_FAIL; 00270 cnt.tuple[4] = ID_FAIL; 00271 varnr += 1; // one variable was used 00272 00273 // create count atom 00274 if (!!boost::fusion::at_c<1>(source)) { 00275 int elementNr = 0; 00276 BOOST_FOREACH (ID choiceElement, boost::fusion::at_c<1>(source).get()) { 00277 // copy choice rule 00278 target.push_back(choiceElement); 00279 00280 // extract choice atom as first element of the rule's head 00281 ID choiceAtomID = reg->rules.getByID(choiceElement).head[0]; 00282 00283 // constructor an aggregate of kind: V[i+1]=#count{ ChoiceAtom(...) : ChoiceCondition(...) } 00284 ID cntID; 00285 std::set<ID> vars; 00286 reg->getVariablesInID(choiceAtomID, vars); 00287 Tuple curVarVector; 00288 curVarVector.insert(curVarVector.end(), vars.begin(), vars.end()); 00289 curVarVector.push_back(ID::termFromInteger(elementNr++)); 00290 cnt.mvariables.push_back(curVarVector); 00291 Tuple curLitVector; 00292 curLitVector.push_back(ID::posLiteralFromAtom(choiceAtomID)); 00293 cnt.mliterals.push_back(curLitVector); 00294 } 00295 } 00296 ID cntID = reg->aatoms.storeAndGetID(cnt); 00297 DBGLOG(DBG, "Result: " << printToString<RawPrinter>(cntID, reg)); 00298 r.body.push_back(ID::posLiteralFromAtom(cntID)); 00299 00300 // Add up to two choice constraints. Note: The rule body of the original choice rule is still missing! 00301 if (bound1.tuple.size() > 0) { 00302 DBGLOG(DBG, "Checking bound 1"); 00303 ID boundID = reg->batoms.storeAndGetID(bound1); 00304 DBGLOG(DBG, "Bound atom 1: " << printToString<RawPrinter>(boundID, reg)); 00305 r.body.push_back(ID::posLiteralFromAtom(boundID)); 00306 ID consRuleID = reg->storeRule(r); 00307 DBGLOG(DBG, "Choice constraint 1: " << printToString<RawPrinter>(consRuleID, reg)); 00308 target.push_back(consRuleID); 00309 r.body.pop_back(); 00310 } 00311 if (bound2.tuple.size() > 0) { 00312 DBGLOG(DBG, "Checking bound 2"); 00313 ID boundID = reg->batoms.storeAndGetID(bound2); 00314 DBGLOG(DBG, "Bound atom 2: " << printToString<RawPrinter>(boundID, reg)); 00315 r.body.push_back(ID::posLiteralFromAtom(boundID)); 00316 ID consRuleID = reg->storeRule(r); 00317 DBGLOG(DBG, "Choice constraint 2: " << printToString<RawPrinter>(consRuleID, reg)); 00318 target.push_back(consRuleID); 00319 r.body.pop_back(); 00320 } 00321 } 00322 }; 00323 template<> 00324 struct sem<ChoiceParserModuleSemantics::choiceElement> 00325 { 00326 void operator()( 00327 ChoiceParserModuleSemantics& mgr, 00328 const boost::fusion::vector2< 00329 dlvhex::ID, 00330 boost::optional<boost::optional<std::vector<dlvhex::ID> > > 00331 >& source, 00332 ID& target) { 00333 RegistryPtr reg = mgr.ctx.registry(); 00334 00335 // guess between choice atom and negated choice atom 00336 Rule r(ID::MAINKIND_RULE | ID::PROPERTY_RULE_DISJ); 00337 ID choiceAtomID = boost::fusion::at_c<0>(source); 00338 r.head.push_back(choiceAtomID); 00339 OrdinaryAtom negChoiceAtom = reg->lookupOrdinaryAtom(choiceAtomID); 00340 negChoiceAtom.tuple[0] = reg->getAuxiliaryConstantSymbol('c', negChoiceAtom.tuple[0]); 00341 negChoiceAtom.kind |= ID::PROPERTY_AUX; 00342 ID negChoiceAtomID = reg->storeOrdinaryAtom(negChoiceAtom); 00343 r.head.push_back(negChoiceAtomID); 00344 00345 // add condition of choice element to rule body if available 00346 if ((!!boost::fusion::at_c<1>(source)) && (!!boost::fusion::at_c<1>(source).get())) { 00347 r.body = boost::fusion::at_c<1>(source).get().get(); 00348 } 00349 00350 // Note: The rule body of the original choice rule is still missing! 00351 target = reg->storeRule(r); 00352 } 00353 }; 00354 00355 namespace 00356 { 00357 00358 template<typename Iterator, typename Skipper> 00359 struct ChoiceParserModuleGrammarBase: 00360 // we derive from the original hex grammar 00361 // -> we can reuse its rules 00362 public HexGrammarBase<Iterator, Skipper> 00363 { 00364 typedef HexGrammarBase<Iterator, Skipper> Base; 00365 00366 ChoiceParserModuleSemantics& sem; 00367 00368 ChoiceParserModuleGrammarBase(ChoiceParserModuleSemantics& sem): 00369 Base(sem), 00370 sem(sem) { 00371 typedef ChoiceParserModuleSemantics Sem; 00372 00373 choiceRule 00374 = (choiceHead >> -(qi::lit(":-") > (Base::bodyLiteral % qi::char_(',')) ) >> qi::lit('.') ) [ Sem::choiceRule(sem) ]; 00375 00376 choiceHead 00377 = ( 00378 -(Base::term >> Base::builtinOpsBinary) >> qi::lit('{') >> -(choiceElement % qi::lit(';')) >> qi::lit('}') >> -(Base::builtinOpsBinary >> Base::term) > qi::eps 00379 ) [ Sem::choiceHead(sem) ]; 00380 00381 choiceElement 00382 = ( 00383 Base::classicalAtom >> -(qi::lit(':') >> (Base::bodyLiteral % qi::lit(','))) > qi::eps 00384 ) [ Sem::choiceElement(sem) ]; 00385 00386 #ifdef BOOST_SPIRIT_DEBUG 00387 BOOST_SPIRIT_DEBUG_NODE(choiceHead); 00388 BOOST_SPIRIT_DEBUG_NODE(choiceElement); 00389 #endif 00390 } 00391 00392 qi::rule<Iterator, ID(), Skipper> choiceRule; 00393 qi::rule<Iterator, Tuple(), Skipper> choiceHead; 00394 qi::rule<Iterator, ID(), Skipper> choiceElement; 00395 }; 00396 00397 struct ChoiceParserModuleGrammar: 00398 ChoiceParserModuleGrammarBase<HexParserIterator, HexParserSkipper>, 00399 // required for interface 00400 // note: HexParserModuleGrammar = 00401 // boost::spirit::qi::grammar<HexParserIterator, HexParserSkipper> 00402 HexParserModuleGrammar 00403 { 00404 typedef ChoiceParserModuleGrammarBase<HexParserIterator, HexParserSkipper> GrammarBase; 00405 typedef HexParserModuleGrammar QiBase; 00406 00407 ChoiceParserModuleGrammar(ChoiceParserModuleSemantics& sem): 00408 GrammarBase(sem), 00409 QiBase(GrammarBase::choiceRule) { 00410 } 00411 }; 00412 typedef boost::shared_ptr<ChoiceParserModuleGrammar> 00413 ChoiceParserModuleGrammarPtr; 00414 00415 // moduletype = HexParserModule::TOPLEVEL 00416 template<enum HexParserModule::Type moduletype> 00417 class ChoiceParserModule: 00418 public HexParserModule 00419 { 00420 public: 00421 // the semantics manager is stored/owned by this module! 00422 ChoiceParserModuleSemantics sem; 00423 // we also keep a shared ptr to the grammar module here 00424 ChoiceParserModuleGrammarPtr grammarModule; 00425 00426 ChoiceParserModule(ProgramCtx& ctx): 00427 HexParserModule(moduletype), 00428 sem(ctx) { 00429 LOG(INFO,"constructed ChoiceParserModule"); 00430 } 00431 00432 virtual HexParserModuleGrammarPtr createGrammarModule() { 00433 assert(!grammarModule && "for simplicity (storing only one grammarModule pointer) we currently assume this will be called only once .. should be no problem to extend"); 00434 grammarModule.reset(new ChoiceParserModuleGrammar(sem)); 00435 LOG(INFO,"created ChoiceParserModuleGrammar"); 00436 return grammarModule; 00437 } 00438 }; 00439 00440 } // anonymous namespace 00441 00442 00443 // create parser modules that extend and the basic hex grammar 00444 // this parser also stores the query information into the plugin 00445 std::vector<HexParserModulePtr> 00446 ChoicePlugin::createParserModules(ProgramCtx& ctx) 00447 { 00448 DBGLOG(DBG,"ChoicePlugin::createParserModules()"); 00449 std::vector<HexParserModulePtr> ret; 00450 00451 ChoicePlugin::CtxData& ctxdata = ctx.getPluginData<ChoicePlugin>(); 00452 if( ctxdata.enabled ) { 00453 ret.push_back(HexParserModulePtr( 00454 new ChoiceParserModule<HexParserModule::TOPLEVEL>(ctx))); 00455 } 00456 00457 return ret; 00458 } 00459 00460 00461 std::vector<PluginAtomPtr> ChoicePlugin::createAtoms(ProgramCtx& ctx) const 00462 { 00463 std::vector<PluginAtomPtr> ret; 00464 // we don't have external atoms, only a parer plugin and a rewriter 00465 return ret; 00466 } 00467 00468 00469 void ChoicePlugin::setupProgramCtx(ProgramCtx& ctx) 00470 { 00471 ChoicePlugin::CtxData& ctxdata = ctx.getPluginData<ChoicePlugin>(); 00472 if( !ctxdata.enabled ) 00473 return; 00474 00475 RegistryPtr reg = ctx.registry(); 00476 } 00477 00478 00479 DLVHEX_NAMESPACE_END 00480 00481 // this would be the code to use this plugin as a "real" plugin in a .so file 00482 // but we directly use it in dlvhex.cpp 00483 #if 0 00484 ChoicePlugin theChoicePlugin; 00485 00486 // return plain C type s.t. all compilers and linkers will like this code 00487 extern "C" 00488 void * PLUGINIMPORTFUNCTION() 00489 { 00490 return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theChoicePlugin); 00491 } 00492 #endif 00493 00494 00495 // vim:expandtab:ts=4:sw=4: 00496 // mode: C++ 00497 // End: