souffle  2.0.2-371-g6315b36
Utils.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file Utils.cpp
12  *
13  * A collection of utilities operating on AST constructs.
14  *
15  ***********************************************************************/
16 
17 #include "ast/utility/Utils.h"
18 #include "ast/Aggregator.h"
19 #include "ast/Argument.h"
20 #include "ast/Atom.h"
21 #include "ast/BinaryConstraint.h"
22 #include "ast/BooleanConstraint.h"
23 #include "ast/Clause.h"
24 #include "ast/Constraint.h"
25 #include "ast/Directive.h"
26 #include "ast/ExecutionPlan.h"
27 #include "ast/IntrinsicFunctor.h"
28 #include "ast/Literal.h"
29 #include "ast/Negation.h"
30 #include "ast/Node.h"
31 #include "ast/Program.h"
32 #include "ast/QualifiedName.h"
33 #include "ast/Relation.h"
34 #include "ast/TranslationUnit.h"
35 #include "ast/analysis/Functor.h"
37 #include "ast/analysis/Type.h"
39 #include "ast/utility/NodeMapper.h"
40 #include "ast/utility/Visitor.h"
42 #include "souffle/TypeAttribute.h"
47 #include <algorithm>
48 #include <cassert>
49 #include <memory>
50 
51 namespace souffle::ast {
52 
53 std::string pprint(const Node& node) {
54  return toString(node);
55 }
56 
57 std::vector<const Variable*> getVariables(const Node& root) {
58  // simply collect the list of all variables by visiting all variables
59  std::vector<const Variable*> vars;
60  visitDepthFirst(root, [&](const Variable& var) { vars.push_back(&var); });
61  return vars;
62 }
63 
64 std::vector<const RecordInit*> getRecords(const Node& root) {
65  // simply collect the list of all records by visiting all records
66  std::vector<const RecordInit*> recs;
67  visitDepthFirst(root, [&](const RecordInit& rec) { recs.push_back(&rec); });
68  return recs;
69 }
70 
71 std::vector<Clause*> getClauses(const Program& program, const QualifiedName& relationName) {
72  std::vector<Clause*> clauses;
73  for (Clause* clause : program.getClauses()) {
74  if (clause->getHead()->getQualifiedName() == relationName) {
75  clauses.push_back(clause);
76  }
77  }
78  return clauses;
79 }
80 
81 std::vector<Clause*> getClauses(const Program& program, const Relation& rel) {
82  return getClauses(program, rel.getQualifiedName());
83 }
84 
85 std::vector<Directive*> getDirectives(const Program& program, const QualifiedName& relationName) {
86  std::vector<Directive*> directives;
87  for (Directive* dir : program.getDirectives()) {
88  if (dir->getQualifiedName() == relationName) {
89  directives.push_back(dir);
90  }
91  }
92  return directives;
93 }
94 
95 Relation* getRelation(const Program& program, const QualifiedName& name) {
96  return getIf(program.getRelations(), [&](const Relation* r) { return r->getQualifiedName() == name; });
97 }
98 
99 void removeRelation(TranslationUnit& tu, const QualifiedName& name) {
100  Program& program = tu.getProgram();
101  if (getRelation(program, name) != nullptr) {
102  removeRelationClauses(tu, name);
103  removeRelationIOs(tu, name);
104  program.removeRelationDecl(name);
105  }
106 }
107 
108 void removeRelationClauses(TranslationUnit& tu, const QualifiedName& name) {
109  Program& program = tu.getProgram();
110  const auto& relDetail = *tu.getAnalysis<analysis::RelationDetailCacheAnalysis>();
111  for (const auto* clause : relDetail.getClauses(name)) {
112  program.removeClause(clause);
113  }
114 }
115 
116 void removeRelationIOs(TranslationUnit& tu, const QualifiedName& name) {
117  Program& program = tu.getProgram();
118  for (const auto* directive : getDirectives(program, name)) {
119  program.removeDirective(directive);
120  }
121 }
122 
123 const Relation* getAtomRelation(const Atom* atom, const Program* program) {
124  return getRelation(*program, atom->getQualifiedName());
125 }
126 
127 const Relation* getHeadRelation(const Clause* clause, const Program* program) {
128  return getAtomRelation(clause->getHead(), program);
129 }
130 
131 std::set<const Relation*> getBodyRelations(const Clause* clause, const Program* program) {
132  std::set<const Relation*> bodyRelations;
133  for (const auto& lit : clause->getBodyLiterals()) {
135  *lit, [&](const Atom& atom) { bodyRelations.insert(getAtomRelation(&atom, program)); });
136  }
137  for (const auto& arg : clause->getHead()->getArguments()) {
139  *arg, [&](const Atom& atom) { bodyRelations.insert(getAtomRelation(&atom, program)); });
140  }
141  return bodyRelations;
142 }
143 
144 size_t getClauseNum(const Program* program, const Clause* clause) {
145  // TODO (azreika): This number might change between the provenance transformer and the AST->RAM
146  // translation. Might need a better way to assign IDs to clauses... (see PR #1288).
147  const Relation* rel = getRelation(*program, clause->getHead()->getQualifiedName());
148  assert(rel != nullptr && "clause relation does not exist");
149 
150  size_t clauseNum = 1;
151  for (const auto* cur : getClauses(*program, *rel)) {
152  bool isFact = cur->getBodyLiterals().empty();
153  if (cur == clause) {
154  return isFact ? 0 : clauseNum;
155  }
156 
157  if (!isFact) {
158  clauseNum++;
159  }
160  }
161 
162  fatal("clause does not exist");
163 }
164 
165 bool hasClauseWithNegatedRelation(const Relation* relation, const Relation* negRelation,
166  const Program* program, const Literal*& foundLiteral) {
167  for (const Clause* cl : getClauses(*program, *relation)) {
168  for (const auto* neg : getBodyLiterals<Negation>(*cl)) {
169  if (negRelation == getAtomRelation(neg->getAtom(), program)) {
170  foundLiteral = neg;
171  return true;
172  }
173  }
174  }
175  return false;
176 }
177 
178 bool hasClauseWithAggregatedRelation(const Relation* relation, const Relation* aggRelation,
179  const Program* program, const Literal*& foundLiteral) {
180  for (const Clause* cl : getClauses(*program, *relation)) {
181  bool hasAgg = false;
182  visitDepthFirst(*cl, [&](const Aggregator& cur) {
183  visitDepthFirst(cur, [&](const Atom& atom) {
184  if (aggRelation == getAtomRelation(&atom, program)) {
185  foundLiteral = &atom;
186  hasAgg = true;
187  }
188  });
189  });
190  if (hasAgg) {
191  return true;
192  }
193  }
194  return false;
195 }
196 
197 bool isRecursiveClause(const Clause& clause) {
198  QualifiedName relationName = clause.getHead()->getQualifiedName();
199  bool recursive = false;
200  visitDepthFirst(clause.getBodyLiterals(), [&](const Atom& atom) {
201  if (atom.getQualifiedName() == relationName) {
202  recursive = true;
203  }
204  });
205  return recursive;
206 }
207 
208 bool isFact(const Clause& clause) {
209  // there must be a head
210  if (clause.getHead() == nullptr) {
211  return false;
212  }
213  // there must not be any body clauses
214  if (!clause.getBodyLiterals().empty()) {
215  return false;
216  }
217 
218  // and there are no aggregates
219  bool hasAggregatesOrMultiResultFunctor = false;
220  visitDepthFirst(*clause.getHead(), [&](const Argument& arg) {
221  if (isA<Aggregator>(arg)) {
222  hasAggregatesOrMultiResultFunctor = true;
223  }
224 
225  auto* func = as<IntrinsicFunctor>(arg);
226  hasAggregatesOrMultiResultFunctor |=
227  (func != nullptr) && analysis::FunctorAnalysis::isMultiResult(*func);
228  });
229  return !hasAggregatesOrMultiResultFunctor;
230 }
231 
232 bool isRule(const Clause& clause) {
233  return (clause.getHead() != nullptr) && !isFact(clause);
234 }
235 
236 bool isProposition(const Atom* atom) {
237  return atom->getArguments().empty();
238 }
239 
240 bool isDeltaRelation(const QualifiedName& name) {
241  const auto& qualifiers = name.getQualifiers();
242  if (qualifiers.empty()) {
243  return false;
244  }
245  return isPrefix("@delta_", qualifiers[0]);
246 }
247 
248 Clause* cloneHead(const Clause* clause) {
249  auto* clone = new Clause();
250  clone->setSrcLoc(clause->getSrcLoc());
251  clone->setHead(souffle::clone(clause->getHead()));
252  if (clause->getExecutionPlan() != nullptr) {
253  clone->setExecutionPlan(souffle::clone(clause->getExecutionPlan()));
254  }
255  return clone;
256 }
257 
258 Clause* reorderAtoms(const Clause* clause, const std::vector<unsigned int>& newOrder) {
259  // Find all atom positions
260  std::vector<unsigned int> atomPositions;
261  std::vector<Literal*> bodyLiterals = clause->getBodyLiterals();
262  for (unsigned int i = 0; i < bodyLiterals.size(); i++) {
263  if (isA<Atom>(bodyLiterals[i])) {
264  atomPositions.push_back(i);
265  }
266  }
267 
268  // Validate given order
269  assert(newOrder.size() == atomPositions.size());
270  std::vector<unsigned int> nopOrder;
271  for (unsigned int i = 0; i < atomPositions.size(); i++) {
272  nopOrder.push_back(i);
273  }
274  assert(std::is_permutation(nopOrder.begin(), nopOrder.end(), newOrder.begin()));
275 
276  // Create a new clause with the given atom order, leaving the rest unchanged
277  Clause* newClause = cloneHead(clause);
278  unsigned int currentAtom = 0;
279  for (unsigned int currentLiteral = 0; currentLiteral < bodyLiterals.size(); currentLiteral++) {
280  Literal* literalToAdd = bodyLiterals[currentLiteral];
281  if (isA<Atom>(literalToAdd)) {
282  // Atoms should be reordered
283  literalToAdd = bodyLiterals[atomPositions[newOrder[currentAtom++]]];
284  }
285  newClause->addToBody(souffle::clone(literalToAdd));
286  }
287 
288  return newClause;
289 }
290 
291 void negateConstraintInPlace(Constraint& constraint) {
292  if (auto* bcstr = dynamic_cast<BooleanConstraint*>(&constraint)) {
293  bcstr->set(!bcstr->isTrue());
294  } else if (auto* cstr = dynamic_cast<BinaryConstraint*>(&constraint)) {
295  cstr->setBaseOperator(souffle::negatedConstraintOp(cstr->getBaseOperator()));
296  } else {
297  fatal("Unknown ast-constraint type");
298  }
299 }
300 
301 bool renameAtoms(Node& node, const std::map<QualifiedName, QualifiedName>& oldToNew) {
302  struct rename_atoms : public NodeMapper {
303  mutable bool changed{false};
304  const std::map<QualifiedName, QualifiedName>& oldToNew;
305  rename_atoms(const std::map<QualifiedName, QualifiedName>& oldToNew) : oldToNew(oldToNew) {}
306  Own<Node> operator()(Own<Node> node) const override {
307  node->apply(*this);
308  if (auto* atom = dynamic_cast<Atom*>(node.get())) {
309  if (contains(oldToNew, atom->getQualifiedName())) {
310  auto renamedAtom = souffle::clone(atom);
311  renamedAtom->setQualifiedName(oldToNew.at(atom->getQualifiedName()));
312  changed = true;
313  return renamedAtom;
314  }
315  }
316  return node;
317  }
318  };
319  rename_atoms update(oldToNew);
320  node.apply(update);
321  return update.changed;
322 }
323 
324 } // namespace souffle::ast
souffle::ast::cloneHead
Clause * cloneHead(const Clause *clause)
Returns a clause which contains head of the given clause.
Definition: Utils.cpp:254
BinaryConstraintOps.h
TranslationUnit.h
souffle::isPrefix
bool isPrefix(const std::string &prefix, const std::string &element)
Determine if one string is a prefix of another.
Definition: StringUtil.h:292
souffle::ast::removeRelation
void removeRelation(TranslationUnit &tu, const QualifiedName &name)
Remove relation and all its clauses from the program.
Definition: Utils.cpp:105
souffle::ast::removeRelationClauses
void removeRelationClauses(TranslationUnit &tu, const QualifiedName &name)
Removes the set of clauses with the given relation name.
Definition: Utils.cpp:114
ExecutionPlan.h
souffle::ast::reorderAtoms
Clause * reorderAtoms(const Clause *clause, const std::vector< unsigned int > &newOrder)
Reorders the atoms of a clause to be in the given order.
Definition: Utils.cpp:264
souffle::ast::pprint
std::string pprint(const Node &node)
Definition: Utils.cpp:59
Directive.h
souffle::ast::renameAtoms
bool renameAtoms(Node &node, const std::map< QualifiedName, QualifiedName > &oldToNew)
Rename all atoms hat appear in a node to a given name.
Definition: Utils.cpp:307
RelationDetailCache.h
souffle::contains
bool contains(const C &container, const typename C::value_type &element)
A utility to check generically whether a given element is contained in a given container.
Definition: ContainerUtil.h:75
souffle::ast::getClauseNum
size_t getClauseNum(const Program *program, const Clause *clause)
Returns the index of a clause within its relation, ignoring facts.
Definition: Utils.cpp:150
relation
Relation & relation
Definition: Reader.h:130
MiscUtil.h
souffle::ast::isDeltaRelation
bool isDeltaRelation(const QualifiedName &name)
Returns whether the given atom is a delta relation.
Definition: Utils.cpp:246
souffle::ast::Relation
Defines a relation with a name, attributes, qualifiers, and internal representation.
Definition: Relation.h:49
souffle::ast::Clause
Intermediate representation of a horn clause.
Definition: Clause.h:51
souffle::ast::isFact
bool isFact(const Clause &clause)
Returns whether the given clause is a fact.
Definition: Utils.cpp:214
souffle::ast::Program::getClauses
std::vector< Clause * > getClauses() const
Return clauses.
Definition: Program.h:65
Constraint.h
BooleanConstraint.h
souffle::ast::analysis::Variable
A variable to be utilized within constraints to be handled by the constraint solver.
Definition: ConstraintSystem.h:41
Relation.h
IntrinsicFunctor.h
souffle::ast::Atom
An atom class.
Definition: Atom.h:51
Utils.h
NodeMapper.h
souffle::ast::isRecursiveClause
bool isRecursiveClause(const Clause &clause)
Returns whether the given clause is recursive.
Definition: Utils.cpp:203
souffle::ast::hasClauseWithNegatedRelation
bool hasClauseWithNegatedRelation(const Relation *relation, const Relation *negRelation, const Program *program, const Literal *&foundLiteral)
Returns whether the given relation has any clauses which contain a negation of a specific relation.
Definition: Utils.cpp:171
souffle::toString
const std::string & toString(const std::string &str)
A generic function converting strings into strings (trivial case).
Definition: StringUtil.h:234
Argument.h
souffle::ast::Program
The program class consists of relations, clauses and types.
Definition: Program.h:52
souffle::Relation
Object-oriented wrapper class for Souffle's templatized relations.
Definition: SouffleInterface.h:49
souffle::ast::hasClauseWithAggregatedRelation
bool hasClauseWithAggregatedRelation(const Relation *relation, const Relation *aggRelation, const Program *program, const Literal *&foundLiteral)
Returns whether the given relation has any clauses which contain an aggregation over of a specific re...
Definition: Utils.cpp:184
souffle::ast::Atom::getQualifiedName
const QualifiedName & getQualifiedName() const
Return qualified name.
Definition: Atom.h:57
souffle::ast::Directive
a directive has a type (e.g. input/output/printsize/limitsize), qualified relation name,...
Definition: Directive.h:56
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
i
size_t i
Definition: json11.h:663
ContainerUtil.h
souffle::negatedConstraintOp
BinaryConstraintOp negatedConstraintOp(const BinaryConstraintOp op)
Negated Constraint Operator Each operator requires a negated operator which is necessary for the expa...
Definition: BinaryConstraintOps.h:297
souffle::ast::getRelation
Relation * getRelation(const Program &program, const QualifiedName &name)
Returns the relation with the given name in the program.
Definition: Utils.cpp:101
clauses
std::vector< Own< Clause > > clauses
Definition: ComponentInstantiation.cpp:67
StringUtil.h
Atom.h
Literal.h
souffle::ast::getHeadRelation
const Relation * getHeadRelation(const Clause *clause, const Program *program)
Returns the relation referenced by the head of the given clause.
Definition: Utils.cpp:133
Negation.h
souffle::ast::getClauses
std::vector< Clause * > getClauses(const Program &program, const QualifiedName &relationName)
Returns a vector of clauses in the program describing the relation with the given name.
Definition: Utils.cpp:77
souffle::ast::isProposition
bool isProposition(const Atom *atom)
Returns whether the given atom is a propositon.
Definition: Utils.cpp:242
Node.h
Aggregator.h
directives
std::vector< Own< Directive > > directives
Definition: ComponentInstantiation.cpp:66
souffle::getIf
C::value_type getIf(const C &container, std::function< bool(const typename C::value_type)> pred)
Returns the first element in a container that satisfies a given predicate, nullptr otherwise.
Definition: ContainerUtil.h:101
souffle::ast::Node
Abstract class for syntactic elements in an input program.
Definition: Node.h:40
Functor.h
QualifiedName.h
Program.h
souffle::ast::getRecords
std::vector< const RecordInit * > getRecords(const Node &root)
Obtains a list of all records referenced within the AST rooted by the given root node.
Definition: Utils.cpp:70
souffle::fatal
void fatal(const char *format, const Args &... args)
Definition: MiscUtil.h:198
souffle::ast::negateConstraintInPlace
void negateConstraintInPlace(Constraint &constraint)
Negate an ast constraint.
Definition: Utils.cpp:297
Visitor.h
Clause.h
Type.h
souffle::ast::getVariables
std::vector< const Variable * > getVariables(const Node &root)
Obtains a list of all variables referenced within the AST rooted by the given root node.
Definition: Utils.cpp:63
souffle::ast::getAtomRelation
const Relation * getAtomRelation(const Atom *atom, const Program *program)
Returns the relation referenced by the given atom.
Definition: Utils.cpp:129
BinaryConstraint.h
souffle::ast::visitDepthFirst
void visitDepthFirst(const Node &root, Visitor< R, Ps... > &visitor, Args &... args)
A utility function visiting all nodes within the ast rooted by the given node recursively in a depth-...
Definition: Visitor.h:273
FunctionalUtil.h
souffle::ast
Definition: Aggregator.h:35
souffle::ast::removeRelationIOs
void removeRelationIOs(TranslationUnit &tu, const QualifiedName &name)
Removes the set of IOs with the given relation name.
Definition: Utils.cpp:122
souffle::ast::QualifiedName
Qualified Name class defines fully/partially qualified names to identify objects in components.
Definition: QualifiedName.h:39
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::ast::getBodyRelations
std::set< const Relation * > getBodyRelations(const Clause *clause, const Program *program)
Returns the relations referenced in the body of the given clause.
Definition: Utils.cpp:137
souffle::ast::getDirectives
std::vector< Directive * > getDirectives(const Program &program, const QualifiedName &relationName)
Definition: Utils.cpp:91
souffle::ast::RecordInit
Defines a record initialization class.
Definition: RecordInit.h:42
souffle::ast::isRule
bool isRule(const Clause &clause)
Returns whether the given clause is a rule.
Definition: Utils.cpp:238
TypeAttribute.h
souffle::ast::Clause::getBodyLiterals
std::vector< Literal * > getBodyLiterals() const
Obtains a copy of the internally maintained body literals.
Definition: Clause.h:79
TypeSystem.h