parser.cpp
Go to the documentation of this file.
00001 // 
00002 // Copyright (c) Benjamin Kaufmann
00003 // 
00004 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ 
00005 // 
00006 // Clasp is free software; you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation; either version 2 of the License, or
00009 // (at your option) any later version.
00010 // 
00011 // Clasp is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 // 
00016 // You should have received a copy of the GNU General Public License
00017 // along with Clasp; if not, write to the Free Software
00018 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019 //
00020 #include <clasp/parser.h>
00021 #include <clasp/program_builder.h>
00022 #include <clasp/logic_program.h>
00023 #include <clasp/minimize_constraint.h>
00024 #include <clasp/shared_context.h>
00025 #include <clasp/solver.h>
00026 #include <clasp/clause.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <limits.h>
00030 #include <stdarg.h>
00031 #ifdef _WIN32
00032 #pragma warning (disable : 4996)
00033 #endif
00034 const char* clasp_format(char* buf, unsigned size, const char* fmt, ...) {
00035         if (size) { buf[0] = 0; --size; }
00036         va_list args;
00037         va_start(args, fmt);
00038         vsnprintf(buf, size, fmt, args);
00039         va_end(args);
00040         return buf;
00041 }
00042 const char* clasp_format_error(const char* fmt, ...) {
00043         static char buf[1024];
00044         buf[0] = 0;
00045         va_list args;
00046         va_start(args, fmt);
00047         vsnprintf(buf, 1023, fmt, args);
00048         va_end(args);
00049         return buf;
00050 }
00051 namespace Clasp {
00052 ParseError::ParseError(unsigned a_line, const char* a_msg) : ClaspError(clasp_format_error("Parse Error: Line %u, %s", a_line, a_msg)), line(a_line) {}
00054 // StreamSource
00056 StreamSource::StreamSource(std::istream& is) : in_(is), pos_(0), line_(1) {
00057         underflow();
00058 }
00059 
00060 void StreamSource::underflow() {    
00061         pos_ = 0;
00062         buffer_[0] = 0;
00063         if (!in_) return;
00064         in_.read( buffer_, sizeof(buffer_)-1 );
00065         buffer_[in_.gcount()] = 0;
00066 }
00067 
00068 bool StreamSource::parseInt64(int64& val) {
00069         skipSpace();
00070         bool pos = match('+') || !match('-');
00071         int  d   = **this - '0';
00072         if (d < 0 || d > 9) { return false; }
00073         val      = 0;
00074         do {
00075                 val *= 10;
00076                 val += d;
00077                 d    = *(++*this) - '0';
00078         } while (d >= 0 && d <= 9);
00079         val  = pos ? val : -val;
00080         return true;
00081 }
00082 bool StreamSource::parseInt(int& val) { 
00083         int64 x;
00084         return parseInt64(x)
00085                 &&   x >= INT_MIN
00086                 &&   x <= INT_MAX
00087                 &&   (val=(int)x, true);
00088 }
00089 bool StreamSource::parseInt(int& val, int min, int max) { 
00090         int64 x;
00091         return parseInt64(x)
00092                 &&   x >= min
00093                 &&   x <= max
00094                 &&   (val=(int)x, true);
00095 }
00096 
00097 bool StreamSource::matchEol() {
00098         if (match('\n')) {
00099                 ++line_;
00100                 return true;
00101         }
00102         if (match('\r')) {
00103                 match('\n');
00104                 ++line_;
00105                 return true;
00106         }
00107         return false;
00108 }
00109 
00110 bool readLine(StreamSource& in, PodVector<char>::type& buf ) {
00111         char buffer[1024];
00112         bool eol = false;
00113         uint32 i;
00114         buf.clear();
00115         for (i = 0; *in && (eol = in.matchEol()) == false; ++in) {
00116                 buffer[i] = *in;
00117                 if (++i == 1024) {
00118                         buf.insert(buf.end(), buffer, buffer+i);
00119                         i = 0;
00120                 }
00121         }
00122         buf.insert(buf.end(), buffer, buffer+i);
00123         buf.push_back('\0');
00124         return eol;
00125 }
00126 
00127 InputFormat Input_t::detectFormat(std::istream& in) {
00128         std::istream::int_type x = std::char_traits<char>::eof();
00129         while (in && (x = in.peek()) != std::char_traits<char>::eof() ) {
00130                 unsigned char c = static_cast<unsigned char>(x);
00131                 if (c >= '0' && c <= '9') return Problem_t::LPARSE;
00132                 if (c == 'c' || c == 'p') return Problem_t::DIMACS;
00133                 if (c == '*')             return Problem_t::OPB;
00134                 if (c == ' ' || c == '\t') { in.get(); continue; }
00135                 break;
00136         }
00137         char msg[] = "'c': Unrecognized input format!\n";
00138         msg[1]     = (char)(unsigned char)x;
00139         in && x != std::char_traits<char>::eof() 
00140                 ? throw ParseError(1, msg)
00141                 : throw ParseError(0, "Bad input stream!\n");
00142 }
00143 
00144 bool Input_t::parseLparse(std::istream& prg, Asp::LogicProgram& api) {
00145         StreamSource input(prg);
00146         return DefaultLparseParser(api).parse(input);
00147 }
00148 
00149 bool Input_t::parseDimacs(std::istream& prg, SatBuilder& api) {
00150         StreamSource input(prg);
00151         return DimacsParser(api).parse(input);
00152 }
00153 
00154 bool Input_t::parseOPB(std::istream& prg, PBBuilder& api) {
00155         StreamSource input(prg);
00156         return OPBParser(api).parse(input);
00157 }
00158 
00159 StreamParser::StreamParser() : source_(0) {}
00160 StreamParser::~StreamParser() {}
00161 bool StreamParser::check(bool cond, const char* err) const { return cond || (source_->error(err), false); }
00162 bool StreamParser::parse(StreamSource& prg) {
00163         source_ = &prg;
00164         return doParse();
00165 }
00166 // Skips lines starting with str
00167 bool StreamParser::skipComments(const char* str) {
00168         while (match(*source_, str, false)) { skipLine(*source_); }
00169         return true;
00170 }
00172 // LPARSE PARSING
00174 LparseParser::LparseParser(Asp::LogicProgram& prg)
00175         : builder_(&prg)
00176         , active_(0) {
00177 }
00178 void LparseParser::setProgram(Asp::LogicProgram& prg) {
00179         builder_ = &prg;
00180 }
00181 Var LparseParser::parseAtom() {
00182         int r = -1;
00183         check(input()->parseInt(r, 1, (int)varMax), (r == -1 ? "Atom id expected!" : "Atom out of bounds"));
00184         return static_cast<Var>(r);
00185 }
00186 bool LparseParser::addRule(const Asp::Rule& r) const {
00187         builder_->addRule(r);
00188         return true;
00189 }
00190 
00191 bool LparseParser::doParse() {
00192         SingleOwnerPtr<Asp::Rule> active(new Asp::Rule());
00193         active_ = active.get();
00194         return parseRules()
00195                 && parseSymbolTable()
00196                 && parseComputeStatement()
00197                 && parseModels()
00198                 && endParse();
00199 }
00200 
00201 bool LparseParser::parseRules() {
00202         int rt = -1;
00203         while ( input()->skipWhite() && input()->parseInt(rt) && rt != 0 && parseRule(rt) ) {
00204                 active_->clear();
00205         }
00206         return check(rt == 0, "Rule type expected!")
00207                 &&   check(matchEol(*input(), true), "Symbol table expected!")
00208                 &&   input()->skipWhite();
00209 }
00210 
00211 bool LparseParser::parseRule(int rt) {
00212         if (knownRuleType(rt)) {
00213                 int  bound   = -1;
00214                 bool weights = false;
00215                 active_->setType(static_cast<Asp::RuleType>(rt));
00216                 if (rt == Asp::CHOICERULE || rt == Asp::DISJUNCTIVERULE) {
00217                         int heads = input()->parseInt(1, INT_MAX, "Rule has too few heads");
00218                         for (int i = 0; i < heads; ++i) { active_->addHead(parseAtom()); }
00219                 }
00220                 else if (rt == Asp::OPTIMIZERULE) {
00221                         weights = input()->parseInt(0, 0, "Minimize rule: 0 expected!") == 0;
00222                 }
00223                 else {
00224                         active_->addHead(parseAtom());
00225                         weights = rt == Asp::WEIGHTRULE && check(input()->parseInt(bound, 0, INT_MAX), "Weightrule: Positive weight expected!");
00226                 }
00227                 int lits = input()->parseInt(0, INT_MAX, "Number of body literals expected!");
00228                 int neg  = input()->parseInt(0, lits,  "Illegal negative body size!");
00229                 check(rt != Asp::CONSTRAINTRULE || input()->parseInt(bound, 0, INT_MAX), "Constraint rule: Positive bound expected!");
00230                 if (bound >= 0) { active_->setBound(bound); }
00231                 return parseBody(static_cast<uint32>(lits), static_cast<uint32>(neg), weights) && addRule(*active_);
00232         }
00233         else if (rt >= 90 && rt < 93) {
00234                 if (rt == 90) { return input()->parseInt(0, 0, "0 expected") == 0; }
00235                 int a = input()->parseInt(1, INT_MAX, "atom id expected");
00236                 if (rt == 91) { builder_->freeze(a, input()->parseInt(0, 1, "0 or 1 expected") ? value_true:value_false); }
00237                 else          { builder_->unfreeze(a); }
00238                 return true;
00239         }
00240         else {
00241                 return parseRuleExtension(rt);
00242         }
00243 }
00244 bool LparseParser::parseBody(uint32 lits, uint32 neg, bool readWeights) {
00245         for (uint32 i = 0; i != lits; ++i) {
00246                 active_->addToBody(parseAtom(), i >= neg, 1);
00247         }
00248         if (readWeights) {
00249                 for (uint32 i = 0; i < lits; ++i) {
00250                         active_->body[i].second = input()->parseInt(0, INT_MAX, "Weight Rule: bad or missing weight!");
00251                 }
00252         } 
00253         return check(matchEol(*input(), true), "Illformed rule body!");
00254 }
00255 bool LparseParser::parseSymbolTable() {
00256         int a = -1;
00257         PodVector<char>::type buf;
00258         buf.reserve(1024);
00259         while (input()->skipWhite() && input()->parseInt(a) && a != 0) {
00260                 check(a >= 1, "Symbol Table: Atom id out of bounds!");
00261                 check(input()->skipSpace() && readLine(*input(), buf), "Symbol Table: Atom name too long or end of file!");
00262                 builder_->setAtomName(a, &buf[0]);
00263         }
00264         check(a == 0, "Symbol Table: Atom id expected!");
00265         return check(matchEol(*input(), true), "Compute Statement expected!");
00266 }
00267 
00268 bool LparseParser::parseComputeStatement() {
00269         const char* B[2] = { "B+", "B-" };
00270         for (int i = 0; i != 2; ++i) {
00271                 input()->skipWhite();
00272                 check(match(*input(), B[i], false) && matchEol(*input(), true), (i == 0 ? "B+ expected!" : "B- expected!"));
00273                 int id = -1;
00274                 while (input()->skipWhite() && input()->parseInt(id) && id != 0) {
00275                         check(id >= 1, "Compute Statement: Atom out of bounds");
00276                         builder_->setCompute(static_cast<Var>(id), B[i][1] == '+');
00277                 }
00278                 check(id == 0, "Compute Statement: Atom id or 0 expected!");
00279         }
00280         return true;
00281 }
00282 
00283 bool LparseParser::parseModels() {
00284         int m = 1;
00285         check(input()->skipWhite() && input()->parseInt(m, 0, INT_MAX), "Number of models expected!");
00286         return true;
00287 }
00288 
00289 bool LparseParser::endParse() { return true; } 
00290 DefaultLparseParser::DefaultLparseParser(Asp::LogicProgram& api) : LparseParser(api) {}
00291 bool DefaultLparseParser::parseRuleExtension(int) { input()->error("Unsupported rule type!"); return false; }
00293 // DIMACS PARSING
00295 DimacsParser::DimacsParser(SatBuilder& prg) : builder_(&prg) {}
00296 void DimacsParser::setProgram(SatBuilder& prg) { builder_ = &prg; }
00297 
00298 bool DimacsParser::doParse() {
00299         parseHeader();
00300         parseClauses();
00301         check(!**input(), "Unrecognized format!");
00302         return true;
00303 }
00304 // Parses the p line: p [w]cnf #vars #clauses [max clause weight]
00305 void DimacsParser::parseHeader() {
00306         skipComments("c");
00307         check(match(*input(), "p ", false), "Missing problem line!");
00308         wcnf_        = input()->match('w');
00309         check(match(*input(), "cnf", false), "Unrecognized format!");
00310         numVar_      = input()->parseInt(0, (int)varMax, "#vars expected!");
00311         uint32 numC  = (uint32)input()->parseInt(0, INT_MAX, "#clauses expected!");
00312         wsum_t cw    = 0;
00313         if (wcnf_) { input()->parseInt64(cw); }
00314         builder_->prepareProblem(numVar_, cw, numC);
00315         input()->skipWhite();
00316 }
00317 
00318 void DimacsParser::parseClauses() {
00319         LitVec cc;
00320         const bool wcnf = wcnf_;
00321         wsum_t     cw   = 0;
00322         const int  numV = numVar_;
00323         while (input()->skipWhite() && skipComments("c") && **input()) {
00324                 cc.clear();
00325                 if (wcnf) { check(input()->parseInt64(cw) && cw > 0, "wcnf: clause weight expected!"); }
00326                 for (int lit; (lit = input()->parseInt(-numV, numV, "Invalid variable in clause!")) != 0;) {
00327                         cc.push_back( Literal(static_cast<uint32>(lit > 0 ? lit : -lit), lit < 0) );
00328                         input()->skipWhite();
00329                 }
00330                 builder_->addClause(cc, cw);
00331         }
00332         input()->skipWhite();
00333 }
00334 
00336 // OPB PARSING
00338 OPBParser::OPBParser(PBBuilder& prg) : builder_(&prg) {}
00339 void OPBParser::setProgram(PBBuilder& prg) { builder_ = &prg; }
00340 bool OPBParser::doParse() {
00341         parseHeader();
00342         skipComments("*");
00343         parseOptObjective();
00344         for (;;) {
00345                 skipComments("*");
00346                 if (!**input()) { return true; }
00347                 parseConstraint();
00348         }
00349 }
00350 
00351 // * #variable= int #constraint= int [#product= int sizeproduct= int] [#soft= int mincost= int maxcost= int sumcost= int]
00352 // where [] indicate optional parts, i.e.
00353 //  LIN-PBO: * #variable= int #constraint= int
00354 //  NLC-PBO: * #variable= int #constraint= int #product= int sizeproduct= int
00355 //  LIN-WBO: * #variable= int #constraint= int #soft= int mincost= int maxcost= int sumcost= int
00356 //  NLC-WBO: * #variable= int #constraint= int #product= int sizeproduct= int #soft= int mincost= int maxcost= int sumcost= int
00357 void OPBParser::parseHeader() {
00358         check(match(*input(), "* #variable=", false), "Missing problem line '* #variable='!");
00359         input()->skipWhite();
00360         int numV = input()->parseInt(0, (int)varMax, "Number of vars expected");
00361         check(match(*input(), "#constraint=", true), "Bad problem line. Missing '#constraint='!");
00362         int numC = input()->parseInt(0, INT_MAX, "Number of constraints expected!");
00363         int numProd = 0, sizeProd = 0, numSoft = 0;
00364         minCost_    = 0, maxCost_ = 0;
00365         while (match(*input(), "#", true)) {
00366                 if (match(*input(), "product=", true)) { // NLC instance
00367                         numProd = input()->parseInt(0, INT_MAX, "Positive integer expected!");
00368                         check(match(*input(), "sizeproduct=", true), "'sizeproduct=' expected!");
00369                         sizeProd= input()->parseInt(0, INT_MAX, "Positive integer expected!");
00370                         (void)sizeProd;
00371                 }
00372                 else if (match(*input(), "soft=", true)) { // WBO instance
00373                         numSoft = input()->parseInt(0, INT_MAX, "Positive integer expected!");
00374                         check(match(*input(), "mincost=", true), "'mincost=' expected!");
00375                         minCost_= input()->parseInt(0, INT_MAX, "Positive integer expected!");
00376                         check(match(*input(), "maxcost=", true), "'maxcost=' expected!");
00377                         maxCost_= input()->parseInt(0, INT_MAX, "Positive integer expected!");
00378                         check(match(*input(), "sumcost=", true), "'sumcost=' expected!");
00379                         wsum_t sum;
00380                         check(input()->parseInt64(sum) && sum > 0, "Positive integer expected!");
00381                 }
00382                 else { input()->error("Unrecognized problem line!"); }
00383         }
00384         check(matchEol(*input(), true), "Unrecognized characters in problem line!");
00385         builder_->prepareProblem(numV, numProd, numSoft, numC);
00386 }
00387 
00388 // <objective>::= "min:" <zeroOrMoreSpace> <sum>  ";"
00389 // OR
00390 // <softobj>  ::= "soft:" [<unsigned_integer>] ";"
00391 void OPBParser::parseOptObjective() {
00392         if (match(*input(), "min:", true)) {
00393                 input()->skipWhite();
00394                 parseSum();
00395                 builder_->addObjective(active_.lits); 
00396         }
00397         else if (match(*input(), "soft:", true)) {
00398                 wsum_t softCost;
00399                 check(input()->parseInt64(softCost) && softCost > 0, "Positive integer expected!");
00400                 check(match(*input(), ';', true), "Semicolon missing after constraint!");
00401                 builder_->setSoftBound(softCost);
00402                 input()->skipWhite();
00403         }
00404 }
00405 
00406 // <constraint>::= <sum> <relational_operator> <zeroOrMoreSpace> <integer> <zeroOrMoreSpace> ";"
00407 // OR
00408 // <softconstr>::= "[" <zeroOrMoreSpace> <unsigned_integer> <zeroOrMoreSpace> "]" <constraint>
00409 void OPBParser::parseConstraint() {
00410         weight_t cost = 0;
00411         if (match(*input(), '[', true)) {
00412                 cost = input()->parseInt(minCost_, maxCost_, "Invalid soft constraint cost!");
00413                 check(match(*input(), "]", true), "Invalid soft constraint!");
00414         }
00415         parseSum();
00416         active_.eq = match(*input(), "=", true);
00417         check(active_.eq || match(*input(), ">=", false), "Relational operator expected!");
00418         input()->skipWhite();
00419         active_.bound = input()->parseInt(INT_MIN, INT_MAX, "Invalid coefficient on rhs of constraint!");
00420         check(match(*input(), ';', true), "Semicolon missing after constraint!");
00421         builder_->addConstraint(active_.lits, active_.bound, active_.eq, cost);
00422         input()->skipWhite();
00423 }
00424 
00425 // <sum>::= <weightedterm> | <weightedterm> <sum>
00426 // <weightedterm>::= <integer> <oneOrMoreSpace> <term> <oneOrMoreSpace>
00427 void OPBParser::parseSum() {
00428         active_.lits.clear();
00429         while (!match(*input(), ';', true)) {
00430                 int coeff = input()->parseInt(INT_MIN+1, INT_MAX, "Coefficient expected!");
00431                 parseTerm();
00432                 Literal x = term_.size() == 1 ? term_[0] : builder_->addProduct(term_);
00433                 active_.lits.push_back(WeightLiteral(x, coeff));
00434                 if (**input() == '>' || **input() == '=') break;
00435                 input()->skipWhite();
00436         }
00437         input()->skipWhite();
00438 }
00439 // <term>::=<variablename>
00440 // OR
00441 // <term>::= <literal> | <literal> <space>+ <term>
00442 void OPBParser::parseTerm() {
00443         term_.clear();
00444         char peek;
00445         do  {
00446                 match(*input(), '*', true);             // optionally
00447                 bool sign = match(*input(), '~', true); // optionally
00448                 check(match(*input(), 'x', true), "Identifier expected!");
00449                 int var   = input()->parseInt(1, builder_->numVars(), "Invalid identifier!");
00450                 term_.push_back(Literal((uint32)var, sign));
00451                 input()->skipWhite();
00452                 peek = **input();
00453         } while (peek == '*' || peek == '~' || peek == 'x');
00454 }
00455 
00456 }


clasp
Author(s): Benjamin Kaufmann
autogenerated on Thu Aug 27 2015 12:41:39