mirror of
https://github.com/Z3Prover/z3
synced 2026-07-04 22:36:10 +00:00
Update tptp_frontend.cpp
This commit is contained in:
parent
d666ef1ddf
commit
8e70dbaebc
1 changed files with 48 additions and 6 deletions
|
|
@ -87,6 +87,7 @@ struct token {
|
||||||
std::string text;
|
std::string text;
|
||||||
unsigned line = 1;
|
unsigned line = 1;
|
||||||
unsigned col = 1;
|
unsigned col = 1;
|
||||||
|
bool dquote = false; // true for double-quoted strings ("..."): TPTP distinct objects
|
||||||
};
|
};
|
||||||
|
|
||||||
class lexer {
|
class lexer {
|
||||||
|
|
@ -163,6 +164,7 @@ public:
|
||||||
if (peek() == '\'' || peek() == '"') {
|
if (peek() == '\'' || peek() == '"') {
|
||||||
char q = get();
|
char q = get();
|
||||||
t.kind = token_kind::str;
|
t.kind = token_kind::str;
|
||||||
|
t.dquote = (q == '"');
|
||||||
while (!eof()) {
|
while (!eof()) {
|
||||||
char c = get();
|
char c = get();
|
||||||
if (c == '\\' && !eof()) {
|
if (c == '\\' && !eof()) {
|
||||||
|
|
@ -292,6 +294,11 @@ class tptp_parser {
|
||||||
bool m_has_conjecture = false;
|
bool m_has_conjecture = false;
|
||||||
unsigned m_dropped_formulas = 0; // axioms/definitions skipped due to encoding errors
|
unsigned m_dropped_formulas = 0; // axioms/definitions skipped due to encoding errors
|
||||||
bool m_last_name_quoted = false;
|
bool m_last_name_quoted = false;
|
||||||
|
bool m_last_name_dquoted = false; // last parsed name was a double-quoted distinct object
|
||||||
|
// Distinct objects: TPTP double-quoted strings ("...") denote pairwise distinct
|
||||||
|
// domain elements. Collected here (deduplicated by name) so a single global
|
||||||
|
// (distinct ...) constraint can be asserted before solving.
|
||||||
|
std::unordered_map<std::string, expr*> m_distinct_objects;
|
||||||
std::string m_expected_status; // SZS status from the input annotation, if any
|
std::string m_expected_status; // SZS status from the input annotation, if any
|
||||||
std::unordered_map<std::string, sort*> m_sorts;
|
std::unordered_map<std::string, sort*> m_sorts;
|
||||||
sort_ref_vector m_pinned_sorts; // prevents cached sorts from being freed
|
sort_ref_vector m_pinned_sorts; // prevents cached sorts from being freed
|
||||||
|
|
@ -426,6 +433,7 @@ class tptp_parser {
|
||||||
std::string parse_name() {
|
std::string parse_name() {
|
||||||
if (is(token_kind::id) || is(token_kind::str)) {
|
if (is(token_kind::id) || is(token_kind::str)) {
|
||||||
m_last_name_quoted = is(token_kind::str);
|
m_last_name_quoted = is(token_kind::str);
|
||||||
|
m_last_name_dquoted = is(token_kind::str) && m_curr.dquote;
|
||||||
std::string r = m_curr.text;
|
std::string r = m_curr.text;
|
||||||
next();
|
next();
|
||||||
return r;
|
return r;
|
||||||
|
|
@ -957,6 +965,7 @@ class tptp_parser {
|
||||||
return parse_numeral_from_name(n);
|
return parse_numeral_from_name(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dq_name = m_last_name_dquoted;
|
||||||
expr_ref b(m);
|
expr_ref b(m);
|
||||||
// Check bound variables: uppercase (quantifier vars) AND lowercase (let-bound names)
|
// Check bound variables: uppercase (quantifier vars) AND lowercase (let-bound names)
|
||||||
if (!m_last_name_quoted && find_bound(n, b)) {
|
if (!m_last_name_quoted && find_bound(n, b)) {
|
||||||
|
|
@ -1007,7 +1016,10 @@ class tptp_parser {
|
||||||
|
|
||||||
func_decl* f = mk_decl_or_ho_const(n, args.size(), false);
|
func_decl* f = mk_decl_or_ho_const(n, args.size(), false);
|
||||||
coerce_args(f, args);
|
coerce_args(f, args);
|
||||||
return expr_ref(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m);
|
expr_ref term(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m);
|
||||||
|
if (dq_name && args.empty())
|
||||||
|
register_distinct_object(n, term);
|
||||||
|
return term;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grammar: <tff_logic_formula> ::= <tff_unitary_formula> | <tff_binary_formula>
|
// Grammar: <tff_logic_formula> ::= <tff_unitary_formula> | <tff_binary_formula>
|
||||||
|
|
@ -1346,7 +1358,7 @@ class tptp_parser {
|
||||||
// resume precedence-climbing parsing from that negated left operand.
|
// resume precedence-climbing parsing from that negated left operand.
|
||||||
expr_ref operand = parse_unary_formula(true);
|
expr_ref operand = parse_unary_formula(true);
|
||||||
expr_ref neg(m.mk_not(ensure_bool(operand)), m);
|
expr_ref neg(m.mk_not(ensure_bool(operand)), m);
|
||||||
inner = parse_binary_rest(neg, PREC_IFF, true, true);
|
inner = parse_binary_rest(neg, PREC_IFF, true);
|
||||||
} else {
|
} else {
|
||||||
// Binary connective at start of parens — shouldn't happen in valid TPTP
|
// Binary connective at start of parens — shouldn't happen in valid TPTP
|
||||||
throw parse_error("unexpected connective after '(' at " + loc());
|
throw parse_error("unexpected connective after '(' at " + loc());
|
||||||
|
|
@ -1391,6 +1403,7 @@ class tptp_parser {
|
||||||
return parse_numeral_from_name(n);
|
return parse_numeral_from_name(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dq_name = m_last_name_dquoted;
|
||||||
// Check if name is let-bound (works for both uppercase vars and lowercase let-bound names)
|
// Check if name is let-bound (works for both uppercase vars and lowercase let-bound names)
|
||||||
{
|
{
|
||||||
expr_ref b(m);
|
expr_ref b(m);
|
||||||
|
|
@ -1492,7 +1505,10 @@ class tptp_parser {
|
||||||
|
|
||||||
func_decl* pred = mk_decl_or_ho_const(n, args.size(), is_boolean);
|
func_decl* pred = mk_decl_or_ho_const(n, args.size(), is_boolean);
|
||||||
coerce_args(pred, args);
|
coerce_args(pred, args);
|
||||||
return expr_ref(m.mk_app(pred, args.size(), args.data()), m);
|
expr_ref atom(m.mk_app(pred, args.size(), args.data()), m);
|
||||||
|
if (dq_name && args.empty() && !m.is_bool(atom))
|
||||||
|
register_distinct_object(n, atom);
|
||||||
|
return atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grammar: <thf_abstraction> ::= ^ [<thf_variable_list>] : <thf_unitary_formula>
|
// Grammar: <thf_abstraction> ::= ^ [<thf_variable_list>] : <thf_unitary_formula>
|
||||||
|
|
@ -1711,13 +1727,13 @@ class tptp_parser {
|
||||||
// Implements a Pratt-style (precedence climbing) parser for binary connectives.
|
// Implements a Pratt-style (precedence climbing) parser for binary connectives.
|
||||||
expr_ref parse_expr(unsigned min_prec, bool consume_at, bool is_boolean) {
|
expr_ref parse_expr(unsigned min_prec, bool consume_at, bool is_boolean) {
|
||||||
expr_ref e = parse_unary_formula(is_boolean);
|
expr_ref e = parse_unary_formula(is_boolean);
|
||||||
return parse_binary_rest(e, min_prec, consume_at, m.is_bool(e));
|
return parse_binary_rest(e, min_prec, consume_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precedence-climbing loop continued from an already-parsed left operand `e`.
|
// Precedence-climbing loop continued from an already-parsed left operand `e`.
|
||||||
// Split out from parse_expr so callers that have consumed a leading unary unit
|
// Split out from parse_expr so callers that have consumed a leading unary unit
|
||||||
// (e.g. a '~' immediately after '(') can resume binary-connective parsing.
|
// (e.g. a '~' immediately after '(') can resume binary-connective parsing.
|
||||||
expr_ref parse_binary_rest(expr_ref e, unsigned min_prec, bool consume_at = true, bool is_boolean = true) {
|
expr_ref parse_binary_rest(expr_ref e, unsigned min_prec, bool consume_at = true) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Handle @ (function application) with highest precedence
|
// Handle @ (function application) with highest precedence
|
||||||
// But NOT when we're inside a lambda body that's an @ argument
|
// But NOT when we're inside a lambda body that's an @ argument
|
||||||
|
|
@ -1748,7 +1764,15 @@ class tptp_parser {
|
||||||
if (it->second.precedence < min_prec) break;
|
if (it->second.precedence < min_prec) break;
|
||||||
next(); // consume the operator token
|
next(); // consume the operator token
|
||||||
unsigned next_prec = it->second.right_assoc ? it->second.precedence : it->second.precedence + 1;
|
unsigned next_prec = it->second.right_assoc ? it->second.precedence : it->second.precedence + 1;
|
||||||
expr_ref rhs = parse_expr(next_prec, consume_at, is_boolean);
|
// Operands of every connective except '='/'!=' are Boolean; only equality
|
||||||
|
// takes term operands. Derive the operand context from the operator rather
|
||||||
|
// than inheriting is_boolean from the left operand. Otherwise, once a
|
||||||
|
// term-valued equality literal (e.g. 'a = b') sets is_boolean to false, a
|
||||||
|
// predicate atom later in the same clause ('a = b | q') would be parsed as a
|
||||||
|
// term and boxed into '$box_U_to_Bool(q)', severing it from the Boolean
|
||||||
|
// predicate 'q' used elsewhere and making refutable problems satisfiable.
|
||||||
|
bool rhs_is_boolean = it->second.precedence != PREC_EQ;
|
||||||
|
expr_ref rhs = parse_expr(next_prec, consume_at, rhs_is_boolean);
|
||||||
expr_ref_vector args(m);
|
expr_ref_vector args(m);
|
||||||
args.push_back(e);
|
args.push_back(e);
|
||||||
args.push_back(rhs);
|
args.push_back(rhs);
|
||||||
|
|
@ -2176,6 +2200,12 @@ public:
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record a double-quoted string constant as a TPTP distinct object (deduplicated by name).
|
||||||
|
void register_distinct_object(std::string const& name, expr* c) {
|
||||||
|
if (m_distinct_objects.emplace(name, c).second)
|
||||||
|
m_pinned_exprs.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
void parse_input(std::istream& in, std::string const& current_file) {
|
void parse_input(std::istream& in, std::string const& current_file) {
|
||||||
// Save parser state so that included files don't clobber the caller's lexer.
|
// Save parser state so that included files don't clobber the caller's lexer.
|
||||||
std::string saved_input = std::move(m_input);
|
std::string saved_input = std::move(m_input);
|
||||||
|
|
@ -2213,6 +2243,17 @@ public:
|
||||||
|
|
||||||
bool has_conjecture() const { return m_has_conjecture; }
|
bool has_conjecture() const { return m_has_conjecture; }
|
||||||
|
|
||||||
|
// TPTP double-quoted strings ("...") denote pairwise distinct domain elements.
|
||||||
|
// Assert a single global distinctness constraint over all collected distinct objects
|
||||||
|
// so that e.g. "Apple" != "Microsoft" is recognized as a theorem.
|
||||||
|
void assert_distinct_objects() {
|
||||||
|
if (m_distinct_objects.size() < 2) return;
|
||||||
|
expr_ref_vector objs(m);
|
||||||
|
for (auto const& kv : m_distinct_objects)
|
||||||
|
objs.push_back(kv.second);
|
||||||
|
m_cmd.assert_expr(expr_ref(m.mk_distinct(objs.size(), objs.data()), m));
|
||||||
|
}
|
||||||
|
|
||||||
// Number of axioms/definitions that were dropped during parsing because the
|
// Number of axioms/definitions that were dropped during parsing because the
|
||||||
// higher-order encoding could not type-check them. When non-zero, a "sat"
|
// higher-order encoding could not type-check them. When non-zero, a "sat"
|
||||||
// verdict cannot be trusted (the missing constraints may be exactly what
|
// verdict cannot be trusted (the missing constraints may be exactly what
|
||||||
|
|
@ -2339,6 +2380,7 @@ static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
|
||||||
|
|
||||||
tptp_parser p(ctx);
|
tptp_parser p(ctx);
|
||||||
p.parse_input(in, current_file ? current_file : ".");
|
p.parse_input(in, current_file ? current_file : ".");
|
||||||
|
p.assert_distinct_objects();
|
||||||
|
|
||||||
// Suppress default check-sat output; TPTP frontend reports SZS status explicitly.
|
// Suppress default check-sat output; TPTP frontend reports SZS status explicitly.
|
||||||
std::ostringstream sink;
|
std::ostringstream sink;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue