Implement --tlimit for windows (#4716)
[cvc5.git] / src / main / driver_unified.cpp
1 /********************* */
2 /*! \file driver_unified.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Morgan Deters, Liana Hadarean, Tim King
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS
8 ** in the top-level source directory) and their institutional affiliations.
9 ** All rights reserved. See the file COPYING in the top-level source
10 ** directory for licensing information.\endverbatim
11 **
12 ** \brief Driver for CVC4 executable (cvc4)
13 **/
14
15 #include <stdio.h>
16 #include <unistd.h>
17
18 #include <cstdlib>
19 #include <cstring>
20 #include <fstream>
21 #include <iostream>
22 #include <memory>
23 #include <new>
24
25 #include "cvc4autoconfig.h"
26
27 #include "api/cvc4cpp.h"
28 #include "base/configuration.h"
29 #include "base/output.h"
30 #include "expr/expr_iomanip.h"
31 #include "expr/expr_manager.h"
32 #include "main/command_executor.h"
33 #include "main/interactive_shell.h"
34 #include "main/main.h"
35 #include "main/signal_handlers.h"
36 #include "main/time_limit.h"
37 #include "options/options.h"
38 #include "options/set_language.h"
39 #include "parser/parser.h"
40 #include "parser/parser_builder.h"
41 #include "parser/parser_exception.h"
42 #include "smt/command.h"
43 #include "util/result.h"
44 #include "util/statistics_registry.h"
45
46 using namespace std;
47 using namespace CVC4;
48 using namespace CVC4::parser;
49 using namespace CVC4::main;
50
51 namespace CVC4 {
52 namespace main {
53 /** Global options variable */
54 thread_local Options* pOptions;
55
56 /** Full argv[0] */
57 const char *progPath;
58
59 /** Just the basename component of argv[0] */
60 const std::string *progName;
61
62 /** A pointer to the CommandExecutor (the signal handlers need it) */
63 CVC4::main::CommandExecutor* pExecutor = NULL;
64
65 /** A pointer to the totalTime driver stat (the signal handlers need it) */
66 CVC4::TimerStat* pTotalTime = NULL;
67
68 }/* CVC4::main namespace */
69 }/* CVC4 namespace */
70
71
72 void printUsage(Options& opts, bool full) {
73 stringstream ss;
74 ss << "usage: " << opts.getBinaryName() << " [options] [input-file]"
75 << endl << endl
76 << "Without an input file, or with `-', CVC4 reads from standard input."
77 << endl << endl
78 << "CVC4 options:" << endl;
79 if(full) {
80 Options::printUsage( ss.str(), *(opts.getOut()) );
81 } else {
82 Options::printShortUsage( ss.str(), *(opts.getOut()) );
83 }
84 }
85
86 int runCvc4(int argc, char* argv[], Options& opts) {
87
88 // Timer statistic
89 pTotalTime = new TimerStat("totalTime");
90 pTotalTime->start();
91
92 // For the signal handlers' benefit
93 pOptions = &opts;
94
95 // Initialize the signal handlers
96 signal_handlers::install();
97
98 progPath = argv[0];
99
100 // Parse the options
101 vector<string> filenames = Options::parseOptions(&opts, argc, argv);
102
103 install_time_limit(opts);
104
105 string progNameStr = opts.getBinaryName();
106 progName = &progNameStr;
107
108 if( opts.getHelp() ) {
109 printUsage(opts, true);
110 exit(1);
111 } else if( opts.getLanguageHelp() ) {
112 Options::printLanguageHelp(*(opts.getOut()));
113 exit(1);
114 } else if( opts.getVersion() ) {
115 *(opts.getOut()) << Configuration::about().c_str() << flush;
116 exit(0);
117 }
118
119 segvSpin = opts.getSegvSpin();
120
121 // If in competition mode, set output stream option to flush immediately
122 #ifdef CVC4_COMPETITION_MODE
123 *(opts.getOut()) << unitbuf;
124 #endif /* CVC4_COMPETITION_MODE */
125
126 // We only accept one input file
127 if(filenames.size() > 1) {
128 throw Exception("Too many input files specified.");
129 }
130
131 // If no file supplied we will read from standard input
132 const bool inputFromStdin = filenames.empty() || filenames[0] == "-";
133
134 // if we're reading from stdin on a TTY, default to interactive mode
135 if(!opts.wasSetByUserInteractive()) {
136 opts.setInteractive(inputFromStdin && isatty(fileno(stdin)));
137 }
138
139 // Auto-detect input language by filename extension
140 std::string filenameStr("<stdin>");
141 if (!inputFromStdin) {
142 // Use swap to avoid copying the string
143 // TODO: use std::move() when switching to c++11
144 filenameStr.swap(filenames[0]);
145 }
146 const char* filename = filenameStr.c_str();
147
148 if(opts.getInputLanguage() == language::input::LANG_AUTO) {
149 if( inputFromStdin ) {
150 // We can't do any fancy detection on stdin
151 opts.setInputLanguage(language::input::LANG_CVC4);
152 } else {
153 unsigned len = filenameStr.size();
154 if(len >= 5 && !strcmp(".smt2", filename + len - 5)) {
155 opts.setInputLanguage(language::input::LANG_SMTLIB_V2_6);
156 } else if((len >= 2 && !strcmp(".p", filename + len - 2))
157 || (len >= 5 && !strcmp(".tptp", filename + len - 5))) {
158 opts.setInputLanguage(language::input::LANG_TPTP);
159 } else if(( len >= 4 && !strcmp(".cvc", filename + len - 4) )
160 || ( len >= 5 && !strcmp(".cvc4", filename + len - 5) )) {
161 opts.setInputLanguage(language::input::LANG_CVC4);
162 } else if((len >= 3 && !strcmp(".sy", filename + len - 3))
163 || (len >= 3 && !strcmp(".sl", filename + len - 3))) {
164 // version 2 sygus is the default
165 opts.setInputLanguage(language::input::LANG_SYGUS_V2);
166 }
167 }
168 }
169
170 if(opts.getOutputLanguage() == language::output::LANG_AUTO) {
171 opts.setOutputLanguage(language::toOutputLanguage(opts.getInputLanguage()));
172 }
173
174 // Determine which messages to show based on smtcomp_mode and verbosity
175 if(Configuration::isMuzzledBuild()) {
176 DebugChannel.setStream(&CVC4::null_os);
177 TraceChannel.setStream(&CVC4::null_os);
178 NoticeChannel.setStream(&CVC4::null_os);
179 ChatChannel.setStream(&CVC4::null_os);
180 MessageChannel.setStream(&CVC4::null_os);
181 WarningChannel.setStream(&CVC4::null_os);
182 }
183
184 // important even for muzzled builds (to get result output right)
185 (*(opts.getOut())) << language::SetLanguage(opts.getOutputLanguage());
186
187 // Create the command executor to execute the parsed commands
188 pExecutor = new CommandExecutor(opts);
189
190 int returnValue = 0;
191 {
192 // Timer statistic
193 RegisterStatistic statTotalTime(&pExecutor->getStatisticsRegistry(),
194 pTotalTime);
195
196 // Filename statistics
197 ReferenceStat<std::string> s_statFilename("filename", filenameStr);
198 RegisterStatistic statFilenameReg(&pExecutor->getStatisticsRegistry(),
199 &s_statFilename);
200 // notify SmtEngine that we are starting to parse
201 pExecutor->getSmtEngine()->notifyStartParsing(filenameStr);
202
203 // Parse and execute commands until we are done
204 Command* cmd;
205 bool status = true;
206 if(opts.getInteractive() && inputFromStdin) {
207 if(opts.getTearDownIncremental() > 0) {
208 throw OptionException(
209 "--tear-down-incremental doesn't work in interactive mode");
210 }
211 if(!opts.wasSetByUserIncrementalSolving()) {
212 cmd = new SetOptionCommand("incremental", SExpr(true));
213 cmd->setMuted(true);
214 pExecutor->doCommand(cmd);
215 delete cmd;
216 }
217 InteractiveShell shell(pExecutor->getSolver());
218 if(opts.getInteractivePrompt()) {
219 Message() << Configuration::getPackageName()
220 << " " << Configuration::getVersionString();
221 if(Configuration::isGitBuild()) {
222 Message() << " [" << Configuration::getGitId() << "]";
223 }
224 Message() << (Configuration::isDebugBuild() ? " DEBUG" : "")
225 << " assertions:"
226 << (Configuration::isAssertionBuild() ? "on" : "off")
227 << endl << endl;
228 Message() << Configuration::copyright() << endl;
229 }
230
231 while(true) {
232 try {
233 cmd = shell.readCommand();
234 } catch(UnsafeInterruptException& e) {
235 (*opts.getOut()) << CommandInterrupted();
236 break;
237 }
238 if (cmd == NULL)
239 break;
240 status = pExecutor->doCommand(cmd) && status;
241 if (cmd->interrupted()) {
242 delete cmd;
243 break;
244 }
245 delete cmd;
246 }
247 } else if( opts.getTearDownIncremental() > 0) {
248 if(!opts.getIncrementalSolving() && opts.getTearDownIncremental() > 1) {
249 // For tear-down-incremental values greater than 1, need incremental
250 // on too.
251 cmd = new SetOptionCommand("incremental", SExpr(true));
252 cmd->setMuted(true);
253 pExecutor->doCommand(cmd);
254 delete cmd;
255 // if(opts.wasSetByUserIncrementalSolving()) {
256 // throw OptionException(
257 // "--tear-down-incremental incompatible with --incremental");
258 // }
259
260 // cmd = new SetOptionCommand("incremental", SExpr(false));
261 // cmd->setMuted(true);
262 // pExecutor->doCommand(cmd);
263 // delete cmd;
264 }
265
266 ParserBuilder parserBuilder(pExecutor->getSolver(), filename, opts);
267
268 if( inputFromStdin ) {
269 #if defined(CVC4_COMPETITION_MODE) && !defined(CVC4_SMTCOMP_APPLICATION_TRACK)
270 parserBuilder.withStreamInput(cin);
271 #else /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
272 parserBuilder.withLineBufferedStreamInput(cin);
273 #endif /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
274 }
275
276 vector< vector<Command*> > allCommands;
277 allCommands.push_back(vector<Command*>());
278 std::unique_ptr<Parser> parser(parserBuilder.build());
279 int needReset = 0;
280 // true if one of the commands was interrupted
281 bool interrupted = false;
282 while (status)
283 {
284 if (interrupted) {
285 (*opts.getOut()) << CommandInterrupted();
286 break;
287 }
288
289 try {
290 cmd = parser->nextCommand();
291 if (cmd == NULL) break;
292 } catch (UnsafeInterruptException& e) {
293 interrupted = true;
294 continue;
295 }
296
297 if(dynamic_cast<PushCommand*>(cmd) != NULL) {
298 if(needReset >= opts.getTearDownIncremental()) {
299 pExecutor->reset();
300 for(size_t i = 0; i < allCommands.size() && !interrupted; ++i) {
301 if (interrupted) break;
302 for(size_t j = 0; j < allCommands[i].size() && !interrupted; ++j)
303 {
304 Command* ccmd = allCommands[i][j]->clone();
305 ccmd->setMuted(true);
306 pExecutor->doCommand(ccmd);
307 if (ccmd->interrupted())
308 {
309 interrupted = true;
310 }
311 delete ccmd;
312 }
313 }
314 needReset = 0;
315 }
316 allCommands.push_back(vector<Command*>());
317 Command* copy = cmd->clone();
318 allCommands.back().push_back(copy);
319 status = pExecutor->doCommand(cmd);
320 if(cmd->interrupted()) {
321 interrupted = true;
322 continue;
323 }
324 } else if(dynamic_cast<PopCommand*>(cmd) != NULL) {
325 allCommands.pop_back(); // fixme leaks cmds here
326 if (needReset >= opts.getTearDownIncremental()) {
327 pExecutor->reset();
328 for(size_t i = 0; i < allCommands.size() && !interrupted; ++i) {
329 for(size_t j = 0; j < allCommands[i].size() && !interrupted; ++j)
330 {
331 Command* ccmd = allCommands[i][j]->clone();
332 ccmd->setMuted(true);
333 pExecutor->doCommand(ccmd);
334 if (ccmd->interrupted())
335 {
336 interrupted = true;
337 }
338 delete ccmd;
339 }
340 }
341 if (interrupted) continue;
342 (*opts.getOut()) << CommandSuccess();
343 needReset = 0;
344 } else {
345 status = pExecutor->doCommand(cmd);
346 if(cmd->interrupted()) {
347 interrupted = true;
348 continue;
349 }
350 }
351 } else if(dynamic_cast<CheckSatCommand*>(cmd) != NULL ||
352 dynamic_cast<QueryCommand*>(cmd) != NULL) {
353 if(needReset >= opts.getTearDownIncremental()) {
354 pExecutor->reset();
355 for(size_t i = 0; i < allCommands.size() && !interrupted; ++i) {
356 for(size_t j = 0; j < allCommands[i].size() && !interrupted; ++j)
357 {
358 Command* ccmd = allCommands[i][j]->clone();
359 ccmd->setMuted(true);
360 pExecutor->doCommand(ccmd);
361 if (ccmd->interrupted())
362 {
363 interrupted = true;
364 }
365 delete ccmd;
366 }
367 }
368 needReset = 0;
369 } else {
370 ++needReset;
371 }
372 if (interrupted) {
373 continue;
374 }
375
376 status = pExecutor->doCommand(cmd);
377 if(cmd->interrupted()) {
378 interrupted = true;
379 continue;
380 }
381 } else if(dynamic_cast<ResetCommand*>(cmd) != NULL) {
382 pExecutor->doCommand(cmd);
383 allCommands.clear();
384 allCommands.push_back(vector<Command*>());
385 } else {
386 // We shouldn't copy certain commands, because they can cause
387 // an error on replay since there's no associated sat/unsat check
388 // preceding them.
389 if(dynamic_cast<GetUnsatCoreCommand*>(cmd) == NULL &&
390 dynamic_cast<GetProofCommand*>(cmd) == NULL &&
391 dynamic_cast<GetValueCommand*>(cmd) == NULL &&
392 dynamic_cast<GetModelCommand*>(cmd) == NULL &&
393 dynamic_cast<GetAssignmentCommand*>(cmd) == NULL &&
394 dynamic_cast<GetInstantiationsCommand*>(cmd) == NULL &&
395 dynamic_cast<GetAssertionsCommand*>(cmd) == NULL &&
396 dynamic_cast<GetInfoCommand*>(cmd) == NULL &&
397 dynamic_cast<GetOptionCommand*>(cmd) == NULL &&
398 dynamic_cast<EchoCommand*>(cmd) == NULL) {
399 Command* copy = cmd->clone();
400 allCommands.back().push_back(copy);
401 }
402 status = pExecutor->doCommand(cmd);
403 if(cmd->interrupted()) {
404 interrupted = true;
405 continue;
406 }
407
408 if(dynamic_cast<QuitCommand*>(cmd) != NULL) {
409 delete cmd;
410 break;
411 }
412 }
413 delete cmd;
414 }
415 } else {
416 if(!opts.wasSetByUserIncrementalSolving()) {
417 cmd = new SetOptionCommand("incremental", SExpr(false));
418 cmd->setMuted(true);
419 pExecutor->doCommand(cmd);
420 delete cmd;
421 }
422
423 ParserBuilder parserBuilder(pExecutor->getSolver(), filename, opts);
424
425 if( inputFromStdin ) {
426 #if defined(CVC4_COMPETITION_MODE) && !defined(CVC4_SMTCOMP_APPLICATION_TRACK)
427 parserBuilder.withStreamInput(cin);
428 #else /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
429 parserBuilder.withLineBufferedStreamInput(cin);
430 #endif /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
431 }
432
433 std::unique_ptr<Parser> parser(parserBuilder.build());
434 bool interrupted = false;
435 while (status)
436 {
437 if (interrupted) {
438 (*opts.getOut()) << CommandInterrupted();
439 pExecutor->reset();
440 break;
441 }
442 try {
443 cmd = parser->nextCommand();
444 if (cmd == NULL) break;
445 } catch (UnsafeInterruptException& e) {
446 interrupted = true;
447 continue;
448 }
449
450 status = pExecutor->doCommand(cmd);
451 if (cmd->interrupted() && status == 0) {
452 interrupted = true;
453 break;
454 }
455
456 if(dynamic_cast<QuitCommand*>(cmd) != NULL) {
457 delete cmd;
458 break;
459 }
460 delete cmd;
461 }
462 }
463
464 Result result;
465 if(status) {
466 result = pExecutor->getResult();
467 returnValue = 0;
468 } else {
469 // there was some kind of error
470 returnValue = 1;
471 }
472
473 #ifdef CVC4_COMPETITION_MODE
474 opts.flushOut();
475 // exit, don't return (don't want destructors to run)
476 // _exit() from unistd.h doesn't run global destructors
477 // or other on_exit/atexit stuff.
478 _exit(returnValue);
479 #endif /* CVC4_COMPETITION_MODE */
480
481 ReferenceStat< Result > s_statSatResult("sat/unsat", result);
482 RegisterStatistic statSatResultReg(&pExecutor->getStatisticsRegistry(),
483 &s_statSatResult);
484
485 pTotalTime->stop();
486
487 // Tim: I think that following comment is out of date?
488 // Set the global executor pointer to NULL first. If we get a
489 // signal while dumping statistics, we don't want to try again.
490 pExecutor->flushOutputStreams();
491
492 #ifdef CVC4_DEBUG
493 if(opts.getEarlyExit() && opts.wasSetByUserEarlyExit()) {
494 _exit(returnValue);
495 }
496 #else /* CVC4_DEBUG */
497 if(opts.getEarlyExit()) {
498 _exit(returnValue);
499 }
500 #endif /* CVC4_DEBUG */
501 }
502
503 // On exceptional exit, these are leaked, but that's okay... they
504 // need to be around in that case for main() to print statistics.
505 delete pTotalTime;
506 delete pExecutor;
507
508 pTotalTime = NULL;
509 pExecutor = NULL;
510
511 signal_handlers::cleanup();
512
513 return returnValue;
514 }