Rename normalize context to local_context. Create context. Fix bug in name.cpp

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-07-30 00:25:19 -07:00
parent 3cef072cca
commit bed5f09907
6 changed files with 188 additions and 103 deletions

27
src/kernel/context.h Normal file
View file

@ -0,0 +1,27 @@
/*
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
#include "expr.h"
#include "list.h"
namespace lean {
class context_entry {
name m_name;
expr m_type;
expr m_body;
public:
context_entry(name const & n, expr const & t, expr const & b):m_name(n), m_type(t), m_body(b) {}
context_entry(expr const & t, expr const & b):m_type(t), m_body(b) {}
explicit context_entry(expr const & t):m_type(t) {}
~context_entry() {}
name const & get_name() const { return m_name; }
expr const & get_type() const { return m_type; }
expr const & get_body() const { return m_body; }
};
typedef list<context_entry> context;
context extend(context const & c, context_entry const & e) { return cons(e, c); }
}

View file

@ -6,6 +6,8 @@ Author: Leonardo de Moura
*/ */
#include <algorithm> #include <algorithm>
#include "expr.h" #include "expr.h"
#include "context.h"
#include "environment.h"
#include "free_vars.h" #include "free_vars.h"
#include "list.h" #include "list.h"
#include "buffer.h" #include "buffer.h"
@ -15,18 +17,23 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
class value; class value;
typedef list<value> context; typedef list<value> local_context;
enum class value_kind { Expr, Closure, BoundedVar }; enum class value_kind { Expr, Closure, BoundedVar };
class value { class value {
unsigned m_kind:2; unsigned m_kind:2;
unsigned m_bvar:30; unsigned m_bvar:30;
expr m_expr; expr m_expr;
context m_ctx; local_context m_ctx;
public: public:
value() {} value() {}
explicit value(expr const & e):m_kind(static_cast<unsigned>(value_kind::Expr)), m_expr(e) {} explicit value(expr const & e):m_kind(static_cast<unsigned>(value_kind::Expr)), m_expr(e) {}
explicit value(unsigned k):m_kind(static_cast<unsigned>(value_kind::BoundedVar)), m_bvar(k) {} explicit value(unsigned k):m_kind(static_cast<unsigned>(value_kind::BoundedVar)), m_bvar(k) {}
value(expr const & e, context const & c):m_kind(static_cast<unsigned>(value_kind::Closure)), m_expr(e), m_ctx(c) { lean_assert(is_lambda(e)); } value(expr const & e, local_context const & c):
m_kind(static_cast<unsigned>(value_kind::Closure)),
m_expr(e),
m_ctx(c) {
lean_assert(is_lambda(e));
}
value_kind kind() const { return static_cast<value_kind>(m_kind); } value_kind kind() const { return static_cast<value_kind>(m_kind); }
@ -34,112 +41,137 @@ public:
bool is_closure() const { return kind() == value_kind::Closure; } bool is_closure() const { return kind() == value_kind::Closure; }
bool is_bounded_var() const { return kind() == value_kind::BoundedVar; } bool is_bounded_var() const { return kind() == value_kind::BoundedVar; }
expr const & get_expr() const { lean_assert(is_expr() || is_closure()); return m_expr; } expr const & get_expr() const { lean_assert(is_expr() || is_closure()); return m_expr; }
context const & get_ctx() const { lean_assert(is_closure()); return m_ctx; } local_context const & get_ctx() const { lean_assert(is_closure()); return m_ctx; }
unsigned get_var_idx() const { lean_assert(is_bounded_var()); return m_bvar; } unsigned get_var_idx() const { lean_assert(is_bounded_var()); return m_bvar; }
}; };
value_kind kind(value const & v) { return v.kind(); } value_kind kind(value const & v) { return v.kind(); }
expr const & to_expr(value const & v) { return v.get_expr(); } expr const & to_expr(value const & v) { return v.get_expr(); }
context const & ctx_of(value const & v) { return v.get_ctx(); } local_context const & ctx_of(value const & v) { return v.get_ctx(); }
unsigned to_bvar(value const & v) { return v.get_var_idx(); } unsigned to_bvar(value const & v) { return v.get_var_idx(); }
value lookup(context const & c, unsigned i) { local_context extend(local_context const & c, value const & v) { return cons(v, c); }
context const * curr = &c;
while (!is_nil(*curr)) {
if (i == 0)
return head(*curr);
--i;
curr = &tail(*curr);
}
throw exception("unknown free variable");
}
context extend(context const & c, value const & v) { return cons(v, c); } class normalize_fn {
environment const & m_env;
context const & m_ctx;
value normalize(expr const & a, context const & c, unsigned k); value lookup(local_context const & c, unsigned i) {
expr reify(value const & v, unsigned k); local_context const * it1 = &c;
while (!is_nil(*it1)) {
expr reify_closure(expr const & a, context const & c, unsigned k) { if (i == 0)
lean_assert(is_lambda(a)); return head(*it1);
expr new_t = reify(normalize(abst_type(a), c, k), k); --i;
expr new_b = reify(normalize(abst_body(a), extend(c, value(k)), k+1), k+1); it1 = &tail(*it1);
// TODO: ETA-reduction
if (is_app(new_b)) {
// (lambda (x:T) (app f ... (var 0)))
// check eta-rule applicability
unsigned n = num_args(new_b);
if (is_var(arg(new_b, n - 1), 0) &&
std::all_of(begin_args(new_b),
end_args(new_b) - 1,
[](expr const & arg) { return !has_free_var(arg, 0); })) {
if (n == 2)
return lower_free_vars(arg(new_b, 0), 1);
else
return lower_free_vars(app(n - 1, begin_args(new_b)), 1);
} }
return lambda(abst_name(a), new_t, new_b);
}
else {
return lambda(abst_name(a), new_t, new_b);
}
}
expr reify(value const & v, unsigned k) {
lean_trace("normalize", tout << "Reify kind: " << static_cast<unsigned>(v.kind()) << "\n";
if (v.is_bounded_var()) tout << "#" << to_bvar(v); else tout << to_expr(v); tout << "\n";);
switch (v.kind()) {
case value_kind::Expr: return to_expr(v);
case value_kind::BoundedVar: return var(k - to_bvar(v) - 1);
case value_kind::Closure: return reify_closure(to_expr(v), ctx_of(v), k);
}
lean_unreachable();
return expr();
}
value normalize(expr const & a, context const & c, unsigned k) { context const * it2 = &m_ctx;
lean_trace("normalize", tout << "Normalize, k: " << k << "\n" << a << "\n";); while (!is_nil(*it2)) {
switch (a.kind()) { if (i == 0) {
case expr_kind::Var: expr const & b = head(*it2).get_body();
return lookup(c, var_idx(a)); if (!is_null(b))
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Numeral: return value(b);
return value(a); else break;
case expr_kind::App: {
value f = normalize(arg(a, 0), c, k);
unsigned i = 1;
unsigned n = num_args(a);
while (true) {
if (f.is_closure()) {
// beta reduction
expr const & fv = to_expr(f);
lean_trace("normalize", tout << "beta reduction...\n" << fv << "\n";);
context new_c = extend(ctx_of(f), normalize(arg(a, i), c, k));
f = normalize(abst_body(fv), new_c, k);
if (i == n - 1)
return f;
i++;
}
else {
// TODO: support for interpreted symbols
buffer<expr> new_args;
new_args.push_back(reify(f, k));
for (; i < n; i++)
new_args.push_back(reify(normalize(arg(a, i), c, k), k));
return value(app(new_args.size(), new_args.data()));
} }
--i;
it2 = &tail(*it2);
} }
throw exception("unknown free variable");
} }
case expr_kind::Lambda:
return value(a, c); expr reify_closure(expr const & a, local_context const & c, unsigned k) {
case expr_kind::Pi: { lean_assert(is_lambda(a));
expr new_t = reify(normalize(abst_type(a), c, k), k); expr new_t = reify(normalize(abst_type(a), c, k), k);
expr new_b = reify(normalize(abst_body(a), extend(c, value(k)), k+1), k+1); expr new_b = reify(normalize(abst_body(a), extend(c, value(k)), k+1), k+1);
return value(pi(abst_name(a), new_t, new_b)); if (is_app(new_b)) {
}} // (lambda (x:T) (app f ... (var 0)))
lean_unreachable(); // check eta-rule applicability
return value(a); unsigned n = num_args(new_b);
if (is_var(arg(new_b, n - 1), 0) &&
std::all_of(begin_args(new_b),
end_args(new_b) - 1,
[](expr const & arg) { return !has_free_var(arg, 0); })) {
if (n == 2)
return lower_free_vars(arg(new_b, 0), 1);
else
return lower_free_vars(app(n - 1, begin_args(new_b)), 1);
}
return lambda(abst_name(a), new_t, new_b);
}
else {
return lambda(abst_name(a), new_t, new_b);
}
}
expr reify(value const & v, unsigned k) {
lean_trace("normalize", tout << "Reify kind: " << static_cast<unsigned>(v.kind()) << "\n";
if (v.is_bounded_var()) tout << "#" << to_bvar(v); else tout << to_expr(v); tout << "\n";);
switch (v.kind()) {
case value_kind::Expr: return to_expr(v);
case value_kind::BoundedVar: return var(k - to_bvar(v) - 1);
case value_kind::Closure: return reify_closure(to_expr(v), ctx_of(v), k);
}
lean_unreachable();
return expr();
}
value normalize(expr const & a, local_context const & c, unsigned k) {
lean_trace("normalize", tout << "Normalize, k: " << k << "\n" << a << "\n";);
switch (a.kind()) {
case expr_kind::Var:
return lookup(c, var_idx(a));
case expr_kind::Constant: case expr_kind::Type: case expr_kind::Numeral:
return value(a);
case expr_kind::App: {
value f = normalize(arg(a, 0), c, k);
unsigned i = 1;
unsigned n = num_args(a);
while (true) {
if (f.is_closure()) {
// beta reduction
expr const & fv = to_expr(f);
lean_trace("normalize", tout << "beta reduction...\n" << fv << "\n";);
local_context new_c = extend(ctx_of(f), normalize(arg(a, i), c, k));
f = normalize(abst_body(fv), new_c, k);
if (i == n - 1)
return f;
i++;
}
else {
// TODO: support for interpreted symbols
buffer<expr> new_args;
new_args.push_back(reify(f, k));
for (; i < n; i++)
new_args.push_back(reify(normalize(arg(a, i), c, k), k));
return value(app(new_args.size(), new_args.data()));
}
}
}
case expr_kind::Lambda:
return value(a, c);
case expr_kind::Pi: {
expr new_t = reify(normalize(abst_type(a), c, k), k);
expr new_b = reify(normalize(abst_body(a), extend(c, value(k)), k+1), k+1);
return value(pi(abst_name(a), new_t, new_b));
}}
lean_unreachable();
return value(a);
}
public:
normalize_fn(environment const & env, context const & ctx):
m_env(env),
m_ctx(ctx) {
}
expr operator()(expr const & e) {
return reify(normalize(e, local_context(), 0), 0);
}
};
expr normalize(expr const & e, environment const & env, context const & ctx) {
return normalize_fn(env, ctx)(e);
} }
expr normalize(expr const & e) {
return reify(normalize(e, context(), 0), 0);
}
} }

