refactor(frontends/lean/elaborator): delete old_elaborator, and create frontend_elaborator class that will be based on library/elaborator/elaborator

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-10-24 10:45:59 -07:00
parent 449454efdb
commit 71ccec5b9e
8 changed files with 134 additions and 341 deletions

View file

@ -1,3 +1,3 @@
add_library(lean_frontend frontend.cpp operator_info.cpp scanner.cpp parser.cpp pp.cpp
notation.cpp elaborator.cpp elaborator_exception.cpp)
notation.cpp frontend_elaborator.cpp)
target_link_libraries(lean_frontend ${LEAN_LIBS})

View file

@ -1,69 +0,0 @@
/*
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 <limits>
#include <utility>
#include <vector>
#include "util/flet.h"
#include "util/name_set.h"
#include "kernel/normalizer.h"
#include "kernel/context.h"
#include "kernel/builtin.h"
#include "kernel/free_vars.h"
#include "kernel/for_each.h"
#include "kernel/replace.h"
#include "kernel/instantiate.h"
#include "kernel/metavar.h"
#include "kernel/printer.h"
#include "library/placeholder.h"
#include "library/update_expr.h"
#include "library/expr_pair.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/elaborator.h"
#include "frontends/lean/elaborator_exception.h"
namespace lean {
static name g_choice_name = name::mk_internal_unique_name();
static expr g_choice = mk_constant(g_choice_name);
static format g_assignment_fmt = format(":=");
static format g_unification_u_fmt = format("\u2248");
static format g_unification_fmt = format("=?=");
expr mk_choice(unsigned num_fs, expr const * fs) {
lean_assert(num_fs >= 2);
return mk_eq(g_choice, mk_app(num_fs, fs));
}
bool is_choice(expr const & e) {
return is_eq(e) && eq_lhs(e) == g_choice;
}
unsigned get_num_choices(expr const & e) {
lean_assert(is_choice(e));
return num_args(eq_rhs(e));
}
expr const & get_choice(expr const & e, unsigned i) {
lean_assert(is_choice(e));
return arg(eq_rhs(e), i);
}
old_elaborator::old_elaborator(frontend const & ) {}
old_elaborator::~old_elaborator() {}
expr old_elaborator::operator()(expr const & e) { return e; }
expr old_elaborator::operator()(expr const & e, expr const & /* expected_type */) { return e; }
expr const & old_elaborator::get_original(expr const & e) const { return e; }
void old_elaborator::set_interrupt(bool ) {}
void old_elaborator::clear() {}
environment const & old_elaborator::get_environment() const {
static thread_local environment g_env;
return g_env;
}
void old_elaborator::display(std::ostream & ) const {}
format old_elaborator::pp(formatter &, options const &) const { return format(); }
void old_elaborator::print(imp * ptr) { lean_assert(ptr); }
bool old_elaborator::has_constraints() const { return false; }
}

View file

