souffle  2.0.2-371-g6315b36
Explain.h
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2017, 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 Explain.h
12  *
13  * Provenance interface for Souffle; works for compiler and interpreter
14  *
15  ***********************************************************************/
16 
17 #pragma once
18 
22 #include <algorithm>
23 #include <csignal>
24 #include <cstdio>
25 #include <fstream>
26 #include <iostream>
27 #include <map>
28 #include <memory>
29 #include <regex>
30 #include <string>
31 #include <utility>
32 #include <vector>
33 #include <unistd.h>
34 
35 #ifdef USE_NCURSES
36 #include <ncurses.h>
37 #endif
38 
39 #define MAX_TREE_HEIGHT 500
40 #define MAX_TREE_WIDTH 500
41 
42 namespace souffle {
43 class SouffleProgram;
44 
46 public:
47  /* Deleted copy constructor */
48  ExplainConfig(const ExplainConfig&) = delete;
49 
50  /* Deleted assignment operator */
51  ExplainConfig& operator=(const ExplainConfig&) = delete;
52 
53  /* Obtain the global ExplainConfiguration */
55  static ExplainConfig config;
56  return config;
57  }
58 
59  /* Configuration variables */
61  bool json = false;
62  int depthLimit = 4;
63 
64 private:
65  ExplainConfig() = default;
66 };
67 
68 class Explain {
69 public:
71 
73  ~Explain() = default;
74 
75  /* Process a command, a return value of true indicates to continue, returning false indicates to break (if
76  * the command is q/exit) */
77  bool processCommand(std::string& commandLine) {
78  std::vector<std::string> command = split(commandLine, ' ', 1);
79 
80  if (command.empty()) {
81  return true;
82  }
83 
84  if (command[0] == "setdepth") {
85  if (command.size() != 2) {
86  printError("Usage: setdepth <depth>\n");
87  return true;
88  }
89  try {
90  ExplainConfig::getExplainConfig().depthLimit = std::stoi(command[1]);
91  } catch (std::exception& e) {
92  printError("<" + command[1] + "> is not a valid depth\n");
93  return true;
94  }
95  printInfo("Depth is now " + std::to_string(ExplainConfig::getExplainConfig().depthLimit) + "\n");
96  } else if (command[0] == "explain") {
97  std::pair<std::string, std::vector<std::string>> query;
98  if (command.size() != 2) {
99  printError("Usage: explain relation_name(\"<string element1>\", <number element2>, ...)\n");
100  return true;
101  }
102  query = parseTuple(command[1]);
103  printTree(prov.explain(query.first, query.second, ExplainConfig::getExplainConfig().depthLimit));
104  } else if (command[0] == "subproof") {
105  std::pair<std::string, std::vector<std::string>> query;
106  int label = -1;
107  if (command.size() <= 1) {
108  printError("Usage: subproof relation_name(<label>)\n");
109  return true;
110  }
111  query = parseTuple(command[1]);
112  label = std::stoi(query.second[0]);
114  } else if (command[0] == "explainnegation") {
115  std::pair<std::string, std::vector<std::string>> query;
116  if (command.size() != 2) {
117  printError(
118  "Usage: explainnegation relation_name(\"<string element1>\", <number element2>, "
119  "...)\n");
120  return true;
121  }
122  query = parseTuple(command[1]);
123 
124  // a counter for the rule numbers
125  size_t i = 1;
126  std::string rules;
127 
128  // if there are no rules, then this must be an EDB relation
129  if (prov.getRules(query.first).size() == 0) {
130  printInfo("The tuple would be an input fact!\n");
131  return true;
132  }
133 
134  for (auto rule : prov.getRules(query.first)) {
135  rules += std::to_string(i) + ": ";
136  rules += rule;
137  rules += "\n\n";
138  i++;
139  }
140  printInfo(rules);
141 
142  printPrompt("Pick a rule number: ");
143 
144  std::string ruleNum = getInput();
145  auto variables = prov.explainNegationGetVariables(query.first, query.second, std::stoi(ruleNum));
146 
147  // @ and @non_matching are special sentinel values returned by ExplainProvenance
148  if (variables.size() == 1 && variables[0] == "@") {
149  printInfo("The tuple exists, cannot explain negation of it!\n");
150  return true;
151  } else if (variables.size() == 1 && variables[0] == "@non_matching") {
152  printInfo("The variable bindings don't match, cannot explain!\n");
153  return true;
154  } else if (variables.size() == 1 && variables[0] == "@fact") {
155  printInfo("The rule is a fact!\n");
156  return true;
157  }
158 
159  std::map<std::string, std::string> varValues;
160  for (auto var : variables) {
161  printPrompt("Pick a value for " + var + ": ");
162  varValues[var] = getInput();
163  }
164 
165  printTree(prov.explainNegation(query.first, std::stoi(ruleNum), query.second, varValues));
166  } else if (command[0] == "rule" && command.size() == 2) {
167  auto query = split(command[1], ' ');
168  if (query.size() != 2) {
169  printError("Usage: rule <relation name> <rule number>\n");
170  return true;
171  }
172  try {
173  printInfo(prov.getRule(query[0], std::stoi(query[1])) + "\n");
174  } catch (std::exception& e) {
175  printError("Usage: rule <relation name> <rule number>\n");
176  }
177  } else if (command[0] == "measure") {
178  try {
179  printInfo(prov.measureRelation(command[1]));
180  } catch (std::exception& e) {
181  printError("Usage: measure <relation name>\n");
182  }
183  } else if (command[0] == "output") {
184  if (command.size() == 2) {
185  // assign a new filestream, the old one is deleted by unique_ptr
186  ExplainConfig::getExplainConfig().outputStream = mk<std::ofstream>(command[1]);
187  } else if (command.size() == 1) {
189  } else {
190  printError("Usage: output [<filename>]\n");
191  }
192  } else if (command[0] == "format") {
193  if (command.size() == 2 && command[1] == "json") {
195  } else if (command.size() == 2 && command[1] == "proof") {
197  } else {
198  printError("Usage: format <json|proof>\n");
199  }
200  } else if (command[0] == "exit" || command[0] == "q" || command[0] == "quit") {
201  // close file stream so that output is actually written to file
202  printPrompt("Exiting explain\n");
203  return false;
204  } else if (command[0] == "query") {
205  // if there is no given relations, return directly
206  if (command.size() != 2) {
207  printError(
208  "Usage: query <relation1>(<element1>, <element2>, ...), "
209  "<relation2>(<element1>, <element2>, ...), ...\n");
210  return true;
211  }
212  // vector relations stores relation name, args pair parsed by parseQueryTuple()
213  std::vector<std::pair<std::string, std::vector<std::string>>> relations;
214  // regex for relation string
215  std::regex relationRegex(
216  "([a-zA-Z0-9_.-]*)[[:blank:]]*\\(([[:blank:]]*([0-9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*)("
217  "[[:blank:]]*,[[:blank:]]*(["
218  "0-"
219  "9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*))*)?\\)",
220  std::regex_constants::extended);
221  std::smatch relationMatcher;
222  std::string relationStr = command[1];
223  // use relationRegex to match each relation string and call parseQueryTuple() to parse the
224  // relation name and arguments
225  while (std::regex_search(relationStr, relationMatcher, relationRegex)) {
226  relations.push_back(parseQueryTuple(relationMatcher[0]));
227 
228  // check return value for parseQueryTuple, return if relation name is empty string or tuple
229  // arguments is empty
230  if (relations.back().first.size() == 0 || relations.back().second.size() == 0) {
231  printError(
232  "Usage: query <relation1>(<element1>, <element2>, ...), "
233  "<relation2>(<element1>, <element2>, ...), ...\n");
234  return true;
235  }
236  relationStr = relationMatcher.suffix().str();
237  }
238 
239  // is no valid relation can be identified, return directly
240  if (relations.size() == 0) {
241  printError(
242  "Usage: query <relation1>(<element1>, <element2>, ...), "
243  "<relation2>(<element1>, <element2>, ...), ...\n");
244  return true;
245  }
246 
247  // call queryProcess function to process query
249  } else {
250  printError(
251  "\n----------\n"
252  "Commands:\n"
253  "----------\n"
254  "setdepth <depth>: Set a limit for printed derivation tree height\n"
255  "explain <relation>(<element1>, <element2>, ...): Prints derivation tree\n"
256  "explainnegation <relation>(<element1>, <element2>, ...): Enters an interactive\n"
257  " interface where the non-existence of a tuple can be explained\n"
258  "subproof <relation>(<label>): Prints derivation tree for a subproof, label is\n"
259  " generated if a derivation tree exceeds height limit\n"
260  "rule <relation name> <rule number>: Prints a rule\n"
261  "output <filename>: Write output into a file, or provide empty filename to\n"
262  " disable output\n"
263  "format <json|proof>: switch format between json and proof-trees\n"
264  "query <relation1>(<element1>, <element2>, ...), <relation2>(<element1>, <element2>), "
265  "... :\n"
266  "check existence of constant tuples or find solutions for parameterised tuples\n"
267  "for parameterised query, use semicolon to find next solution and dot to break from "
268  "query\n"
269  "exit: Exits this interface\n\n");
270  }
271 
272  return true;
273  }
274 
275  /* The main explain call */
276  virtual void explain() = 0;
277 
278 private:
279  /* Get input */
280  virtual std::string getInput() = 0;
281 
282  /* Print a command prompt, disabled for non-terminal outputs */
283  virtual void printPrompt(const std::string& prompt) = 0;
284 
285  /* Print a tree */
286  virtual void printTree(Own<TreeNode> tree) = 0;
287 
288  /* Print any other information, disabled for non-terminal outputs */
289  virtual void printInfo(const std::string& info) = 0;
290 
291  /* Print an error, such as a wrong command */
292  virtual void printError(const std::string& error) = 0;
293 
294  /**
295  * Parse tuple, split into relation name and values
296  * @param str The string to parse, should be something like "R(x1, x2, x3, ...)"
297  */
298  std::pair<std::string, std::vector<std::string>> parseTuple(const std::string& str) {
299  std::string relName;
300  std::vector<std::string> args;
301 
302  // regex for matching tuples
303  // values matches numbers or strings enclosed in quotation marks
304  std::regex relationRegex(
305  "([a-zA-Z0-9_.-]*)[[:blank:]]*\\(([[:blank:]]*([0-9]+|\"[^\"]*\")([[:blank:]]*,[[:blank:]]*(["
306  "0-"
307  "9]+|\"[^\"]*\"))*)?\\)",
308  std::regex_constants::extended);
309  std::smatch relMatch;
310 
311  // first check that format matches correctly
312  // and extract relation name
313  if (!std::regex_match(str, relMatch, relationRegex) || relMatch.size() < 3) {
314  return std::make_pair(relName, args);
315  }
316 
317  // set relation name
318  relName = relMatch[1];
319 
320  // extract each argument
321  std::string argsList = relMatch[2];
322  std::smatch argsMatcher;
323  std::regex argRegex(R"([0-9]+|"[^"]*")", std::regex_constants::extended);
324 
325  while (std::regex_search(argsList, argsMatcher, argRegex)) {
326  // match the start of the arguments
327  std::string currentArg = argsMatcher[0];
328  args.push_back(currentArg);
329 
330  // use the rest of the arguments
331  argsList = argsMatcher.suffix().str();
332  }
333 
334  return std::make_pair(relName, args);
335  }
336 
337  /**
338  * Parse tuple for query, split into relation name and args, additionally allow varaible as argument in
339  * relation tuple
340  * @param str The string to parse, should be in form "R(x1, x2, x3, ...)"
341  */
342  std::pair<std::string, std::vector<std::string>> parseQueryTuple(const std::string& str) {
343  std::string relName;
344  std::vector<std::string> args;
345  // regex for matching tuples
346  // values matches numbers or strings enclosed in quotation marks
347  std::regex relationRegex(
348  "([a-zA-Z0-9_.-]*)[[:blank:]]*\\(([[:blank:]]*([0-9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*)([[:"
349  "blank:]]*,[[:blank:]]*(["
350  "0-"
351  "9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*))*)?\\)",
352  std::regex_constants::extended);
353  std::smatch relMatch;
354 
355  // if the given string does not match relationRegex, return a pair of empty string and empty vector
356  if (!std::regex_match(str, relMatch, relationRegex) || relMatch.size() < 3) {
357  return std::make_pair(relName, args);
358  }
359 
360  // set relation name
361  relName = relMatch[1];
362 
363  // extract each argument
364  std::string argsList = relMatch[2];
365  std::smatch argsMatcher;
366  std::regex argRegex(R"([0-9]+|"[^"]*"|[a-zA-Z_][a-zA-Z_0-9]*)", std::regex_constants::extended);
367  while (std::regex_search(argsList, argsMatcher, argRegex)) {
368  // match the start of the arguments
369  std::string currentArg = argsMatcher[0];
370  args.push_back(currentArg);
371 
372  // use the rest of the arguments
373  argsList = argsMatcher.suffix().str();
374  }
375 
376  return std::make_pair(relName, args);
377  }
378 };
379 
380 class ExplainConsole : public Explain {
381 public:
382  explicit ExplainConsole(ExplainProvenance& provenance) : Explain(provenance) {}
383 
384  /* The main explain call */
385  void explain() override {
386  printPrompt("Explain is invoked.\n");
387 
388  while (true) {
389  printPrompt("Enter command > ");
390  std::string line = getInput();
391  // a return value of false indicates that an exit/q command has been processed
392  if (!processCommand(line)) {
393  break;
394  }
395  }
396  }
397 
398 private:
399  /* Get input */
400  std::string getInput() override {
401  std::string line;
402 
403  if (!getline(std::cin, line)) {
404  // if EOF has been reached, quit
405  line = "q";
406  }
407 
408  return line;
409  }
410 
411  /* Print a command prompt, disabled for non-terminal outputs */
412  void printPrompt(const std::string& prompt) override {
413  if (isatty(fileno(stdin)) == 0) {
414  return;
415  }
416  std::cout << prompt;
417  }
418 
419  /* Print a tree */
420  void printTree(Own<TreeNode> tree) override {
421  if (!tree) {
422  return;
423  }
424 
425  // handle a file ostream output
426  std::ostream* output;
427  if (ExplainConfig::getExplainConfig().outputStream == nullptr) {
428  output = &std::cout;
429  } else {
431  }
432 
433  if (!ExplainConfig::getExplainConfig().json) {
434  tree->place(0, 0);
435  ScreenBuffer screenBuffer(tree->getWidth(), tree->getHeight());
436  tree->render(screenBuffer);
437  *output << screenBuffer.getString();
438  } else {
439  *output << "{ \"proof\":\n";
440  tree->printJSON(*output, 1);
441  *output << ",";
442  prov.printRulesJSON(*output);
443  *output << "}\n";
444  }
445  }
446 
447  /* Print any other information, disabled for non-terminal outputs */
448  void printInfo(const std::string& info) override {
449  if (isatty(fileno(stdin)) == 0) {
450  return;
451  }
452  std::cout << info;
453  }
454 
455  /* Print an error, such as a wrong command */
456  void printError(const std::string& error) override {
457  std::cout << error;
458  }
459 };
460 
461 #ifdef USE_NCURSES
462 class ExplainNcurses : public Explain {
463 public:
464  explicit ExplainNcurses(ExplainProvenance& provenance) : Explain(provenance) {}
465 
466  /* The main explain call */
467  void explain() override {
468  if (ExplainConfig::getExplainConfig().outputStream == nullptr) {
469  initialiseWindow();
470  std::signal(SIGWINCH, nullptr);
471  }
472 
473  printPrompt("Explain is invoked.\n");
474 
475  while (true) {
476  clearDisplay();
477  printPrompt("Enter command > ");
478  std::string line = getInput();
479 
480  // a return value of false indicates that an exit/q command has been processed
481  if (!processCommand(line)) {
482  break;
483  }
484 
485  // refresh treePad and allow scrolling
486  prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
487  scrollTree(maxx, maxy);
488  }
489 
490  // clean up
491  endwin();
492  }
493 
494 private:
495  WINDOW* treePad = nullptr;
496  WINDOW* queryWindow = nullptr;
497  int maxx, maxy;
498 
499  /* Get input */
500  std::string getInput() override {
501  char buf[100];
502 
503  curs_set(1);
504  echo();
505 
506  // get next command
507  wgetnstr(queryWindow, buf, 100);
508  noecho();
509  curs_set(0);
510  std::string line = buf;
511 
512  return line;
513  }
514 
515  /* Print a command prompt, disabled for non-terminal outputs */
516  void printPrompt(const std::string& prompt) override {
517  if (!isatty(fileno(stdin))) {
518  return;
519  }
520  werase(queryWindow);
521  wrefresh(queryWindow);
522  mvwprintw(queryWindow, 1, 0, prompt.c_str());
523  }
524 
525  /* Print a tree */
526  void printTree(Own<TreeNode> tree) override {
527  if (!tree) {
528  return;
529  }
530 
531  if (!ExplainConfig::getExplainConfig().json) {
532  tree->place(0, 0);
533  ScreenBuffer screenBuffer(tree->getWidth(), tree->getHeight());
534  tree->render(screenBuffer);
535  wprintw(treePad, screenBuffer.getString().c_str());
536  } else {
537  if (ExplainConfig::getExplainConfig().outputStream == nullptr) {
538  std::stringstream ss;
539  ss << "{ \"proof\":\n";
540  tree->printJSON(ss, 1);
541  ss << ",";
542  prov.printRulesJSON(ss);
543  ss << "}\n";
544 
545  wprintw(treePad, ss.str().c_str());
546  } else {
547  std::ostream* output = ExplainConfig::getExplainConfig().outputStream.get();
548  *output << "{ \"proof\":\n";
549  tree->printJSON(*output, 1);
550  *output << ",";
551  prov.printRulesJSON(*output);
552  *output << "}\n";
553  }
554  }
555  }
556 
557  /* Print any other information, disabled for non-terminal outputs */
558  void printInfo(const std::string& info) override {
559  if (!isatty(fileno(stdin))) {
560  return;
561  }
562  wprintw(treePad, info.c_str());
563  prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
564  }
565 
566  /* Print an error, such as a wrong command */
567  void printError(const std::string& error) override {
568  wprintw(treePad, error.c_str());
569  prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
570  }
571 
572  /* Initialise ncurses command prompt window */
573  WINDOW* makeQueryWindow() {
574  WINDOW* queryWindow = newwin(3, maxx, maxy - 2, 0);
575  wrefresh(queryWindow);
576  return queryWindow;
577  }
578 
579  /* Initialise ncurses window */
580  void initialiseWindow() {
581  initscr();
582 
583  // get size of window
584  getmaxyx(stdscr, maxy, maxx);
585 
586  // create windows for query and tree display
587  queryWindow = makeQueryWindow();
588  treePad = newpad(MAX_TREE_HEIGHT, MAX_TREE_WIDTH);
589 
590  keypad(treePad, true);
591  }
592 
593  /* Allow scrolling of provenance tree */
594  void scrollTree(int maxx, int maxy) {
595  int x = 0;
596  int y = 0;
597 
598  while (true) {
599  int ch = wgetch(treePad);
600 
601  if (ch == KEY_LEFT) {
602  if (x > 2) x -= 3;
603  } else if (ch == KEY_RIGHT) {
604  if (x < MAX_TREE_WIDTH - 3) x += 3;
605  } else if (ch == KEY_UP) {
606  if (y > 0) y -= 1;
607  } else if (ch == KEY_DOWN) {
608  if (y < MAX_TREE_HEIGHT - 1) y += 1;
609  } else {
610  ungetch(ch);
611  break;
612  }
613 
614  prefresh(treePad, y, x, 0, 0, maxy - 3, maxx - 1);
615  }
616  }
617 
618  /* Clear the tree display */
619  void clearDisplay() {
620  // reset tree display on each loop
621  werase(treePad);
622  prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
623  }
624 };
625 #endif
626 
627 inline void explain(SouffleProgram& prog, bool ncurses) {
628  ExplainProvenanceImpl prov(prog);
629 
630  if (ncurses) {
631 #ifdef USE_NCURSES
632  ExplainNcurses exp(prov);
633  exp.explain();
634 #else
635  std::cout << "The ncurses-based interface is not enabled\n";
636 #endif
637  } else {
638  ExplainConsole exp(prov);
639  exp.explain();
640  }
641 }
642 
643 // this is necessary because ncurses.h defines TRUE and FALSE macros, and they otherwise clash with our parser
644 #ifdef USE_NCURSES
645 #undef TRUE
646 #undef FALSE
647 #endif
648 
649 } // end of namespace souffle
ExplainProvenance.h
souffle::ExplainProvenance::explainNegation
virtual Own< TreeNode > explainNegation(std::string relName, size_t ruleNum, const std::vector< std::string > &tuple, std::map< std::string, std::string > &bodyVariables)=0
souffle::explain
void explain(SouffleProgram &prog, bool ncurses)
Definition: Explain.h:627
souffle::ExplainProvenance::measureRelation
virtual std::string measureRelation(std::string relName)=0
souffle::Explain::printInfo
virtual void printInfo(const std::string &info)=0
souffle::Explain::prov
ExplainProvenance & prov
Definition: Explain.h:70
e
l j a showGridBackground &&c b raw series this eventEmitter e
Definition: htmlJsChartistMin.h:15
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
souffle::ExplainConfig::getExplainConfig
static ExplainConfig & getExplainConfig()
Definition: Explain.h:54
ExplainProvenanceImpl.h
relations
std::vector< Own< Relation > > relations
Definition: ComponentInstantiation.cpp:65
souffle::ExplainConfig::ExplainConfig
ExplainConfig()=default
ExplainTree.h
MAX_TREE_WIDTH
#define MAX_TREE_WIDTH
Definition: Explain.h:40
souffle::ExplainProvenance::explainSubproof
virtual Own< TreeNode > explainSubproof(std::string relName, RamDomain label, size_t depthLimit)=0
str
const std::string & str
Definition: json11.h:662
souffle::ExplainProvenanceImpl
Definition: ExplainProvenanceImpl.h:54
souffle::ExplainConfig::operator=
ExplainConfig & operator=(const ExplainConfig &)=delete
souffle::Explain::Explain
Explain(ExplainProvenance &prov)
Definition: Explain.h:72
souffle::SouffleProgram
Abstract base class for generated Datalog programs.
Definition: SouffleInterface.h:693
i
size_t i
Definition: json11.h:663
souffle::ExplainProvenance::queryProcess
virtual void queryProcess(const std::vector< std::pair< std::string, std::vector< std::string >>> &rels)=0
Process query with given arguments.
souffle::Explain::printPrompt
virtual void printPrompt(const std::string &prompt)=0
souffle::Explain::getInput
virtual std::string getInput()=0
souffle::Explain::processCommand
bool processCommand(std::string &commandLine)
Definition: Explain.h:77
souffle::ExplainConfig::json
bool json
Definition: Explain.h:61
souffle::split
std::vector< std::string > split(const std::string &s, char delim, int times=-1)
utility function to split a string
Definition: ExplainProvenance.h:146
souffle::ExplainProvenance::getRule
virtual std::string getRule(std::string relName, size_t ruleNum)=0
souffle::ExplainConfig::outputStream
Own< std::ostream > outputStream
Definition: Explain.h:60
souffle::ExplainProvenance::printRulesJSON
virtual void printRulesJSON(std::ostream &os)=0
souffle::ExplainProvenance::explain
virtual Own< TreeNode > explain(std::string relName, std::vector< std::string > tuple, size_t depthLimit)=0
souffle::ExplainConfig
Definition: Explain.h:45
souffle::ExplainProvenance::getRules
virtual std::vector< std::string > getRules(const std::string &relName)=0
souffle::Explain::printTree
virtual void printTree(Own< TreeNode > tree)=0
souffle::Explain::explain
virtual void explain()=0
souffle::ExplainProvenance::explainNegationGetVariables
virtual std::vector< std::string > explainNegationGetVariables(std::string relName, std::vector< std::string > args, size_t ruleNum)=0
souffle::ast::DirectiveType::output
@ output
rule
Rule & rule
Definition: Reader.h:85
souffle::Explain::printError
virtual void printError(const std::string &error)=0
souffle
Definition: AggregateOp.h:25
souffle::Explain::~Explain
~Explain()=default
souffle::ExplainProvenance
Definition: ExplainProvenance.h:165
MAX_TREE_HEIGHT
#define MAX_TREE_HEIGHT
Definition: Explain.h:39
souffle::Explain::parseTuple
std::pair< std::string, std::vector< std::string > > parseTuple(const std::string &str)
Parse tuple, split into relation name and values.
Definition: Explain.h:298
souffle::ExplainConfig::depthLimit
int depthLimit
Definition: Explain.h:62
souffle::profile::ss
class souffle::profile::Tui ss
Definition: Tui.h:336
souffle::Explain
Definition: Explain.h:68