GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/main/driver_unified.cpp Lines: 71 134 53.0 %
Date: 2021-09-29 Branches: 130 444 29.3 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Morgan Deters, Liana Hadarean, Tim King
4
 *
5
 * This file is part of the cvc5 project.
6
 *
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
 * ****************************************************************************
12
 *
13
 * Driver for cvc5 executable (cvc5).
14
 */
15
16
#include <stdio.h>
17
#include <unistd.h>
18
19
#include <cstdlib>
20
#include <cstring>
21
#include <fstream>
22
#include <iostream>
23
#include <memory>
24
#include <new>
25
26
#include "api/cpp/cvc5.h"
27
#include "base/configuration.h"
28
#include "base/cvc5config.h"
29
#include "base/output.h"
30
#include "main/command_executor.h"
31
#include "main/interactive_shell.h"
32
#include "main/main.h"
33
#include "main/options.h"
34
#include "main/signal_handlers.h"
35
#include "main/time_limit.h"
36
#include "parser/parser.h"
37
#include "parser/parser_builder.h"
38
#include "smt/command.h"
39
#include "smt/smt_engine.h"
40
#include "util/result.h"
41
42
using namespace std;
43
using namespace cvc5;
44
using namespace cvc5::parser;
45
using namespace cvc5::main;
46
47
namespace cvc5 {
48
namespace main {
49
50
/** Full argv[0] */
51
const char* progPath;
52
53
/** Just the basename component of argv[0] */
54
7575
std::string progName;
55
56
/** A pointer to the CommandExecutor (the signal handlers need it) */
57
7575
std::unique_ptr<cvc5::main::CommandExecutor> pExecutor;
58
59
}  // namespace main
60
}  // namespace cvc5
61
62
    void printUsage(const api::DriverOptions& dopts, bool full)
63
    {
64
      std::stringstream ss;
65
      ss << "usage: " << progName << " [options] [input-file]" << std::endl
66
         << std::endl
67
         << "Without an input file, or with `-', cvc5 reads from standard "
68
            "input."
69
         << std::endl
70
         << std::endl
71
         << "cvc5 options:" << std::endl;
72
      if (full)
73
      {
74
        main::printUsage(ss.str(), dopts.out());
75
      }
76
      else
77
      {
78
        main::printShortUsage(ss.str(), dopts.out());
79
      }
80
    }
