lean4-htt/src/kernel/for_each_fn.cpp
2026-01-12 17:14:09 +00:00

225 lines
6.4 KiB
C++

/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <vector>
#include <unordered_map>
#include <utility>
#include "runtime/memory.h"
#include "runtime/interrupt.h"
#include "runtime/flet.h"
#include "kernel/for_each_fn.h"
namespace lean {
/*
If `partial_apps = true`, then given a term `g a b`, we also apply the function `m_f` to `g a`,
and not only to `g`, `a`, and `b`.
*/
template<bool partial_apps> class for_each_fn {
lean::unordered_set<lean_object *> m_cache;
std::function<bool(expr const &)> m_f; // NOLINT
bool visited(expr const & e) {
if (is_likely_unshared(e)) return false;
if (m_cache.find(e.raw()) != m_cache.end()) return true;
m_cache.insert(e.raw());
return false;
}
void apply_fn(expr const & e) {
if (is_app(e)) {
apply_fn(app_fn(e));
apply(app_arg(e));
} else {
apply(e);
}
}
void apply(expr const & e) {
switch (e.kind()) {
case expr_kind::Const: case expr_kind::BVar: case expr_kind::Sort:
m_f(e);
return;
default:
break;
}
if (visited(e))
return;
if (!m_f(e))
return;
switch (e.kind()) {
case expr_kind::Const: case expr_kind::BVar:
case expr_kind::Sort: case expr_kind::Lit:
case expr_kind::MVar: case expr_kind::FVar:
return;
case expr_kind::MData:
apply(mdata_expr(e));
return;
case expr_kind::Proj:
apply(proj_expr(e));
return;
case expr_kind::App:
if (partial_apps)
apply(app_fn(e));
else
apply_fn(app_fn(e));
apply(app_arg(e));
return;
case expr_kind::Lambda: case expr_kind::Pi:
apply(binding_domain(e));
apply(binding_body(e));
return;
case expr_kind::Let:
apply(let_type(e));
apply(let_value(e));
apply(let_body(e));
return;
}
}
public:
for_each_fn(std::function<bool(expr const &)> && f):m_f(f) {} // NOLINT
for_each_fn(std::function<bool(expr const &)> const & f):m_f(f) {} // NOLINT
void operator()(expr const & e) { apply(e); }
};
class for_each_offset_fn {
struct key_hasher {
std::size_t operator()(std::pair<lean_object *, unsigned> const & p) const {
return hash((size_t)p.first, p.second);
}
};
lean::unordered_set<std::pair<lean_object *, unsigned>, key_hasher> m_cache;
std::function<bool(expr const &, unsigned)> m_f; // NOLINT
bool visited(expr const & e, unsigned offset) {
if (is_likely_unshared(e)) return false;
if (m_cache.find(std::make_pair(e.raw(), offset)) != m_cache.end()) return true;
m_cache.insert(std::make_pair(e.raw(), offset));
return false;
}
void apply(expr const & e, unsigned offset) {
switch (e.kind()) {
case expr_kind::Const: case expr_kind::BVar: case expr_kind::Sort:
m_f(e, offset);
return;
default:
break;
}
if (visited(e, offset))
return;
if (!m_f(e, offset))
return;
switch (e.kind()) {
case expr_kind::Const: case expr_kind::BVar:
case expr_kind::Sort: case expr_kind::Lit:
case expr_kind::MVar: case expr_kind::FVar:
return;
case expr_kind::MData:
apply(mdata_expr(e), offset);
return;
case expr_kind::Proj:
apply(proj_expr(e), offset);
return;
case expr_kind::App:
apply(app_fn(e), offset);
apply(app_arg(e), offset);
return;
case expr_kind::Lambda: case expr_kind::Pi:
apply(binding_domain(e), offset);
apply(binding_body(e), offset+1);
return;
case expr_kind::Let:
apply(let_type(e), offset);
apply(let_value(e), offset);
apply(let_body(e), offset+1);
return;
}
}
public:
for_each_offset_fn(std::function<bool(expr const &, unsigned)> && f):m_f(f) {} // NOLINT
for_each_offset_fn(std::function<bool(expr const &, unsigned)> const & f):m_f(f) {} // NOLINT
void operator()(expr const & e) { apply(e, 0); }
};
void for_each(expr const & e, std::function<bool(expr const &)> && f) { // NOLINT
return for_each_fn<true>(f)(e);
}
void for_each(expr const & e, std::function<bool(expr const &, unsigned)> && f) { // NOLINT
return for_each_offset_fn(f)(e);
}
extern "C" LEAN_EXPORT obj_res lean_find_expr(b_obj_arg p, b_obj_arg e_) {
lean_object * found = nullptr;
expr const & e = TO_REF(expr, e_);
for_each_fn<true>([&](expr const & e) {
if (found != nullptr) return false;
lean_inc(p);
lean_inc(e.raw());
if (lean_unbox(lean_apply_1(p, e.raw()))) {
found = e.raw();
return false;
}
return true;
})(e);
if (found) {
lean_inc(found);
lean_object * r = lean_alloc_ctor(1, 1, 0);
lean_ctor_set(r, 0, found);
return r;
} else {
return lean_box(0);
}
}
/*
Similar to `lean_find_expr`, but `p` returns
```
inductive FindStep where
/-- Found desired subterm -/ | found
/-- Search subterms -/ | visit
/-- Do not search subterms -/ | done
```
*/
extern "C" LEAN_EXPORT obj_res lean_find_ext_expr(b_obj_arg p, b_obj_arg e_) {
lean_object * found = nullptr;
expr const & e = TO_REF(expr, e_);
// Recall that `findExt?` skips partial applications.
for_each_fn<false>([&](expr const & e) {
if (found != nullptr) return false;
lean_inc(p);
lean_inc(e.raw());
switch(lean_unbox(lean_apply_1(p, e.raw()))) {
case 0: // found
found = e.raw();
return false;
case 1: // visit
return true;
case 2: // done
return false;
default:
lean_unreachable();
}
})(e);
if (found) {
lean_inc(found);
lean_object * r = lean_alloc_ctor(1, 1, 0);
lean_ctor_set(r, 0, found);
return r;
} else {
return lean_box(0);
}
}
}