/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_smt2_pp.cpp Abstract: Pretty printer of AST formulas using SMT2 format. This printer is more expensive than the one in ast_smt_pp.h, but is supposed to generated a "prettier" and SMT2 compliant output. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include"ast_smt2_pp.h" #include"shared_occs.h" #include"pp.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"algebraic_numbers.h" using namespace format_ns; #define ALIAS_PREFIX "a" #define MAX_INDENT 16 #define SMALL_INDENT 2 bool is_smt2_simple_symbol_char(char s) { return ('0' <= s && s <= '9') || ('a' <= s && s <= 'z') || ('A' <= s && s <= 'Z') || s == '~' || s == '!' || s == '@' || s == '$' || s == '%' || s == '^' || s == '&' || s == '*' || s == '_' || s == '-' || s == '+' || s == '=' || s == '<' || s == '>' || s == '.' || s == '?' || s == '/'; } bool is_smt2_quoted_symbol(char const * s) { if (s == 0) return false; if ('0' <= s[0] && s[0] <= '9') return true; unsigned len = static_cast(strlen(s)); for (unsigned i = 0; i < len; i++) if (!is_smt2_simple_symbol_char(s[i])) return true; return false; } bool is_smt2_quoted_symbol(symbol const & s) { if (s.is_numerical()) return false; return is_smt2_quoted_symbol(s.bare_str()); } std::string mk_smt2_quoted_symbol(symbol const & s) { SASSERT(is_smt2_quoted_symbol(s)); string_buffer<> buffer; buffer.append('|'); char const * str = s.bare_str(); while (*str) { if (*str == '|' || *str == '\\') buffer.append('\\'); buffer.append(*str); str++; } buffer.append('|'); return std::string(buffer.c_str()); } format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) const { ast_manager & m = get_manager(); if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); len = static_cast(str.length()); return mk_string(m, str.c_str()); } else if (s.is_numerical()) { std::string str = s.str(); len = static_cast(str.length()); return mk_string(m, str.c_str()); } else { len = static_cast(strlen(s.bare_str())); return mk_string(m, s.bare_str()); } } format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const { ast_manager & m = get_manager(); if (m.is_implies(f)) { len = 2; return mk_string(m, "=>"); } else if (m.is_ite(f)) { len = 3; return mk_string(m, "ite"); } else if (m.is_iff(f)) { len = 1; return mk_string(m, "="); } else { symbol s = f->get_name(); return pp_fdecl_name(s, len); } } bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const { if (f->get_family_id() == null_family_id) return false; unsigned num = f->get_num_parameters(); unsigned i; for (i = 0; i < num; i++) { if (f->get_parameter(i).is_int()) continue; if (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())) continue; break; } return i == num && num > 0; } bool smt2_pp_environment::is_sort_param(func_decl * f) const { return f->get_family_id() != null_family_id && f->get_num_parameters() == 1 && f->get_parameter(0).is_ast() && is_sort(f->get_parameter(0).get_ast()) && f->get_range() == to_sort(f->get_parameter(0).get_ast()); } format * smt2_pp_environment::pp_as(format * fname, sort * s) { format * buf[2] = { fname, pp_sort(s) }; SASSERT(buf[0] != 0 && buf[1] != 0); return mk_seq1(get_manager(), buf, buf + 2, f2f(), "as"); } format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { SASSERT(is_indexed_fdecl(f)); unsigned num = f->get_num_parameters(); ptr_buffer fs; fs.push_back(fname); for (unsigned i = 0; i < num; i++) { SASSERT(f->get_parameter(i).is_int() || (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); if (f->get_parameter(i).is_int()) fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int())); else fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); } return mk_seq1(get_manager(), fs.begin(), fs.end(), f2f(), "_"); } format * smt2_pp_environment::pp_fdecl(func_decl * f, unsigned & len) { format * fname = pp_fdecl_name(f, len); if (f->get_family_id() == null_family_id) return fname; if (is_sort_param(f)) { len = UINT_MAX; return pp_as(fname, f->get_range()); } if (is_indexed_fdecl(f)) { len = UINT_MAX; return pp_fdecl_params(fname, f); } return fname; } format * smt2_pp_environment::pp_signature(format * f_name, func_decl * f) { if (is_indexed_fdecl(f)) { f_name = pp_fdecl_params(f_name, f); } ptr_buffer f_domain; for (unsigned i = 0; i < f->get_arity(); i++) f_domain.push_back(pp_sort(f->get_domain(i))); ptr_buffer args; args.push_back(f_name); args.push_back(mk_seq5(get_manager(), f_domain.begin(), f_domain.end(), f2f())); args.push_back(pp_sort(f->get_range())); return mk_seq5(get_manager(), args.begin(), args.end(), f2f()); } format * smt2_pp_environment::pp_fdecl_ref(func_decl * f) { unsigned len; format * f_name = pp_fdecl_name(f, len); if (f->get_family_id() == null_family_id) { return f_name; } return pp_signature(f_name, f); } format * smt2_pp_environment::pp_bv_literal(app * t, bool use_bv_lits, bool bv_neg) { bv_util & u = get_bvutil(); SASSERT(u.is_numeral(t)); rational val; unsigned bv_size = 1; u.is_numeral(t, val, bv_size); SASSERT(val.is_int()); val = u.norm(val, bv_size, bv_neg); bool is_neg = false; if (val.is_neg()) { val.neg(); is_neg = true; } SASSERT(val.is_nonneg()); format * vf; if (!use_bv_lits) { string_buffer<> buf; buf << "(_ bv" << val.to_string().c_str() << " " << bv_size << ")"; vf = mk_string(get_manager(), buf.c_str()); } else { sbuffer buf; unsigned sz = 0; buf.push_back('#'); if (bv_size % 4 == 0) { buf.push_back('x'); while (val.is_pos()) { rational c = val % rational(16); val = div(val, rational(16)); SASSERT(rational(0) <= c && c < rational(16)); if (c <= rational(9)) buf.push_back('0' + c.get_unsigned()); else buf.push_back('a' + (c.get_unsigned() - 10)); sz+=4; } while (sz < bv_size) { buf.push_back('0'); sz+=4; } } else { buf.push_back('b'); while (val.is_pos()) { rational c = val % rational(2); val = div(val, rational(2)); SASSERT(rational(0) <= c && c < rational(2)); if (c.is_zero()) buf.push_back('0'); else buf.push_back('1'); sz += 1; } while (sz < bv_size) { buf.push_back('0'); sz += 1; } } SASSERT(sz == bv_size); std::reverse(buf.begin()+2, buf.end()); buf.push_back(0); vf = mk_string(get_manager(), buf.begin()); } if (is_neg) { format * buffer[1] = {vf}; return mk_seq1(get_manager(), buffer, buffer+1, f2f(), "bvneg"); } return vf; } format * smt2_pp_environment::pp_float_literal(app * t) { mpf_manager & fm = get_futil().fm(); scoped_mpf v(fm); format * body = 0; VERIFY(get_futil().is_value(t, v)); if (fm.is_nan(v)) { body = mk_string(get_manager(), "NaN"); } else if (fm.is_pinf(v)) { body = mk_string(get_manager(), "plusInfinity"); } else if (fm.is_ninf(v)) { body = mk_string(get_manager(), "minusInfinity"); } else if (fm.is_pzero(v)) { // TODO: make it SMT 2.0 compatible body = mk_string(get_manager(), "+0.0"); } else if (fm.is_nzero(v)) { // TODO: make it SMT 2.0 compatible body = mk_string(get_manager(), "-0.0"); } else { // TODO: make it SMT 2.0 compatible std::string val = fm.to_string(v); body = mk_string(get_manager(), val.c_str()); } return pp_as(body, get_manager().get_sort(t)); } // generate (- f) format * smt2_pp_environment::mk_neg(format * f) const { format * buffer[1] = {f}; return mk_seq1(get_manager(), buffer, buffer+1, f2f(), "-"); } // Return the format string .0 where num is the value of val. format * smt2_pp_environment::mk_float(rational const & val) const { SASSERT(val.is_nonneg()); SASSERT(val.is_int()); std::string s = val.to_string(); s += ".0"; return mk_string(get_manager(), s.c_str()); } format * smt2_pp_environment::pp_arith_literal(app * t, bool decimal, unsigned decimal_prec) { arith_util & u = get_autil(); SASSERT(u.is_numeral(t) || u.is_irrational_algebraic_numeral(t)); rational val; bool is_int = true; if (u.is_numeral(t, val, is_int)) { if (is_int) { if (val.is_nonneg()) { return mk_string(get_manager(), val.to_string().c_str()); } else { val.neg(); return mk_neg(mk_string(get_manager(), val.to_string().c_str())); } } else { bool is_neg = val.is_neg(); if (is_neg) val.neg(); format * vf; if (val.is_int()) { vf = mk_float(val); } else if (decimal) { std::ostringstream buffer; val.display_decimal(buffer, decimal_prec); vf = mk_string(get_manager(), buffer.str().c_str()); } else { format * buffer[2] = { mk_float(numerator(val)), mk_float(denominator(val)) }; vf = mk_seq1(get_manager(), buffer, buffer+2, f2f(), "/"); } return is_neg ? mk_neg(vf) : vf; } } else { SASSERT(u.is_irrational_algebraic_numeral(t)); anum const & val = u.to_irrational_algebraic_numeral(t); algebraic_numbers::manager & am = u.am(); format * vf; std::ostringstream buffer; bool is_neg = false; if (decimal) { scoped_anum abs_val(am); am.set(abs_val, val); if (am.is_neg(val)) { is_neg = true; am.neg(abs_val); } am.display_decimal(buffer, abs_val, decimal_prec); } else { am.display_root_smt2(buffer, val); } vf = mk_string(get_manager(), buffer.str().c_str()); return is_neg ? mk_neg(vf) : vf; } } format * smt2_pp_environment::pp_datalog_literal(app * t) { uint64 v; VERIFY (get_dlutil().is_numeral(t, v)); std::ostringstream buffer; buffer << v; return mk_string(get_manager(), buffer.str().c_str()); } format_ns::format * smt2_pp_environment::pp_sort(sort * s) { // Basic sort pretty printing. // This method is redefined in cmd_context::pp_env: support for parametric sorts. // Here, we just pretty print builtin sorts: Bool, Int, Real, BitVec and Array. ast_manager & m = get_manager(); if (m.is_bool(s)) return mk_string(m, "Bool"); if (get_autil().is_int(s)) return mk_string(m, "Int"); if (get_autil().is_real(s)) return mk_string(m, "Real"); if (get_bvutil().is_bv_sort(s)) { unsigned sz = get_bvutil().get_bv_size(s); ptr_buffer fs; fs.push_back(mk_string(m, "BitVec")); fs.push_back(mk_unsigned(m, sz)); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "_"); } if (get_arutil().is_array(s)) { ptr_buffer fs; unsigned sz = get_array_arity(s); for (unsigned i = 0; i < sz; i++) { fs.push_back(pp_sort(get_array_domain(s, i))); } fs.push_back(pp_sort(get_array_range(s))); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "Array"); } if (get_futil().is_float(s)) { unsigned ebits = get_futil().get_ebits(s); unsigned sbits = get_futil().get_sbits(s); ptr_buffer fs; fs.push_back(mk_string(m, "FP")); fs.push_back(mk_unsigned(m, ebits)); fs.push_back(mk_unsigned(m, sbits)); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "_"); } return format_ns::mk_string(get_manager(), s->get_name().str().c_str()); } typedef app_ref_vector format_ref_vector; class smt2_printer { ast_manager & m_manager; pp_params const & m_params; smt2_pp_environment & m_env; shared_occs m_soccs; expr * m_root; typedef obj_map expr2alias; // expr -> position @ m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. ptr_vector m_expr2alias_stack; expr2alias * m_expr2alias; // expr -> position @ m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. ptr_vector m_aliased_exprs; format_ref_vector m_aliased_pps; svector > m_aliased_lvls_names; unsigned m_next_alias_idx; struct scope { unsigned m_aliased_exprs_lim; unsigned m_old_next_alias_idx; expr * m_old_root; scope(unsigned lim, unsigned idx, expr * r):m_aliased_exprs_lim(lim), m_old_next_alias_idx(idx), m_old_root(r) {} }; svector m_scopes; // size of m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. svector m_var_names; typedef hashtable symbol_set; symbol_set m_var_names_set; struct frame { expr * m_curr; unsigned m_idx; unsigned m_spos; bool m_use_alias; // if new aliases can be created frame(expr * c, unsigned i, unsigned s, bool use_alias):m_curr(c), m_idx(i), m_spos(s), m_use_alias(use_alias) {} }; svector m_frame_stack; format_ref_vector m_format_stack; struct info { unsigned m_lvl; unsigned m_weight; unsigned m_depth; info(unsigned l, unsigned w, unsigned d):m_lvl(l), m_weight(w), m_depth(d) {} }; svector m_info_stack; string_buffer<> m_next_name_buffer; symbol next_name(char const * prefix, unsigned & idx) { while (true) { m_next_name_buffer.reset(); m_next_name_buffer.append(prefix); m_next_name_buffer.append("!"); m_next_name_buffer.append(idx); symbol r(m_next_name_buffer.c_str()); idx++; if (m_env.uses(r)) continue; if (m_var_names_set.contains(r)) continue; return r; } } symbol next_alias() { return next_name(ALIAS_PREFIX, m_next_alias_idx); } void register_alias(expr * n, format * nf, unsigned lvl, symbol const & name) { SASSERT(m_aliased_exprs.size() == m_aliased_pps.size()); SASSERT(m_aliased_exprs.size() == m_aliased_lvls_names.size()); unsigned idx = m_aliased_exprs.size(); m_expr2alias->insert(n, idx); m_aliased_exprs.push_back(n); m_aliased_pps.push_back(nf); m_aliased_lvls_names.push_back(std::make_pair(lvl, name)); } void push_frame(expr * n, bool use_alias) { m_frame_stack.push_back(frame(n, 0, m_format_stack.size(), use_alias)); } void pop_frame() { m_frame_stack.pop_back(); } ast_manager & m() const { return m_manager; } ast_manager & fm() const { return format_ns::fm(m()); } void pp_var(var * v) { format * f; if (v->get_idx() < m_var_names.size()) { symbol s = m_var_names[m_var_names.size() - v->get_idx() - 1]; f = mk_string(m(), s.str().c_str()); } else { // fallback... it is not supposed to happen when the printer is correctly used. string_buffer<> buf; buf.append("(:var "); buf.append(v->get_idx()); buf.append(")"); f = mk_string(m(), buf.c_str()); } m_format_stack.push_back(f); m_info_stack.push_back(info(0, 1, 1)); } format * pp_attribute(char const * attr, format * f) { return mk_compose(m(), mk_string(m(), attr), mk_indent(m(), static_cast(strlen(attr)), f)); } format * pp_simple_attribute(char const * attr, int v) { return mk_compose(m(), mk_string(m(), attr), mk_int(m(), v)); } format * pp_simple_attribute(char const * attr, symbol const & s) { return mk_compose(m(), mk_string(m(), attr), mk_string(m(), s.str().c_str())); } format * pp_labels(bool is_pos, buffer const & names, format * f) { if (names.empty()) return f; ptr_buffer buf; buf.push_back(f); for (unsigned i = 0; i < names.size(); i++) { buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", names[i])); } return mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } void pp_const(app * c) { format * f; if (m_env.get_autil().is_numeral(c) || m_env.get_autil().is_irrational_algebraic_numeral(c)) { f = m_env.pp_arith_literal(c, m_params.m_pp_decimal, m_params.m_pp_decimal_precision); } else if (m_env.get_bvutil().is_numeral(c)) { f = m_env.pp_bv_literal(c, m_params.m_pp_bv_lits, m_params.m_pp_bv_neg); } else if (m_env.get_futil().is_value(c)) { f = m_env.pp_float_literal(c); } else if (m_env.get_dlutil().is_numeral(c)) { f = m_env.pp_datalog_literal(c); } else { buffer names; if (m().is_label_lit(c, names)) { f = pp_labels(true, names, mk_string(m(), "true")); } else { unsigned len; f = m_env.pp_fdecl(c->get_decl(), len); } } m_format_stack.push_back(f); m_info_stack.push_back(info(0, 1, 1)); } bool pp_aliased(expr * t) { unsigned idx; if (m_expr2alias->find(t, idx)) { unsigned lvl = m_aliased_lvls_names[idx].first; symbol const & s = m_aliased_lvls_names[idx].second; m_format_stack.push_back(mk_string(m(), s.str().c_str())); m_info_stack.push_back(info(lvl+1, 1, 1)); return true; } return false; } void process_var(var * v) { pp_var(v); pop_frame(); } bool process_args(app * t, frame & fr) { unsigned num = t->get_num_args(); while (fr.m_idx < num) { expr * arg = t->get_arg(fr.m_idx); fr.m_idx++; if (pp_aliased(arg)) continue; switch (arg->get_kind()) { case AST_VAR: pp_var(to_var(arg)); break; case AST_APP: if (to_app(arg)->get_num_args() == 0) { pp_const(to_app(arg)); } else { push_frame(arg, fr.m_use_alias); return false; } break; case AST_QUANTIFIER: push_frame(arg, fr.m_use_alias); return false; default: UNREACHABLE(); } } return true; } void store_result(expr * t, frame & fr, format * f, info & f_info) { m_format_stack.shrink(fr.m_spos); m_info_stack.shrink(fr.m_spos); if (fr.m_use_alias && m_root != t && ((f_info.m_depth >= m_params.m_pp_max_depth) || ((f_info.m_weight >= m_params.m_pp_min_alias_size || is_quantifier(t)) && m_soccs.is_shared(t)))) { symbol a = next_alias(); TRACE("smt2_pp", tout << "a: " << a << " depth: " << f_info.m_depth << ", weight: " << f_info.m_weight << ", lvl: " << f_info.m_lvl << " t: #" << t->get_id() << "\n" << mk_ll_pp(t, m()) << ", is-shared: " << m_soccs.is_shared(t) << "\n";); register_alias(t, f, f_info.m_lvl, a); m_format_stack.push_back(mk_string(m(), a.str().c_str())); m_info_stack.push_back(info(f_info.m_lvl + 1, 1, 1)); } else { m_format_stack.push_back(f); m_info_stack.push_back(f_info); } pop_frame(); } bool flat_assoc(app * t, frame const & fr) { if (!m_params.m_pp_flat_assoc) return false; func_decl * f = t->get_decl(); if (f->is_associative() && m_frame_stack.size() >= 2 && !m_soccs.is_shared(t)) { frame const & prev_fr = m_frame_stack[m_frame_stack.size() - 2]; return is_app(prev_fr.m_curr) && to_app(prev_fr.m_curr)->get_decl() == f; } return false; } void process_app(app * t, frame & fr) { if (fr.m_idx == 0) { if (pp_aliased(t)) { pop_frame(); return; } } if (!process_args(t, fr)) return; if (t->get_num_args() == 0) { pp_const(t); pop_frame(); return; } if (flat_assoc(t, fr)) { pop_frame(); return; } buffer labels; bool is_pos; format * f = 0; format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = m_format_stack.c_ptr() + m_format_stack.size(); if (m().is_label(t, is_pos, labels)) { SASSERT(it + 1 == end); f = pp_labels(is_pos, labels, *it); } else if (m().is_pattern(t)) { f = mk_seq5(m(), it, end, f2f()); } else { unsigned len; SASSERT(it < end); format * fname = m_env.pp_fdecl(t->get_decl(), len); if (len > MAX_INDENT) { f = mk_group(m(), mk_compose(m(), mk_indent(m(), 1, mk_compose(m(), mk_string(m(), "("), fname)), mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_seq(m(), it, end, f2f()), mk_string(m(), ")"))))); } else { format * first = *it; ++it; f = mk_group(m(), mk_compose(m(), mk_indent(m(), 1, mk_compose(m(), mk_string(m(), "("), fname)), mk_indent(m(), len + 2, mk_compose(m(), mk_string(m(), " "), first, mk_seq(m(), it, end, f2f()), mk_string(m(), ")"))))); } } info f_info(0, 1, 1); info * it2 = m_info_stack.begin() + fr.m_spos; info * end2 = m_info_stack.end(); for (; it2 != end2; ++it2) { if (it2->m_lvl > f_info.m_lvl) f_info.m_lvl = it2->m_lvl; f_info.m_weight += it2->m_weight; if (it2->m_depth > f_info.m_depth) f_info.m_depth = it2->m_depth; } f_info.m_depth++; store_result(t, fr, f, f_info); } // Add let decls used to build f. format * pp_let(format * f, unsigned & num_lets) { unsigned old_sz = m_scopes.empty() ? 0 : m_scopes.back().m_aliased_exprs_lim; unsigned sz = m_aliased_exprs.size(); SASSERT(old_sz <= sz); num_lets = sz - old_sz; TRACE("pp_let", tout << "old_sz: " << old_sz << ", sz: " << sz << "\n";); if (old_sz == sz) return f; vector > decls; for (unsigned i = old_sz; i < sz; i++) { unsigned lvl = m_aliased_lvls_names[i].first; symbol f_name = m_aliased_lvls_names[i].second; format * f_def[1] = { m_aliased_pps.get(i) }; decls.reserve(lvl+1); ptr_vector & lvl_decls = decls[lvl]; lvl_decls.push_back(mk_seq1(m(), f_def, f_def+1, f2f(), f_name.str().c_str())); } TRACE("pp_let", tout << "decls.size(): " << decls.size() << "\n";); ptr_buffer buf; unsigned num_op = 0; vector >::iterator it = decls.begin(); vector >::iterator end = decls.end(); for (; it != end; ++it) { ptr_vector & lvl_decls = *it; if (lvl_decls.empty()) continue; if (num_op > 0) buf.push_back(mk_line_break(m())); num_op++; buf.push_back(mk_string(m(), "(let ")); buf.push_back(mk_indent(m(), 5, mk_seq5(m(), lvl_decls.begin(), lvl_decls.end(), f2f()))); } TRACE("pp_let", tout << "num_op: " << num_op << "\n";); if (num_op == 0) return f; buf.push_back(mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_line_break(m()), f))); for (unsigned i = 0; i < num_op; i++) buf.push_back(mk_string(m(), ")")); return mk_compose(m(), buf.size(), buf.c_ptr()); } format * pp_let(format * f) { unsigned num_lets; return pp_let(f, num_lets); } void begin_scope() { SASSERT(m_aliased_exprs.size() == m_aliased_pps.size()); SASSERT(m_aliased_exprs.size() == m_aliased_lvls_names.size()); TRACE("pp_scope", tout << "[begin-scope] sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); m_scopes.push_back(scope(m_aliased_exprs.size(), m_next_alias_idx, m_root)); unsigned lvl = m_scopes.size(); while (lvl >= m_expr2alias_stack.size()) m_expr2alias_stack.push_back(alloc(expr2alias)); m_expr2alias = m_expr2alias_stack[lvl]; m_next_alias_idx = 1; } void end_scope() { TRACE("pp_scope", tout << "[end-scope] before sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); m_expr2alias->reset(); scope & s = m_scopes.back(); unsigned old_sz = s.m_aliased_exprs_lim; m_root = s.m_old_root; m_next_alias_idx = s.m_old_next_alias_idx; m_scopes.pop_back(); unsigned new_lvl = m_scopes.size(); m_expr2alias = m_expr2alias_stack[new_lvl]; SASSERT(old_sz <= m_aliased_exprs.size()); m_aliased_exprs.shrink(old_sz); m_aliased_pps.shrink(old_sz); m_aliased_lvls_names.shrink(old_sz); TRACE("pp_scope", tout << "[end-scope] after sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); } void register_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { symbol name = q->get_decl_name(i); if (name.is_numerical()) { unsigned idx = 1; name = next_name("x", idx); } else if (m_env.uses(name) || m_var_names_set.contains(name)) { unsigned idx = 1; name = next_name(name.bare_str(), idx); } SASSERT(!m_var_names_set.contains(name)); m_var_names.push_back(name); m_var_names_set.insert(name); } } void unregister_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { symbol s = m_var_names.back(); m_var_names.pop_back(); m_var_names_set.erase(s); } } format * pp_var_decls(quantifier * q) { ptr_buffer buf; unsigned num_decls = q->get_num_decls(); SASSERT(num_decls <= m_var_names.size()); symbol * it = m_var_names.end() - num_decls; for (unsigned i = 0; i < num_decls; i++, it++) { format * fs[1] = { m_env.pp_sort(q->get_decl_sort(i)) }; buf.push_back(mk_seq1(m(), fs, fs+1, f2f(), it->str().c_str())); } return mk_seq5(m(), buf.begin(), buf.end(), f2f()); } void process_quantifier(quantifier * q, frame & fr) { if (fr.m_idx == 0) { begin_scope(); m_root = q->get_expr(); register_var_names(q); } unsigned num_children = q->get_num_patterns() + q->get_num_no_patterns() + 1; if (fr.m_idx < num_children) { unsigned idx = fr.m_idx; fr.m_idx++; if (idx < q->get_num_patterns()) { push_frame(q->get_pattern(idx), false); } else if (idx < q->get_num_patterns() + q->get_num_no_patterns()) { push_frame(q->get_no_pattern(idx - q->get_num_patterns()), false); } else { push_frame(q->get_expr(), fr.m_use_alias); } return; } unsigned num_lets = 0; format * f_body = pp_let(m_format_stack.back(), num_lets); // The current SMT2 frontend uses weight 1 as default. #define MIN_WEIGHT 1 if (q->has_patterns() || q->get_weight() > MIN_WEIGHT || q->get_skid() != symbol::null || (q->get_qid() != symbol::null && !q->get_qid().is_numerical())) { ptr_buffer buf; buf.push_back(f_body); if (q->get_num_patterns() > 0) { format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = it + q->get_num_patterns(); for (; it != end; ++it) { buf.push_back(pp_attribute(":pattern ", *it)); } } if (q->get_num_no_patterns() > 0) { format ** it = m_format_stack.c_ptr() + fr.m_spos + q->get_num_patterns(); format ** end = it + q->get_num_no_patterns(); for (; it != end; ++it) { buf.push_back(pp_attribute(":no-pattern ", *it)); } } if (q->get_weight() > MIN_WEIGHT) { buf.push_back(pp_simple_attribute(":weight ", q->get_weight())); } if (q->get_skid() != symbol::null) { buf.push_back(pp_simple_attribute(":skid ", q->get_skid())); } if (q->get_qid() != symbol::null) { #if 0 buf.push_back(pp_simple_attribute(":qid ", q->get_qid())); #else if (!q->get_qid().is_numerical()) buf.push_back(pp_simple_attribute(":qid ", q->get_qid())); #endif } f_body = mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } format * f_decls = pp_var_decls(q); format * fs[2] = { f_decls, f_body }; char const * header = q->is_forall() ? "forall" : "exists"; format * f = mk_seq3(m(), fs, fs+2, f2f(), header, 1, SMALL_INDENT); info f_info = m_info_stack.back(); f_info.m_lvl = 0; // quantifiers don't depend on any let-decls, pp_let added all dependencies for the body. f_info.m_depth++; f_info.m_weight += q->get_num_decls()*2 + num_lets*8; unregister_var_names(q); end_scope(); store_result(q, fr, f, f_info); } void init_expr2alias_stack() { SASSERT(m_expr2alias_stack.empty()); expr2alias * new_map = alloc(expr2alias); m_expr2alias_stack.push_back(new_map); m_expr2alias = new_map; } void del_expr2alias_stack() { std::for_each(m_expr2alias_stack.begin(), m_expr2alias_stack.end(), delete_proc()); m_expr2alias_stack.reset(); m_expr2alias = 0; } void reset_expr2alias_stack() { SASSERT(!m_expr2alias_stack.empty()); ptr_vector::iterator it = m_expr2alias_stack.begin(); ptr_vector::iterator end = m_expr2alias_stack.end(); for (; it != end; ++it) (*it)->reset(); m_expr2alias = m_expr2alias_stack[0]; } void reset_stacks() { m_next_alias_idx = 1; reset_expr2alias_stack(); m_aliased_exprs.reset(); m_aliased_pps.reset(); m_aliased_lvls_names.reset(); m_scopes.reset(); m_frame_stack.reset(); m_format_stack.reset(); m_info_stack.reset(); } void process(expr * n, format_ref & r) { reset_stacks(); SASSERT(&(r.get_manager()) == &(fm())); m_soccs(n); TRACE("smt2_pp_shared", tout << "shared terms for:\n" << mk_pp(n, m()) << "\n"; tout << "------>\n"; shared_occs::iterator it = m_soccs.begin_shared(); shared_occs::iterator end = m_soccs.end_shared(); for (; it != end; ++it) { tout << mk_pp(*it, m()) << "\n"; }); m_root = n; push_frame(n, true); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); switch (fr.m_curr->get_kind()) { case AST_QUANTIFIER: process_quantifier(to_quantifier(fr.m_curr), fr); break; case AST_APP: process_app(to_app(fr.m_curr), fr); break; case AST_VAR: process_var(to_var(fr.m_curr)); break; default: UNREACHABLE(); } } r = pp_let(m_format_stack.back()); m_format_stack.pop_back(); } void reset_var_names() { m_var_names.reset(); m_var_names_set.reset(); } public: smt2_printer(smt2_pp_environment & env, pp_params const & params): m_manager(env.get_manager()), m_params(params), m_env(env), m_soccs(m_manager), m_root(0), m_aliased_pps(fm()), m_next_alias_idx(1), m_format_stack(fm()) { init_expr2alias_stack(); } ~smt2_printer() { del_expr2alias_stack(); } void operator()(expr * n, format_ref & r) { reset_var_names(); process(n, r); } void operator()(expr * n, unsigned num, char const * var_prefix, format_ref & r, sbuffer & var_names) { reset_var_names(); if (var_prefix == 0) var_prefix = "x"; if (strcmp(var_prefix, ALIAS_PREFIX) == 0) { var_prefix = "_a"; } unsigned idx = 1; for (unsigned i = 0; i < num; i++) { symbol name = next_name(var_prefix, idx); var_names.push_back(name); m_var_names_set.insert(name); m_var_names.push_back(name); } std::reverse(m_var_names.begin(), m_var_names.end()); process(n, r); } void operator()(sort * s, format_ref & r) { r = m_env.pp_sort(s); } void operator()(func_decl * f, format_ref & r) { unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); format * args[3]; args[0] = fname; ptr_buffer buf; for (unsigned i = 0; i < arity; i++) { buf.push_back(m_env.pp_sort(f->get_domain(i))); } args[1] = mk_seq5(m(), buf.begin(), buf.end(), f2f()); args[2] = m_env.pp_sort(f->get_range()); r = mk_seq1(m(), args, args+3, f2f(), "declare-fun"); } }; void mk_smt2_format(expr * n, smt2_pp_environment & env, pp_params const & p, unsigned num_vars, char const * var_prefix, format_ref & r, sbuffer & var_names) { smt2_printer pr(env, p); pr(n, num_vars, var_prefix, r, var_names); } void mk_smt2_format(sort * s, smt2_pp_environment & env, pp_params const & p, format_ref & r) { smt2_printer pr(env, p); pr(s, r); } void mk_smt2_format(func_decl * f, smt2_pp_environment & env, pp_params const & p, format_ref & r) { smt2_printer pr(env, p); pr(f, r); } std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, pp_params const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(n, env, p, num_vars, var_prefix, r, var_names); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, pp_params const & p, unsigned indent) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(s, env, p, r); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, pp_params const & p, unsigned indent) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(f, env, p, r); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, pp_params const & p, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), m_params(p), m_indent(indent), m_num_vars(num_vars), m_var_prefix(var_prefix) { } mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), m_params(get_pp_default_params()), m_indent(indent), m_num_vars(num_vars), m_var_prefix(var_prefix) { } std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { smt2_pp_environment_dbg env(p.m_manager); if (is_expr(p.m_ast)) { ast_smt2_pp(out, to_expr(p.m_ast), env, p.m_params, p.m_indent, p.m_num_vars, p.m_var_prefix); } else if (is_sort(p.m_ast)) { ast_smt2_pp(out, to_sort(p.m_ast), env, p.m_params, p.m_indent); } else { SASSERT(is_func_decl(p.m_ast)); ast_smt2_pp(out, to_func_decl(p.m_ast), env, p.m_params, p.m_indent); } return out; } #ifdef Z3DEBUG void pp(expr const * n, ast_manager & m) { std::cout << mk_ismt2_pp(const_cast(n), m) << std::endl; } void pp(expr_ref const & n) { std::cout << mk_ismt2_pp(n.get(), n.m()) << std::endl; } #endif