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