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/main_options.h"
38 #include "options/option_exception.h"
39 #include "options/options.h"
40 #include "options/options_public.h"
41 #include "options/parser_options.h"
42 #include "options/set_language.h"
43 #include "parser/parser.h"
44 #include "parser/parser_builder.h"
45 #include "smt/command.h"
46 #include "smt/smt_engine.h"
47 #include "util/result.h"
51 using namespace cvc5::parser
;
52 using namespace cvc5::main
;
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(const 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
>();
102 // Initialize the signal handlers
103 signal_handlers::install();
108 std::vector
<string
> filenames
= options::parse(opts
, argc
, argv
, progName
);
110 auto limit
= install_time_limit(opts
);
112 if (opts
.driver
.help
)
114 printUsage(opts
, true);
117 else if (opts
.base
.languageHelp
)
119 options::printLanguageHelp(*opts
.base
.out
);
122 else if (opts
.driver
.version
)
124 *opts
.base
.out
<< Configuration::about().c_str() << flush
;
128 segvSpin
= opts
.driver
.segvSpin
;
130 // If in competition mode, set output stream option to flush immediately
131 #ifdef CVC5_COMPETITION_MODE
132 *opts
.base
.out
<< unitbuf
;
133 #endif /* CVC5_COMPETITION_MODE */
135 // We only accept one input file
136 if(filenames
.size() > 1) {
137 throw Exception("Too many input files specified.");
140 // If no file supplied we will read from standard input
141 const bool inputFromStdin
= filenames
.empty() || filenames
[0] == "-";
143 // if we're reading from stdin on a TTY, default to interactive mode
144 if (!opts
.driver
.interactiveWasSetByUser
)
146 opts
.driver
.interactive
= inputFromStdin
&& isatty(fileno(stdin
));
149 // Auto-detect input language by filename extension
150 std::string
filenameStr("<stdin>");
151 if (!inputFromStdin
) {
152 filenameStr
= std::move(filenames
[0]);
154 const char* filename
= filenameStr
.c_str();
156 if (opts
.base
.inputLanguage
== language::input::LANG_AUTO
)
158 if( inputFromStdin
) {
159 // We can't do any fancy detection on stdin
160 opts
.base
.inputLanguage
= language::input::LANG_CVC
;
162 size_t len
= filenameStr
.size();
163 if(len
>= 5 && !strcmp(".smt2", filename
+ len
- 5)) {
164 opts
.base
.inputLanguage
= language::input::LANG_SMTLIB_V2_6
;
165 } else if((len
>= 2 && !strcmp(".p", filename
+ len
- 2))
166 || (len
>= 5 && !strcmp(".tptp", filename
+ len
- 5))) {
167 opts
.base
.inputLanguage
= language::input::LANG_TPTP
;
168 } else if(( len
>= 4 && !strcmp(".cvc", filename
+ len
- 4) )
169 || ( len
>= 5 && !strcmp(".cvc4", filename
+ len
- 5) )) {
170 opts
.base
.inputLanguage
= language::input::LANG_CVC
;
171 } else if((len
>= 3 && !strcmp(".sy", filename
+ len
- 3))
172 || (len
>= 3 && !strcmp(".sl", filename
+ len
- 3))) {
173 // version 2 sygus is the default
174 opts
.base
.inputLanguage
= language::input::LANG_SYGUS_V2
;
179 if (opts
.base
.outputLanguage
== language::output::LANG_AUTO
)
181 opts
.base
.outputLanguage
= language::toOutputLanguage(opts
.base
.inputLanguage
);
184 // Determine which messages to show based on smtcomp_mode and verbosity
185 if(Configuration::isMuzzledBuild()) {
186 DebugChannel
.setStream(&cvc5::null_os
);
187 TraceChannel
.setStream(&cvc5::null_os
);
188 NoticeChannel
.setStream(&cvc5::null_os
);
189 ChatChannel
.setStream(&cvc5::null_os
);
190 MessageChannel
.setStream(&cvc5::null_os
);
191 WarningChannel
.setStream(&cvc5::null_os
);
194 // important even for muzzled builds (to get result output right)
196 << language::SetLanguage(opts
.base
.outputLanguage
);
198 // Create the command executor to execute the parsed commands
199 pExecutor
= std::make_unique
<CommandExecutor
>(opts
);
203 // notify SmtEngine that we are starting to parse
204 pExecutor
->getSmtEngine()->notifyStartParsing(filenameStr
);
206 // Parse and execute commands until we are done
207 std::unique_ptr
<Command
> cmd
;
209 if (opts
.driver
.interactive
&& inputFromStdin
)
211 if (!opts
.base
.incrementalSolvingWasSetByUser
)
213 cmd
.reset(new SetOptionCommand("incremental", "true"));
215 pExecutor
->doCommand(cmd
);
217 InteractiveShell
shell(pExecutor
->getSolver(),
218 pExecutor
->getSymbolManager());
220 CVC5Message() << Configuration::getPackageName() << " "
221 << Configuration::getVersionString();
222 if (Configuration::isGitBuild())
224 CVC5Message() << " [" << Configuration::getGitId() << "]";
226 CVC5Message() << (Configuration::isDebugBuild() ? " DEBUG" : "")
228 << (Configuration::isAssertionBuild() ? "on" : "off")
231 CVC5Message() << Configuration::copyright() << endl
;
235 cmd
.reset(shell
.readCommand());
236 } catch(UnsafeInterruptException
& e
) {
237 *opts
.base
.out
<< CommandInterrupted();
242 status
= pExecutor
->doCommand(cmd
) && status
;
243 if (cmd
->interrupted()) {
250 if (!opts
.base
.incrementalSolvingWasSetByUser
)
252 cmd
.reset(new SetOptionCommand("incremental", "false"));
254 pExecutor
->doCommand(cmd
);
257 ParserBuilder
parserBuilder(pExecutor
->getSolver(),
258 pExecutor
->getSymbolManager(),
260 std::unique_ptr
<Parser
> parser(parserBuilder
.build());
261 if( inputFromStdin
) {
262 parser
->setInput(Input::newStreamInput(
263 opts
.base
.inputLanguage
, cin
, filename
));
267 parser
->setInput(Input::newFileInput(opts
.base
.inputLanguage
,
269 opts
.parser
.memoryMap
));
272 bool interrupted
= false;
276 *opts
.base
.out
<< CommandInterrupted();
281 cmd
.reset(parser
->nextCommand());
282 if (cmd
== nullptr) break;
283 } catch (UnsafeInterruptException
& e
) {
288 status
= pExecutor
->doCommand(cmd
);
289 if (cmd
->interrupted() && status
== 0) {
294 if(dynamic_cast<QuitCommand
*>(cmd
.get()) != nullptr) {
302 result
= pExecutor
->getResult();
305 // there was some kind of error
309 #ifdef CVC5_COMPETITION_MODE
310 if (opts
.base
.out
!= nullptr)
312 *opts
.base
.out
<< std::flush
;
314 // exit, don't return (don't want destructors to run)
315 // _exit() from unistd.h doesn't run global destructors
316 // or other on_exit/atexit stuff.
318 #endif /* CVC5_COMPETITION_MODE */
321 pExecutor
->flushOutputStreams();
324 if (opts
.driver
.earlyExit
&& opts
.driver
.earlyExitWasSetByUser
)
328 #else /* CVC5_DEBUG */
329 if (opts
.driver
.earlyExit
)
333 #endif /* CVC5_DEBUG */
338 signal_handlers::cleanup();