/********************* */ /*! \file tptp.cpp ** \verbatim ** Top contributors (to current version): ** Andrew Reynolds, Francois Bobot, Haniel Barbosa ** This file is part of the CVC4 project. ** Copyright (c) 2009-2021 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 Definition of TPTP parser. **/ // Do not #include "parser/antlr_input.h" directly. Rely on the header. #include "parser/tptp/tptp.h" #include #include #include "api/cvc4cpp.h" #include "base/check.h" #include "options/options.h" #include "parser/parser.h" #include "smt/command.h" // ANTLR defines these, which is really bad! #undef true #undef false namespace CVC4 { namespace parser { Tptp::Tptp(api::Solver* solver, SymbolManager* sm, Input* input, bool strictMode, bool parseOnly) : Parser(solver, sm, input, strictMode, parseOnly), d_cnf(false), d_fof(false) { addTheory(Tptp::THEORY_CORE); /* Try to find TPTP dir */ // From tptp4x FileUtilities //----Try the TPTP directory, and TPTP variations char* home = getenv("TPTP"); if(home == NULL) { home = getenv("TPTP_HOME"); // //----If no TPTP_HOME, try the tptp user (aaargh) // if(home == NULL && (PasswdEntry = getpwnam("tptp")) != NULL) { // home = PasswdEntry->pw_dir; // } //----Now look in the TPTP directory from there if(home != NULL) { d_tptpDir = home; d_tptpDir.append("/TPTP/"); } } else { d_tptpDir = home; //add trailing "/" if(d_tptpDir[d_tptpDir.size() - 1] != '/') { d_tptpDir.append("/"); } } d_hasConjecture = false; } Tptp::~Tptp() { for( unsigned i=0; ifree(d_in_created[i]); } } void Tptp::addTheory(Theory theory) { switch(theory) { case THEORY_CORE: //TPTP (CNF and FOF) is unsorted so we define this common type { std::string d_unsorted_name = "$$unsorted"; d_unsorted = d_solver->mkUninterpretedSort(d_unsorted_name); preemptCommand(new DeclareSortCommand(d_unsorted_name, 0, d_unsorted)); } // propositionnal defineType("Bool", d_solver->getBooleanSort()); defineVar("$true", d_solver->mkTrue()); defineVar("$false", d_solver->mkFalse()); addOperator(api::AND); addOperator(api::EQUAL); addOperator(api::IMPLIES); // addOperator(api::ITE); //only for tff thf addOperator(api::NOT); addOperator(api::OR); addOperator(api::XOR); addOperator(api::APPLY_UF); //Add quantifiers? break; default: std::stringstream ss; ss << "internal error: Tptp::addTheory(): unhandled theory " << theory; throw ParserException(ss.str()); } } /* The include are managed in the lexer but called in the parser */ // Inspired by http://www.antlr3.org/api/C/interop.html bool newInputStream(std::string fileName, pANTLR3_LEXER lexer, std::vector< pANTLR3_INPUT_STREAM >& inc ) { Debug("parser") << "Including " << fileName << std::endl; // Create a new input stream and take advantage of built in stream stacking // in C target runtime. // pANTLR3_INPUT_STREAM in; #ifdef CVC4_ANTLR3_OLD_INPUT_STREAM in = antlr3AsciiFileStreamNew((pANTLR3_UINT8) fileName.c_str()); #else /* CVC4_ANTLR3_OLD_INPUT_STREAM */ in = antlr3FileStreamNew((pANTLR3_UINT8) fileName.c_str(), ANTLR3_ENC_8BIT); #endif /* CVC4_ANTLR3_OLD_INPUT_STREAM */ if(in == NULL) { Debug("parser") << "Can't open " << fileName << std::endl; return false; } // Same thing as the predefined PUSHSTREAM(in); lexer->pushCharStream(lexer,in); // restart it //lexer->rec->state->tokenStartCharIndex = -10; //lexer->emit(lexer); // Note that the input stream is not closed when it EOFs, I don't bother // to do it here, but it is up to you to track streams created like this // and destroy them when the whole parse session is complete. Remember that you // don't want to do this until all tokens have been manipulated all the way through // your tree parsers etc as the token does not store the text it just refers // back to the input stream and trying to get the text for it will abort if you // close the input stream too early. // inc.push_back( in ); //TODO what said before return true; } void Tptp::includeFile(std::string fileName) { // security for online version if(!canIncludeFile()) { parseError("include-file feature was disabled for this run."); } // Get the lexer AntlrInput * ai = static_cast(getInput()); pANTLR3_LEXER lexer = ai->getAntlr3Lexer(); // push the inclusion scope; will be popped by our special popCharStream // would be necessary for handling symbol filtering in inclusions //pushScope(); // get the name of the current stream "Does it work inside an include?" const std::string inputName = ai->getInputStreamName(); // Test in the directory of the actual parsed file std::string currentDirFileName; if(inputName != "") { // TODO: Use dirname or Boost::filesystem? size_t pos = inputName.rfind('/'); if(pos != std::string::npos) { currentDirFileName = std::string(inputName, 0, pos + 1); } currentDirFileName.append(fileName); if(newInputStream(currentDirFileName,lexer, d_in_created)) { return; } } else { currentDirFileName = ""; } if(d_tptpDir.empty()) { parseError("Couldn't open included file: " + fileName + " at " + currentDirFileName + " and the TPTP directory is not specified (environment variable TPTP)"); }; std::string tptpDirFileName = d_tptpDir + fileName; if(! newInputStream(tptpDirFileName,lexer, d_in_created)) { parseError("Couldn't open included file: " + fileName + " at " + currentDirFileName + " or " + tptpDirFileName); } } void Tptp::checkLetBinding(const std::vector& bvlist, api::Term lhs, api::Term rhs, bool formula) { if (lhs.getKind() != api::APPLY_UF) { parseError("malformed let: LHS must be a flat function application"); } const std::multiset vars{lhs.begin(), lhs.end()}; if (formula && !lhs.getSort().isBoolean()) { parseError("malformed let: LHS must be formula"); } for (const CVC4::api::Term& var : vars) { if (var.hasOp()) { parseError("malformed let: LHS must be flat, illegal child: " + var.toString()); } } // ensure all let-bound variables appear on the LHS, and appear only once for (const api::Term& bound_var : bvlist) { const size_t count = vars.count(bound_var); if (count == 0) { parseError( "malformed let: LHS must make use of all quantified variables, " "missing `" + bound_var.toString() + "'"); } else if (count >= 2) { parseError("malformed let: LHS cannot use same bound variable twice: " + bound_var.toString()); } } } api::Term Tptp::parseOpToExpr(ParseOp& p) { api::Term expr; if (!p.d_expr.isNull()) { return p.d_expr; } // if it has a kind, it's a builtin one and this function should not have been // called Assert(p.d_kind == api::NULL_EXPR); if (isDeclared(p.d_name)) { // already appeared expr = getVariable(p.d_name); } else { api::Sort t = p.d_type == d_solver->getBooleanSort() ? p.d_type : d_unsorted; expr = bindVar(p.d_name, t, true); // must define at level zero preemptCommand(new DeclareFunctionCommand(p.d_name, expr, t)); } return expr; } api::Term Tptp::applyParseOp(ParseOp& p, std::vector& args) { if (Debug.isOn("parser")) { Debug("parser") << "applyParseOp: " << p << " to:" << std::endl; for (std::vector::iterator i = args.begin(); i != args.end(); ++i) { Debug("parser") << "++ " << *i << std::endl; } } Assert(!args.empty()); // If operator already defined, just build application if (!p.d_expr.isNull()) { // this happens with some arithmetic kinds, which are wrapped around // lambdas. args.insert(args.begin(), p.d_expr); return d_solver->mkTerm(api::APPLY_UF, args); } bool isBuiltinKind = false; // the builtin kind of the overall return expression api::Kind kind = api::NULL_EXPR; // First phase: piece operator together if (p.d_kind == api::NULL_EXPR) { // A non-built-in function application, get the expression api::Term v; if (isDeclared(p.d_name)) { // already appeared v = getVariable(p.d_name); } else { std::vector sorts(args.size(), d_unsorted); api::Sort t = p.d_type == d_solver->getBooleanSort() ? p.d_type : d_unsorted; t = d_solver->mkFunctionSort(sorts, t); v = bindVar(p.d_name, t, true); // must define at level zero preemptCommand(new DeclareFunctionCommand(p.d_name, v, t)); } // args might be rationals, in which case we need to create // distinct constants of the "unsorted" sort to represent them for (size_t i = 0; i < args.size(); ++i) { if (args[i].getSort().isReal() && v.getSort().getFunctionDomainSorts()[i] == d_unsorted) { args[i] = convertRatToUnsorted(args[i]); } } Assert(!v.isNull()); checkFunctionLike(v); kind = getKindForFunction(v); args.insert(args.begin(), v); } else { kind = p.d_kind; isBuiltinKind = true; } Assert(kind != api::NULL_EXPR); const Options& opts = d_solver->getOptions(); // Second phase: apply parse op to the arguments if (isBuiltinKind) { if (!opts.getUfHo() && (kind == api::EQUAL || kind == api::DISTINCT)) { // need --uf-ho if these operators are applied over function args for (std::vector::iterator i = args.begin(); i != args.end(); ++i) { if ((*i).getSort().isFunction()) { parseError( "Cannot apply equalty to functions unless --uf-ho is set."); } } } if (!strictModeEnabled() && (kind == api::AND || kind == api::OR) && args.size() == 1) { // Unary AND/OR can be replaced with the argument. return args[0]; } if (kind == api::MINUS && args.size() == 1) { return d_solver->mkTerm(api::UMINUS, args[0]); } return d_solver->mkTerm(kind, args); } // check if partially applied function, in this case we use HO_APPLY if (args.size() >= 2) { api::Sort argt = args[0].getSort(); if (argt.isFunction()) { unsigned arity = argt.getFunctionArity(); if (args.size() - 1 < arity) { if (!opts.getUfHo()) { parseError("Cannot partially apply functions unless --uf-ho is set."); } Debug("parser") << "Partial application of " << args[0]; Debug("parser") << " : #argTypes = " << arity; Debug("parser") << ", #args = " << args.size() - 1 << std::endl; // must curry the partial application return d_solver->mkTerm(api::HO_APPLY, args); } } } return d_solver->mkTerm(kind, args); } api::Term Tptp::mkDecimal( std::string& snum, std::string& sden, bool pos, size_t exp, bool posE) { // the numerator and the denominator std::stringstream ssn; std::stringstream ssd; if (exp != 0) { if (posE) { // see if we need to pad zeros on the end, e.g. 1.2E5 ---> 120000 if (exp >= sden.size()) { ssn << snum << sden; for (size_t i = 0, nzero = (exp - sden.size()); i < nzero; i++) { ssn << "0"; } ssd << "0"; } else { ssn << snum << sden.substr(0, exp); ssd << sden.substr(exp); } } else { // see if we need to pad zeros on the beginning, e.g. 1.2E-5 ---> 0.000012 if (exp >= snum.size()) { ssn << "0"; for (size_t i = 0, nzero = (exp - snum.size()); i < nzero; i++) { ssd << "0"; } ssd << snum << sden; } else { ssn << snum.substr(0, exp); ssd << snum.substr(exp) << sden; } } } else { ssn << snum; ssd << sden; } std::stringstream ss; if (!pos) { ss << "-"; } ss << ssn.str() << "." << ssd.str(); return d_solver->mkReal(ss.str()); } void Tptp::forceLogic(const std::string& logic) { Parser::forceLogic(logic); preemptCommand(new SetBenchmarkLogicCommand(logic)); } void Tptp::addFreeVar(api::Term var) { Assert(cnf()); d_freeVar.push_back(var); } std::vector Tptp::getFreeVar() { Assert(cnf()); std::vector r; r.swap(d_freeVar); return r; } api::Term Tptp::convertRatToUnsorted(api::Term expr) { // Create the conversion function If they doesn't exists if (d_rtu_op.isNull()) { api::Sort t; // Conversion from rational to unsorted t = d_solver->mkFunctionSort(d_solver->getRealSort(), d_unsorted); d_rtu_op = d_solver->mkConst(t, "$$rtu"); preemptCommand(new DeclareFunctionCommand("$$rtu", d_rtu_op, t)); // Conversion from unsorted to rational t = d_solver->mkFunctionSort(d_unsorted, d_solver->getRealSort()); d_utr_op = d_solver->mkConst(t, "$$utr"); preemptCommand(new DeclareFunctionCommand("$$utr", d_utr_op, t)); } // Add the inverse in order to show that over the elements that // appear in the problem there is a bijection between unsorted and // rational api::Term ret = d_solver->mkTerm(api::APPLY_UF, d_rtu_op, expr); if (d_r_converted.find(expr) == d_r_converted.end()) { d_r_converted.insert(expr); api::Term eq = d_solver->mkTerm( api::EQUAL, expr, d_solver->mkTerm(api::APPLY_UF, d_utr_op, ret)); preemptCommand(new AssertCommand(eq)); } return api::Term(ret); } api::Term Tptp::convertStrToUnsorted(std::string str) { api::Term& e = d_distinct_objects[str]; if (e.isNull()) { e = d_solver->mkConst(d_unsorted, str); } return e; } api::Term Tptp::mkLambdaWrapper(api::Kind k, api::Sort argType) { Debug("parser") << "mkLambdaWrapper: kind " << k << " and type " << argType << "\n"; std::vector lvars; std::vector domainTypes = argType.getFunctionDomainSorts(); for (unsigned i = 0, size = domainTypes.size(); i < size; ++i) { // the introduced variable is internal (not parsable) std::stringstream ss; ss << "_lvar_" << i; api::Term v = d_solver->mkVar(domainTypes[i], ss.str()); lvars.push_back(v); } // apply body of lambda to variables api::Term wrapper = d_solver->mkTerm(api::LAMBDA, d_solver->mkTerm(api::BOUND_VAR_LIST, lvars), d_solver->mkTerm(k, lvars)); return wrapper; } api::Term Tptp::getAssertionExpr(FormulaRole fr, api::Term expr) { switch (fr) { case FR_AXIOM: case FR_HYPOTHESIS: case FR_DEFINITION: case FR_ASSUMPTION: case FR_LEMMA: case FR_THEOREM: case FR_NEGATED_CONJECTURE: case FR_PLAIN: // it's a usual assert return expr; case FR_CONJECTURE: // it should be negated when asserted return d_solver->mkTerm(api::NOT, expr); case FR_UNKNOWN: case FR_FI_DOMAIN: case FR_FI_FUNCTORS: case FR_FI_PREDICATES: case FR_TYPE: // it does not correspond to an assertion return d_nullExpr; break; } Assert(false); // unreachable return d_nullExpr; } api::Term Tptp::getAssertionDistinctConstants() { std::vector constants; for (std::pair& cs : d_distinct_objects) { constants.push_back(cs.second); } if (constants.size() > 1) { return d_solver->mkTerm(api::DISTINCT, constants); } return d_nullExpr; } Command* Tptp::makeAssertCommand(FormulaRole fr, api::Term expr, bool cnf, bool inUnsatCore) { // For SZS ontology compliance. // if we're in cnf() though, conjectures don't result in "Theorem" or // "CounterSatisfiable". if (!cnf && (fr == FR_NEGATED_CONJECTURE || fr == FR_CONJECTURE)) { d_hasConjecture = true; Assert(!expr.isNull()); } if( expr.isNull() ){ return new EmptyCommand("Untreated role for expression"); }else{ return new AssertCommand(expr, inUnsatCore); } } }/* CVC4::parser namespace */ }/* CVC4 namespace */