1 //
2 // Copyright (c) 2016-2017 Benjamin Kaufmann
3 //
4 // This file is part of Potassco.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <potassco/aspif_text.h>
25 #include <potassco/string_convert.h>
26 #include <potassco/rule_utils.h>
27 #include <cctype>
28 #include <cstring>
29 #include <ostream>
30 #include <string>
31 #include <vector>
32 #include <cassert>
33 namespace Potassco {
34 struct AspifTextInput::Data {
clearPotassco::AspifTextInput::Data35 void clear() { rule.clear(); symbol.clear(); }
atomsPotassco::AspifTextInput::Data36 AtomSpan atoms() const { return rule.head(); }
litsPotassco::AspifTextInput::Data37 LitSpan lits() const { return rule.body(); }
38 RuleBuilder rule;
39 std::string symbol;
40 };
AspifTextInput(AbstractProgram * out)41 AspifTextInput::AspifTextInput(AbstractProgram* out) : out_(out), data_(0) {}
doAttach(bool & inc)42 bool AspifTextInput::doAttach(bool& inc) {
43 char n = peek(true);
44 if (out_ && (!n || std::islower(static_cast<unsigned char>(n)) || std::strchr(".#%{:", n))) {
45 while (n == '%') {
46 skipLine();
47 n = peek(true);
48 }
49 inc = match("#incremental", false) && require(match("."), "unrecognized directive");
50 out_->initProgram(inc);
51 return true;
52 }
53 return false;
54 }
55
doParse()56 bool AspifTextInput::doParse() {
57 out_->beginStep();
58 if (!parseStatements()) { return false; }
59 out_->endStep();
60 return true;
61 }
62
parseStatements()63 bool AspifTextInput::parseStatements() {
64 require(out_ != 0, "output not set");
65 Data data;
66 data_ = &data;
67 for (char c; (c = peek(true)) != 0; data.clear()) {
68 if (c == '.') { match("."); }
69 else if (c == '#') { if (!matchDirective()) break; }
70 else if (c == '%') { skipLine(); }
71 else { matchRule(c); }
72 }
73 return true;
74 }
75
matchRule(char c)76 void AspifTextInput::matchRule(char c) {
77 if (c == '{') { match("{"); data_->rule.start(Head_t::Choice); matchAtoms(";,"); match("}"); }
78 else { data_->rule.start(); matchAtoms(";|"); }
79 if (match(":-", false)) {
80 c = peek(true);
81 if (!StreamType::isDigit(c) && c != '-') {
82 data_->rule.startBody();
83 matchLits();
84 }
85 else {
86 data_->rule.startSum(matchInt());
87 matchAgg();
88 }
89 }
90 match(".");
91 data_->rule.end(out_);
92 }
93
matchDirective()94 bool AspifTextInput::matchDirective() {
95 if (match("#minimize", false)) {
96 data_->rule.startMinimize(0);
97 matchAgg();
98 Weight_t prio = match("@", false) ? matchInt() : 0;
99 match(".");
100 data_->rule.setBound(prio);
101 data_->rule.end(out_);
102 }
103 else if (match("#project", false)) {
104 data_->rule.start();
105 if (match("{", false)) {
106 matchAtoms(",");
107 match("}");
108 }
109 match(".");
110 out_->project(data_->atoms());
111 }
112 else if (match("#output", false)) {
113 matchTerm();
114 matchCondition();
115 match(".");
116 out_->output(toSpan(data_->symbol), data_->lits());
117 }
118 else if (match("#external", false)) {
119 Atom_t a = matchId();
120 Value_t v = Value_t::False;
121 match(".");
122 if (match("[", false)) {
123 if (match("true", false)) { v = Value_t::True; }
124 else if (match("free", false)) { v = Value_t::Free; }
125 else if (match("release", false)) { v = Value_t::Release; }
126 else { match("false"); }
127 match("]");
128 }
129 out_->external(a, v);
130 }
131 else if (match("#assume", false)) {
132 data_->rule.startBody();
133 if (match("{", false)) {
134 matchLits();
135 match("}");
136 }
137 match(".");
138 out_->assume(data_->lits());
139 }
140 else if (match("#heuristic", false)) {
141 Atom_t a = matchId();
142 matchCondition();
143 match(".");
144 match("[");
145 int v = matchInt();
146 int p = 0;
147 if (match("@", false)) { p = matchInt(); require(p >= 0, "positive priority expected"); }
148 match(",");
149 int h = -1;
150 for (unsigned x = 0; x <= static_cast<unsigned>(Heuristic_t::eMax); ++x) {
151 if (match(toString(static_cast<Heuristic_t>(x)), false)) {
152 h = static_cast<int>(x);
153 break;
154 }
155 }
156 require(h >= 0, "unrecognized heuristic modification");
157 skipws();
158 match("]");
159 out_->heuristic(a, static_cast<Heuristic_t>(h), v, static_cast<unsigned>(p), data_->lits());
160 }
161 else if (match("#edge", false)) {
162 int s, t;
163 match("("), s = matchInt(), match(","), t = matchInt(), match(")");
164 matchCondition();
165 match(".");
166 out_->acycEdge(s, t, data_->lits());
167 }
168 else if (match("#step", false)) {
169 require(incremental(), "#step requires incremental program");
170 match(".");
171 return false;
172 }
173 else if (match("#incremental", false)) {
174 match(".");
175 }
176 else {
177 require(false, "unrecognized directive");
178 }
179 return true;
180 }
181
skipws()182 void AspifTextInput::skipws() {
183 stream()->skipWs();
184 }
match(const char * term,bool req)185 bool AspifTextInput::match(const char* term, bool req) {
186 if (ProgramReader::match(term, false)) { skipws(); return true; }
187 require(!req, POTASSCO_FORMAT("'%s' expected", term));
188 return false;
189 }
matchAtoms(const char * seps)190 void AspifTextInput::matchAtoms(const char* seps) {
191 if (std::islower(static_cast<unsigned char>(peek(true))) != 0) {
192 do {
193 Lit_t x = matchLit();
194 require(x > 0, "positive atom expected");
195 data_->rule.addHead(static_cast<Atom_t>(x));
196 } while (std::strchr(seps, stream()->peek()) && stream()->get() && (skipws(), true));
197 }
198 }
matchLits()199 void AspifTextInput::matchLits() {
200 if (std::islower(static_cast<unsigned char>(peek(true))) != 0) {
201 do {
202 data_->rule.addGoal(matchLit());
203 } while (match(",", false));
204 }
205 }
matchCondition()206 void AspifTextInput::matchCondition() {
207 data_->rule.startBody();
208 if (match(":", false)) { matchLits(); }
209 }
matchAgg()210 void AspifTextInput::matchAgg() {
211 if (match("{") && !match("}", false)) {
212 do {
213 WeightLit_t wl = {matchLit(), 1};
214 if (match("=", false)) { wl.weight = matchInt(); }
215 data_->rule.addGoal(wl);
216 }
217 while (match(",", false));
218 match("}");
219 }
220 }
221
matchLit()222 Lit_t AspifTextInput::matchLit() {
223 int s = match("not ", false) ? -1 : 1;
224 return static_cast<Lit_t>(matchId()) * s;
225 }
226
matchInt()227 int AspifTextInput::matchInt() {
228 int i = ProgramReader::matchInt();
229 skipws();
230 return i;
231 }
matchId()232 Atom_t AspifTextInput::matchId() {
233 char c = stream()->get();
234 char n = stream()->peek();
235 require(std::islower(static_cast<unsigned char>(c)) != 0, "<id> expected");
236 require(std::islower(static_cast<unsigned char>(n)) == 0, "<pos-integer> expected");
237 if (c == 'x' && (BufferedStream::isDigit(n) || n == '_')) {
238 if (n == '_') { stream()->get(); }
239 int i = matchInt();
240 require(i > 0, "<pos-integer> expected");
241 return static_cast<Atom_t>(i);
242 }
243 else {
244 skipws();
245 return static_cast<Atom_t>(c - 'a') + 1;
246 }
247 }
push(char c)248 void AspifTextInput::push(char c) {
249 data_->symbol.append(1, c);
250 }
251
matchTerm()252 void AspifTextInput::matchTerm() {
253 char c = stream()->peek();
254 if (std::islower(static_cast<unsigned char>(c)) != 0 || c == '_') {
255 do { push(stream()->get()); } while (std::isalnum(static_cast<unsigned char>(c = stream()->peek())) != 0 || c == '_');
256 skipws();
257 if (match("(", false)) {
258 push('(');
259 for (;;) {
260 matchAtomArg();
261 if (!match(",", false)) break;
262 push(',');
263 }
264 match(")");
265 push(')');
266 }
267 }
268 else if (c == '"') { matchStr(); }
269 else { require(false, "<term> expected"); }
270 skipws();
271 }
matchAtomArg()272 void AspifTextInput::matchAtomArg() {
273 char c;
274 for (int p = 0; (c = stream()->peek()) != 0; ) {
275 if (c == '"') {
276 matchStr();
277 }
278 else {
279 if (c == ')' && --p < 0) { break; }
280 else if (c == ',' && p == 0) { break; }
281 p += int(c == '(');
282 push(stream()->get());
283 skipws();
284 }
285 }
286 }
matchStr()287 void AspifTextInput::matchStr() {
288 match("\""), push('"');
289 bool quoted = false;
290 for (char c; (c = stream()->peek()) != 0 && (c != '\"' || quoted);) {
291 quoted = !quoted && c == '\\';
292 push(stream()->get());
293 }
294 match("\""), push('"');
295 }
296 /////////////////////////////////////////////////////////////////////////////////////////
297 // AspifTextOutput
298 /////////////////////////////////////////////////////////////////////////////////////////
299 struct AspifTextOutput::Data {
300 typedef std::vector<std::string> StringVec;
301 typedef std::vector<Id_t> AtomMap;
302 typedef std::vector<Lit_t> LitVec;
303 typedef std::vector<uint32_t> RawVec;
getConditionPotassco::AspifTextOutput::Data304 LitSpan getCondition(Id_t id) const {
305 return toSpan(&conditions[id + 1], static_cast<size_t>(conditions[id]));
306 }
addConditionPotassco::AspifTextOutput::Data307 Id_t addCondition(const LitSpan& cond) {
308 if (conditions.empty()) { conditions.push_back(0); }
309 if (empty(cond)) { return 0; }
310 Id_t id = static_cast<Id_t>(conditions.size());
311 conditions.push_back(static_cast<Lit_t>(size(cond)));
312 conditions.insert(conditions.end(), begin(cond), end(cond));
313 return id;
314 }
addStringPotassco::AspifTextOutput::Data315 Id_t addString(const StringSpan& str) {
316 Id_t id = static_cast<Id_t>(strings.size());
317 strings.push_back(std::string(Potassco::begin(str), Potassco::end(str)));
318 return id;
319 }
320 RawVec directives;
321 StringVec strings;
322 AtomMap atoms; // maps into strings
323 LitVec conditions;
324 uint32_t readPos;
resetPotassco::AspifTextOutput::Data325 void reset() { directives.clear(); strings.clear(); atoms.clear(); conditions.clear(); readPos = 0; }
326 };
AspifTextOutput(std::ostream & os)327 AspifTextOutput::AspifTextOutput(std::ostream& os) : os_(os), step_(-1) {
328 data_ = new Data();
329 }
~AspifTextOutput()330 AspifTextOutput::~AspifTextOutput() {
331 delete data_;
332 }
addAtom(Atom_t id,const StringSpan & str)333 void AspifTextOutput::addAtom(Atom_t id, const StringSpan& str) {
334 if (id >= data_->atoms.size()) { data_->atoms.resize(id + 1, idMax); }
335 data_->atoms[id] = data_->addString(str);
336 }
printName(std::ostream & os,Lit_t lit) const337 std::ostream& AspifTextOutput::printName(std::ostream& os, Lit_t lit) const {
338 if (lit < 0) { os << "not "; }
339 Atom_t id = Potassco::atom(lit);
340 if (id < data_->atoms.size() && data_->atoms[id] < data_->strings.size()) {
341 os << data_->strings[data_->atoms[id]];
342 }
343 else {
344 os << "x_" << id;
345 }
346 return os;
347 }
initProgram(bool incremental)348 void AspifTextOutput::initProgram(bool incremental) {
349 step_ = incremental ? 0 : -1;
350 data_->reset();
351 }
beginStep()352 void AspifTextOutput::beginStep() {
353 if (step_ >= 0) {
354 if (step_) { os_ << "% #program step(" << step_ << ").\n"; theory_.update(); }
355 else { os_ << "% #program base.\n"; }
356 ++step_;
357 }
358 }
rule(Head_t ht,const AtomSpan & head,const LitSpan & body)359 void AspifTextOutput::rule(Head_t ht, const AtomSpan& head, const LitSpan& body) {
360 push(Directive_t::Rule).push(static_cast<uint32_t>(ht)).push(head).push(Body_t::Normal).push(body);
361 }
rule(Head_t ht,const AtomSpan & head,Weight_t bound,const WeightLitSpan & lits)362 void AspifTextOutput::rule(Head_t ht, const AtomSpan& head, Weight_t bound, const WeightLitSpan& lits) {
363 if (size(lits) == 0) {
364 AspifTextOutput::rule(ht, head, toSpan<Lit_t>());
365 }
366 push(Directive_t::Rule).push(static_cast<uint32_t>(ht)).push(head);
367 uint32_t top = static_cast<uint32_t>(data_->directives.size());
368 Weight_t min = weight(*begin(lits)), max = min;
369 push(Body_t::Sum).push(bound).push(static_cast<uint32_t>(size(lits)));
370 for (const WeightLit_t* it = begin(lits), *end = Potassco::end(lits); it != end; ++it) {
371 push(Potassco::lit(*it)).push(Potassco::weight(*it));
372 if (Potassco::weight(*it) < min) { min = Potassco::weight(*it); }
373 if (Potassco::weight(*it) > max) { max = Potassco::weight(*it); }
374 }
375 if (min == max) {
376 data_->directives.resize(top);
377 bound = (bound + min-1)/min;
378 push(Body_t::Count).push(bound).push(static_cast<uint32_t>(size(lits)));
379 for (const WeightLit_t* it = begin(lits), *end = Potassco::end(lits); it != end; ++it) {
380 push(Potassco::lit(*it));
381 }
382 }
383 }
minimize(Weight_t prio,const WeightLitSpan & lits)384 void AspifTextOutput::minimize(Weight_t prio, const WeightLitSpan& lits) {
385 push(Directive_t::Minimize).push(lits).push(prio);
386 }
output(const StringSpan & str,const LitSpan & cond)387 void AspifTextOutput::output(const StringSpan& str, const LitSpan& cond) {
388 bool isAtom = size(str) > 0 && (std::islower(static_cast<unsigned char>(*begin(str))) || *begin(str) == '_');
389 if (size(cond) == 1 && lit(*begin(cond)) > 0 && isAtom) {
390 addAtom(Potassco::atom(*begin(cond)), str);
391 }
392 else {
393 push(Directive_t::Output).push(data_->addString(str)).push(cond);
394 }
395 }
external(Atom_t a,Value_t v)396 void AspifTextOutput::external(Atom_t a, Value_t v) {
397 push(Directive_t::External).push(a).push(static_cast<uint32_t>(v));
398 }
assume(const LitSpan & lits)399 void AspifTextOutput::assume(const LitSpan& lits) {
400 push(Directive_t::Assume).push(lits);
401 }
project(const AtomSpan & atoms)402 void AspifTextOutput::project(const AtomSpan& atoms) {
403 push(Directive_t::Project).push(atoms);
404 }
acycEdge(int s,int t,const LitSpan & condition)405 void AspifTextOutput::acycEdge(int s, int t, const LitSpan& condition) {
406 push(Directive_t::Edge).push(s).push(t).push(condition);
407 }
heuristic(Atom_t a,Heuristic_t t,int bias,unsigned prio,const LitSpan & condition)408 void AspifTextOutput::heuristic(Atom_t a, Heuristic_t t, int bias, unsigned prio, const LitSpan& condition) {
409 push(Directive_t::Heuristic).push(a).push(condition).push(bias).push(prio).push(static_cast<uint32_t>(t));
410 }
theoryTerm(Id_t termId,int number)411 void AspifTextOutput::theoryTerm(Id_t termId, int number) {
412 theory_.addTerm(termId, number);
413 }
theoryTerm(Id_t termId,const StringSpan & name)414 void AspifTextOutput::theoryTerm(Id_t termId, const StringSpan& name) {
415 theory_.addTerm(termId, name);
416 }
theoryTerm(Id_t termId,int compound,const IdSpan & args)417 void AspifTextOutput::theoryTerm(Id_t termId, int compound, const IdSpan& args) {
418 theory_.addTerm(termId, compound, args);
419 }
theoryElement(Id_t id,const IdSpan & terms,const LitSpan & cond)420 void AspifTextOutput::theoryElement(Id_t id, const IdSpan& terms, const LitSpan& cond) {
421 theory_.addElement(id, terms, data_->addCondition(cond));
422 }
theoryAtom(Id_t atomOrZero,Id_t termId,const IdSpan & elements)423 void AspifTextOutput::theoryAtom(Id_t atomOrZero, Id_t termId, const IdSpan& elements) {
424 theory_.addAtom(atomOrZero, termId, elements);
425 }
theoryAtom(Id_t atomOrZero,Id_t termId,const IdSpan & elements,Id_t op,Id_t rhs)426 void AspifTextOutput::theoryAtom(Id_t atomOrZero, Id_t termId, const IdSpan& elements, Id_t op, Id_t rhs) {
427 theory_.addAtom(atomOrZero, termId, elements, op, rhs);
428 }
429 template <class T>
get()430 T AspifTextOutput::get() {
431 return static_cast<T>(data_->directives[data_->readPos++]);
432 }
push(uint32_t x)433 AspifTextOutput& AspifTextOutput::push(uint32_t x) {
434 data_->directives.push_back(x);
435 return *this;
436 }
push(const AtomSpan & atoms)437 AspifTextOutput& AspifTextOutput::push(const AtomSpan& atoms) {
438 data_->directives.push_back(static_cast<uint32_t>(size(atoms)));
439 data_->directives.insert(data_->directives.end(), begin(atoms), end(atoms));
440 return *this;
441 }
push(const LitSpan & lits)442 AspifTextOutput& AspifTextOutput::push(const LitSpan& lits) {
443 data_->directives.push_back(static_cast<uint32_t>(size(lits)));
444 data_->directives.insert(data_->directives.end(), begin(lits), end(lits));
445 return *this;
446 }
push(const WeightLitSpan & wlits)447 AspifTextOutput& AspifTextOutput::push(const WeightLitSpan& wlits) {
448 data_->directives.reserve(data_->directives.size() + (2*size(wlits)));
449 data_->directives.push_back(static_cast<uint32_t>(size(wlits)));
450 for (WeightLitSpan::iterator it = begin(wlits), end = Potassco::end(wlits); it != end; ++it) {
451 data_->directives.push_back(static_cast<uint32_t>(lit(*it)));
452 data_->directives.push_back(static_cast<uint32_t>(weight(*it)));
453 }
454 return *this;
455 }
writeDirectives()456 void AspifTextOutput::writeDirectives() {
457 const char* sep = 0, *term = 0;
458 data_->readPos = 0;
459 for (uint32_t x; (x = get<uint32_t>()) != Directive_t::End;) {
460 sep = term = "";
461 switch (x) {
462 case Directive_t::Rule:
463 if (get<uint32_t>() != 0) { os_ << "{"; term = "}"; }
464 for (uint32_t n = get<uint32_t>(); n--; sep = !*term ? "|" : ";") { printName(os_ << sep, get<Atom_t>()); }
465 if (*sep) { os_ << term; sep = " :- "; }
466 else { os_ << ":- "; }
467 term = ".";
468 switch (uint32_t bt = get<uint32_t>()) {
469 case Body_t::Normal:
470 for (uint32_t n = get<uint32_t>(); n--; sep = ", ") { printName(os_ << sep, get<Lit_t>()); }
471 break;
472 case Body_t::Count: // fall through
473 case Body_t::Sum:
474 os_ << sep << get<Weight_t>();
475 sep = "{";
476 for (uint32_t n = get<uint32_t>(); n--; sep = "; ") {
477 printName(os_ << sep, get<Lit_t>());
478 if (bt == Body_t::Sum) { os_ << "=" << get<Weight_t>(); }
479 }
480 os_ << "}";
481 break;
482 }
483 break;
484 case Directive_t::Minimize:
485 sep = "#minimize{"; term = ".";
486 for (uint32_t n = get<uint32_t>(); n--; sep = "; ") {
487 printName(os_ << sep, get<Lit_t>());
488 os_ << "=" << get<Weight_t>();
489 }
490 os_ << "}@" << get<Weight_t>();
491 break;
492 case Directive_t::Project:
493 sep = "#project{"; term = "}.";
494 for (uint32_t n = get<uint32_t>(); n--; sep = ", ") { printName(os_ << sep, get<Lit_t>()); }
495 break;
496 case Directive_t::Output:
497 sep = " : "; term = ".";
498 os_ << "#show " << data_->strings[get<uint32_t>()];
499 for (uint32_t n = get<uint32_t>(); n--; sep = ", ") {
500 printName(os_ << sep, get<Lit_t>());
501 }
502 break;
503 case Directive_t::External:
504 sep = "#external "; term = ".";
505 printName(os_ << sep, get<Atom_t>());
506 switch (get<uint32_t>()) {
507 default: break;
508 case Value_t::Free: term = ". [free]"; break;
509 case Value_t::True: term = ". [true]"; break;
510 case Value_t::Release: term = ". [release]"; break;
511 }
512 break;
513 case Directive_t::Assume:
514 sep = "#assume{"; term = "}.";
515 for (uint32_t n = get<uint32_t>(); n--; sep = ", ") { printName(os_ << sep, get<Lit_t>()); }
516 break;
517 case Directive_t::Heuristic:
518 sep = " : "; term = "";
519 os_ << "#heuristic ";
520 printName(os_, get<Atom_t>());
521 for (uint32_t n = get<uint32_t>(); n--; sep = ", ") { printName(os_ << sep, get<Lit_t>()); }
522 os_ << ". [" << get<int32_t>();
523 if (uint32_t p = get<uint32_t>()) { os_ << "@" << p; }
524 os_ << ", " << toString(static_cast<Heuristic_t>(get<uint32_t>())) << "]";
525 break;
526 case Directive_t::Edge:
527 sep = " : "; term = ".";
528 os_ << "#edge(" << get<int32_t>() << ",";
529 os_ << get<int32_t>() << ")";
530 for (uint32_t n = get<uint32_t>(); n--; sep = ", ") { printName(os_ << sep, get<Lit_t>()); }
531 break;
532 default: break;
533 }
534 os_ << term << "\n";
535 }
536 }
visitTheories()537 void AspifTextOutput::visitTheories() {
538 struct BuildStr : public TheoryAtomStringBuilder {
539 explicit BuildStr(AspifTextOutput& s) : self(&s) {}
540 virtual LitSpan getCondition(Id_t condId) const {
541 return self->data_->getCondition(condId);
542 }
543 virtual std::string getName(Atom_t id) const {
544 if (id < self->data_->atoms.size() && self->data_->atoms[id] < self->data_->strings.size()) {
545 return self->data_->strings[self->data_->atoms[id]];
546 }
547 return std::string("x_").append(Potassco::toString(id));
548 }
549 AspifTextOutput* self;
550 } toStr(*this);
551 for (TheoryData::atom_iterator it = theory_.currBegin(), end = theory_.end(); it != end; ++it) {
552 Atom_t atom = (*it)->atom();
553 std::string name = toStr.toString(theory_, **it);
554 if (!atom) {
555 os_ << name << ".\n";
556 }
557 else {
558 POTASSCO_REQUIRE(atom >= data_->atoms.size() || data_->atoms[atom] == idMax,
559 "Redefinition: theory atom '%u' already shown as '%s'", atom, data_->strings[data_->atoms[atom]].c_str());
560 addAtom(atom, toSpan(name));
561 }
562 }
563 }
endStep()564 void AspifTextOutput::endStep() {
565 visitTheories();
566 push(Directive_t::End);
567 writeDirectives();
568 Data::RawVec().swap(data_->directives);
569 if (step_ < 0) { theory_.reset(); }
570 }
571 /////////////////////////////////////////////////////////////////////////////////////////
572 // TheoryAtomStringBuilder
573 /////////////////////////////////////////////////////////////////////////////////////////
toString(const TheoryData & td,const TheoryAtom & a)574 std::string TheoryAtomStringBuilder::toString(const TheoryData& td, const TheoryAtom& a) {
575 res_.clear();
576 add('&').term(td, td.getTerm(a.term())).add('{');
577 const char* sep = "";
578 for (TheoryElement::iterator eIt = a.begin(), eEnd = a.end(); eIt != eEnd; ++eIt, sep = "; ") {
579 add(sep).element(td, td.getElement(*eIt));
580 }
581 add('}');
582 if (a.guard()) {
583 add(' ').term(td, td.getTerm(*a.guard()));
584 }
585 if (a.rhs()) {
586 add(' ').term(td, td.getTerm(*a.rhs()));
587 }
588 return res_;
589 }
function(const TheoryData & td,const TheoryTerm & f)590 bool TheoryAtomStringBuilder::function(const TheoryData& td, const TheoryTerm& f) {
591 TheoryTerm x = td.getTerm(f.function());
592 if (x.type() == Theory_t::Symbol && std::strchr("/!<=>+-*\\?&@|:;~^.", *x.symbol()) != 0) {
593 if (f.size() == 1) {
594 term(td, x).term(td, td.getTerm(*f.begin()));
595 return false;
596 }
597 else if (f.size() == 2) {
598 term(td, td.getTerm(*f.begin())).add(' ').term(td, x).add(' ').term(td, td.getTerm(*(f.begin() + 1)));
599 return false;
600 }
601 }
602 term(td, x);
603 return true;
604 }
term(const TheoryData & data,const TheoryTerm & t)605 TheoryAtomStringBuilder& TheoryAtomStringBuilder::term(const TheoryData& data, const TheoryTerm& t) {
606 switch (t.type()) {
607 default: assert(false);
608 case Theory_t::Number: add(Potassco::toString(t.number())); break;
609 case Theory_t::Symbol: add(t.symbol()); break;
610 case Theory_t::Compound: {
611 if (!t.isFunction() || function(data, t)) {
612 const char* parens = Potassco::toString(t.isTuple() ? t.tuple() : Potassco::Tuple_t::Paren);
613 const char* sep = "";
614 add(parens[0]);
615 for (TheoryTerm::iterator it = t.begin(), end = t.end(); it != end; ++it, sep = ", ") {
616 add(sep).term(data, data.getTerm(*it));
617 }
618 add(parens[1]);
619 }
620 }
621 }
622 return *this;
623 }
element(const TheoryData & data,const TheoryElement & e)624 TheoryAtomStringBuilder& TheoryAtomStringBuilder::element(const TheoryData& data, const TheoryElement& e) {
625 const char* sep = "";
626 for (TheoryElement::iterator it = e.begin(), end = e.end(); it != end; ++it, sep = ", ") {
627 add(sep).term(data, data.getTerm(*it));
628 }
629 if (e.condition()) {
630 LitSpan cond = getCondition(e.condition());
631 sep = " : ";
632 for (const Lit_t* it = begin(cond), *end = Potassco::end(cond); it != end; ++it, sep = ", ") {
633 add(sep);
634 if (*it < 0) { add("not "); }
635 add(getName(atom(*it)));
636 }
637 }
638 return *this;
639 }
640 } // namespace Potassco
641