feat(elaborator): add new elaborator interface

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-10-03 15:02:07 -07:00
parent 827c65b5e9
commit 1548ffabb1
14 changed files with 277 additions and 46 deletions

View file

@ -135,6 +135,8 @@ add_subdirectory(library/all)
set(LEAN_LIBS ${LEAN_LIBS} alllib) set(LEAN_LIBS ${LEAN_LIBS} alllib)
add_subdirectory(library/rewriter) add_subdirectory(library/rewriter)
set(LEAN_LIBS ${LEAN_LIBS} rewriter) set(LEAN_LIBS ${LEAN_LIBS} rewriter)
add_subdirectory(library/elaborator)
set(LEAN_LIBS ${LEAN_LIBS} elaborator)
add_subdirectory(frontends/lean) add_subdirectory(frontends/lean)
set(LEAN_LIBS ${LEAN_LIBS} lean_frontend) set(LEAN_LIBS ${LEAN_LIBS} lean_frontend)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread ${LEAN_EXTRA_LINKER_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread ${LEAN_EXTRA_LINKER_FLAGS}")

View file

@ -52,19 +52,19 @@ expr const & get_choice(expr const & e, unsigned i) {
return arg(eq_rhs(e), i); return arg(eq_rhs(e), i);
} }
elaborator::elaborator(frontend const & ) {} old_elaborator::old_elaborator(frontend const & ) {}
elaborator::~elaborator() {} old_elaborator::~old_elaborator() {}
expr elaborator::operator()(expr const & e) { return e; } expr old_elaborator::operator()(expr const & e) { return e; }
expr elaborator::operator()(expr const & e, expr const & /* expected_type */) { return e; } expr old_elaborator::operator()(expr const & e, expr const & /* expected_type */) { return e; }
expr const & elaborator::get_original(expr const & e) const { return e; } expr const & old_elaborator::get_original(expr const & e) const { return e; }
void elaborator::set_interrupt(bool ) {} void old_elaborator::set_interrupt(bool ) {}
void elaborator::clear() {} void old_elaborator::clear() {}
environment const & elaborator::get_environment() const { environment const & old_elaborator::get_environment() const {
static thread_local environment g_env; static thread_local environment g_env;
return g_env; return g_env;
} }
void elaborator::display(std::ostream & ) const {} void old_elaborator::display(std::ostream & ) const {}
format elaborator::pp(formatter &, options const &) const { return format(); } format old_elaborator::pp(formatter &, options const &) const { return format(); }
void elaborator::print(imp * ptr) { lean_assert(ptr); } void old_elaborator::print(imp * ptr) { lean_assert(ptr); }
bool elaborator::has_constraints() const { return false; } bool old_elaborator::has_constraints() const { return false; }
} }

View file