@ -1,116 +0,0 @@
/*
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 "frontends/lean/elaborator_exception.h"
#include "frontends/lean/elaborator.h"
namespace lean {
format pp_elaborator_state(formatter fmt, old_elaborator const & elb, options const & opts) {
unsigned indent = get_pp_indent(opts);
format r;
if (elb.has_constraints()) {
format elb_fmt = elb.pp(fmt, opts);
r += compose(line(), format("Elaborator state"));
r += nest(indent, compose(line(), elb_fmt));
}
return r;
}
format pp(formatter fmt, context const & ctx, std::vector<expr> const & exprs, std::vector<expr> const & types, options const & opts) {
unsigned indent = get_pp_indent(opts);
lean_assert(exprs.size() == types.size());
auto it1 = exprs.begin();
auto it2 = types.begin();
format r;
for (; it1 != exprs.end(); ++it1, ++it2) {
r += nest(indent, compose(line(), group(format{fmt(ctx, *it1, false, opts), space(), colon(),
nest(indent, format{line(), fmt(ctx, *it2, false, opts)})})));
}
return r;
}
format old_elaborator_exception::pp(formatter const & fmt, options const & opts) const {
unsigned indent = get_pp_indent(opts);
format expr_fmt = fmt(get_context(), get_expr(), false, opts);
format r;
r += format{format(what()), space(), format("at term")};
r += nest(indent, compose(line(), expr_fmt));
r += pp_elaborator_state(fmt, get_elaborator(), opts);
return r;
}
format overload_exception::pp(formatter const & fmt, options const & opts) const {
context const & ctx = get_context();
format r;
r += format{format(what()), line(), format("Candidates:")};
r += ::lean::pp(fmt, ctx, get_fs(), get_f_types(), opts);
r += format{line(), format("Arguments:")};
r += ::lean::pp(fmt, ctx, get_args(), get_arg_types(), opts);
return r;
}
format unification_app_mismatch_exception::pp(formatter const & fmt, options const & opts) const {
unsigned indent = get_pp_indent(opts);
auto const & ctx = get_context();
expr const & app = get_expr();
auto args_it = get_args().begin();
auto args_end = get_args().end();
auto types_it = get_types().begin();
format app_fmt = fmt(ctx, app, false, opts);
format r = format{format(what()), nest(indent, compose(line(), app_fmt))};
format fun_type_fmt = fmt(ctx, *types_it, false, opts);
r += compose(line(), format("Function type:"));
r += nest(indent, compose(line(), fun_type_fmt));
++args_it;
++types_it;
if (get_args().size() > 2)
r += compose(line(), format("Arguments types:"));
else
r += compose(line(), format("Argument type:"));
for (; args_it != args_end; ++args_it, ++types_it) {
format arg_fmt = fmt(ctx, *args_it, false, opts);
format type_fmt = fmt(ctx, *types_it, false, opts);
r += nest(indent, compose(line(), group(format{arg_fmt, space(), colon(), nest(indent, format{line(), type_fmt})})));
}
r += pp_elaborator_state(fmt, get_elaborator(), opts);
return r;
}
format unification_type_mismatch_exception::pp(formatter const & fmt, options const & opts) const {
unsigned indent = get_pp_indent(opts);
auto const & ctx = get_context();
expr const & e = get_expr();
expr const & p = get_processed_expr();
expr const & exp = get_expected_type();
expr const & given = get_given_type();
format r = format{format(what()), nest(indent, compose(line(), fmt(ctx, e, false, opts)))};
if (p != e) {
r += compose(line(), format("Term after elaboration:"));
r += nest(indent, compose(line(), fmt(ctx, p, false, opts)));
}
r += compose(line(), format("Expected type:"));
r += nest(indent, compose(line(), fmt(ctx, exp, false, opts)));
if (given) {
r += compose(line(), format("Got:"));
r += nest(indent, compose(line(), fmt(ctx, given, false, opts)));
}
r += pp_elaborator_state(fmt, get_elaborator(), opts);
return r;
}
regular const & operator<<(regular const & out, old_elaborator_exception const & ex) {
options const & opts = out.m_state.get_options();
out.m_state.get_regular_channel().get_stream() << mk_pair(ex.pp(out.m_state.get_formatter(), opts), opts);
return out;
}
diagnostic const & operator<<(diagnostic const & out, old_elaborator_exception const & ex) {
options const & opts = out.m_state.get_options();
out.m_state.get_diagnostic_channel().get_stream() << mk_pair(ex.pp(out.m_state.get_formatter(), opts), opts);
return out;
}
}

View file

@ -1,121 +0,0 @@
/*
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 <vector>
#include "kernel/kernel_exception.h"
#include "library/state.h"
#include "frontends/lean/elaborator.h"
namespace lean {
/**
\brief Base class for elaborator exceptions.
\remark We reuse kernel exceptions to sign type errors during
elaboration. Perhaps we should wrap them as elaborator exceptions.
*/
class old_elaborator_exception : public exception {
protected:
old_elaborator m_elb;
context m_context;
expr m_expr;
public:
old_elaborator_exception(old_elaborator const & elb, context const & ctx, expr const & e):m_elb(elb), m_context(ctx), m_expr(e) {}
virtual ~old_elaborator_exception() noexcept {}
old_elaborator const & get_elaborator() const { return m_elb; }
expr const & get_expr() const { return m_expr; }
context const & get_context() const { return m_context; }
virtual format pp(formatter const & fmt, options const & opts) const;
};
class invalid_placeholder_exception : public old_elaborator_exception {
public:
invalid_placeholder_exception(old_elaborator const & elb, context const & ctx, expr const & e):old_elaborator_exception(elb, ctx, e) {}
virtual char const * what() const noexcept { return "invalid placeholder occurrence, placeholder cannot be used as application head"; }
};
class unsolved_placeholder_exception : public old_elaborator_exception {
public:
unsolved_placeholder_exception(old_elaborator const & elb, context const & ctx, expr const & e):old_elaborator_exception(elb, ctx, e) {}
virtual char const * what() const noexcept { return "unsolved placeholder"; }
};
class unification_app_mismatch_exception : public old_elaborator_exception {
std::vector<expr> m_args;
std::vector<expr> m_types;
public:
unification_app_mismatch_exception(old_elaborator const & elb, context const & ctx, expr const & s,
std::vector<expr> const & args, std::vector<expr> const & types):
old_elaborator_exception(elb, ctx, s),
m_args(args),
m_types(types) {}
virtual ~unification_app_mismatch_exception() {}
std::vector<expr> const & get_args() const { return m_args; }
std::vector<expr> const & get_types() const { return m_types; }
virtual char const * what() const noexcept { return "application type mismatch during term elaboration"; }
virtual format pp(formatter const & fmt, options const & opts) const;
};
class unification_type_mismatch_exception : public old_elaborator_exception {
expr m_processed_expr;
expr m_expected_type;
expr m_given_type;
public:
unification_type_mismatch_exception(old_elaborator const & elb, context const & ctx, expr const & s,
expr const & processed, expr const & expected, expr const & given):
old_elaborator_exception(elb, ctx, s), m_processed_expr(processed), m_expected_type(expected), m_given_type(given) {}
virtual ~unification_type_mismatch_exception() {}
expr const & get_processed_expr() const { return m_processed_expr; }
expr const & get_expected_type() const { return m_expected_type; }
expr const & get_given_type() const { return m_given_type; }
virtual char const * what() const noexcept { return "type mismatch during term elaboration"; }
virtual format pp(formatter const & fmt, options const & opts) const;
};
class overload_exception : public old_elaborator_exception {
std::vector<expr> m_fs;
std::vector<expr> m_f_types;
std::vector<expr> m_args;
std::vector<expr> m_arg_types;
public:
overload_exception(old_elaborator const & elb, context const & ctx, expr const & s,
unsigned num_fs, expr const * fs, expr const * ftypes,
unsigned num_args, expr const * args, expr const * argtypes):
old_elaborator_exception(elb, ctx, s),
m_fs(fs, fs + num_fs),
m_f_types(ftypes, ftypes + num_fs),
m_args(args, args + num_args),
m_arg_types(argtypes, argtypes + num_args) {
}
virtual ~overload_exception() {}
std::vector<expr> const & get_fs() const { return m_fs; }
std::vector<expr> const & get_f_types() const { return m_f_types; }
std::vector<expr> const & get_args() const { return m_args; }
std::vector<expr> const & get_arg_types() const { return m_arg_types; }
virtual format pp(formatter const & fmt, options const & opts) const;
};
class no_overload_exception : public overload_exception {
public:
no_overload_exception(old_elaborator const & elb, context const & ctx, expr const & s,
unsigned num_fs, expr const * fs, expr const * ftypes,
unsigned num_args, expr const * args, expr const * argtypes):
overload_exception(elb, ctx, s, num_fs, fs, ftypes, num_args, args, argtypes) {}
virtual char const * what() const noexcept { return "application type mismatch, none of the overloads can be used"; }
};
class ambiguous_overload_exception : public overload_exception {
public:
ambiguous_overload_exception(old_elaborator const & elb, context const & ctx, expr const & s,
unsigned num_fs, expr const * fs, expr const * ftypes,
unsigned num_args, expr const * args, expr const * argtypes):
overload_exception(elb, ctx, s, num_fs, fs, ftypes, num_args, args, argtypes) {}
virtual char const * what() const noexcept { return "ambiguous overloads"; }
};
regular const & operator<<(regular const & out, old_elaborator_exception const & ex);
diagnostic const & operator<<(diagnostic const & out, old_elaborator_exception const & ex);
}

