/********************* */ /*! \file translator.cpp ** \verbatim ** Top contributors (to current version): ** Morgan Deters, Tim King, Aina Niemetz ** This file is part of the CVC4 project. ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief CVC4 translator ** ** The CVC4 translator executable. This program translates from one of ** CVC4's input languages to one of its output languages. **/ #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace CVC4; using namespace CVC4::language; using namespace CVC4::parser; enum { INPUT_LANG = 'L', OUTPUT_LANG = 'O', OUTPUT_FILE = 'o', HELP = 'h', DEFAULT_DAG_THRESH, EXPAND_DEFINITIONS, COMBINE_ASSERTIONS, };/* enum */ const struct option longopts[] = { { "output-lang", required_argument, NULL, OUTPUT_LANG }, { "output-language", required_argument, NULL, OUTPUT_LANG }, { "expand-definitions", no_argument, NULL, EXPAND_DEFINITIONS }, { "combine-assertions", no_argument, NULL, COMBINE_ASSERTIONS }, { "default-dag-thresh", required_argument, NULL, DEFAULT_DAG_THRESH }, { "lang", required_argument, NULL, INPUT_LANG }, { "language", required_argument, NULL, INPUT_LANG }, { "out", required_argument, NULL, OUTPUT_FILE }, { "help", no_argument, NULL, HELP }, { NULL, no_argument, NULL, 0 }, };/* longopts */ static void showHelp() { cerr << "cvc4-translator translation tool" << endl << " --output-language | -O set output language (default smt2)" << endl << " --input-language | -L set input language (default auto)" << endl << " --out | -o set output file (- for stdout)" << endl << " --default-dag-thresh=N set DAG threshold" << endl << " --expand-definitions expand define-funs" << endl << " --combine-assertions combine all assertions into one" << endl << " --help | -h this help" << endl << "Options and input filenames can be intermixed, and order is important." << endl << "For instance, \"-O smt2 x -O cvc4 y\" reads file x in smt2 format and" << "file y in cvc4 format and writes all output to stdout." << endl << "Some canonicalization may occur." << endl << "Comments and formatting are not preserved." << endl; } static void readFile(const char* filename, InputLanguage fromLang, OutputLanguage toLang, bool expand_definitions, bool combine_assertions, ostream* out) { if(fromLang == input::LANG_AUTO) { unsigned len = strlen(filename); if(len >= 5 && !strcmp(".smt2", filename + len - 5)) { fromLang = language::input::LANG_SMTLIB_V2; } else if((len >= 2 && !strcmp(".p", filename + len - 2)) || (len >= 5 && !strcmp(".tptp", filename + len - 5))) { fromLang = language::input::LANG_TPTP; } else if(( len >= 4 && !strcmp(".cvc", filename + len - 4) ) || ( len >= 5 && !strcmp(".cvc4", filename + len - 5) )) { fromLang = language::input::LANG_CVC4; } else { throw Exception("cannot determine input language to use for `" + string(filename) + "'"); } } if(toLang == output::LANG_AUTO) { toLang = toOutputLanguage(fromLang); } *out << language::SetLanguage(toLang); Options opts; opts.setInputLanguage(fromLang); ExprManager exprMgr(opts); std::unique_ptr solver = std::unique_ptr(new api::Solver(&opts)); ParserBuilder parserBuilder(solver.get(), filename, opts); if(!strcmp(filename, "-")) { parserBuilder.withFilename(""); parserBuilder.withLineBufferedStreamInput(cin); } Parser *parser = parserBuilder.build(); // we only use the SmtEngine to do definition expansion for us SmtEngine *smt = expand_definitions ? new SmtEngine(&exprMgr) : NULL; // store up the assertions into one big conjunction std::vector assertions; while(Command* cmd = parser->nextCommand()) { if(expand_definitions && dynamic_cast(cmd) != NULL) { // tell the SmtEngine about the definition, but don't print it cmd->invoke(smt); } else { // have to switch on the command and expand definitions inside it if(expand_definitions && dynamic_cast(cmd) != NULL) { Expr e = static_cast(cmd)->getExpr(); delete cmd; cmd = new AssertCommand(smt->expandDefinitions(e)); } else if(expand_definitions && dynamic_cast(cmd) != NULL) { Expr e = static_cast(cmd)->getExpr(); delete cmd; cmd = new QueryCommand(smt->expandDefinitions(e)); } else if(expand_definitions && dynamic_cast(cmd) != NULL) { Expr e = static_cast(cmd)->getExpr(); delete cmd; cmd = new CheckSatCommand(smt->expandDefinitions(e)); } else if(expand_definitions && dynamic_cast(cmd) != NULL) { Expr e = static_cast(cmd)->getTerm(); delete cmd; cmd = new SimplifyCommand(smt->expandDefinitions(e)); } else if(expand_definitions && dynamic_cast(cmd) != NULL) { Expr e = static_cast(cmd)->getTerm(); delete cmd; cmd = new ExpandDefinitionsCommand(smt->expandDefinitions(e)); } else if(expand_definitions && dynamic_cast(cmd) != NULL) { vector terms = static_cast(cmd)->getTerms(); delete cmd; for(size_t i = 0; i < terms.size(); ++i) { terms[i] = smt->expandDefinitions(terms[i]); } cmd = new GetValueCommand(terms); } if(combine_assertions && dynamic_cast(cmd) != NULL) { // store up the assertion, don't print it yet assertions.push_back(static_cast(cmd)->getExpr()); } else { if(combine_assertions && ( dynamic_cast(cmd) != NULL || dynamic_cast(cmd) != NULL || dynamic_cast(cmd) != NULL || dynamic_cast(cmd) != NULL || dynamic_cast(cmd) != NULL || dynamic_cast(cmd) != NULL )) { // combine all the stored assertions and print them at this point if(!assertions.empty()) { if(assertions.size() > 1) { *out << AssertCommand(exprMgr.mkExpr(kind::AND, assertions)) << endl; } else { *out << AssertCommand(assertions[0]) << endl; } assertions.clear(); } } // now print the cmd we just parsed *out << cmd << endl; } } if(dynamic_cast(cmd) != NULL) { delete cmd; break; } delete cmd; } if(!assertions.empty()) { if(assertions.size() > 1) { *out << AssertCommand(exprMgr.mkExpr(kind::AND, assertions)) << endl; } else { *out << AssertCommand(assertions[0]) << endl; } assertions.clear(); } *out << flush; delete smt; delete parser; } /** * Translate from an input language to an output language. */ int main(int argc, char* argv[]) { ostream* out = &cout; InputLanguage fromLang = input::LANG_AUTO; OutputLanguage toLang = output::LANG_SMTLIB_V2; size_t files = 0; int dag_thresh = 1; bool expand_definitions = false; bool combine_assertions = false; try { int c; while((c = getopt_long(argc, argv, "-L:O:o:h", longopts, NULL)) != -1) { switch(c) { case 1: ++files; *out << expr::ExprDag(dag_thresh); readFile(optarg, (!strcmp(optarg, "-") && fromLang == input::LANG_AUTO) ? input::LANG_CVC4 : fromLang, toLang, expand_definitions, combine_assertions, out); break; case INPUT_LANG: if(!strcmp(optarg, "help")) { Options::printLanguageHelp(cerr); exit(1); } fromLang = toInputLanguage(optarg); break; case OUTPUT_LANG: if(!strcmp(optarg, "help")) { Options::printLanguageHelp(cerr); exit(1); } toLang = toOutputLanguage(optarg); break; case OUTPUT_FILE: out->flush(); if(out != &cout) { ((ofstream*)out)->close(); delete out; } if(strcmp(optarg, "-")) { out = new ofstream(optarg); } else { out = &cout; } break; case DEFAULT_DAG_THRESH: { if(!isdigit(*optarg)) { cerr << "error: --default-dag-thresh requires non-negative argument: `" << optarg << "' invalid." << endl; exit(1); } char* end; unsigned long ul = strtoul(optarg, &end, 10); if(errno != 0 || *end != '\0') { cerr << "error: --default-dag-thresh argument malformed: `" << optarg << "'." << endl; exit(1); } dag_thresh = ul > INT_MAX ? INT_MAX : int(ul); } break; case EXPAND_DEFINITIONS: expand_definitions = true; break; case COMBINE_ASSERTIONS: combine_assertions = true; break; case 'h': showHelp(); exit(0); case '?': showHelp(); exit(1); default: cerr << "internal error. translator failed (" << char(c) << "," << c << ")." << endl; exit(1); } } if(files == 0) { *out << expr::ExprDag(dag_thresh); readFile("-", fromLang == input::LANG_AUTO ? input::LANG_CVC4 : fromLang, toLang, expand_definitions, combine_assertions, out); exit(0); } } catch(Exception& e) { cerr << e << endl; exit(1); } return 0; }