lean2/src/kernel/expr.h

620 lines
30 KiB
C
Raw Normal View History

/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
2013-09-13 23:14:24 +00:00
#include <algorithm>
#include <atomic>
#include <iostream>
#include <limits>
#include <utility>
#include "util/rc.h"
#include "util/name.h"
#include "util/hash.h"
#include "util/buffer.h"
#include "util/list_fn.h"
#include "util/sexpr/format.h"
#include "kernel/level.h"
namespace lean {
class value;
/* =======================================
Expressions
expr ::= Var idx
| Constant name
| Value value
| App [expr]
| Lambda name expr expr
| Pi name expr expr
| Type universe
| Eq expr expr (heterogeneous equality)
| Let name expr expr expr
| Metavar idx meta_ctx
meta_ctx ::= [meta_entry]
meta_entry ::= lift idx idx
| inst idx expr
TODO(Leo): match expressions.
The main API is divided in the following sections
- Testers
- Constructors
- Accessors
- Miscellaneous
======================================= */
enum class expr_kind { Var, Constant, Value, App, Lambda, Pi, Type, Eq, Let, MetaVar };
class meta_entry;
/**
\brief A metavariable context is just a list of meta_entries.
\see meta_entry
*/
typedef list<meta_entry> meta_ctx;
/**
\brief Base class used to represent expressions.
In principle, the expr_cell class and subclasses should be located in the .cpp file.
However, this is performance critical code, and we want to be able to have
inline definitions.
*/
class expr_cell {
protected:
unsigned short m_kind;
std::atomic_ushort m_flags;
unsigned m_hash;
MK_LEAN_RC(); // Declare m_rc counter
void dealloc();
bool max_shared() const { return (m_flags & 1) != 0; }
void set_max_shared() { m_flags |= 1; }
friend class max_sharing_fn;
bool is_closed() const { return (m_flags & 2) != 0; }
void set_closed() { m_flags |= 2; }
friend class has_free_var_fn;
public:
expr_cell(expr_kind k, unsigned h, bool has_mv);
expr_kind kind() const { return static_cast<expr_kind>(m_kind); }
unsigned hash() const { return m_hash; }
bool has_metavar() const { return (m_flags & 4) != 0; }
};
/**
\brief Exprs for encoding formulas/expressions, types and proofs.
*/
class expr {
private:
expr_cell * m_ptr;
explicit expr(expr_cell * ptr):m_ptr(ptr) {}
public:
expr():m_ptr(nullptr) {}
expr(expr const & s):m_ptr(s.m_ptr) { if (m_ptr) m_ptr->inc_ref(); }
2013-08-01 20:57:43 +00:00
expr(expr && s):m_ptr(s.m_ptr) { s.m_ptr = nullptr; }
~expr() { if (m_ptr) m_ptr->dec_ref(); }
static expr const & null();
friend void swap(expr & a, expr & b) { std::swap(a.m_ptr, b.m_ptr); }
void release() { if (m_ptr) m_ptr->dec_ref(); m_ptr = nullptr; }
expr & operator=(expr const & s) { LEAN_COPY_REF(expr, s); }
expr & operator=(expr && s) { LEAN_MOVE_REF(expr, s); }
expr_kind kind() const { return m_ptr->kind(); }
unsigned hash() const { return m_ptr ? m_ptr->hash() : 23; }
bool has_metavar() const { return m_ptr->has_metavar(); }
expr_cell * raw() const { return m_ptr; }
operator bool() const { return m_ptr != nullptr; }
friend expr mk_var(unsigned idx);
friend expr mk_constant(name const & n);
friend expr mk_value(value & v);
friend expr mk_app(unsigned num_args, expr const * args);
friend expr mk_eq(expr const & l, expr const & r);
friend expr mk_lambda(name const & n, expr const & t, expr const & e);
friend expr mk_pi(name const & n, expr const & t, expr const & e);
friend expr mk_type(level const & l);
friend expr mk_let(name const & n, expr const & t, expr const & v, expr const & e);
friend expr mk_metavar(unsigned idx, meta_ctx const & ctx);
friend bool is_eqp(expr const & a, expr const & b) { return a.m_ptr == b.m_ptr; }
// Overloaded operator() can be used to create applications
expr operator()(expr const & a1) const;
expr operator()(expr const & a1, expr const & a2) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5, expr const & a6) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5, expr const & a6, expr const & a7) const;
expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5, expr const & a6, expr const & a7, expr const & a8) const;
};
// =======================================
// Expr (internal) Representation
/** \brief Free variables. They are encoded using de Bruijn's indices. */
class expr_var : public expr_cell {
unsigned m_vidx; // de Bruijn index
public:
expr_var(unsigned idx);
unsigned get_vidx() const { return m_vidx; }
};
/** \brief Constants. */
class expr_const : public expr_cell {
name m_name;
public:
expr_const(name const & n);
name const & get_name() const { return m_name; }
};
/** \brief Function Applications */
class expr_app : public expr_cell {
unsigned m_num_args;
expr m_args[0];
friend expr mk_app(unsigned num_args, expr const * args);
public:
expr_app(unsigned size, bool has_mv);
~expr_app();
unsigned get_num_args() const { return m_num_args; }
expr const & get_arg(unsigned idx) const { lean_assert(idx < m_num_args); return m_args[idx]; }
expr const * begin_args() const { return m_args; }
expr const * end_args() const { return m_args + m_num_args; }
};
/** \brief Heterogeneous equality */
class expr_eq : public expr_cell {
expr m_lhs;
expr m_rhs;
public:
expr_eq(expr const & lhs, expr const & rhs);
~expr_eq();
expr const & get_lhs() const { return m_lhs; }
expr const & get_rhs() const { return m_rhs; }
};
/** \brief Super class for lambda abstraction and pi (functional spaces). */
class expr_abstraction : public expr_cell {
name m_name;
expr m_domain;
expr m_body;
public:
expr_abstraction(expr_kind k, name const & n, expr const & t, expr const & e);
name const & get_name() const { return m_name; }
expr const & get_domain() const { return m_domain; }
expr const & get_body() const { return m_body; }
};
/** \brief Lambda abstractions */
class expr_lambda : public expr_abstraction {
public:
expr_lambda(name const & n, expr const & t, expr const & e);
};
/** \brief (dependent) Functional spaces */
class expr_pi : public expr_abstraction {
public:
expr_pi(name const & n, expr const & t, expr const & e);
};
/** \brief Let expressions */
class expr_let : public expr_cell {
name m_name;
expr m_type;
expr m_value;
expr m_body;
public:
expr_let(name const & n, expr const & t, expr const & v, expr const & b);
~expr_let();
name const & get_name() const { return m_name; }
expr const & get_type() const { return m_type; }
expr const & get_value() const { return m_value; }
expr const & get_body() const { return m_body; }
};
/** \brief Type */
class expr_type : public expr_cell {
level m_level;
public:
expr_type(level const & l);
~expr_type();
level const & get_level() const { return m_level; }
};
/** \brief Base class for semantic attachment cells. */
class value {
void dealloc() { delete this; }
MK_LEAN_RC();
protected:
/**
\brief Auxiliary method used for implementing a total order on semantic
attachments. It is invoked by operator<, and it is only invoked when
<tt>get_name() == other.get_name()</tt>
*/
virtual bool lt(value const &) const { return false; }
public:
value():m_rc(0) {}
virtual ~value() {}
virtual expr get_type() const = 0;
virtual name get_name() const = 0;
virtual name get_unicode_name() const;
virtual bool normalize(unsigned num_args, expr const * args, expr & r) const;
virtual bool operator==(value const & other) const;
bool operator<(value const & other) const;
virtual void display(std::ostream & out) const;
virtual format pp() const;
virtual format pp(bool unicode) const;
virtual unsigned hash() const;
};
/** \brief Semantic attachments */
class expr_value : public expr_cell {
value & m_val;
friend expr copy(expr const & a);
public:
expr_value(value & v);
~expr_value();
value const & get_value() const { return m_val; }
};
/**
\see meta_entry
*/
enum class meta_entry_kind { Lift, Inst };
/**
\brief An entry in a metavariable context.
It represents objects of the form:
<code>
| Lift s n
| Inst s v
</code>
where \c s and \c n are unsigned integers, and
\c v is an expression
The meaning of <tt>Lift s n</tt> is: lift the free variables greater than or equal to \c s by \c n.
The meaning of <tt>Inst s v</tt> is: instantiate free variable \c s with the expression \c v, and
lower free variables greater than \c s.
The metavariable context records operations that must be applied
whenever we substitute a metavariable with an actual expression.
For example, let ?M be a metavariable with context
<code>
[ Inst 0 a, Lift 1 2 ]
</code>
Now, assume we want to instantiate \c ?M with <tt>f #0 (g #2)</tt>.
Then, we apply the metavariable entries from right to left.
Thus, first we apply <tt>(Lift 1 2)</tt> and obtain the term
<code>
f #0 (g #4)
</code>
Then, we apply <tt>(Inst 0 a)</tt> and produce
<code>
f a (g #3)
</code>
*/
class meta_entry {
meta_entry_kind m_kind;
unsigned m_s;
unsigned m_n;
expr m_v;
meta_entry(unsigned s, unsigned n);
meta_entry(unsigned s, expr const & v);
public:
~meta_entry();
friend meta_entry mk_lift(unsigned s, unsigned n);
friend meta_entry mk_inst(unsigned s, expr const & v);
meta_entry_kind kind() const { return m_kind; }
bool is_inst() const { return kind() == meta_entry_kind::Inst; }
bool is_lift() const { return kind() == meta_entry_kind::Lift; }
unsigned s() const { return m_s; }
unsigned n() const { lean_assert(is_lift()); return m_n; }
expr const & v() const { lean_assert(is_inst()); return m_v; }
};
inline meta_entry mk_lift(unsigned s, unsigned n) { return meta_entry(s, n); }
inline meta_entry mk_inst(unsigned s, expr const & v) { return meta_entry(s, v); }
/** \brief Metavariables */
class expr_metavar : public expr_cell {
unsigned m_midx;
meta_ctx m_ctx;
public:
expr_metavar(unsigned i, meta_ctx const & c);
~expr_metavar();
unsigned get_midx() const { return m_midx; }
meta_ctx const & get_ctx() const { return m_ctx; }
};
// =======================================
// =======================================
// Testers
inline bool is_var(expr_cell * e) { return e->kind() == expr_kind::Var; }
inline bool is_constant(expr_cell * e) { return e->kind() == expr_kind::Constant; }
inline bool is_value(expr_cell * e) { return e->kind() == expr_kind::Value; }
inline bool is_app(expr_cell * e) { return e->kind() == expr_kind::App; }
inline bool is_eq(expr_cell * e) { return e->kind() == expr_kind::Eq; }
inline bool is_lambda(expr_cell * e) { return e->kind() == expr_kind::Lambda; }
inline bool is_pi(expr_cell * e) { return e->kind() == expr_kind::Pi; }
inline bool is_type(expr_cell * e) { return e->kind() == expr_kind::Type; }
inline bool is_let(expr_cell * e) { return e->kind() == expr_kind::Let; }
inline bool is_metavar(expr_cell * e) { return e->kind() == expr_kind::MetaVar; }
inline bool is_abstraction(expr_cell * e) { return is_lambda(e) || is_pi(e); }
inline bool is_var(expr const & e) { return e.kind() == expr_kind::Var; }
inline bool is_constant(expr const & e) { return e.kind() == expr_kind::Constant; }
inline bool is_value(expr const & e) { return e.kind() == expr_kind::Value; }
inline bool is_app(expr const & e) { return e.kind() == expr_kind::App; }
inline bool is_eq(expr const & e) { return e.kind() == expr_kind::Eq; }
inline bool is_lambda(expr const & e) { return e.kind() == expr_kind::Lambda; }
inline bool is_pi(expr const & e) { return e.kind() == expr_kind::Pi; }
bool is_arrow(expr const & e);
inline bool is_type(expr const & e) { return e.kind() == expr_kind::Type; }
inline bool is_let(expr const & e) { return e.kind() == expr_kind::Let; }
inline bool is_metavar(expr const & e) { return e.kind() == expr_kind::MetaVar; }
inline bool is_abstraction(expr const & e) { return is_lambda(e) || is_pi(e); }
// =======================================
// =======================================
// Constructors
inline expr mk_var(unsigned idx) { return expr(new expr_var(idx)); }
inline expr Var(unsigned idx) { return mk_var(idx); }
inline expr mk_constant(name const & n) { return expr(new expr_const(n)); }
inline expr Const(name const & n) { return mk_constant(n); }
inline expr mk_value(value & v) { return expr(new expr_value(v)); }
inline expr to_expr(value & v) { return mk_value(v); }
expr mk_app(unsigned num_args, expr const * args);
inline expr mk_app(std::initializer_list<expr> const & l) { return mk_app(l.size(), l.begin()); }
inline expr mk_app(expr const & e1, expr const & e2) { return mk_app({e1, e2}); }
inline expr mk_app(expr const & e1, expr const & e2, expr const & e3) { return mk_app({e1, e2, e3}); }
inline expr mk_app(expr const & e1, expr const & e2, expr const & e3, expr const & e4) { return mk_app({e1, e2, e3, e4}); }
inline expr mk_app(expr const & e1, expr const & e2, expr const & e3, expr const & e4, expr const & e5) { return mk_app({e1, e2, e3, e4, e5}); }
inline expr mk_eq(expr const & l, expr const & r) { return expr(new expr_eq(l, r)); }
inline expr Eq(expr const & l, expr const & r) { return mk_eq(l, r); }
inline expr mk_lambda(name const & n, expr const & t, expr const & e) { return expr(new expr_lambda(n, t, e)); }
inline expr mk_pi(name const & n, expr const & t, expr const & e) { return expr(new expr_pi(n, t, e)); }
inline expr mk_arrow(expr const & t, expr const & e) { return mk_pi(name("_"), t, e); }
inline expr operator>>(expr const & t, expr const & e) { return mk_arrow(t, e); }
inline expr mk_let(name const & n, expr const & t, expr const & v, expr const & e) { return expr(new expr_let(n, t, v, e)); }
inline expr mk_type(level const & l) { return expr(new expr_type(l)); }
expr mk_type();
inline expr Type(level const & l) { return mk_type(l); }
inline expr Type() { return mk_type(); }
inline expr mk_metavar(unsigned idx, meta_ctx const & ctx = meta_ctx()) { return expr(new expr_metavar(idx, ctx)); }
inline expr expr::operator()(expr const & a1) const { return mk_app({*this, a1}); }
inline expr expr::operator()(expr const & a1, expr const & a2) const { return mk_app({*this, a1, a2}); }
inline expr expr::operator()(expr const & a1, expr const & a2, expr const & a3) const { return mk_app({*this, a1, a2, a3}); }
inline expr expr::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { return mk_app({*this, a1, a2, a3, a4}); }
inline expr expr::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { return mk_app({*this, a1, a2, a3, a4, a5}); }
inline expr expr::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5, expr const & a6) const { return mk_app({*this, a1, a2, a3, a4, a5, a6}); }
inline expr expr::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5, expr const & a6, expr const & a7) const { return mk_app({*this, a1, a2, a3, a4, a5, a6, a7}); }
inline expr expr::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5, expr const & a6, expr const & a7, expr const & a8) const { return mk_app({*this, a1, a2, a3, a4, a5, a6, a7, a8}); }
// =======================================
// =======================================
// Casting (these functions are only needed for low-level code)
inline expr_var * to_var(expr_cell * e) { lean_assert(is_var(e)); return static_cast<expr_var*>(e); }
inline expr_const * to_constant(expr_cell * e) { lean_assert(is_constant(e)); return static_cast<expr_const*>(e); }
inline expr_app * to_app(expr_cell * e) { lean_assert(is_app(e)); return static_cast<expr_app*>(e); }
inline expr_eq * to_eq(expr_cell * e) { lean_assert(is_eq(e)); return static_cast<expr_eq*>(e); }
inline expr_abstraction * to_abstraction(expr_cell * e) { lean_assert(is_abstraction(e)); return static_cast<expr_abstraction*>(e); }
inline expr_lambda * to_lambda(expr_cell * e) { lean_assert(is_lambda(e)); return static_cast<expr_lambda*>(e); }
inline expr_pi * to_pi(expr_cell * e) { lean_assert(is_pi(e)); return static_cast<expr_pi*>(e); }
inline expr_type * to_type(expr_cell * e) { lean_assert(is_type(e)); return static_cast<expr_type*>(e); }
inline expr_let * to_let(expr_cell * e) { lean_assert(is_let(e)); return static_cast<expr_let*>(e); }
inline expr_metavar * to_metavar(expr_cell * e) { lean_assert(is_metavar(e)); return static_cast<expr_metavar*>(e); }
inline expr_var * to_var(expr const & e) { return to_var(e.raw()); }
inline expr_const * to_constant(expr const & e) { return to_constant(e.raw()); }
inline expr_app * to_app(expr const & e) { return to_app(e.raw()); }
inline expr_eq * to_eq(expr const & e) { return to_eq(e.raw()); }
inline expr_abstraction * to_abstraction(expr const & e) { return to_abstraction(e.raw()); }
inline expr_lambda * to_lambda(expr const & e) { return to_lambda(e.raw()); }
inline expr_pi * to_pi(expr const & e) { return to_pi(e.raw()); }
inline expr_let * to_let(expr const & e) { return to_let(e.raw()); }
inline expr_type * to_type(expr const & e) { return to_type(e.raw()); }
inline expr_metavar * to_metavar(expr const & e) { return to_metavar(e.raw()); }
// =======================================
// =======================================
// Accessors
inline unsigned get_rc(expr_cell * e) { return e->get_rc(); }
inline bool is_shared(expr_cell * e) { return get_rc(e) > 1; }
inline unsigned var_idx(expr_cell * e) { return to_var(e)->get_vidx(); }
inline bool is_var(expr_cell * e, unsigned i) { return is_var(e) && var_idx(e) == i; }
inline name const & const_name(expr_cell * e) { return to_constant(e)->get_name(); }
inline value const & to_value(expr_cell * e) { lean_assert(is_value(e)); return static_cast<expr_value*>(e)->get_value(); }
inline unsigned num_args(expr_cell * e) { return to_app(e)->get_num_args(); }
inline expr const & arg(expr_cell * e, unsigned idx) { return to_app(e)->get_arg(idx); }
inline expr const & eq_lhs(expr_cell * e) { return to_eq(e)->get_lhs(); }
inline expr const & eq_rhs(expr_cell * e) { return to_eq(e)->get_rhs(); }
inline name const & abst_name(expr_cell * e) { return to_abstraction(e)->get_name(); }
inline expr const & abst_domain(expr_cell * e) { return to_abstraction(e)->get_domain(); }
inline expr const & abst_body(expr_cell * e) { return to_abstraction(e)->get_body(); }
inline level const & ty_level(expr_cell * e) { return to_type(e)->get_level(); }
inline name const & let_name(expr_cell * e) { return to_let(e)->get_name(); }
inline expr const & let_value(expr_cell * e) { return to_let(e)->get_value(); }
inline expr const & let_type(expr_cell * e) { return to_let(e)->get_type(); }
inline expr const & let_body(expr_cell * e) { return to_let(e)->get_body(); }
inline unsigned metavar_idx(expr_cell * e) { return to_metavar(e)->get_midx(); }
inline meta_ctx const & metavar_ctx(expr_cell * e) { return to_metavar(e)->get_ctx(); }
/** \brief Return the reference counter of the given expression. */
inline unsigned get_rc(expr const & e) { return e.raw()->get_rc(); }
/** \brief Return true iff the reference counter of the given expression is greater than 1. */
inline bool is_shared(expr const & e) { return get_rc(e) > 1; }
/** \brief Return the de Bruijn index of the given expression. \pre is_var(e) */
inline unsigned var_idx(expr const & e) { return to_var(e)->get_vidx(); }
/** \brief Return true iff the given expression is a variable with de Bruijn index equal to \c i. */
inline bool is_var(expr const & e, unsigned i) { return is_var(e) && var_idx(e) == i; }
inline name const & const_name(expr const & e) { return to_constant(e)->get_name(); }
/** \brief Return true iff the given expression is a constant with name \c n. */
inline bool is_constant(expr const & e, name const & n) {
return is_constant(e) && const_name(e) == n;
}
inline value const & to_value(expr const & e) { return to_value(e.raw()); }
inline unsigned num_args(expr const & e) { return to_app(e)->get_num_args(); }
inline expr const & arg(expr const & e, unsigned idx) { return to_app(e)->get_arg(idx); }
inline expr const * begin_args(expr const & e) { return to_app(e)->begin_args(); }
inline expr const * end_args(expr const & e) { return to_app(e)->end_args(); }
inline expr const & eq_lhs(expr const & e) { return to_eq(e)->get_lhs(); }
inline expr const & eq_rhs(expr const & e) { return to_eq(e)->get_rhs(); }
inline name const & abst_name(expr const & e) { return to_abstraction(e)->get_name(); }
inline expr const & abst_domain(expr const & e) { return to_abstraction(e)->get_domain(); }
inline expr const & abst_body(expr const & e) { return to_abstraction(e)->get_body(); }
inline level const & ty_level(expr const & e) { return to_type(e)->get_level(); }
inline name const & let_name(expr const & e) { return to_let(e)->get_name(); }
inline expr const & let_type(expr const & e) { return to_let(e)->get_type(); }
inline expr const & let_value(expr const & e) { return to_let(e)->get_value(); }
inline expr const & let_body(expr const & e) { return to_let(e)->get_body(); }
inline unsigned metavar_idx(expr const & e) { return to_metavar(e)->get_midx(); }
inline meta_ctx const & metavar_ctx(expr const & e) { return to_metavar(e)->get_ctx(); }
inline bool has_metavar(expr const & e) { return e.has_metavar(); }
// =======================================
// =======================================
// Structural equality
bool operator==(expr const & a, expr const & b);
inline bool operator!=(expr const & a, expr const & b) { return !operator==(a, b); }
// =======================================
// =======================================
// Expression+Offset
typedef std::pair<expr, unsigned> expr_offset;
typedef std::pair<expr_cell*, unsigned> expr_cell_offset;
// =======================================
// =======================================
// Auxiliary functionals
/** \brief Functional object for hashing kernel expressions. */
struct expr_hash { unsigned operator()(expr const & e) const { return e.hash(); } };
/** \brief Functional object for testing pointer equality between kernel expressions. */
struct expr_eqp { bool operator()(expr const & e1, expr const & e2) const { return is_eqp(e1, e2); } };
/** \brief Functional object for hashing kernel expression cells. */
struct expr_cell_hash { unsigned operator()(expr_cell * e) const { return e->hash(); } };
/** \brief Functional object for testing pointer equality between kernel cell expressions. */
struct expr_cell_eqp { bool operator()(expr_cell * e1, expr_cell * e2) const { return e1 == e2; } };
/** \brief Functional object for hashing a pair (n, k) where n is a kernel expressions, and k is an offset. */
struct expr_offset_hash { unsigned operator()(expr_offset const & p) const { return hash(p.first.hash(), p.second); } };
/** \brief Functional object for comparing pairs (expression, offset). */
struct expr_offset_eqp { unsigned operator()(expr_offset const & p1, expr_offset const & p2) const { return is_eqp(p1.first, p2.first) && p1.second == p2.second; } };
/** \brief Functional object for hashing a pair (n, k) where n is a kernel cell expressions, and k is an offset. */
struct expr_cell_offset_hash { unsigned operator()(expr_cell_offset const & p) const { return hash(p.first->hash(), p.second); } };
/** \brief Functional object for comparing pairs (expression cell, offset). */
struct expr_cell_offset_eqp { unsigned operator()(expr_cell_offset const & p1, expr_cell_offset const & p2) const { return p1 == p2; } };
// =======================================
// =======================================
// Miscellaneous
/**
\brief Wrapper for iterating over application arguments.
If \c n is an application, it allows us to write
\code
for (expr const & arg : app_args(n)) {
... do something with argument
}
\endcode
*/
struct args {
expr const & m_app;
args(expr const & a):m_app(a) { lean_assert(is_app(a)); }
expr const * begin() const { return &arg(m_app, 0); }
expr const * end() const { return begin() + num_args(m_app); }
};
/**
\brief Return a shallow copy of \c e
*/
expr copy(expr const & e);
// =======================================
// =======================================
// Update
template<typename F> expr update_app(expr const & e, F f) {
static_assert(std::is_same<typename std::result_of<F(expr const &)>::type, expr>::value,
"update_app: return type of f is not expr");
buffer<expr> new_args;
bool modified = false;
for (expr const & a : args(e)) {
new_args.push_back(f(a));
if (!is_eqp(a, new_args.back()))
modified = true;
}
if (modified)
return mk_app(new_args.size(), new_args.data());
else
return e;
}
template<typename F> expr update_abst(expr const & e, F f) {
static_assert(std::is_same<typename std::result_of<F(expr const &, expr const &)>::type,
std::pair<expr, expr>>::value,
"update_abst: return type of f is not pair<expr, expr>");
expr const & old_t = abst_domain(e);
expr const & old_b = abst_body(e);
std::pair<expr, expr> p = f(old_t, old_b);
if (!is_eqp(p.first, old_t) || !is_eqp(p.second, old_b)) {
name const & n = abst_name(e);
return is_pi(e) ? mk_pi(n, p.first, p.second) : mk_lambda(n, p.first, p.second);
} else {
return e;
}
}
template<typename F> expr update_let(expr const & e, F f) {
static_assert(std::is_same<typename std::result_of<F(expr const &, expr const &, expr const &)>::type,
std::tuple<expr, expr, expr>>::value,
"update_let: return type of f is not pair<expr, expr>");
expr const & old_t = let_type(e);
expr const & old_v = let_value(e);
expr const & old_b = let_body(e);
std::tuple<expr, expr, expr> t = f(old_t, old_v, old_b);
if (!is_eqp(std::get<0>(t), old_t) || !is_eqp(std::get<1>(t), old_v) || !is_eqp(std::get<2>(t), old_b))
return mk_let(let_name(e), std::get<0>(t), std::get<1>(t), std::get<2>(t));
else
return e;
}
template<typename F> expr update_eq(expr const & e, F f) {
static_assert(std::is_same<typename std::result_of<F(expr const &, expr const &)>::type,
std::pair<expr, expr>>::value,
"update_eq: return type of f is not pair<expr, expr>");
expr const & old_l = eq_lhs(e);
expr const & old_r = eq_rhs(e);
std::pair<expr, expr> p = f(old_l, old_r);
if (!is_eqp(p.first, old_l) || !is_eqp(p.second, old_r))
return mk_eq(p.first, p.second);
else
return e;
}
template<typename F> expr update_metavar(expr const & e, unsigned i, F f) {
static_assert(std::is_same<typename std::result_of<F(meta_entry const &)>::type,
meta_entry>::value,
"update_metavar: return type of f(meta_entry) is not meta_entry");
buffer<meta_entry> new_entries;
bool modified = (i != metavar_idx(e));
for (meta_entry const & me : metavar_ctx(e)) {
meta_entry new_me = f(me);
if (new_me.kind() != me.kind() || new_me.s() != me.s()) {
modified = true;
} else if (new_me.is_inst()) {
if (!is_eqp(new_me.v(), me.v()))
modified = true;
} else if (new_me.n() != me.n()) {
modified = true;
}
new_entries.push_back(new_me);
}
if (modified)
return mk_metavar(i, to_list(new_entries.begin(), new_entries.end()));
else
return e;
}
template<typename F> expr update_metavar(expr const & e, F f) {
static_assert(std::is_same<typename std::result_of<F(meta_entry const &)>::type,
meta_entry>::value,
"update_metavar: return type of f(meta_entry) is not meta_entry");
return update_metavar(e, metavar_idx(e), f);
}
// =======================================
// Remark: the implementation is at library/printer.cpp
// We add the definition here to prevent the compiler from using the coercion to bool.
std::ostream & operator<<(std::ostream & out, expr const & e);
}