1
/* VerifyPN - TAPAAL Petri Net Engine
2
* Copyright (C) 2014 Jiri Srba <srba.jiri@gmail.com>,
3
* Peter Gjøl Jensen <root@petergjoel.dk>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#include "PetriParse/QueryXMLParser.h"
20
#include "PetriEngine/PQL/Expressions.h"
30
int getChildCount(rapidxml::xml_node<> *n)
33
for (rapidxml::xml_node<> *child = n->first_node(); child != nullptr; child = child->next_sibling())
40
QueryXMLParser::QueryXMLParser() = default;
42
QueryXMLParser::~QueryXMLParser() = default;
44
bool QueryXMLParser::parse(std::ifstream& xml, const std::set<size_t>& parse_only) {
46
rapidxml::xml_document<> doc;
47
vector<char> buffer((istreambuf_iterator<char>(xml)), istreambuf_iterator<char>());
48
buffer.push_back('\0');
49
doc.parse<0>(&buffer[0]);
50
rapidxml::xml_node<>* root = doc.first_node();
53
parsingOK = parsePropertySet(root, parse_only);
62
bool QueryXMLParser::parsePropertySet(rapidxml::xml_node<>* element, const std::set<size_t>& parse_only) {
63
if (strcmp(element->name(), "property-set") != 0) {
64
fprintf(stderr, "ERROR missing property-set\n");
65
return false; // missing property-set element
69
for (auto it = element->first_node(); it; it = it->next_sibling()) {
70
if(parse_only.empty() || parse_only.count(i) > 0)
72
if (!parseProperty(it)) {
79
queryItem.query = nullptr;
80
queryItem.parsingResult = QueryItem::PARSING_OK;
81
queries.push_back(queryItem);
88
bool QueryXMLParser::parseProperty(rapidxml::xml_node<>* element) {
89
if (strcmp(element->name(), "property") != 0) {
90
fprintf(stderr, "ERROR missing property\n");
91
return false; // unexpected element (only property is allowed)
95
rapidxml::xml_node<>* formulaPtr = nullptr;
96
for (auto it = element->first_node(); it; it = it->next_sibling()) {
97
if (strcmp(it->name(), "id") == 0) {
99
} else if (strcmp(it->name(), "formula") == 0) {
101
} else if (strcmp(it->name(), "tags") == 0) {
102
tagsOK = parseTags(it);
107
fprintf(stderr, "ERROR a query with empty id\n");
115
queryItem.query = parseFormula(formulaPtr);
116
assert(queryItem.query);
117
queryItem.parsingResult = QueryItem::PARSING_OK;
119
queryItem.query = nullptr;
120
queryItem.parsingResult = QueryItem::UNSUPPORTED_QUERY;
122
queries.push_back(queryItem);
126
bool QueryXMLParser::parseTags(rapidxml::xml_node<>* element) {
127
// we can accept only reachability query
128
for (auto it = element->first_node(); it; it = it->next_sibling()) {
129
if (strcmp(it->name(), "is-reachability") == 0 && strcmp(it->value(), "true") == 0) {
136
void QueryXMLParser::fatal_error(const std::string &token) {
137
std::cerr << "An error occurred while parsing the query." << std::endl;
138
std::cerr << token << std::endl;
143
Condition_ptr QueryXMLParser::parseFormula(rapidxml::xml_node<>* element) {
144
if (getChildCount(element) != 1)
149
auto child = element->first_node();
150
string childName = child->name();
151
Condition_ptr cond = nullptr;
153
// Formula is either CTL/Reachability, UpperBounds or one of the global properties
154
// - k-safe (contains integer bound) : for all p. it holds that AG p <= bound
155
// - quasi-liveness : for all t. EF is-fireable(t)
156
// - stable-marking : exists p. and x. s.t. AG p == x (i.e. the initial marking)
157
// - liveness : for all t. AG EF is-fireable(t)
158
// we unfold global properties here to avoid introducing special-cases in the AST.
159
if (childName == "k-safe")
161
Expr_ptr bound = nullptr;
162
for (auto it = child->first_node(); it ; it = it->next_sibling()) {
163
if(bound != nullptr) fatal_error(childName);
164
bound = parseIntegerExpression(child->first_node());
165
if(bound == nullptr) fatal_error(childName);
167
if(bound == nullptr) fatal_error(childName);
168
return std::make_shared<KSafeCondition>(bound);
170
else if(childName == "quasi-liveness")
172
return std::make_shared<QuasiLivenessCondition>();
174
else if(childName == "stable-marking")
176
return std::make_shared<StableMarkingCondition>();
178
else if(childName == "liveness")
180
return std::make_shared<LivenessCondition>();
182
else if (childName == "place-bound") {
183
std::vector<std::string> places;
184
for (auto it = child->first_node(); it ; it = it->next_sibling()) {
185
if (strcmp(it->name(), "place") != 0)
190
auto place = parsePlace(it);
194
return nullptr; // invalid place name
196
places.push_back(place);
198
auto bnds = std::make_shared<UpperBoundsCondition>(places);
199
return std::make_shared<EFCondition>(bnds);
200
} else if ((cond = parseBooleanFormula(child)) != nullptr) {
209
Condition_ptr QueryXMLParser::parseBooleanFormula(rapidxml::xml_node<>* element) {
211
Describe here how to parse
212
* INV phi = AG phi = not EF not phi
213
* IMPOS phi = AG not phi = not EF phi
215
* NEG INV phi = not AG phi = EF not phi
216
* NEG IMPOS phi = not AG not phi = EF phi
217
* NEG POS phi = not EF phi
220
string elementName = element->name();
221
Condition_ptr cond = nullptr, cond2 = nullptr;
223
if (elementName == "invariant") {
224
if ((cond = parseBooleanFormula(element->first_node())) != nullptr)
225
return std::make_shared<NotCondition>(std::make_shared<EFCondition>(std::make_shared<NotCondition>(cond)));
226
} else if (elementName == "impossibility") {
227
if ((cond = parseBooleanFormula(element->first_node())) != nullptr)
228
return std::make_shared<NotCondition>(std::make_shared<EFCondition>(cond));
229
} else if (elementName == "possibility") {
230
if ((cond = parseBooleanFormula(element->first_node())) != nullptr)
231
return std::make_shared<EFCondition>(cond);
232
} else if (elementName == "exists-path") {
233
if (getChildCount(element) != 1)
238
auto child = element->first_node();
239
if (strcmp(child->name(), "next") == 0) {
240
if (getChildCount(child) != 1)
245
if ((cond = parseBooleanFormula(child->first_node())) != nullptr)
246
return std::make_shared<EXCondition>(cond);
247
} else if (strcmp(child->name(), "globally") == 0) {
248
if (getChildCount(child) != 1)
253
if ((cond = parseBooleanFormula(child->first_node())) != nullptr)
254
return std::make_shared<EGCondition>(cond);
255
} else if (strcmp(child->name(), "finally") == 0) {
256
if (getChildCount(child) != 1)
261
if ((cond = parseBooleanFormula(child->first_node())) != nullptr)
262
return std::make_shared<EFCondition>(cond);
263
} else if (strcmp(child->name(), "until") == 0) {
264
if (getChildCount(child) != 2)
269
auto before = child->first_node();
270
auto reach = before->next_sibling();
271
if (getChildCount(before) != 1 || getChildCount(reach) != 1 ||
272
strcmp(before->name(), "before") != 0 || strcmp(reach->name(), "reach") != 0)
277
if ((cond = parseBooleanFormula(before->first_node())) != nullptr) {
278
if ((cond2 = parseBooleanFormula(reach->first_node())) != nullptr) {
279
return std::make_shared<EUCondition>(cond, cond2);
283
} else if (elementName == "all-paths") {
284
if (getChildCount(element) != 1)
289
auto child = element->first_node();
290
if (strcmp(child->name(), "next") == 0) {
291
if (getChildCount(child) != 1)
296
if ((cond = parseBooleanFormula(child->first_node())) != nullptr)
297
return std::make_shared<AXCondition>(cond);
298
} else if (strcmp(child->name(), "globally") == 0) {
299
if (getChildCount(child) != 1)
304
if ((cond = parseBooleanFormula(child->first_node())) != nullptr)
305
return std::make_shared<AGCondition>(cond);
306
} else if (strcmp(child->name(), "finally") == 0) {
307
if (getChildCount(child) != 1)
312
if ((cond = parseBooleanFormula(child->first_node())) != nullptr)
313
return std::make_shared<AFCondition>(cond);
314
} else if (strcmp(child->name(), "until") == 0) {
315
if (getChildCount(child) != 2)
320
auto before = child->first_node();
321
auto reach = before->next_sibling();
322
if (getChildCount(before) != 1 || getChildCount(reach) != 1 ||
323
strcmp(before->name(), "before") != 0 || strcmp(reach->name(), "reach") != 0)
328
if ((cond = parseBooleanFormula(before->first_node())) != nullptr){
329
if ((cond2 = parseBooleanFormula(reach->first_node())) != nullptr) {
330
return std::make_shared<AUCondition>(cond, cond2);
334
} else if (elementName == "deadlock") {
335
return std::make_shared<DeadlockCondition>();
336
} else if (elementName == "true") {
337
return BooleanCondition::TRUE_CONSTANT;
338
} else if (elementName == "false") {
339
return BooleanCondition::FALSE_CONSTANT;
340
} else if (elementName == "negation") {
341
if (getChildCount(element) != 1)
346
auto child = element->first_node();
347
if (strcmp(child->name(), "invariant") == 0) {
348
if ((cond = parseBooleanFormula(child->first_node())) != nullptr) {
349
return std::make_shared<EFCondition>(std::make_shared<NotCondition>(cond));
351
} else if (strcmp(child->name(), "impossibility") == 0) {
352
if ((cond = parseBooleanFormula(child->first_node())) != nullptr) {
353
return std::make_shared<EFCondition>(cond);
355
} else if (strcmp(child->name(), "possibility") == 0) {
356
if ((cond = parseBooleanFormula(child->first_node())) != nullptr) {
357
return std::make_shared<NotCondition>(std::make_shared<EFCondition>(cond));
360
if ((cond = parseBooleanFormula(child)) != nullptr) {
361
return std::make_shared<NotCondition>(cond);
364
} else if (elementName == "conjunction") {
365
auto children = element->first_node();
366
if (getChildCount(element) < 2)
372
cond = parseBooleanFormula(it);
374
for (it = it->next_sibling(); it; it = it->next_sibling()) {
375
Condition_ptr child = parseBooleanFormula(it);
376
if(child == nullptr || cond == nullptr)
381
cond = std::make_shared<AndCondition>(cond, child);
384
} else if (elementName == "disjunction") {
385
auto children = element->first_node();
386
if (getChildCount(element) < 2)
392
cond = parseBooleanFormula(it);
394
for (it = it->next_sibling(); it; it = it->next_sibling()) {
395
Condition_ptr child = parseBooleanFormula(it);
396
if(child == nullptr || cond == nullptr)
401
cond = std::make_shared<OrCondition>(cond, child);
404
} else if (elementName == "exclusive-disjunction") {
405
auto children = element->first_node();
406
if (getChildCount(element) != 2)
411
cond = parseBooleanFormula(children);
412
cond2 = parseBooleanFormula(children->next_sibling());
413
if (cond == nullptr || cond2 == nullptr)
418
return std::make_shared<OrCondition>(
419
std::make_shared<AndCondition>(cond, std::make_shared<NotCondition>(cond2)),
420
std::make_shared<AndCondition>(std::make_shared<NotCondition>(cond), cond2));
421
} else if (elementName == "implication") {
422
auto children = element->first_node();
423
if (getChildCount(element) != 2)
428
cond = parseBooleanFormula(children);
429
cond2 = parseBooleanFormula(children->next_sibling());
430
if (cond == nullptr || cond2 == nullptr)
435
return std::make_shared<OrCondition>(std::make_shared<NotCondition>(cond), cond2);
436
} else if (elementName == "equivalence") {
437
auto children = element->first_node();
438
if (getChildCount(element) != 2)
443
cond = parseBooleanFormula(children);
444
cond2 = parseBooleanFormula(children->next_sibling());
445
if (cond == nullptr || cond2 == nullptr) return nullptr;
446
return std::make_shared<OrCondition>(std::make_shared<AndCondition>(cond, cond2),
447
std::make_shared<AndCondition>(std::make_shared<NotCondition>(cond),
448
std::make_shared<NotCondition>(cond2)));
449
} else if (elementName == "integer-eq" || elementName == "integer-ne" ||
450
elementName == "integer-lt" || elementName == "integer-le" ||
451
elementName == "integer-gt" || elementName == "integer-ge") {
452
auto children = element->first_node();
453
if (getChildCount(element) != 2)
458
Expr_ptr expr1 = parseIntegerExpression(children);
459
Expr_ptr expr2 = parseIntegerExpression(children->next_sibling());
460
if(expr1 == nullptr || expr2 == nullptr)
465
if (elementName == "integer-eq") return std::make_shared<EqualCondition>(expr1, expr2);
466
else if (elementName == "integer-ne") return std::make_shared<NotEqualCondition>(expr1, expr2);
467
else if (elementName == "integer-lt") return std::make_shared<LessThanCondition>(expr1, expr2);
468
else if (elementName == "integer-le") return std::make_shared<LessThanOrEqualCondition>(expr1, expr2);
469
else if (elementName == "integer-gt") return std::make_shared<GreaterThanCondition>(expr1, expr2);
470
else if (elementName == "integer-ge") return std::make_shared<GreaterThanOrEqualCondition>(expr1, expr2);
471
} else if (elementName == "is-fireable") {
472
size_t nrOfChildren = getChildCount(element);
473
if (nrOfChildren == 0)
478
std::vector<Condition_ptr> conds;
479
for (auto it = element->first_node(); it; it = it->next_sibling()) {
480
if (strcmp(it->name(), "transition") != 0)
485
conds.emplace_back(std::make_shared<FireableCondition>(it->value()));
487
return std::make_shared<OrCondition>(conds);
489
fatal_error(elementName);
493
Expr_ptr QueryXMLParser::parseIntegerExpression(rapidxml::xml_node<>* element) {
494
string elementName = element->name();
495
if (elementName == "integer-constant") {
497
if (sscanf(element->value(), "%d", &i) == EOF)
502
return std::make_shared<LiteralExpr>(i);
503
} else if (elementName == "tokens-count") {
504
auto children = element->first_node();
505
std::vector<Expr_ptr> ids;
506
for (auto it = children; it; it = it->next_sibling()) {
507
if (strcmp(it->name(), "place") != 0)
512
string placeName = parsePlace(it);
513
if (placeName.empty())
516
return nullptr; // invalid place name
518
auto id = std::make_shared<IdentifierExpr>(placeName);
519
ids.emplace_back(id);
527
if (ids.size() == 1) return ids[0];
529
return std::make_shared<PlusExpr>(std::move(ids), true);
530
} else if (elementName == "integer-sum" || elementName == "integer-product") {
531
auto children = element->first_node();
533
if (elementName == "integer-sum") isMult = false;
534
else if (elementName == "integer-product") isMult = true;
536
std::vector<Expr_ptr> els;
539
for (; it; it = it->next_sibling()) {
540
els.emplace_back(parseIntegerExpression(it));
555
std::dynamic_pointer_cast<Expr>(std::make_shared<MultiplyExpr>(std::move(els))) :
556
std::dynamic_pointer_cast<Expr>(std::make_shared<PlusExpr>(std::move(els)));
558
} else if (elementName == "integer-difference") {
559
auto children = element->first_node();
560
std::vector<Expr_ptr> els;
561
for (auto it = children; it; it = it->next_sibling()) {
562
els.emplace_back(parseIntegerExpression(it));
565
els.emplace(els.begin(), std::make_shared<LiteralExpr>(0));
566
return std::make_shared<SubtractExpr>(std::move(els));
572
string QueryXMLParser::parsePlace(rapidxml::xml_node<>* element) {
573
if (strcmp(element->name(), "place") != 0) return ""; // missing place tag
574
string placeName = element->value();
575
placeName.erase(std::remove_if(placeName.begin(), placeName.end(), ::isspace), placeName.end());
579
void QueryXMLParser::printQueries(size_t i) {
580
// QueryXMLParser::QueriesIterator it;
581
if (i <= 0 || i > queries.size()) {
582
cout << "In printQueries the query index is out of scope\n\n";
585
QueryItem it = queries[i - 1];
586
cout << it.id << ": " ;
587
if (it.parsingResult == QueryItem::UNSUPPORTED_QUERY) {
588
cout << "\t---------- unsupported query ----------" << endl;
591
it.query->toString(cout);
596
void QueryXMLParser::printQueries() {
597
for (size_t i = 1; i <= queries.size(); i++) {