View file

@ -6,7 +6,10 @@ Author: Leonardo de Moura
*/ */
#pragma once #pragma once
#include "expr.h" #include "expr.h"
#include "environment.h"
#include "context.h"
namespace lean { namespace lean {
expr normalize(expr const & e); class environment;
expr normalize(expr const & e, environment const & env, context const & ctx = context());
} }

View file

@ -11,6 +11,11 @@ Author: Leonardo de Moura
#include "sets.h" #include "sets.h"
using namespace lean; using namespace lean;
expr normalize(expr const & e) {
environment env;
return normalize(e, env);
}
static void eval(expr const & e) { std::cout << e << " --> " << normalize(e) << "\n"; } static void eval(expr const & e) { std::cout << e << " --> " << normalize(e) << "\n"; }
static expr t() { return constant("t"); } static expr t() { return constant("t"); }
static expr lam(expr const & e) { return lambda("_", t(), e); } static expr lam(expr const & e) { return lambda("_", t(), e); }
@ -118,9 +123,21 @@ static void tst1() {
lean_assert(normalize(lam(l12(l01))) == lam(lam(v(1)(v(1))))); lean_assert(normalize(lam(l12(l01))) == lam(lam(v(1)(v(1)))));
} }
static void tst2() {
environment env;
expr f = constant("f");
expr a = constant("a");
expr b = constant("b");
expr x = var(0);
expr y = var(1);
expr t = type(level());
std::cout << normalize(f(x,x), env, extend(context(), context_entry(t, f(a)))) << "\n";
}
int main() { int main() {
continue_on_violation(true); continue_on_violation(true);
tst1();
tst_church_numbers(); tst_church_numbers();
tst1();
tst2();
return has_violations() ? 1 : 0; return has_violations() ? 1 : 0;
} }

View file

@ -16,6 +16,11 @@ Author: Leonardo de Moura
#include "test.h" #include "test.h"
using namespace lean; using namespace lean;
expr normalize(expr const & e) {
environment env;
return normalize(e, env);
}
static void mk(expr const & a) { static void mk(expr const & a) {
expr b = constant("b"); expr b = constant("b");
for (unsigned i = 0; i < 100; i++) { for (unsigned i = 0; i < 100; i++) {

View file

@ -115,7 +115,8 @@ name::name(unsigned k):name(name(), k) {
} }
name::name(name const & other):m_ptr(other.m_ptr) { name::name(name const & other):m_ptr(other.m_ptr) {
m_ptr->inc_ref(); if (m_ptr)
m_ptr->inc_ref();
} }
name::name(name && other):m_ptr(other.m_ptr) { name::name(name && other):m_ptr(other.m_ptr) {