39 #define MAX_TREE_HEIGHT 500
40 #define MAX_TREE_WIDTH 500
78 std::vector<std::string> command =
split(commandLine,
' ', 1);
80 if (command.empty()) {
84 if (command[0] ==
"setdepth") {
85 if (command.size() != 2) {
91 }
catch (std::exception&
e) {
92 printError(
"<" + command[1] +
"> is not a valid depth\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");
104 }
else if (command[0] ==
"subproof") {
105 std::pair<std::string, std::vector<std::string>> query;
107 if (command.size() <= 1) {
108 printError(
"Usage: subproof relation_name(<label>)\n");
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) {
118 "Usage: explainnegation relation_name(\"<string element1>\", <number element2>, "
130 printInfo(
"The tuple would be an input fact!\n");
135 rules += std::to_string(
i) +
": ";
148 if (variables.size() == 1 && variables[0] ==
"@") {
149 printInfo(
"The tuple exists, cannot explain negation of it!\n");
151 }
else if (variables.size() == 1 && variables[0] ==
"@non_matching") {
152 printInfo(
"The variable bindings don't match, cannot explain!\n");
154 }
else if (variables.size() == 1 && variables[0] ==
"@fact") {
159 std::map<std::string, std::string> varValues;
160 for (
auto var : variables) {
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");
174 }
catch (std::exception&
e) {
175 printError(
"Usage: rule <relation name> <rule number>\n");
177 }
else if (command[0] ==
"measure") {
180 }
catch (std::exception&
e) {
181 printError(
"Usage: measure <relation name>\n");
183 }
else if (command[0] ==
"output") {
184 if (command.size() == 2) {
187 }
else if (command.size() == 1) {
192 }
else if (command[0] ==
"format") {
193 if (command.size() == 2 && command[1] ==
"json") {
195 }
else if (command.size() == 2 && command[1] ==
"proof") {
200 }
else if (command[0] ==
"exit" || command[0] ==
"q" || command[0] ==
"quit") {
204 }
else if (command[0] ==
"query") {
206 if (command.size() != 2) {
208 "Usage: query <relation1>(<element1>, <element2>, ...), "
209 "<relation2>(<element1>, <element2>, ...), ...\n");
213 std::vector<std::pair<std::string, std::vector<std::string>>>
relations;
215 std::regex relationRegex(
216 "([a-zA-Z0-9_.-]*)[[:blank:]]*\\(([[:blank:]]*([0-9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*)("
217 "[[:blank:]]*,[[:blank:]]*(["
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];
225 while (std::regex_search(relationStr, relationMatcher, relationRegex)) {
226 relations.push_back(parseQueryTuple(relationMatcher[0]));
232 "Usage: query <relation1>(<element1>, <element2>, ...), "
233 "<relation2>(<element1>, <element2>, ...), ...\n");
236 relationStr = relationMatcher.suffix().str();
242 "Usage: query <relation1>(<element1>, <element2>, ...), "
243 "<relation2>(<element1>, <element2>, ...), ...\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"
263 "format <json|proof>: switch format between json and proof-trees\n"
264 "query <relation1>(<element1>, <element2>, ...), <relation2>(<element1>, <element2>), "
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 "
269 "exit: Exits this interface\n\n");
283 virtual void printPrompt(
const std::string& prompt) = 0;
289 virtual void printInfo(
const std::string& info) = 0;
292 virtual void printError(
const std::string& error) = 0;
298 std::pair<std::string, std::vector<std::string>>
parseTuple(
const std::string&
str) {
300 std::vector<std::string> args;
304 std::regex relationRegex(
305 "([a-zA-Z0-9_.-]*)[[:blank:]]*\\(([[:blank:]]*([0-9]+|\"[^\"]*\")([[:blank:]]*,[[:blank:]]*(["
307 "9]+|\"[^\"]*\"))*)?\\)",
308 std::regex_constants::extended);
309 std::smatch relMatch;
313 if (!std::regex_match(
str, relMatch, relationRegex) || relMatch.size() < 3) {
314 return std::make_pair(relName, args);
318 relName = relMatch[1];
321 std::string argsList = relMatch[2];
322 std::smatch argsMatcher;
323 std::regex argRegex(R
"([0-9]+|"[^"]*")", std::regex_constants::extended);
325 while (std::regex_search(argsList, argsMatcher, argRegex)) {
327 std::string currentArg = argsMatcher[0];
328 args.push_back(currentArg);
331 argsList = argsMatcher.suffix().str();
334 return std::make_pair(relName, args);
342 std::pair<std::string, std::vector<std::string>> parseQueryTuple(
const std::string&
str) {
344 std::vector<std::string> args;
347 std::regex relationRegex(
348 "([a-zA-Z0-9_.-]*)[[:blank:]]*\\(([[:blank:]]*([0-9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*)([[:"
349 "blank:]]*,[[:blank:]]*(["
351 "9]+|\"[^\"]*\"|[a-zA-Z_][a-zA-Z_0-9]*))*)?\\)",
352 std::regex_constants::extended);
353 std::smatch relMatch;
356 if (!std::regex_match(
str, relMatch, relationRegex) || relMatch.size() < 3) {
357 return std::make_pair(relName, args);
361 relName = relMatch[1];
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)) {
369 std::string currentArg = argsMatcher[0];
370 args.push_back(currentArg);
373 argsList = argsMatcher.suffix().str();
376 return std::make_pair(relName, args);
380 class ExplainConsole :
public Explain {
382 explicit ExplainConsole(ExplainProvenance& provenance) : Explain(provenance) {}
386 printPrompt(
"Explain is invoked.\n");
389 printPrompt(
"Enter command > ");
390 std::string line = getInput();
392 if (!processCommand(line)) {
400 std::string getInput()
override {
403 if (!getline(std::cin, line)) {
412 void printPrompt(
const std::string& prompt)
override {
413 if (isatty(fileno(stdin)) == 0) {
420 void printTree(Own<TreeNode> tree)
override {
435 ScreenBuffer screenBuffer(tree->getWidth(), tree->getHeight());
436 tree->render(screenBuffer);
437 *
output << screenBuffer.getString();
439 *
output <<
"{ \"proof\":\n";
440 tree->printJSON(*output, 1);
448 void printInfo(
const std::string& info)
override {
449 if (isatty(fileno(stdin)) == 0) {
456 void printError(
const std::string& error)
override {
462 class ExplainNcurses :
public Explain {
464 explicit ExplainNcurses(ExplainProvenance& provenance) : Explain(provenance) {}
470 std::signal(SIGWINCH,
nullptr);
473 printPrompt(
"Explain is invoked.\n");
477 printPrompt(
"Enter command > ");
478 std::string line = getInput();
481 if (!processCommand(line)) {
486 prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
487 scrollTree(maxx, maxy);
495 WINDOW* treePad =
nullptr;
496 WINDOW* queryWindow =
nullptr;
500 std::string getInput()
override {
507 wgetnstr(queryWindow, buf, 100);
510 std::string line = buf;
516 void printPrompt(
const std::string& prompt)
override {
517 if (!isatty(fileno(stdin))) {
521 wrefresh(queryWindow);
522 mvwprintw(queryWindow, 1, 0, prompt.c_str());
526 void printTree(Own<TreeNode> tree)
override {
533 ScreenBuffer screenBuffer(tree->getWidth(), tree->getHeight());
534 tree->render(screenBuffer);
535 wprintw(treePad, screenBuffer.getString().c_str());
538 std::stringstream
ss;
539 ss <<
"{ \"proof\":\n";
540 tree->printJSON(
ss, 1);
545 wprintw(treePad,
ss.str().c_str());
548 *
output <<
"{ \"proof\":\n";
549 tree->printJSON(*output, 1);
558 void printInfo(
const std::string& info)
override {
559 if (!isatty(fileno(stdin))) {
562 wprintw(treePad, info.c_str());
563 prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
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);
573 WINDOW* makeQueryWindow() {
574 WINDOW* queryWindow = newwin(3, maxx, maxy - 2, 0);
575 wrefresh(queryWindow);
580 void initialiseWindow() {
584 getmaxyx(stdscr, maxy, maxx);
587 queryWindow = makeQueryWindow();
590 keypad(treePad,
true);
594 void scrollTree(
int maxx,
int maxy) {
599 int ch = wgetch(treePad);
601 if (ch == KEY_LEFT) {
603 }
else if (ch == KEY_RIGHT) {
605 }
else if (ch == KEY_UP) {
607 }
else if (ch == KEY_DOWN) {
614 prefresh(treePad, y, x, 0, 0, maxy - 3, maxx - 1);
619 void clearDisplay() {
622 prefresh(treePad, 0, 0, 0, 0, maxy - 3, maxx - 1);
632 ExplainNcurses exp(prov);
635 std::cout <<
"The ncurses-based interface is not enabled\n";
638 ExplainConsole exp(prov);