View file

@ -0,0 +1,88 @@
/*
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 "kernel/type_checker.h"
#include "library/elaborator/elaborator.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/frontend_elaborator.h"
namespace lean {
static name g_choice_name = name::mk_internal_unique_name();
static expr g_choice = mk_constant(g_choice_name);
static format g_assignment_fmt = format(":=");
static format g_unification_u_fmt = format("\u2248");
static format g_unification_fmt = format("=?=");
expr mk_choice(unsigned num_fs, expr const * fs) {
lean_assert(num_fs >= 2);
return mk_eq(g_choice, mk_app(num_fs, fs));
}
bool is_choice(expr const & e) {
return is_eq(e) && eq_lhs(e) == g_choice;
}
unsigned get_num_choices(expr const & e) {
lean_assert(is_choice(e));
return num_args(eq_rhs(e));
}
expr const & get_choice(expr const & e, unsigned i) {
lean_assert(is_choice(e));
return arg(eq_rhs(e), i);
}
class frontend_elaborator::imp {
frontend const & m_frontend;
environment const & m_env;
type_checker m_type_checker;
volatile bool m_interrupted;
public:
imp(frontend const & fe):
m_frontend(fe),
m_env(fe.get_environment()),
m_type_checker(m_env) {
m_interrupted = false;
}
expr elaborate(expr const & e) {
// TODO(Leo)
return e;
}
std::pair<expr, expr> elaborate(expr const & t, expr const & e) {
// TODO(Leo)
return mk_pair(t, e);
}
expr const & get_original(expr const & e) {
// TODO(Leo)
return e;
}
void set_interrupt(bool f) {
m_interrupted = f;
m_type_checker.set_interrupt(f);
}
void clear() {
// TODO(Leo)
}
environment const & get_environment() const {
return m_env;
}
};
frontend_elaborator::frontend_elaborator(frontend const & fe):m_ptr(new imp(fe)) {}
frontend_elaborator::~frontend_elaborator() {}
expr frontend_elaborator::operator()(expr const & e) { return m_ptr->elaborate(e); }
std::pair<expr, expr> frontend_elaborator::operator()(expr const & t, expr const & e) { return m_ptr->elaborate(t, e); }
expr const & frontend_elaborator::get_original(expr const & e) const { return m_ptr->get_original(e); }
void frontend_elaborator::set_interrupt(bool f) { m_ptr->set_interrupt(f); }
void frontend_elaborator::clear() { m_ptr->clear(); }
environment const & frontend_elaborator::get_environment() const { return m_ptr->get_environment(); }
}

View file

@ -12,20 +12,29 @@ Author: Leonardo de Moura
namespace lean {
class frontend;
/**
\brief Expression elaborator, it is responsible for "filling" holes
\brief Expression elaborator for the Lean default frontend, it is responsible for "filling" holes
in terms left by the user. This is the main module resposible for computing
the value of implicit arguments.
the value of implicit arguments. It is based on the general purpose elaborator defined at
library/elaborator/elaborator.h
*/
class old_elaborator {
class frontend_elaborator {
class imp;
std::shared_ptr<imp> m_ptr;
static void print(imp * ptr);
public:
explicit old_elaborator(frontend const & fe);
~old_elaborator();
explicit frontend_elaborator(frontend const & fe);
~frontend_elaborator();
/**
\brief Elaborate the given expression.
*/
expr operator()(expr const & e);
expr operator()(expr const & e, expr const & expected_type);
/**
\brief Elaborate the given type and expression. The typeof(e) == t.
This information is used by the elaborator. The result is a new
elaborated type and expression.
*/
std::pair<expr, expr> operator()(expr const & t, expr const & e);
/**
\brief If \c e is an expression instantiated by the elaborator, then it
@ -41,12 +50,8 @@ public:
void clear();
environment const & get_environment() const;
void display(std::ostream & out) const;
format pp(formatter & f, options const & o) const;
bool has_constraints() const;
};
/**
\brief Create a choice expression for the given functions.
It is used to mark which functions can be used in a particular application.

View file

@ -29,9 +29,9 @@ Author: Leonardo de Moura
#include "library/arith/arith.h"
#include "library/state.h"
#include "library/placeholder.h"
#include "library/elaborator/elaborator_exception.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/elaborator.h"
#include "frontends/lean/elaborator_exception.h"
#include "frontends/lean/frontend_elaborator.h"
#include "frontends/lean/parser.h"
#include "frontends/lean/scanner.h"
#include "frontends/lean/notation.h"
@ -115,24 +115,24 @@ class parser::imp {
typedef std::pair<unsigned, unsigned> pos_info;
typedef expr_map<pos_info> expr_pos_info;
typedef buffer<std::tuple<pos_info, name, expr, bool>> bindings_buffer;
frontend m_frontend;
scanner m_scanner;
old_elaborator m_elaborator;
scanner::token m_curr;
bool m_use_exceptions;
bool m_interactive;
bool m_found_errors;
local_decls m_local_decls;
unsigned m_num_local_decls;
expr_pos_info m_expr_pos_info;
pos_info m_last_cmd_pos;
frontend m_frontend;
scanner m_scanner;
frontend_elaborator m_elaborator;
scanner::token m_curr;
bool m_use_exceptions;
bool m_interactive;
bool m_found_errors;
local_decls m_local_decls;
unsigned m_num_local_decls;
expr_pos_info m_expr_pos_info;
pos_info m_last_cmd_pos;
// Reference to temporary parser used to process import command.
// We need this reference to be able to interrupt it.
interruptable_ptr<parser> m_import_parser;
interruptable_ptr<normalizer> m_normalizer;
bool m_verbose;
bool m_show_errors;
bool m_verbose;
bool m_show_errors;
/** \brief Exception used to track parsing erros, it does not leak outside of this class. */
struct parser_error : public exception {
@ -1030,14 +1030,14 @@ class parser::imp {
/** \brief Auxiliary method used for parsing definitions and theorems. */
void parse_def_core(bool is_definition) {
next();
expr type, val;
expr pre_type, pre_val;
name id = check_identifier_next("invalid definition, identifier expected");
bindings_buffer bindings;
if (curr_is_colon()) {
next();
type = m_elaborator(parse_expr());
pre_type = parse_expr();
check_assign_next("invalid definition, ':=' expected");
val = m_elaborator(parse_expr(), type);
pre_val = parse_expr();
} else {
mk_scope scope(*this);
parse_object_bindings(bindings);
@ -1045,9 +1045,12 @@ class parser::imp {
expr type_body = parse_expr();
check_assign_next("invalid definition, ':=' expected");
expr val_body = parse_expr();
type = m_elaborator(mk_abstraction(false, bindings, type_body));
val = m_elaborator(mk_abstraction(true, bindings, val_body), type);
pre_type = mk_abstraction(false, bindings, type_body);
pre_val = mk_abstraction(true, bindings, val_body);
}
auto type_val_pair = m_elaborator(pre_type, pre_val);
expr type = type_val_pair.first;
expr val = type_val_pair.second;
if (is_definition) {
m_frontend.add_definition(id, type, val);
if (m_verbose)
@ -1519,10 +1522,13 @@ class parser::imp {
sync();
}
void display_error(old_elaborator_exception const & ex) {
void display_error(elaborator_exception const &) {
#if 0
// TODO(Leo)
display_error_pos(m_elaborator.get_original(ex.get_expr()));
regular(m_frontend) << " " << ex << endl;
sync();
#endif
}
void updt_options() {
@ -1586,7 +1592,7 @@ public:
display_error(ex);
if (m_use_exceptions)
throw;
} catch (old_elaborator_exception & ex) {
} catch (elaborator_exception & ex) {
m_found_errors = true;
if (m_show_errors)
display_error(ex);

View file

@ -25,7 +25,7 @@ Author: Leonardo de Moura
#include "frontends/lean/pp.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/coercion.h"
#include "frontends/lean/elaborator.h"
#include "frontends/lean/frontend_elaborator.h"
#ifndef LEAN_DEFAULT_PP_MAX_DEPTH
#define LEAN_DEFAULT_PP_MAX_DEPTH std::numeric_limits<unsigned>::max()