@ -16,13 +16,13 @@ class frontend;
in terms left by the user. This is the main module resposible for computing in terms left by the user. This is the main module resposible for computing
the value of implicit arguments. the value of implicit arguments.
*/ */
class elaborator { class old_elaborator {
class imp; class imp;
std::shared_ptr<imp> m_ptr; std::shared_ptr<imp> m_ptr;
static void print(imp * ptr); static void print(imp * ptr);
public: public:
explicit elaborator(frontend const & fe); explicit old_elaborator(frontend const & fe);
~elaborator(); ~old_elaborator();
expr operator()(expr const & e); expr operator()(expr const & e);
expr operator()(expr const & e, expr const & expected_type); expr operator()(expr const & e, expr const & expected_type);

View file

@ -9,7 +9,7 @@ Author: Leonardo de Moura
#include "frontends/lean/elaborator.h" #include "frontends/lean/elaborator.h"
namespace lean { namespace lean {
format pp_elaborator_state(formatter fmt, elaborator const & elb, options const & opts) { format pp_elaborator_state(formatter fmt, old_elaborator const & elb, options const & opts) {
unsigned indent = get_pp_indent(opts); unsigned indent = get_pp_indent(opts);
format r; format r;
if (elb.has_constraints()) { if (elb.has_constraints()) {
@ -33,7 +33,7 @@ format pp(formatter fmt, context const & ctx, std::vector<expr> const & exprs, s
return r; return r;
} }
format elaborator_exception::pp(formatter const & fmt, options const & opts) const { format old_elaborator_exception::pp(formatter const & fmt, options const & opts) const {
unsigned indent = get_pp_indent(opts); unsigned indent = get_pp_indent(opts);
format expr_fmt = fmt(get_context(), get_expr(), false, opts); format expr_fmt = fmt(get_context(), get_expr(), false, opts);
format r; format r;
@ -102,13 +102,13 @@ format unification_type_mismatch_exception::pp(formatter const & fmt, options co
return r; return r;
} }
regular const & operator<<(regular const & out, elaborator_exception const & ex) { regular const & operator<<(regular const & out, old_elaborator_exception const & ex) {
options const & opts = out.m_state.get_options(); 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); out.m_state.get_regular_channel().get_stream() << mk_pair(ex.pp(out.m_state.get_formatter(), opts), opts);
return out; return out;
} }
diagnostic const & operator<<(diagnostic const & out, elaborator_exception const & ex) { diagnostic const & operator<<(diagnostic const & out, old_elaborator_exception const & ex) {
options const & opts = out.m_state.get_options(); 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); out.m_state.get_diagnostic_channel().get_stream() << mk_pair(ex.pp(out.m_state.get_formatter(), opts), opts);
return out; return out;

View file

@ -17,39 +17,39 @@ namespace lean {
\remark We reuse kernel exceptions to sign type errors during \remark We reuse kernel exceptions to sign type errors during
elaboration. Perhaps we should wrap them as elaborator exceptions. elaboration. Perhaps we should wrap them as elaborator exceptions.
*/ */
class elaborator_exception : public exception { class old_elaborator_exception : public exception {
protected: protected:
elaborator m_elb; old_elaborator m_elb;
context m_context; context m_context;
expr m_expr; expr m_expr;
public: public:
elaborator_exception(elaborator const & elb, context const & ctx, expr const & e):m_elb(elb), m_context(ctx), m_expr(e) {} old_elaborator_exception(old_elaborator const & elb, context const & ctx, expr const & e):m_elb(elb), m_context(ctx), m_expr(e) {}
virtual ~elaborator_exception() noexcept {} virtual ~old_elaborator_exception() noexcept {}
elaborator const & get_elaborator() const { return m_elb; } old_elaborator const & get_elaborator() const { return m_elb; }
expr const & get_expr() const { return m_expr; } expr const & get_expr() const { return m_expr; }
context const & get_context() const { return m_context; } context const & get_context() const { return m_context; }
virtual format pp(formatter const & fmt, options const & opts) const; virtual format pp(formatter const & fmt, options const & opts) const;
}; };
class invalid_placeholder_exception : public elaborator_exception { class invalid_placeholder_exception : public old_elaborator_exception {
public: public:
invalid_placeholder_exception(elaborator const & elb, context const & ctx, expr const & e):elaborator_exception(elb, ctx, e) {} 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"; } virtual char const * what() const noexcept { return "invalid placeholder occurrence, placeholder cannot be used as application head"; }
}; };
class unsolved_placeholder_exception : public elaborator_exception { class unsolved_placeholder_exception : public old_elaborator_exception {
public: public:
unsolved_placeholder_exception(elaborator const & elb, context const & ctx, expr const & e):elaborator_exception(elb, ctx, e) {} 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"; } virtual char const * what() const noexcept { return "unsolved placeholder"; }
}; };
class unification_app_mismatch_exception : public elaborator_exception { class unification_app_mismatch_exception : public old_elaborator_exception {
std::vector<expr> m_args; std::vector<expr> m_args;
std::vector<expr> m_types; std::vector<expr> m_types;
public: public:
unification_app_mismatch_exception(elaborator const & elb, context const & ctx, expr const & s, unification_app_mismatch_exception(old_elaborator const & elb, context const & ctx, expr const & s,
std::vector<expr> const & args, std::vector<expr> const & types): std::vector<expr> const & args, std::vector<expr> const & types):
elaborator_exception(elb, ctx, s), old_elaborator_exception(elb, ctx, s),
m_args(args), m_args(args),
m_types(types) {} m_types(types) {}
virtual ~unification_app_mismatch_exception() {} virtual ~unification_app_mismatch_exception() {}
@ -59,14 +59,14 @@ public:
virtual format pp(formatter const & fmt, options const & opts) const; virtual format pp(formatter const & fmt, options const & opts) const;
}; };
class unification_type_mismatch_exception : public elaborator_exception { class unification_type_mismatch_exception : public old_elaborator_exception {
expr m_processed_expr; expr m_processed_expr;
expr m_expected_type; expr m_expected_type;
expr m_given_type; expr m_given_type;
public: public:
unification_type_mismatch_exception(elaborator const & elb, context const & ctx, expr const & s, unification_type_mismatch_exception(old_elaborator const & elb, context const & ctx, expr const & s,
expr const & processed, expr const & expected, expr const & given): expr const & processed, expr const & expected, expr const & given):
elaborator_exception(elb, ctx, s), m_processed_expr(processed), m_expected_type(expected), m_given_type(given) {} old_elaborator_exception(elb, ctx, s), m_processed_expr(processed), m_expected_type(expected), m_given_type(given) {}
virtual ~unification_type_mismatch_exception() {} virtual ~unification_type_mismatch_exception() {}
expr const & get_processed_expr() const { return m_processed_expr; } expr const & get_processed_expr() const { return m_processed_expr; }
expr const & get_expected_type() const { return m_expected_type; } expr const & get_expected_type() const { return m_expected_type; }
@ -75,16 +75,16 @@ public:
virtual format pp(formatter const & fmt, options const & opts) const; virtual format pp(formatter const & fmt, options const & opts) const;
}; };
class overload_exception : public elaborator_exception { class overload_exception : public old_elaborator_exception {
std::vector<expr> m_fs; std::vector<expr> m_fs;
std::vector<expr> m_f_types; std::vector<expr> m_f_types;
std::vector<expr> m_args; std::vector<expr> m_args;
std::vector<expr> m_arg_types; std::vector<expr> m_arg_types;
public: public:
overload_exception(elaborator const & elb, context const & ctx, expr const & s, overload_exception(old_elaborator const & elb, context const & ctx, expr const & s,
unsigned num_fs, expr const * fs, expr const * ftypes, unsigned num_fs, expr const * fs, expr const * ftypes,
unsigned num_args, expr const * args, expr const * argtypes): unsigned num_args, expr const * args, expr const * argtypes):
elaborator_exception(elb, ctx, s), old_elaborator_exception(elb, ctx, s),
m_fs(fs, fs + num_fs), m_fs(fs, fs + num_fs),
m_f_types(ftypes, ftypes + num_fs), m_f_types(ftypes, ftypes + num_fs),
m_args(args, args + num_args), m_args(args, args + num_args),
@ -100,7 +100,7 @@ public:
class no_overload_exception : public overload_exception { class no_overload_exception : public overload_exception {
public: public:
no_overload_exception(elaborator const & elb, context const & ctx, expr const & s, no_overload_exception(old_elaborator const & elb, context const & ctx, expr const & s,
unsigned num_fs, expr const * fs, expr const * ftypes, unsigned num_fs, expr const * fs, expr const * ftypes,
unsigned num_args, expr const * args, expr const * argtypes): unsigned num_args, expr const * args, expr const * argtypes):
overload_exception(elb, ctx, s, num_fs, fs, ftypes, num_args, args, argtypes) {} overload_exception(elb, ctx, s, num_fs, fs, ftypes, num_args, args, argtypes) {}
@ -109,13 +109,13 @@ public:
class ambiguous_overload_exception : public overload_exception { class ambiguous_overload_exception : public overload_exception {
public: public:
ambiguous_overload_exception(elaborator const & elb, context const & ctx, expr const & s, ambiguous_overload_exception(old_elaborator const & elb, context const & ctx, expr const & s,
unsigned num_fs, expr const * fs, expr const * ftypes, unsigned num_fs, expr const * fs, expr const * ftypes,
unsigned num_args, expr const * args, expr const * argtypes): unsigned num_args, expr const * args, expr const * argtypes):
overload_exception(elb, ctx, s, num_fs, fs, ftypes, num_args, args, argtypes) {} overload_exception(elb, ctx, s, num_fs, fs, ftypes, num_args, args, argtypes) {}
virtual char const * what() const noexcept { return "ambiguous overloads"; } virtual char const * what() const noexcept { return "ambiguous overloads"; }
}; };
regular const & operator<<(regular const & out, elaborator_exception const & ex); regular const & operator<<(regular const & out, old_elaborator_exception const & ex);
diagnostic const & operator<<(diagnostic const & out, elaborator_exception const & ex); diagnostic const & operator<<(diagnostic const & out, old_elaborator_exception const & ex);
} }

View file

@ -117,7 +117,7 @@ class parser::imp {
typedef buffer<std::tuple<pos_info, name, expr, bool>> bindings_buffer; typedef buffer<std::tuple<pos_info, name, expr, bool>> bindings_buffer;
frontend m_frontend; frontend m_frontend;
scanner m_scanner; scanner m_scanner;
elaborator m_elaborator; old_elaborator m_elaborator;
scanner::token m_curr; scanner::token m_curr;
bool m_use_exceptions; bool m_use_exceptions;
bool m_interactive; bool m_interactive;
@ -1502,20 +1502,24 @@ class parser::imp {
return display_error_pos(m_last_cmd_pos); return display_error_pos(m_last_cmd_pos);
} }
} }
void display_error(char const * msg, unsigned line, unsigned pos) { void display_error(char const * msg, unsigned line, unsigned pos) {
display_error_pos(line, pos); display_error_pos(line, pos);
regular(m_frontend) << " " << msg << endl; regular(m_frontend) << " " << msg << endl;
sync(); sync();
} }
void display_error(char const * msg) { void display_error(char const * msg) {
display_error(msg, m_scanner.get_line(), m_scanner.get_pos()); display_error(msg, m_scanner.get_line(), m_scanner.get_pos());
} }
void display_error(kernel_exception const & ex) { void display_error(kernel_exception const & ex) {
display_error_pos(m_elaborator.get_original(ex.get_main_expr())); display_error_pos(m_elaborator.get_original(ex.get_main_expr()));
regular(m_frontend) << " " << ex << endl; regular(m_frontend) << " " << ex << endl;
sync(); sync();
} }
void display_error(elaborator_exception const & ex) {
void display_error(old_elaborator_exception const & ex) {
display_error_pos(m_elaborator.get_original(ex.get_expr())); display_error_pos(m_elaborator.get_original(ex.get_expr()));
regular(m_frontend) << " " << ex << endl; regular(m_frontend) << " " << ex << endl;
sync(); sync();
@ -1582,7 +1586,7 @@ public:
display_error(ex); display_error(ex);
if (m_use_exceptions) if (m_use_exceptions)
throw; throw;
} catch (elaborator_exception & ex) { } catch (old_elaborator_exception & ex) {
m_found_errors = true; m_found_errors = true;
if (m_show_errors) if (m_show_errors)
display_error(ex); display_error(ex);

View file

@ -24,7 +24,7 @@ static format add_context(formatter const & fmt, options const & opts, context c
} }
static format add_trace(formatter const & fmt, options const & opts, format const & body, trace const & tr, bool include_trace) { static format add_trace(formatter const & fmt, options const & opts, format const & body, trace const & tr, bool include_trace) {
if (include_trace) { if (tr && include_trace) {
unsigned indent = get_pp_indent(opts); unsigned indent = get_pp_indent(opts);
return format{body, line(), format("Trace:"), nest(indent, compose(line(), tr.pp(fmt, opts)))}; return format{body, line(), format("Trace:"), nest(indent, compose(line(), tr.pp(fmt, opts)))};
} else { } else {

View file

@ -0,0 +1,2 @@
add_library(elaborator elaborator.cpp)
target_link_libraries(elaborator ${LEAN_LIBS})

View file

@ -0,0 +1,59 @@
/*
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 "library/elaborator/elaborator.h"
namespace lean {
class elaborator::imp {
environment const & m_env;
std::shared_ptr<synthesizer> m_synthesizer;
std::shared_ptr<elaborator_plugin> m_plugin;
bool m_interrupted;
public:
imp(environment const & env, metavar_env const &, unsigned, unification_constraint const *,
std::shared_ptr<synthesizer> const & s, std::shared_ptr<elaborator_plugin> const & p):
m_env(env),
m_synthesizer(s),
m_plugin(p) {
m_interrupted = false;
}
substitution next() {
// TODO(Leo)
return substitution();
}
void interrupt() {
m_interrupted = true;
}
};
elaborator::elaborator(environment const & env,
metavar_env const & menv,
unsigned num_cnstrs,
unification_constraint const * cnstrs,
std::shared_ptr<synthesizer> const & s,
std::shared_ptr<elaborator_plugin> const & p):
m_ptr(new imp(env, menv, num_cnstrs, cnstrs, s, p)) {
}
elaborator::elaborator(environment const & env,
metavar_env const & menv,
context const & ctx, expr const & lhs, expr const & rhs):
elaborator(env, menv, { mk_eq_constraint(ctx, lhs, rhs, trace()) }) {
}
elaborator::~elaborator() {
}
substitution elaborator::next() {
return m_ptr->next();
}
void elaborator::interrupt() {
m_ptr->interrupt();
}
}

View file

@ -0,0 +1,63 @@
/*
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 "kernel/expr.h"
#include "kernel/environment.h"
#include "kernel/metavar.h"
#include "kernel/unification_constraint.h"
#include "library/elaborator/elaborator_plugin.h"
#include "library/elaborator/synthesizer.h"
namespace lean {
/**
\brief Elaborator is the main object used to fill "holes" in Lean.
Each hole is represented using a metavariable. This object is
responsible for solving the easy "holes" and invoking external
plugins/synthesizers for filling the other ones. It is also
responsible for managing the search space (i.e., managing the
backtracking search).
The elaborator can be customized using:
1) Elaborator plugins. They are invoked whenever the elaborator
does not know how to solve a unification constraint.
2) Synthesizers. They are invoked whenever the elaborator does not
have unification constraints for inferring a particular hole.
The result is a sequence of substitutions. Each substitution
represents a different way of filling the holes.
*/
class elaborator {
public:
class imp;
std::shared_ptr<imp> m_ptr;
public:
elaborator(environment const & env,
metavar_env const & menv,
unsigned num_cnstrs,
unification_constraint const * cnstrs,
std::shared_ptr<synthesizer> const & s = std::shared_ptr<synthesizer>(),
std::shared_ptr<elaborator_plugin> const & p = std::shared_ptr<elaborator_plugin>());
elaborator(environment const & env,
metavar_env const & menv,
std::initializer_list<unification_constraint> const & cnstrs,
std::shared_ptr<synthesizer> const & s = std::shared_ptr<synthesizer>(),
std::shared_ptr<elaborator_plugin> const & p = std::shared_ptr<elaborator_plugin>()):
elaborator(env, menv, cnstrs.size(), cnstrs.begin(), s, p) {}
elaborator(environment const & env,
metavar_env const & menv,
context const & ctx, expr const & lhs, expr const & rhs);
~elaborator();
substitution next();
void interrupt();
};
}

View file

@ -0,0 +1,24 @@
/*
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 "util/exception.h"
#include "kernel/trace.h"
namespace lean {
/**
\brief Elaborator and related components store the reason for
failure in trace objects.
*/
class elaborator_exception : public exception {
trace m_trace;
public:
elaborator_exception(trace const & tr):m_trace(tr) {}
virtual ~elaborator_exception() {}
virtual char const * what() const noexcept { return "elaborator exception"; }
trace const & get_trace() const { return m_trace; }
};
}

View file

@ -0,0 +1,40 @@
/*
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 <memory>
#include "util/list.h"
#include "kernel/environment.h"
#include "kernel/context.h"
#include "kernel/unification_constraint.h"
namespace lean {
class elaborator_plugin {
public:
virtual ~elaborator_plugin() {}
/** \brief The plugin produces a "result" object that can generates the sequence of possible solutions. */
class result {
public:
virtual ~result() {}
/**
\brief Return the next possible solution. An elaborator_exception is throw in case of failure.
Each result is represented by a pair: the new metavariable
environment and a new list of constraints to be solved.
*/
virtual std::pair<metavar_env, list<unification_constraint>> next() = 0;
/** \brief Interrupt the computation for the next solution. */
virtual void interrupt() = 0;
};
/**
\brief Ask plugin to solve the constraint \c cnstr in the given
environment and metavar environment.
*/
virtual std::unique_ptr<result> operator()(environment const & env, metavar_env const & menv, unification_constraint const & cnstr) = 0;
};
}

View file

@ -0,0 +1,37 @@
/*
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 <memory>
#include "kernel/environment.h"
#include "kernel/context.h"
#include "library/elaborator/elaborator_exception.h"
namespace lean {
/**
\brief A synthesizer generates a sequence of expressions of a given type.
*/
class synthesizer {
public:
virtual ~synthesizer() {}
/** \brief The synthesizer produces a "result" object that can generates the sequence of possible solutions. */
class result {
public:
virtual ~result() {}
/** \brief Return the next possible solution. An elaborator_exception is throw in case of failure. */
virtual expr next() = 0;
/** \brief Interrupt the computation for the next solution. */
virtual void interrupt() = 0;
};
/**
\brief Return an object for computing a sequence of expressions
of type \c type in the given environment and context.
*/
virtual std::unique_ptr<result> operator()(environment const & env, context const & ctx, expr const & type) = 0;
};
}

View file

@ -20,7 +20,7 @@ Author: Leonardo de Moura
using namespace lean; using namespace lean;
expr elaborate(expr const & e, frontend const & env) { expr elaborate(expr const & e, frontend const & env) {
elaborator elb(env); old_elaborator elb(env);
return elb(e); return elb(e);
} }