2014-01-13 01:45:24 +00:00
|
|
|
/*
|
2014-08-03 23:03:58 +00:00
|
|
|
Copyright (c) 2013-2014 Microsoft Corporation. All rights reserved.
|
2014-01-13 01:45:24 +00:00
|
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
|
|
|
|
Author: Leonardo de Moura
|
|
|
|
*/
|
2014-08-03 23:03:58 +00:00
|
|
|
#include "kernel/abstract.h"
|
2014-01-13 01:45:24 +00:00
|
|
|
#include "kernel/instantiate.h"
|
|
|
|
#include "library/kernel_bindings.h"
|
2014-08-03 23:03:58 +00:00
|
|
|
#include "library/locals.h"
|
2014-08-05 00:32:07 +00:00
|
|
|
#include "library/match.h"
|
2014-01-13 01:45:24 +00:00
|
|
|
|
|
|
|
namespace lean {
|
2014-08-05 00:32:07 +00:00
|
|
|
class match_fn {
|
2014-08-03 23:03:58 +00:00
|
|
|
buffer<optional<expr>> & m_subst;
|
|
|
|
name_generator m_ngen;
|
|
|
|
name_map<name> * m_name_subst;
|
2014-08-05 00:32:07 +00:00
|
|
|
matcher_plugin const * m_plugin;
|
2014-08-03 23:03:58 +00:00
|
|
|
|
|
|
|
void assign(expr const & p, expr const & t) {
|
|
|
|
lean_assert(var_idx(p) < m_subst.size());
|
|
|
|
unsigned vidx = var_idx(p);
|
|
|
|
unsigned sz = m_subst.size();
|
|
|
|
m_subst[sz - vidx - 1] = t;
|
2014-01-13 01:45:24 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
optional<expr> get_subst(expr const & x) const {
|
|
|
|
unsigned vidx = var_idx(x);
|
2014-01-13 01:45:24 +00:00
|
|
|
unsigned sz = m_subst.size();
|
|
|
|
if (vidx >= sz)
|
|
|
|
throw exception("ill-formed higher-order matching problem");
|
|
|
|
return m_subst[sz - vidx - 1];
|
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool args_are_distinct_locals(buffer<expr> const & args) {
|
|
|
|
for (auto it = args.begin(); it != args.end(); it++) {
|
|
|
|
if (!is_local(*it) || contains_local(*it, args.begin(), it))
|
2014-01-13 01:45:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
optional<expr> proj(expr const & t, buffer<expr> const & args) {
|
|
|
|
expr r = Fun(args, t);
|
|
|
|
if (has_local(r))
|
2014-01-13 01:45:24 +00:00
|
|
|
return none_expr();
|
|
|
|
else
|
|
|
|
return some_expr(r);
|
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool match_plugin(expr const & p, expr const & t) {
|
|
|
|
if (!m_plugin)
|
2014-01-14 22:37:28 +00:00
|
|
|
return false;
|
2014-08-03 23:03:58 +00:00
|
|
|
return (*m_plugin)(p, t, m_subst, m_ngen.mk_child());
|
2014-01-14 22:37:28 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool match_binding(expr p, expr t) {
|
|
|
|
lean_assert(is_binding(p) && is_binding(t));
|
|
|
|
buffer<expr> ls;
|
|
|
|
expr_kind k = p.kind();
|
|
|
|
while (p.kind() == k && t.kind() == k) {
|
|
|
|
if (m_name_subst)
|
|
|
|
(*m_name_subst)[binding_name(p)] = binding_name(t);
|
|
|
|
expr p_d = instantiate_rev(binding_domain(p), ls.size(), ls.data());
|
|
|
|
expr t_d = instantiate_rev(binding_domain(t), ls.size(), ls.data());
|
|
|
|
if (!match(p_d, t_d))
|
|
|
|
return false;
|
|
|
|
expr l = mk_local(m_ngen.next(), binding_name(t), t_d, binding_info(t));
|
|
|
|
ls.push_back(l);
|
|
|
|
p = binding_body(p);
|
|
|
|
t = binding_body(t);
|
2014-01-19 18:32:06 +00:00
|
|
|
}
|
2014-08-03 23:03:58 +00:00
|
|
|
if (p.kind() == k || t.kind() == k)
|
|
|
|
return false;
|
|
|
|
p = instantiate_rev(p, ls.size(), ls.data());
|
|
|
|
t = instantiate_rev(t, ls.size(), ls.data());
|
|
|
|
return match(p, t);
|
2014-01-19 18:32:06 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool match_macro(expr const & p, expr const & t) {
|
|
|
|
if (macro_def(p) == macro_def(t) && macro_num_args(p) == macro_num_args(t)) {
|
|
|
|
for (unsigned i = 0; i < macro_num_args(p); i++) {
|
|
|
|
if (!match(macro_arg(p, i), macro_arg(t, i)))
|
|
|
|
return false;
|
|
|
|
}
|
2014-01-19 18:32:06 +00:00
|
|
|
return true;
|
2014-08-03 23:03:58 +00:00
|
|
|
}
|
2014-01-19 18:32:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool match_app(expr const & p, expr const & t) {
|
|
|
|
return match_core(app_fn(p), app_fn(t)) && match(app_arg(p), app_arg(t));
|
2014-01-20 04:35:43 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool match_core(expr const & p, expr const & t) {
|
|
|
|
if (p.kind() != t.kind())
|
|
|
|
return match_plugin(p, t);
|
|
|
|
switch (p.kind()) {
|
|
|
|
case expr_kind::Local: case expr_kind::Meta:
|
|
|
|
return mlocal_name(p) == mlocal_name(t);
|
|
|
|
case expr_kind::Var:
|
|
|
|
lean_unreachable(); // LCOV_EXCL_LINE
|
|
|
|
case expr_kind::Constant:
|
|
|
|
// TODO(Leo): universe levels
|
|
|
|
return const_name(p) == const_name(t);
|
|
|
|
case expr_kind::Sort:
|
|
|
|
// TODO(Leo): universe levels
|
|
|
|
return true;
|
|
|
|
case expr_kind::Lambda: case expr_kind::Pi:
|
|
|
|
return match_binding(p, t) || match_plugin(p, t);
|
|
|
|
case expr_kind::Macro:
|
|
|
|
return match_macro(p, t) || match_plugin(p, t);
|
|
|
|
case expr_kind::App:
|
|
|
|
return match_app(p, t) || match_plugin(p, t);
|
2014-01-20 04:35:43 +00:00
|
|
|
}
|
2014-08-03 23:03:58 +00:00
|
|
|
lean_unreachable(); // LCOV_EXCL_LINE
|
|
|
|
}
|
2014-01-20 04:35:43 +00:00
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
public:
|
2014-08-05 00:32:07 +00:00
|
|
|
match_fn(buffer<optional<expr>> & subst, name_generator const & ngen, name_map<name> * name_subst, matcher_plugin const * plugin):
|
2014-08-03 23:03:58 +00:00
|
|
|
m_subst(subst), m_ngen(ngen), m_name_subst(name_subst), m_plugin(plugin) {}
|
2014-01-20 04:35:43 +00:00
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
bool match(expr const & p, expr const & t) {
|
|
|
|
if (is_var(p)) {
|
|
|
|
auto s = get_subst(p);
|
2014-01-13 01:45:24 +00:00
|
|
|
if (s) {
|
2014-08-03 23:03:58 +00:00
|
|
|
return match_core(*s, t);
|
|
|
|
} else if (has_local(t)) {
|
2014-01-13 01:45:24 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
2014-08-03 23:03:58 +00:00
|
|
|
assign(p, t);
|
2014-01-13 01:45:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-03 23:03:58 +00:00
|
|
|
} else if (is_app(p)) {
|
|
|
|
buffer<expr> args;
|
|
|
|
expr const & f = get_app_rev_args(p, args);
|
|
|
|
if (is_var(f)) {
|
2014-01-20 04:35:43 +00:00
|
|
|
// higher-order pattern case
|
2014-08-03 23:03:58 +00:00
|
|
|
auto s = get_subst(f);
|
2014-01-20 04:35:43 +00:00
|
|
|
if (s) {
|
2014-08-03 23:03:58 +00:00
|
|
|
expr new_p = apply_beta(*s, args.size(), args.data());
|
|
|
|
return match_core(new_p, t);
|
|
|
|
}
|
|
|
|
if (args_are_distinct_locals(args)) {
|
|
|
|
optional<expr> new_t = proj(t, args);
|
2014-01-20 04:35:43 +00:00
|
|
|
if (new_t) {
|
2014-08-03 23:03:58 +00:00
|
|
|
assign(f, *new_t);
|
2014-01-20 04:35:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-01-13 01:45:24 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-20 04:35:43 +00:00
|
|
|
// fallback to the first-order case
|
2014-01-13 01:45:24 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
return match_core(p, t);
|
2014-01-13 01:45:24 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-08-03 23:03:58 +00:00
|
|
|
static name g_tmp_prefix = name::mk_internal_unique_name();
|
2014-08-05 00:32:07 +00:00
|
|
|
bool match(expr const & p, expr const & t, buffer<optional<expr>> & subst, name const * prefix,
|
|
|
|
name_map<name> * name_subst, matcher_plugin const * plugin) {
|
2014-08-03 23:03:58 +00:00
|
|
|
lean_assert(closed(t));
|
|
|
|
if (prefix)
|
2014-08-05 00:32:07 +00:00
|
|
|
return match_fn(subst, name_generator(*prefix), name_subst, plugin).match(p, t);
|
2014-08-03 23:03:58 +00:00
|
|
|
else
|
2014-08-05 00:32:07 +00:00
|
|
|
return match_fn(subst, name_generator(g_tmp_prefix), name_subst, plugin).match(p, t);
|
2014-01-13 01:45:24 +00:00
|
|
|
}
|
|
|
|
|
2014-08-05 00:32:07 +00:00
|
|
|
static int match(lua_State * L) {
|
2014-08-03 23:03:58 +00:00
|
|
|
expr p = to_expr(L, 1);
|
|
|
|
expr t = to_expr(L, 2);
|
|
|
|
if (!closed(t))
|
|
|
|
throw exception("higher-order pattern matching failure, input term must not contain free variables");
|
|
|
|
unsigned k = get_free_var_range(p);
|
2014-01-13 01:45:24 +00:00
|
|
|
buffer<optional<expr>> subst;
|
|
|
|
subst.resize(k);
|
2014-08-05 00:32:07 +00:00
|
|
|
if (match(p, t, subst, nullptr, nullptr, nullptr)) {
|
2014-01-13 01:45:24 +00:00
|
|
|
lua_newtable(L);
|
|
|
|
int i = 1;
|
|
|
|
for (auto s : subst) {
|
2014-08-03 23:03:58 +00:00
|
|
|
if (s)
|
2014-01-13 01:45:24 +00:00
|
|
|
push_expr(L, *s);
|
2014-08-03 23:03:58 +00:00
|
|
|
else
|
2014-01-16 07:51:17 +00:00
|
|
|
lua_pushboolean(L, false);
|
2014-01-13 01:45:24 +00:00
|
|
|
lua_rawseti(L, -2, i);
|
|
|
|
i = i + 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-08-05 00:32:07 +00:00
|
|
|
void open_match(lua_State * L) {
|
|
|
|
SET_GLOBAL_FUN(match, "match");
|
2014-01-13 01:45:24 +00:00
|
|
|
}
|
|
|
|
}
|