souffle  2.0.2-371-g6315b36
ClauseNormalisation.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2020, The Souffle Developers. 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 ClauseNormalisation.cpp
12  *
13  * Defines functionality for classes related to clause normalisation
14  *
15  ***********************************************************************/
16 
18 #include "AggregateOp.h"
19 #include "ast/Aggregator.h"
20 #include "ast/Atom.h"
21 #include "ast/BinaryConstraint.h"
22 #include "ast/Clause.h"
23 #include "ast/Literal.h"
24 #include "ast/Negation.h"
25 #include "ast/NilConstant.h"
26 #include "ast/Node.h"
27 #include "ast/NumericConstant.h"
28 #include "ast/Program.h"
29 #include "ast/QualifiedName.h"
30 #include "ast/StringConstant.h"
31 #include "ast/TranslationUnit.h"
32 #include "ast/UnnamedVariable.h"
33 #include "ast/Variable.h"
38 #include <algorithm>
39 #include <cassert>
40 #include <memory>
41 #include <ostream>
42 
43 namespace souffle::ast::analysis {
44 
45 NormalisedClause::NormalisedClause(const Clause* clause) {
46  // head
47  QualifiedName name("@min:head");
48  std::vector<std::string> headVars;
49  for (const auto* arg : clause->getHead()->getArguments()) {
50  headVars.push_back(normaliseArgument(arg));
51  }
52  clauseElements.push_back({.name = name, .params = headVars});
53 
54  // body
55  for (const auto* lit : clause->getBodyLiterals()) {
56  addClauseBodyLiteral("@min:scope:0", lit);
57  }
58 }
59 
61  const std::string& qualifier, const std::string& scopeID, const Atom* atom) {
62  QualifiedName name(atom->getQualifiedName());
63  name.prepend(qualifier);
64 
65  std::vector<std::string> vars;
66  vars.push_back(scopeID);
67  for (const auto* arg : atom->getArguments()) {
68  vars.push_back(normaliseArgument(arg));
69  }
70  clauseElements.push_back({.name = name, .params = vars});
71 }
72 
73 void NormalisedClause::addClauseBodyLiteral(const std::string& scopeID, const Literal* lit) {
74  if (const auto* atom = dynamic_cast<const Atom*>(lit)) {
75  addClauseAtom("@min:atom", scopeID, atom);
76  } else if (const auto* neg = dynamic_cast<const Negation*>(lit)) {
77  addClauseAtom("@min:neg", scopeID, neg->getAtom());
78  } else if (const auto* bc = dynamic_cast<const BinaryConstraint*>(lit)) {
79  QualifiedName name(toBinaryConstraintSymbol(bc->getBaseOperator()));
80  name.prepend("@min:operator");
81  std::vector<std::string> vars;
82  vars.push_back(scopeID);
83  vars.push_back(normaliseArgument(bc->getLHS()));
84  vars.push_back(normaliseArgument(bc->getRHS()));
85  clauseElements.push_back({.name = name, .params = vars});
86  } else {
87  assert(lit != nullptr && "unexpected nullptr lit");
88  fullyNormalised = false;
89  std::stringstream qualifier;
90  qualifier << "@min:unhandled:lit:" << scopeID;
91  QualifiedName name(toString(*lit));
92  name.prepend(qualifier.str());
93  clauseElements.push_back({.name = name, .params = std::vector<std::string>()});
94  }
95 }
96 
97 std::string NormalisedClause::normaliseArgument(const Argument* arg) {
98  if (auto* stringCst = dynamic_cast<const StringConstant*>(arg)) {
99  std::stringstream name;
100  name << "@min:cst:str" << *stringCst;
101  constants.insert(name.str());
102  return name.str();
103  } else if (auto* numericCst = dynamic_cast<const NumericConstant*>(arg)) {
104  std::stringstream name;
105  name << "@min:cst:num:" << *numericCst;
106  constants.insert(name.str());
107  return name.str();
108  } else if (isA<NilConstant>(arg)) {
109  constants.insert("@min:cst:nil");
110  return "@min:cst:nil";
111  } else if (auto* var = dynamic_cast<const ast::Variable*>(arg)) {
112  auto name = var->getName();
113  variables.insert(name);
114  return name;
115  } else if (dynamic_cast<const UnnamedVariable*>(arg)) {
116  static size_t countUnnamed = 0;
117  std::stringstream name;
118  name << "@min:unnamed:" << countUnnamed++;
119  variables.insert(name.str());
120  return name.str();
121  } else if (auto* aggr = dynamic_cast<const Aggregator*>(arg)) {
122  // Set the scope to uniquely identify the aggregator
123  std::stringstream scopeID;
124  scopeID << "@min:scope:" << ++aggrScopeCount;
125  variables.insert(scopeID.str());
126 
127  // Set the type signature of this aggregator
128  std::stringstream aggrTypeSignature;
129  aggrTypeSignature << "@min:aggrtype";
130  std::vector<std::string> aggrTypeSignatureComponents;
131 
132  // - the operator is fixed and cannot be changed
133  aggrTypeSignature << ":" << aggr->getBaseOperator();
134 
135  // - the scope can be remapped as a variable
136  aggrTypeSignatureComponents.push_back(scopeID.str());
137 
138  // - the normalised target expression can be remapped as a variable
139  if (aggr->getTargetExpression() != nullptr) {
140  std::string normalisedExpr = normaliseArgument(aggr->getTargetExpression());
141  aggrTypeSignatureComponents.push_back(normalisedExpr);
142  }
143 
144  // Type signature is its own special atom
145  clauseElements.push_back({.name = aggrTypeSignature.str(), .params = aggrTypeSignatureComponents});
146 
147  // Add each contained normalised clause literal, tying it with the new scope ID
148  for (const auto* literal : aggr->getBodyLiterals()) {
149  addClauseBodyLiteral(scopeID.str(), literal);
150  }
151 
152  // Aggregator identified by the scope ID
153  return scopeID.str();
154  } else {
155  fullyNormalised = false;
156  return "@min:unhandled:arg";
157  }
158 }
159 
160 void ClauseNormalisationAnalysis::run(const TranslationUnit& translationUnit) {
161  const auto& program = translationUnit.getProgram();
162  for (const auto* clause : program.getClauses()) {
163  assert(!contains(normalisations, clause) && "clause already processed");
164  normalisations[clause] = NormalisedClause(clause);
165  }
166 }
167 
168 void ClauseNormalisationAnalysis::print(std::ostream& os) const {
169  for (const auto& [clause, norm] : normalisations) {
170  os << "Normalise(" << *clause << ") = {";
171  const auto& els = norm.getElements();
172  for (size_t i = 0; i < els.size(); i++) {
173  if (i != 0) {
174  os << ", ";
175  }
176  os << els[i].name << ":" << els[i].params;
177  }
178  os << "}" << std::endl;
179  }
180 }
181 
182 const NormalisedClause& ClauseNormalisationAnalysis::getNormalisation(const Clause* clause) const {
183  assert(contains(normalisations, clause) && "clause not normalised");
184  return normalisations.at(clause);
185 }
186 
187 } // namespace souffle::ast::analysis
BinaryConstraintOps.h
TranslationUnit.h
souffle::ast::analysis::NormalisedClause::fullyNormalised
bool fullyNormalised
Definition: ClauseNormalisation.h:74
NilConstant.h
UnnamedVariable.h
AggregateOp.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::toBinaryConstraintSymbol
char const * toBinaryConstraintSymbol(const BinaryConstraintOp op)
Converts operator to its symbolic representation.
Definition: BinaryConstraintOps.h:336
souffle::ast::analysis::Variable
A variable to be utilized within constraints to be handled by the constraint solver.
Definition: ConstraintSystem.h:41
souffle::ast::analysis::ClauseNormalisationAnalysis::print
void print(std::ostream &os) const override
print the analysis result in HTML format
Definition: ClauseNormalisation.cpp:174
souffle::ast::analysis::ClauseNormalisationAnalysis::run
void run(const TranslationUnit &translationUnit) override
run analysis for a Ast translation unit
Definition: ClauseNormalisation.cpp:166
souffle::ast::analysis::NormalisedClause::NormalisedClause
NormalisedClause()=default
souffle::toString
const std::string & toString(const std::string &str)
A generic function converting strings into strings (trivial case).
Definition: StringUtil.h:234
StringConstant.h
ClauseNormalisation.h
i
size_t i
Definition: json11.h:663
ContainerUtil.h
souffle::ast::analysis::NormalisedClause::variables
std::set< std::string > variables
Definition: ClauseNormalisation.h:76
StringUtil.h
Atom.h
souffle::ast::analysis::ClauseNormalisationAnalysis::normalisations
std::map< const Clause *, NormalisedClause > normalisations
Definition: ClauseNormalisation.h:109
Literal.h
Negation.h
souffle::ast::analysis::NormalisedClause::clauseElements
std::vector< NormalisedClauseElement > clauseElements
Definition: ClauseNormalisation.h:78
Node.h
souffle::ast::analysis::ClauseNormalisationAnalysis::getNormalisation
const NormalisedClause & getNormalisation(const Clause *clause) const
Definition: ClauseNormalisation.cpp:188
Aggregator.h
QualifiedName.h
souffle::ast::analysis::NormalisedClause::constants
std::set< std::string > constants
Definition: ClauseNormalisation.h:77
Program.h
souffle::ast::analysis::NormalisedClause::aggrScopeCount
size_t aggrScopeCount
Definition: ClauseNormalisation.h:75
StreamUtil.h
Clause.h
souffle::ast::analysis
Definition: Aggregate.cpp:39
BinaryConstraint.h
souffle::ast::NumericConstant
Numeric Constant.
Definition: NumericConstant.h:43
souffle::ast::QualifiedName
Qualified Name class defines fully/partially qualified names to identify objects in components.
Definition: QualifiedName.h:39
NumericConstant.h
souffle::ast::analysis::NormalisedClause::addClauseBodyLiteral
void addClauseBodyLiteral(const std::string &scopeID, const Literal *lit)
Parse a body literal into the element list.
Definition: ClauseNormalisation.cpp:79
souffle::ast::analysis::NormalisedClause::normaliseArgument
std::string normaliseArgument(const Argument *arg)
Return a normalised string repr of an argument.
Definition: ClauseNormalisation.cpp:103
Variable.h
souffle::ast::analysis::NormalisedClause::addClauseAtom
void addClauseAtom(const std::string &qualifier, const std::string &scopeID, const Atom *atom)
Parse an atom with a preset name qualifier into the element list.
Definition: ClauseNormalisation.cpp:66