81
82
6527
int runCvc5(int argc, char* argv[], std::unique_ptr<api::Solver>& solver)
83
{
84
  // Initialize the signal handlers
85
6527
  signal_handlers::install();
86
87
6527
  progPath = argv[0];
88
89
  // Create the command executor to execute the parsed commands
90
6527
  pExecutor = std::make_unique<CommandExecutor>(solver);
91
6527
  api::DriverOptions dopts = solver->getDriverOptions();
92
93
  // Parse the options
94
10426
  std::vector<string> filenames = main::parse(*solver, argc, argv, progName);
95
96
7798
  auto limit = install_time_limit(solver->getOptionInfo("tlimit").uintValue());
97
98
3899
  if (solver->getOptionInfo("help").boolValue())
99
  {
100
    printUsage(dopts, true);
101
    exit(1);
102
  }
103
3899
  else if (solver->getOptionInfo("language-help").boolValue())
104
  {
105
    main::printLanguageHelp(dopts.out());
106
    exit(1);
107
  }
108
3899
  else if (solver->getOptionInfo("version").boolValue())
109
  {
110
    dopts.out() << Configuration::about().c_str() << flush;
111
    exit(0);
112
  }
113
114
3899
  segvSpin = solver->getOptionInfo("segv-spin").boolValue();
115
116
  // If in competition mode, set output stream option to flush immediately
117
#ifdef CVC5_COMPETITION_MODE
118
  dopts.out() << unitbuf;
119
#endif /* CVC5_COMPETITION_MODE */
120
121
  // We only accept one input file
122
3899
  if(filenames.size() > 1) {
123
    throw Exception("Too many input files specified.");
124
  }
125
126
  // If no file supplied we will read from standard input
127
3899
  const bool inputFromStdin = filenames.empty() || filenames[0] == "-";
128
129
  // if we're reading from stdin on a TTY, default to interactive mode
130
3899
  if (!solver->getOptionInfo("interactive").setByUser)
131
  {
132
3899
    solver->setOption("interactive", (inputFromStdin && isatty(fileno(stdin))) ? "true" : "false");
133
  }
134
135
  // Auto-detect input language by filename extension
136
7798
  std::string filenameStr("<stdin>");
137
3899
  if (!inputFromStdin) {
138
3899
    filenameStr = std::move(filenames[0]);
139
  }
140
3899
  const char* filename = filenameStr.c_str();
141
142
3899
  if (solver->getOption("input-language") == "LANG_AUTO")
143
  {
144
3685
    if( inputFromStdin ) {
145
      // We can't do any fancy detection on stdin
146
      solver->setOption("input-language", "smt2");
147
    } else {
148
3685
      size_t len = filenameStr.size();
149
3685
      if(len >= 5 && !strcmp(".smt2", filename + len - 5)) {
150
3628
        solver->setOption("input-language", "smt2");
151
57
      } else if((len >= 2 && !strcmp(".p", filename + len - 2))
152
16
                || (len >= 5 && !strcmp(".tptp", filename + len - 5))) {
153
41
        solver->setOption("input-language", "tptp");
154
16
      } else if((len >= 3 && !strcmp(".sy", filename + len - 3))
155
                || (len >= 3 && !strcmp(".sl", filename + len - 3))) {
156
        // version 2 sygus is the default
157
16
        solver->setOption("input-language", "sygus2");
158
      }
159
    }
160
  }
161
162
3899
  if (solver->getOption("output-language") == "LANG_AUTO")
163
  {
164
3899
    solver->setOption("output-language", solver->getOption("input-language"));
165
  }
166
3899
  pExecutor->storeOptionsAsOriginal();
167
168
  // Determine which messages to show based on smtcomp_mode and verbosity
169
3899
  if(Configuration::isMuzzledBuild()) {
170
    DebugChannel.setStream(&cvc5::null_os);
171
    TraceChannel.setStream(&cvc5::null_os);
172
    NoticeChannel.setStream(&cvc5::null_os);
173
    ChatChannel.setStream(&cvc5::null_os);
174
    MessageChannel.setStream(&cvc5::null_os);
175
    WarningChannel.setStream(&cvc5::null_os);
176
  }
177
178
3899
  int returnValue = 0;
179
  {
180
3899
    solver->setInfo("filename", filenameStr);
181
182
    // Parse and execute commands until we are done
183
7798
    std::unique_ptr<Command> cmd;
184
3899
    bool status = true;
185
3899
    if (solver->getOptionInfo("interactive").boolValue() && inputFromStdin)
186
    {
187
      if (!solver->getOptionInfo("incremental").setByUser)
188
      {
189
        cmd.reset(new SetOptionCommand("incremental", "true"));
190
        cmd->setMuted(true);
191
        pExecutor->doCommand(cmd);
192
      }
193
      InteractiveShell shell(pExecutor->getSolver(),
194
                             pExecutor->getSymbolManager(),
195
                             dopts.in(),
196
                             dopts.out());
197
198
      CVC5Message() << Configuration::getPackageName() << " "
199
                    << Configuration::getVersionString();
200
      if (Configuration::isGitBuild())
201
      {
202
        CVC5Message() << " [" << Configuration::getGitId() << "]";
203
      }
204
      CVC5Message() << (Configuration::isDebugBuild() ? " DEBUG" : "")
205
                    << " assertions:"
206
                    << (Configuration::isAssertionBuild() ? "on" : "off")
207
                    << endl
208
                    << endl;
209
      CVC5Message() << Configuration::copyright() << endl;
210
211
      while(true) {
212
        try {
213
          cmd.reset(shell.readCommand());
214
        } catch(UnsafeInterruptException& e) {
215
          dopts.out() << CommandInterrupted();
216
          break;
217
        }
218
        if (cmd == nullptr)
219
          break;
220
        status = pExecutor->doCommand(cmd) && status;
221
        if (cmd->interrupted()) {
222
          break;
223
        }
224
      }
225
    }
226
    else
227
    {
228
3899
      if (!solver->getOptionInfo("incremental").setByUser)
229
      {
230
3505
        cmd.reset(new SetOptionCommand("incremental", "false"));
231
3505
        cmd->setMuted(true);
232
3505
        pExecutor->doCommand(cmd);
233
      }
234
235
      ParserBuilder parserBuilder(
236
7798
          pExecutor->getSolver(), pExecutor->getSymbolManager(), true);
237
7798
      std::unique_ptr<Parser> parser(parserBuilder.build());
238
3899
      if( inputFromStdin ) {
239
        parser->setInput(Input::newStreamInput(
240
            solver->getOption("input-language"), cin, filename));
241
      }
242
      else
243
      {
244
15596
        parser->setInput(
245
7798
            Input::newFileInput(solver->getOption("input-language"),
246
                                filename,
247
7798
                                solver->getOptionInfo("mmap").boolValue()));
248
      }
249
250
3899
      bool interrupted = false;
251
421141
      while (status)
252
      {
253
212485
        if (interrupted) {
254
          dopts.out() << CommandInterrupted();
255
          pExecutor->reset();
256
          break;
257
        }
258
        try {
259
212485
          cmd.reset(parser->nextCommand());
260
212467
          if (cmd == nullptr) break;
261
        } catch (UnsafeInterruptException& e) {
262
          interrupted = true;
263
          continue;
264
        }
265
266
209044
        status = pExecutor->doCommand(cmd);
267
209044
        if (cmd->interrupted() && status == 0) {
268
          interrupted = true;
269
          break;
270
        }
271
272
209044
        if(dynamic_cast<QuitCommand*>(cmd.get()) != nullptr) {
273
423
          break;
274
        }
275
      }
276
    }
277
278
7762
    api::Result result;
279
3881
    if(status) {
280
3846
      result = pExecutor->getResult();
281
3846
      returnValue = 0;
282
    } else {
283
      // there was some kind of error
284
35
      returnValue = 1;
285
    }
286
287
#ifdef CVC5_COMPETITION_MODE
288
    dopts.out() << std::flush;
289
    // exit, don't return (don't want destructors to run)
290
    // _exit() from unistd.h doesn't run global destructors
291
    // or other on_exit/atexit stuff.
292
    _exit(returnValue);
293
#endif /* CVC5_COMPETITION_MODE */
294
295
3881
    pExecutor->flushOutputStreams();
296
297
#ifdef CVC5_DEBUG
298
    {
299
7762
      auto info = solver->getOptionInfo("early-exit");
300
3881
      if (info.boolValue() && info.setByUser)
301
      {
302
        _exit(returnValue);
303
      }
304
    }
305
#else  /* CVC5_DEBUG */
306
    if (solver->getOptionInfo("early-exit").boolValue())
307
    {
308
      _exit(returnValue);
309
    }
310
#endif /* CVC5_DEBUG */
311
  }
312
313
3881
  pExecutor.reset();
314
315
3881
  signal_handlers::cleanup();
316
317
7762
  return returnValue;
318
22725
}