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/options.h"
37 #include "options/set_language.h"
38 #include "parser/parser.h"
39 #include "parser/parser_builder.h"
40 #include "smt/command.h"
41 #include "smt/smt_engine.h"
42 #include "util/result.h"
46 using namespace cvc5::parser
;
47 using namespace cvc5::main
;
51 /** Global options variable */
52 thread_local Options
* pOptions
;
57 /** Just the basename component of argv[0] */
58 const std::string
* progName
;
60 /** A pointer to the CommandExecutor (the signal handlers need it) */
61 std::unique_ptr
<cvc5::main::CommandExecutor
> pExecutor
;
63 /** The time point the binary started, accessible to signal handlers */
64 std::unique_ptr
<TotalTimer
> totalTime
;
66 TotalTimer::~TotalTimer()
68 if (pExecutor
!= nullptr)
70 auto duration
= std::chrono::steady_clock::now() - d_start
;
71 pExecutor
->getSmtEngine()->setTotalTimeStatistic(
72 std::chrono::duration
<double>(duration
).count());
79 void printUsage(Options
& opts
, bool full
) {
81 ss
<< "usage: " << opts
.getBinaryName() << " [options] [input-file]" << endl
83 << "Without an input file, or with `-', cvc5 reads from standard input."
86 << "cvc5 options:" << endl
;
88 Options::printUsage( ss
.str(), *(opts
.getOut()) );
90 Options::printShortUsage( ss
.str(), *(opts
.getOut()) );
94 int runCvc5(int argc
, char* argv
[], Options
& opts
)
96 main::totalTime
= std::make_unique
<TotalTimer
>();
97 // For the signal handlers' benefit
100 // Initialize the signal handlers
101 signal_handlers::install();
106 vector
<string
> filenames
= Options::parseOptions(&opts
, argc
, argv
);
108 auto limit
= install_time_limit(opts
);
110 string progNameStr
= opts
.getBinaryName();
111 progName
= &progNameStr
;
113 if( opts
.getHelp() ) {
114 printUsage(opts
, true);
116 } else if( opts
.getLanguageHelp() ) {
117 Options::printLanguageHelp(*(opts
.getOut()));
119 } else if( opts
.getVersion() ) {
120 *(opts
.getOut()) << Configuration::about().c_str() << flush
;
124 segvSpin
= opts
.getSegvSpin();
126 // If in competition mode, set output stream option to flush immediately
127 #ifdef CVC5_COMPETITION_MODE
128 *(opts
.getOut()) << unitbuf
;
129 #endif /* CVC5_COMPETITION_MODE */
131 // We only accept one input file
132 if(filenames
.size() > 1) {
133 throw Exception("Too many input files specified.");
136 // If no file supplied we will read from standard input
137 const bool inputFromStdin
= filenames
.empty() || filenames
[0] == "-";
139 // if we're reading from stdin on a TTY, default to interactive mode
140 if(!opts
.wasSetByUserInteractive()) {
141 opts
.setInteractive(inputFromStdin
&& isatty(fileno(stdin
)));
144 // Auto-detect input language by filename extension
145 std::string
filenameStr("<stdin>");
146 if (!inputFromStdin
) {
147 // Use swap to avoid copying the string
148 // TODO: use std::move() when switching to c++11
149 filenameStr
.swap(filenames
[0]);
151 const char* filename
= filenameStr
.c_str();
153 if(opts
.getInputLanguage() == language::input::LANG_AUTO
) {
154 if( inputFromStdin
) {
155 // We can't do any fancy detection on stdin
156 opts
.setInputLanguage(language::input::LANG_CVC
);
158 unsigned len
= filenameStr
.size();
159 if(len
>= 5 && !strcmp(".smt2", filename
+ len
- 5)) {
160 opts
.setInputLanguage(language::input::LANG_SMTLIB_V2_6
);
161 } else if((len
>= 2 && !strcmp(".p", filename
+ len
- 2))
162 || (len
>= 5 && !strcmp(".tptp", filename
+ len
- 5))) {
163 opts
.setInputLanguage(language::input::LANG_TPTP
);
164 } else if(( len
>= 4 && !strcmp(".cvc", filename
+ len
- 4) )
165 || ( len
>= 5 && !strcmp(".cvc4", filename
+ len
- 5) )) {
166 opts
.setInputLanguage(language::input::LANG_CVC
);
167 } else if((len
>= 3 && !strcmp(".sy", filename
+ len
- 3))
168 || (len
>= 3 && !strcmp(".sl", filename
+ len
- 3))) {
169 // version 2 sygus is the default
170 opts
.setInputLanguage(language::input::LANG_SYGUS_V2
);
175 if(opts
.getOutputLanguage() == language::output::LANG_AUTO
) {
176 opts
.setOutputLanguage(language::toOutputLanguage(opts
.getInputLanguage()));
179 // Determine which messages to show based on smtcomp_mode and verbosity
180 if(Configuration::isMuzzledBuild()) {
181 DebugChannel
.setStream(&cvc5::null_os
);
182 TraceChannel
.setStream(&cvc5::null_os
);
183 NoticeChannel
.setStream(&cvc5::null_os
);
184 ChatChannel
.setStream(&cvc5::null_os
);
185 MessageChannel
.setStream(&cvc5::null_os
);
186 WarningChannel
.setStream(&cvc5::null_os
);
189 // important even for muzzled builds (to get result output right)
190 (*(opts
.getOut())) << language::SetLanguage(opts
.getOutputLanguage());
192 // Create the command executor to execute the parsed commands
193 pExecutor
= std::make_unique
<CommandExecutor
>(opts
);
197 // notify SmtEngine that we are starting to parse
198 pExecutor
->getSmtEngine()->notifyStartParsing(filenameStr
);
200 // Parse and execute commands until we are done
201 std::unique_ptr
<Command
> cmd
;
203 if(opts
.getInteractive() && inputFromStdin
) {
204 if(opts
.getTearDownIncremental() > 0) {
206 "--tear-down-incremental doesn't work in interactive mode");
208 if(!opts
.wasSetByUserIncrementalSolving()) {
209 cmd
.reset(new SetOptionCommand("incremental", "true"));
211 pExecutor
->doCommand(cmd
);
213 InteractiveShell
shell(pExecutor
->getSolver(),
214 pExecutor
->getSymbolManager());
215 if(opts
.getInteractivePrompt()) {
216 CVC5Message() << Configuration::getPackageName() << " "
217 << Configuration::getVersionString();
218 if(Configuration::isGitBuild()) {
219 CVC5Message() << " [" << Configuration::getGitId() << "]";
221 CVC5Message() << (Configuration::isDebugBuild() ? " DEBUG" : "")
223 << (Configuration::isAssertionBuild() ? "on" : "off")
226 CVC5Message() << Configuration::copyright() << endl
;
231 cmd
.reset(shell
.readCommand());
232 } catch(UnsafeInterruptException
& e
) {
233 (*opts
.getOut()) << CommandInterrupted();
238 status
= pExecutor
->doCommand(cmd
) && status
;
239 if (cmd
->interrupted()) {
243 } else if( opts
.getTearDownIncremental() > 0) {
244 if(!opts
.getIncrementalSolving() && opts
.getTearDownIncremental() > 1) {
245 // For tear-down-incremental values greater than 1, need incremental
247 cmd
.reset(new SetOptionCommand("incremental", "true"));
249 pExecutor
->doCommand(cmd
);
250 // if(opts.wasSetByUserIncrementalSolving()) {
251 // throw OptionException(
252 // "--tear-down-incremental incompatible with --incremental");
255 // cmd.reset(new SetOptionCommand("incremental", "false"));
256 // cmd->setMuted(true);
257 // pExecutor->doCommand(cmd);
260 ParserBuilder
parserBuilder(pExecutor
->getSolver(),
261 pExecutor
->getSymbolManager(),
265 if( inputFromStdin
) {
266 parserBuilder
.withStreamInput(cin
);
269 vector
< vector
<Command
*> > allCommands
;
270 allCommands
.push_back(vector
<Command
*>());
271 std::unique_ptr
<Parser
> parser(parserBuilder
.build());
273 // true if one of the commands was interrupted
274 bool interrupted
= false;
278 (*opts
.getOut()) << CommandInterrupted();
283 cmd
.reset(parser
->nextCommand());
284 if (cmd
== nullptr) break;
285 } catch (UnsafeInterruptException
& e
) {
290 if(dynamic_cast<PushCommand
*>(cmd
.get()) != nullptr) {
291 if(needReset
>= opts
.getTearDownIncremental()) {
293 for(size_t i
= 0; i
< allCommands
.size() && !interrupted
; ++i
) {
294 if (interrupted
) break;
295 for(size_t j
= 0; j
< allCommands
[i
].size() && !interrupted
; ++j
)
297 Command
* ccmd
= allCommands
[i
][j
]->clone();
298 ccmd
->setMuted(true);
299 pExecutor
->doCommand(ccmd
);
300 if (ccmd
->interrupted())
309 allCommands
.push_back(vector
<Command
*>());
310 Command
* copy
= cmd
->clone();
311 allCommands
.back().push_back(copy
);
312 status
= pExecutor
->doCommand(cmd
);
313 if(cmd
->interrupted()) {
317 } else if(dynamic_cast<PopCommand
*>(cmd
.get()) != nullptr) {
318 allCommands
.pop_back(); // fixme leaks cmds here
319 if (needReset
>= opts
.getTearDownIncremental()) {
321 for(size_t i
= 0; i
< allCommands
.size() && !interrupted
; ++i
) {
322 for(size_t j
= 0; j
< allCommands
[i
].size() && !interrupted
; ++j
)
324 std::unique_ptr
<Command
> ccmd(allCommands
[i
][j
]->clone());
325 ccmd
->setMuted(true);
326 pExecutor
->doCommand(ccmd
);
327 if (ccmd
->interrupted())
333 if (interrupted
) continue;
334 (*opts
.getOut()) << CommandSuccess();
337 status
= pExecutor
->doCommand(cmd
);
338 if(cmd
->interrupted()) {
343 } else if(dynamic_cast<CheckSatCommand
*>(cmd
.get()) != nullptr ||
344 dynamic_cast<QueryCommand
*>(cmd
.get()) != nullptr) {
345 if(needReset
>= opts
.getTearDownIncremental()) {
347 for(size_t i
= 0; i
< allCommands
.size() && !interrupted
; ++i
) {
348 for(size_t j
= 0; j
< allCommands
[i
].size() && !interrupted
; ++j
)
350 Command
* ccmd
= allCommands
[i
][j
]->clone();
351 ccmd
->setMuted(true);
352 pExecutor
->doCommand(ccmd
);
353 if (ccmd
->interrupted())
368 status
= pExecutor
->doCommand(cmd
);
369 if(cmd
->interrupted()) {
373 } else if(dynamic_cast<ResetCommand
*>(cmd
.get()) != nullptr) {
374 pExecutor
->doCommand(cmd
);
376 allCommands
.push_back(vector
<Command
*>());
378 // We shouldn't copy certain commands, because they can cause
379 // an error on replay since there's no associated sat/unsat check
381 if(dynamic_cast<GetUnsatCoreCommand
*>(cmd
.get()) == nullptr &&
382 dynamic_cast<GetProofCommand
*>(cmd
.get()) == nullptr &&
383 dynamic_cast<GetValueCommand
*>(cmd
.get()) == nullptr &&
384 dynamic_cast<GetModelCommand
*>(cmd
.get()) == nullptr &&
385 dynamic_cast<GetAssignmentCommand
*>(cmd
.get()) == nullptr &&
386 dynamic_cast<GetInstantiationsCommand
*>(cmd
.get()) == nullptr &&
387 dynamic_cast<GetAssertionsCommand
*>(cmd
.get()) == nullptr &&
388 dynamic_cast<GetInfoCommand
*>(cmd
.get()) == nullptr &&
389 dynamic_cast<GetOptionCommand
*>(cmd
.get()) == nullptr &&
390 dynamic_cast<EchoCommand
*>(cmd
.get()) == nullptr) {
391 Command
* copy
= cmd
->clone();
392 allCommands
.back().push_back(copy
);
394 status
= pExecutor
->doCommand(cmd
);
395 if(cmd
->interrupted()) {
400 if(dynamic_cast<QuitCommand
*>(cmd
.get()) != nullptr) {
406 if(!opts
.wasSetByUserIncrementalSolving()) {
407 cmd
.reset(new SetOptionCommand("incremental", "false"));
409 pExecutor
->doCommand(cmd
);
412 ParserBuilder
parserBuilder(pExecutor
->getSolver(),
413 pExecutor
->getSymbolManager(),
417 if( inputFromStdin
) {
418 parserBuilder
.withStreamInput(cin
);
421 std::unique_ptr
<Parser
> parser(parserBuilder
.build());
422 bool interrupted
= false;
426 (*opts
.getOut()) << CommandInterrupted();
431 cmd
.reset(parser
->nextCommand());
432 if (cmd
== nullptr) break;
433 } catch (UnsafeInterruptException
& e
) {
438 status
= pExecutor
->doCommand(cmd
);
439 if (cmd
->interrupted() && status
== 0) {
444 if(dynamic_cast<QuitCommand
*>(cmd
.get()) != nullptr) {
452 result
= pExecutor
->getResult();
455 // there was some kind of error
459 #ifdef CVC5_COMPETITION_MODE
461 // exit, don't return (don't want destructors to run)
462 // _exit() from unistd.h doesn't run global destructors
463 // or other on_exit/atexit stuff.
465 #endif /* CVC5_COMPETITION_MODE */
468 pExecutor
->flushOutputStreams();
471 if(opts
.getEarlyExit() && opts
.wasSetByUserEarlyExit()) {
474 #else /* CVC5_DEBUG */
475 if(opts
.getEarlyExit()) {
478 #endif /* CVC5_DEBUG */
483 signal_handlers::cleanup();