1 /******************************************************************************
2 * Top contributors (to current version):
3 * Morgan Deters, Liana Hadarean, Tim King
5 * This file is part of the cvc5 project.
7 * Copyright (c) 2009-2021 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.
11 * ****************************************************************************
13 * Driver for cvc5 executable (cvc5).
27 #include "api/cpp/cvc5.h"
28 #include "base/configuration.h"
29 #include "base/cvc5config.h"
30 #include "base/output.h"
31 #include "main/command_executor.h"
32 #include "main/interactive_shell.h"
33 #include "main/main.h"
34 #include "main/signal_handlers.h"
35 #include "main/time_limit.h"
36 #include "options/base_options.h"
37 #include "options/options.h"
38 #include "options/parser_options.h"
39 #include "options/main_options.h"
40 #include "options/set_language.h"
41 #include "parser/parser.h"
42 #include "parser/parser_builder.h"
43 #include "smt/command.h"
44 #include "smt/smt_engine.h"
45 #include "util/result.h"
49 using namespace cvc5::parser
;
50 using namespace cvc5::main
;
54 /** Global options variable */
55 thread_local Options
* pOptions
;
60 /** Just the basename component of argv[0] */
63 /** A pointer to the CommandExecutor (the signal handlers need it) */
64 std::unique_ptr
<cvc5::main::CommandExecutor
> pExecutor
;
66 /** The time point the binary started, accessible to signal handlers */
67 std::unique_ptr
<TotalTimer
> totalTime
;
69 TotalTimer::~TotalTimer()
71 if (pExecutor
!= nullptr)
73 auto duration
= std::chrono::steady_clock::now() - d_start
;
74 pExecutor
->getSmtEngine()->setTotalTimeStatistic(
75 std::chrono::duration
<double>(duration
).count());
82 void printUsage(Options
& opts
, bool full
) {
84 ss
<< "usage: " << progName
<< " [options] [input-file]"
87 << "Without an input file, or with `-', cvc5 reads from standard input."
90 << "cvc5 options:" << endl
;
92 Options::printUsage(ss
.str(), *opts
.base
.out
);
94 Options::printShortUsage(ss
.str(), *opts
.base
.out
);
98 int runCvc5(int argc
, char* argv
[], Options
& opts
)
100 main::totalTime
= std::make_unique
<TotalTimer
>();
101 // For the signal handlers' benefit
104 // Initialize the signal handlers
105 signal_handlers::install();
110 std::vector
<string
> filenames
=
111 Options::parseOptions(&opts
, argc
, argv
, progName
);
113 auto limit
= install_time_limit(opts
);
115 if (opts
.driver
.help
)
117 printUsage(opts
, true);
120 else if (opts
.base
.languageHelp
)
122 Options::printLanguageHelp(*opts
.base
.out
);
125 else if (opts
.driver
.version
)
127 *opts
.base
.out
<< Configuration::about().c_str() << flush
;
131 segvSpin
= opts
.driver
.segvSpin
;
133 // If in competition mode, set output stream option to flush immediately
134 #ifdef CVC5_COMPETITION_MODE
135 *opts
.base
.out
<< unitbuf
;
136 #endif /* CVC5_COMPETITION_MODE */
138 // We only accept one input file
139 if(filenames
.size() > 1) {
140 throw Exception("Too many input files specified.");
143 // If no file supplied we will read from standard input
144 const bool inputFromStdin
= filenames
.empty() || filenames
[0] == "-";
146 // if we're reading from stdin on a TTY, default to interactive mode
147 if (!opts
.driver
.interactiveWasSetByUser
)
149 opts
.driver
.interactive
= inputFromStdin
&& isatty(fileno(stdin
));
152 // Auto-detect input language by filename extension
153 std::string
filenameStr("<stdin>");
154 if (!inputFromStdin
) {
155 // Use swap to avoid copying the string
156 // TODO: use std::move() when switching to c++11
157 filenameStr
.swap(filenames
[0]);
159 const char* filename
= filenameStr
.c_str();
161 if (opts
.base
.inputLanguage
== language::input::LANG_AUTO
)
163 if( inputFromStdin
) {
164 // We can't do any fancy detection on stdin
165 opts
.base
.inputLanguage
= language::input::LANG_CVC
;
167 size_t len
= filenameStr
.size();
168 if(len
>= 5 && !strcmp(".smt2", filename
+ len
- 5)) {
169 opts
.base
.inputLanguage
= language::input::LANG_SMTLIB_V2_6
;
170 } else if((len
>= 2 && !strcmp(".p", filename
+ len
- 2))
171 || (len
>= 5 && !strcmp(".tptp", filename
+ len
- 5))) {
172 opts
.base
.inputLanguage
= language::input::LANG_TPTP
;
173 } else if(( len
>= 4 && !strcmp(".cvc", filename
+ len
- 4) )
174 || ( len
>= 5 && !strcmp(".cvc4", filename
+ len
- 5) )) {
175 opts
.base
.inputLanguage
= language::input::LANG_CVC
;
176 } else if((len
>= 3 && !strcmp(".sy", filename
+ len
- 3))
177 || (len
>= 3 && !strcmp(".sl", filename
+ len
- 3))) {
178 // version 2 sygus is the default
179 opts
.base
.inputLanguage
= language::input::LANG_SYGUS_V2
;
184 if (opts
.base
.outputLanguage
== language::output::LANG_AUTO
)
186 opts
.base
.outputLanguage
= language::toOutputLanguage(opts
.base
.inputLanguage
);
189 // Determine which messages to show based on smtcomp_mode and verbosity
190 if(Configuration::isMuzzledBuild()) {
191 DebugChannel
.setStream(&cvc5::null_os
);
192 TraceChannel
.setStream(&cvc5::null_os
);
193 NoticeChannel
.setStream(&cvc5::null_os
);
194 ChatChannel
.setStream(&cvc5::null_os
);
195 MessageChannel
.setStream(&cvc5::null_os
);
196 WarningChannel
.setStream(&cvc5::null_os
);
199 // important even for muzzled builds (to get result output right)
201 << language::SetLanguage(opts
.base
.outputLanguage
);
203 // Create the command executor to execute the parsed commands
204 pExecutor
= std::make_unique
<CommandExecutor
>(opts
);
208 // notify SmtEngine that we are starting to parse
209 pExecutor
->getSmtEngine()->notifyStartParsing(filenameStr
);
211 // Parse and execute commands until we are done
212 std::unique_ptr
<Command
> cmd
;
214 if (opts
.driver
.interactive
&& inputFromStdin
)
216 if (opts
.driver
.tearDownIncremental
> 0)
219 "--tear-down-incremental doesn't work in interactive mode");
221 if (!opts
.base
.incrementalSolvingWasSetByUser
)
223 cmd
.reset(new SetOptionCommand("incremental", "true"));
225 pExecutor
->doCommand(cmd
);
227 InteractiveShell
shell(pExecutor
->getSolver(),
228 pExecutor
->getSymbolManager());
229 if (opts
.driver
.interactivePrompt
)
231 CVC5Message() << Configuration::getPackageName() << " "
232 << Configuration::getVersionString();
233 if(Configuration::isGitBuild()) {
234 CVC5Message() << " [" << Configuration::getGitId() << "]";
236 CVC5Message() << (Configuration::isDebugBuild() ? " DEBUG" : "")
238 << (Configuration::isAssertionBuild() ? "on" : "off")
241 CVC5Message() << Configuration::copyright() << endl
;
246 cmd
.reset(shell
.readCommand());
247 } catch(UnsafeInterruptException
& e
) {
248 *opts
.base
.out
<< CommandInterrupted();
253 status
= pExecutor
->doCommand(cmd
) && status
;
254 if (cmd
->interrupted()) {
259 else if (opts
.driver
.tearDownIncremental
> 0)
261 if (!opts
.base
.incrementalSolving
262 && opts
.driver
.tearDownIncremental
> 1)
264 // For tear-down-incremental values greater than 1, need incremental
266 cmd
.reset(new SetOptionCommand("incremental", "true"));
268 pExecutor
->doCommand(cmd
);
269 // if(opts.base.incrementalWasSetByUser) {
270 // throw OptionException(
271 // "--tear-down-incremental incompatible with --incremental");
274 // cmd.reset(new SetOptionCommand("incremental", "false"));
275 // cmd->setMuted(true);
276 // pExecutor->doCommand(cmd);
279 ParserBuilder
parserBuilder(pExecutor
->getSolver(),
280 pExecutor
->getSymbolManager(),
282 std::unique_ptr
<Parser
> parser(parserBuilder
.build());
283 if( inputFromStdin
) {
284 parser
->setInput(Input::newStreamInput(
285 opts
.base
.inputLanguage
, cin
, filename
));
289 parser
->setInput(Input::newFileInput(opts
.base
.inputLanguage
,
291 opts
.parser
.memoryMap
));
294 vector
< vector
<Command
*> > allCommands
;
295 allCommands
.push_back(vector
<Command
*>());
297 // true if one of the commands was interrupted
298 bool interrupted
= false;
302 *opts
.base
.out
<< CommandInterrupted();
307 cmd
.reset(parser
->nextCommand());
308 if (cmd
== nullptr) break;
309 } catch (UnsafeInterruptException
& e
) {
314 if(dynamic_cast<PushCommand
*>(cmd
.get()) != nullptr) {
315 if (needReset
>= opts
.driver
.tearDownIncremental
)
318 for(size_t i
= 0; i
< allCommands
.size() && !interrupted
; ++i
) {
319 if (interrupted
) break;
320 for(size_t j
= 0; j
< allCommands
[i
].size() && !interrupted
; ++j
)
322 Command
* ccmd
= allCommands
[i
][j
]->clone();
323 ccmd
->setMuted(true);
324 pExecutor
->doCommand(ccmd
);
325 if (ccmd
->interrupted())
334 allCommands
.push_back(vector
<Command
*>());
335 Command
* copy
= cmd
->clone();
336 allCommands
.back().push_back(copy
);
337 status
= pExecutor
->doCommand(cmd
);
338 if(cmd
->interrupted()) {
342 } else if(dynamic_cast<PopCommand
*>(cmd
.get()) != nullptr) {
343 allCommands
.pop_back(); // fixme leaks cmds here
344 if (needReset
>= opts
.driver
.tearDownIncremental
)
347 for(size_t i
= 0; i
< allCommands
.size() && !interrupted
; ++i
) {
348 for(size_t j
= 0; j
< allCommands
[i
].size() && !interrupted
; ++j
)
350 std::unique_ptr
<Command
> ccmd(allCommands
[i
][j
]->clone());
351 ccmd
->setMuted(true);
352 pExecutor
->doCommand(ccmd
);
353 if (ccmd
->interrupted())
359 if (interrupted
) continue;
360 *opts
.base
.out
<< CommandSuccess();
365 status
= pExecutor
->doCommand(cmd
);
366 if(cmd
->interrupted()) {
371 } else if(dynamic_cast<CheckSatCommand
*>(cmd
.get()) != nullptr ||
372 dynamic_cast<QueryCommand
*>(cmd
.get()) != nullptr) {
373 if (needReset
>= opts
.driver
.tearDownIncremental
)
376 for(size_t i
= 0; i
< allCommands
.size() && !interrupted
; ++i
) {
377 for(size_t j
= 0; j
< allCommands
[i
].size() && !interrupted
; ++j
)
379 Command
* ccmd
= allCommands
[i
][j
]->clone();
380 ccmd
->setMuted(true);
381 pExecutor
->doCommand(ccmd
);
382 if (ccmd
->interrupted())
399 status
= pExecutor
->doCommand(cmd
);
400 if(cmd
->interrupted()) {
404 } else if(dynamic_cast<ResetCommand
*>(cmd
.get()) != nullptr) {
405 pExecutor
->doCommand(cmd
);
407 allCommands
.push_back(vector
<Command
*>());
409 // We shouldn't copy certain commands, because they can cause
410 // an error on replay since there's no associated sat/unsat check
412 if(dynamic_cast<GetUnsatCoreCommand
*>(cmd
.get()) == nullptr &&
413 dynamic_cast<GetProofCommand
*>(cmd
.get()) == nullptr &&
414 dynamic_cast<GetValueCommand
*>(cmd
.get()) == nullptr &&
415 dynamic_cast<GetModelCommand
*>(cmd
.get()) == nullptr &&
416 dynamic_cast<GetAssignmentCommand
*>(cmd
.get()) == nullptr &&
417 dynamic_cast<GetInstantiationsCommand
*>(cmd
.get()) == nullptr &&
418 dynamic_cast<GetAssertionsCommand
*>(cmd
.get()) == nullptr &&
419 dynamic_cast<GetInfoCommand
*>(cmd
.get()) == nullptr &&
420 dynamic_cast<GetOptionCommand
*>(cmd
.get()) == nullptr &&
421 dynamic_cast<EchoCommand
*>(cmd
.get()) == nullptr) {
422 Command
* copy
= cmd
->clone();
423 allCommands
.back().push_back(copy
);
425 status
= pExecutor
->doCommand(cmd
);
426 if(cmd
->interrupted()) {
431 if(dynamic_cast<QuitCommand
*>(cmd
.get()) != nullptr) {
439 if (!opts
.base
.incrementalSolvingWasSetByUser
)
441 cmd
.reset(new SetOptionCommand("incremental", "false"));
443 pExecutor
->doCommand(cmd
);
446 ParserBuilder
parserBuilder(pExecutor
->getSolver(),
447 pExecutor
->getSymbolManager(),
449 std::unique_ptr
<Parser
> parser(parserBuilder
.build());
450 if( inputFromStdin
) {
451 parser
->setInput(Input::newStreamInput(
452 opts
.base
.inputLanguage
, cin
, filename
));
456 parser
->setInput(Input::newFileInput(opts
.base
.inputLanguage
,
458 opts
.parser
.memoryMap
));
461 bool interrupted
= false;
465 *opts
.base
.out
<< CommandInterrupted();
470 cmd
.reset(parser
->nextCommand());
471 if (cmd
== nullptr) break;
472 } catch (UnsafeInterruptException
& e
) {
477 status
= pExecutor
->doCommand(cmd
);
478 if (cmd
->interrupted() && status
== 0) {
483 if(dynamic_cast<QuitCommand
*>(cmd
.get()) != nullptr) {
491 result
= pExecutor
->getResult();
494 // there was some kind of error
498 #ifdef CVC5_COMPETITION_MODE
499 if (opts
.base
.out
!= nullptr)
501 *opts
.base
.out
<< std::flush
;
503 // exit, don't return (don't want destructors to run)
504 // _exit() from unistd.h doesn't run global destructors
505 // or other on_exit/atexit stuff.
507 #endif /* CVC5_COMPETITION_MODE */
510 pExecutor
->flushOutputStreams();
513 if (opts
.driver
.earlyExit
&& opts
.driver
.earlyExitWasSetByUser
)
517 #else /* CVC5_DEBUG */
518 if (opts
.driver
.earlyExit
)
522 #endif /* CVC5_DEBUG */
527 signal_handlers::cleanup();