refactor(frontends/lean): remove dead code

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-05-15 15:51:41 -07:00
parent 924b2344af
commit c6af56260e
36 changed files with 8 additions and 8283 deletions

View file

@ -225,8 +225,8 @@ set(LEAN_LIBS ${LEAN_LIBS} library)
# set(LEAN_LIBS ${LEAN_LIBS} tactic)
add_subdirectory(library/error_handling)
set(LEAN_LIBS ${LEAN_LIBS} error_handling)
# add_subdirectory(frontends/lean)
# set(LEAN_LIBS ${LEAN_LIBS} lean_frontend)
add_subdirectory(frontends/lean)
set(LEAN_LIBS ${LEAN_LIBS} lean_frontend)
add_subdirectory(frontends/lua)
set(LEAN_LIBS ${LEAN_LIBS} leanlua)
if("${MULTI_THREAD}" MATCHES "ON")
@ -238,7 +238,7 @@ set(CMAKE_EXE_LINKER_FLAGS_TESTCOV "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ft
set(EXTRA_LIBS ${LEAN_LIBS} ${EXTRA_LIBS})
add_subdirectory(shell)
# add_subdirectory(builtin)
# add_subdirectory(emacs)
add_subdirectory(emacs)
add_subdirectory(tests/util)
add_subdirectory(tests/util/numerics)

View file

@ -1,7 +1,2 @@
add_library(lean_frontend frontend.cpp operator_info.cpp scanner.cpp
parser.cpp parser_imp.cpp parser_expr.cpp parser_error.cpp
parser_imp.cpp parser_cmds.cpp parser_level.cpp parser_tactic.cpp
parser_macros.cpp parser_calc.cpp pp.cpp frontend_elaborator.cpp
register_module.cpp environment_scope.cpp coercion.cpp shell.cpp)
add_library(lean_frontend register_module.cpp)
target_link_libraries(lean_frontend ${LEAN_LIBS})

View file

@ -1,19 +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 "library/io_state_stream.h"
#include "frontends/lean/coercion.h"
#include "frontends/lean/frontend.h"
namespace lean {
void coercion_declaration::write(serializer & s) const {
s << "Coercion" << m_coercion;
}
static void read_coercion(environment const & env, io_state const &, deserializer & d) {
add_coercion(env, read_expr(d));
}
static object_cell::register_deserializer_fn coercion_ds("Coercion", read_coercion);
}

View file

@ -1,24 +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 "kernel/object.h"
namespace lean {
/**
\brief Object for tracking coercion declarations.
This object is mainly used for recording the declaration.
*/
class coercion_declaration : public neutral_object_cell {
expr m_coercion;
public:
coercion_declaration(expr const & c):m_coercion(c) {}
virtual ~coercion_declaration() {}
virtual char const * keyword() const { return "coercion"; }
expr const & get_coercion() const { return m_coercion; }
virtual void write(serializer & s) const;
};
}

View file

@ -1,126 +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 <algorithm>
#include "util/list.h"
#include "util/name_set.h"
#include "util/name_map.h"
#include "kernel/environment.h"
#include "kernel/replace_fn.h"
#include "kernel/abstract.h"
#include "library/io_state_stream.h"
#include "frontends/lean/frontend.h"
namespace lean {
typedef std::vector<name> dependencies;
struct object_info {
object m_object;
unsigned m_pos;
dependencies m_deps;
expr m_ref; // a reference to this object, it can be a constant or an application containing the dependencies
object_info(object const & obj, unsigned pos, dependencies const & deps, expr const & ref):m_object(obj), m_pos(pos), m_deps(deps), m_ref(ref) {}
};
static expr convert(expr const & e, name_map<object_info> & info_map, name_set & dep_set) {
return replace(e, [&](expr const & e, unsigned) -> expr {
if (is_constant(e)) {
auto it = info_map.find(const_name(e));
if (it != info_map.end()) {
auto const & info = it->second;
if (info.m_object.is_axiom() || info.m_object.is_var_decl())
dep_set.insert(const_name(e));
for (auto const & d : info.m_deps)
dep_set.insert(d);
return info.m_ref;
}
}
return e;
});
}
static dependencies mk_dependencies(name_map<object_info> & info_map, name_set & dep_set) {
dependencies r;
for (auto d : dep_set) r.push_back(d);
std::sort(r.begin(), r.end(), [&](name const & n1, name const & n2) {
return info_map.find(n1)->second.m_pos < info_map.find(n2)->second.m_pos;
});
return r;
}
static expr mk_ref(object const & obj, dependencies const & deps) {
if (obj.is_axiom() || obj.is_var_decl()) {
return mk_constant(obj.get_name());
} else {
buffer<expr> args;
args.push_back(mk_constant(obj.get_name()));
for (auto d : deps) args.push_back(mk_constant(d));
if (args.size() == 1)
return args[0];
else
return mk_app(args);
}
}
static expr abstract(bool is_lambda, expr e, name_map<object_info> & info_map, dependencies const & deps) {
auto it = deps.end();
auto begin = deps.begin();
while (it != begin) {
--it;
expr const & type = info_map.find(*it)->second.m_object.get_type();
if (is_lambda)
e = Fun(*it, type, e);
else
e = Pi(*it, type, e);
}
return e;
}
static expr Pi(expr e, name_map<object_info> & info_map, dependencies const & deps) {
return abstract(false, e, info_map, deps);
}
static expr Fun(expr e, name_map<object_info> & info_map, dependencies const & deps) {
return abstract(true, e, info_map, deps);
}
std::vector<object> export_local_objects(environment const & env) {
// TODO(Leo): Revise using Parameters
if (!env->has_parent())
return std::vector<object>();
name_map<object_info> info_map;
name_set dep_set;
unsigned pos = 0;
std::vector<object> new_objects;
auto it = env->begin_local_objects();
auto end = env->end_local_objects();
for (; it != end; ++it) {
object const & obj = *it;
if (!obj.is_axiom() && !obj.is_var_decl() && !obj.is_theorem() && !obj.is_definition())
continue;
if (is_explicit(env, obj.get_name()))
continue;
dep_set.clear();
if (obj.is_axiom() || obj.is_var_decl()) {
expr new_type = convert(obj.get_type(), info_map, dep_set);
auto new_deps = mk_dependencies(info_map, dep_set);
info_map.insert(mk_pair(obj.get_name(), object_info(obj, pos, new_deps, mk_ref(obj, new_deps))));
} else {
expr new_type = convert(obj.get_type(), info_map, dep_set);
expr new_val = convert(obj.get_value(), info_map, dep_set);
auto new_deps = mk_dependencies(info_map, dep_set);
new_type = Pi(new_type, info_map, new_deps);
new_val = Fun(new_val, info_map, new_deps);
auto new_obj = obj.is_theorem() ? mk_theorem(obj.get_name(), new_type, new_val) : mk_definition(obj.get_name(), new_type, new_val, obj.get_weight());
new_objects.push_back(new_obj);
info_map.insert(mk_pair(obj.get_name(), object_info(new_obj, pos, new_deps, mk_ref(new_obj, new_deps))));
}
pos++;
}
return new_objects;
}
}

View file

@ -1,12 +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/environment.h"
namespace lean {
std::vector<object> export_local_objects(environment const & env);
}

View file

@ -1,669 +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 <utility>
#include <functional>
#include <string>
#include "util/thread.h"
#include "util/map.h"
#include "util/sstream.h"
#include "util/exception.h"
#include "util/name_map.h"
#include "util/name_set.h"
#include "kernel/environment.h"
#include "kernel/expr_maps.h"
#include "kernel/expr_sets.h"
#include "kernel/kernel.h"
#include "kernel/io_state.h"
#include "library/io_state_stream.h"
#include "library/expr_pair.h"
#include "library/expr_pair_maps.h"
#include "library/arith/nat.h"
#include "library/arith/int.h"
#include "library/arith/real.h"
#include "frontends/lean/operator_info.h"
#include "frontends/lean/coercion.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/notation.h"
#include "frontends/lean/pp.h"
namespace lean {
class mark_implicit_command : public neutral_object_cell {
name m_obj_name;
std::vector<bool> m_implicit;
public:
mark_implicit_command(name const & n, unsigned sz, bool const * implicit):
m_obj_name(n), m_implicit(implicit, implicit+sz) {}
virtual ~mark_implicit_command() {}
virtual char const * keyword() const { return "MarkImplicit"; }
virtual void write(serializer & s) const {
unsigned sz = m_implicit.size();
s << "Imp" << m_obj_name << sz;
for (auto b : m_implicit)
s << b;
}
};
static void read_mark_implicit(environment const & env, io_state const &, deserializer & d) {
name n = read_name(d);
buffer<bool> implicit;
unsigned num = d.read_unsigned();
for (unsigned i = 0; i < num; i++)
implicit.push_back(d.read_bool());
mark_implicit_arguments(env, n, implicit.size(), implicit.data());
}
static object_cell::register_deserializer_fn mark_implicit_ds("Imp", read_mark_implicit);
static std::vector<bool> g_empty_vector;
/**
\brief Environment extension object for the Lean default frontend.
*/
struct lean_extension : public environment_extension {
typedef std::pair<std::vector<bool>, name> implicit_info;
// Remark: only named objects are stored in the dictionary.
typedef name_map<operator_info> operator_table;
typedef name_map<implicit_info> implicit_table;
typedef name_map<unsigned> precedence_table;
typedef expr_struct_map<list<operator_info>> expr_to_operators;
typedef expr_pair_struct_map<expr> coercion_map;
typedef expr_struct_map<list<expr_pair>> expr_to_coercions;
typedef expr_struct_set coercion_set;
typedef expr_struct_map<list<name>> inv_aliases;
operator_table m_nud; // nud table for Pratt's parser
operator_table m_led; // led table for Pratt's parser
// The following table stores the precedence of other operator parts.
// The m_nud and m_led only map the first part of an operator to its definition.
precedence_table m_other_lbp;
expr_to_operators m_expr_to_operators; // map denotations to operators (this is used for pretty printing)
implicit_table m_implicit_table; // track the number of implicit arguments for a symbol.
coercion_map m_coercion_map; // mapping from (given_type, expected_type) -> coercion
coercion_set m_coercion_set; // Set of coercions
expr_to_coercions m_type_coercions; // mapping type -> list (to-type, function)
name_set m_explicit_names; // set of explicit version of constants with implicit parameters
name_map<expr> m_aliases;
inv_aliases m_inv_aliases; // inverse map for m_aliases
lean_extension() {
}
lean_extension const * get_parent() const {
return environment_extension::get_parent<lean_extension>();
}
/** \brief Return the nud operator for the given symbol. */
operator_info find_nud(name const & n) const {
auto it = m_nud.find(n);
if (it != m_nud.end())
return it->second;
lean_extension const * parent = get_parent();
if (parent)
return parent->find_nud(n);
else
return operator_info();
}
/** \brief Return the led operator for the given symbol. */
operator_info find_led(name const & n) const {
auto it = m_led.find(n);
if (it != m_led.end())
return it->second;
lean_extension const * parent = get_parent();
if (parent)
return parent->find_led(n);
else
return operator_info();
}
optional<unsigned> get_other_lbp(name const & n) const {
auto it = m_other_lbp.find(n);
if (it != m_other_lbp.end())
return some(it->second);
lean_extension const * parent = get_parent();
if (parent)
return parent->get_other_lbp(n);
else
return optional<unsigned>();
}
/** \brief Return the precedence (aka binding power) of the given name. */
optional<unsigned> get_lbp(name const & n) const {
operator_info info = find_led(n);
if (info)
return some(info.get_precedence());
else
return get_other_lbp(n);
}
/**
\brief Return true if the given operator is defined in this
frontend object (parent frontends are ignored).
*/
bool defined_here(operator_info const & n, bool led) const {
if (led)
return m_led.find(n.get_op_name()) != m_led.end();
else
return m_nud.find(n.get_op_name()) != m_nud.end();
}
/** \brief Return the led/nud operator for the given symbol. */
operator_info find_op(name const & n, bool led) const {
return led ? find_led(n) : find_nud(n);
}
/** \brief Insert a new led/nud operator. */
void insert_op(operator_info const & op, bool led) {
if (led)
insert(m_led, op.get_op_name(), op);
else
insert(m_nud, op.get_op_name(), op);
}
/** \brief Find the operator that is used as notation for the given expression. */
operator_info find_op_for(expr const & e, bool unicode) const {
auto it = m_expr_to_operators.find(e);
if (it != m_expr_to_operators.end()) {
auto l = it->second;
for (auto op : l) {
if (unicode || op.is_safe_ascii())
return op;
}
}
lean_extension const * parent = get_parent();
if (parent)
return parent->find_op_for(e, unicode);
else
return operator_info();
}
/** \brief Remove all internal denotations that are associated with the given operator symbol (aka notation) */
void remove_bindings(operator_info const & op) {
lean_extension const * parent = get_parent();
for (expr const & d : op.get_denotations()) {
if (parent && parent->find_op_for(d, true)) {
// parent has an association for d... we must hide it.
insert(m_expr_to_operators, d, list<operator_info>(operator_info()));
} else {
m_expr_to_operators.erase(d);
}
}
}
/** \brief Add a new entry d -> op in the mapping m_expr_to_operators */
void insert_expr_to_operator_entry(expr const & d, operator_info const & op) {
list<operator_info> & l = m_expr_to_operators[d];
l = cons(op, l);
}
void check_precedence(name const & n, unsigned prec, io_state const & ios) const {
auto old_prec = get_lbp(n);
if (old_prec && *old_prec != prec)
diagnostic(ios) << "The precedence of '" << n << "' changed from " << *old_prec << " to " << prec << ".\n";
}
/** \brief Register the new operator in the tables for parsing and pretty printing. */
void register_new_op(operator_info new_op, expr const & d, bool led, io_state const & ios) {
new_op.add_expr(d);
insert_op(new_op, led);
insert_expr_to_operator_entry(d, new_op);
auto parts = new_op.get_op_name_parts();
auto prec = new_op.get_precedence();
if (led)
check_precedence(head(parts), prec, ios);
for (name const & part : tail(parts)) {
check_precedence(part, prec, ios);
m_other_lbp[part] = prec;
}
}
/**
\brief Two operator (aka notation) denotations are compatible
iff after ignoring all implicit arguments in the prefix and
explicit arguments in the suffix, the remaining implicit arguments
occur in the same positions.
Let us denote implicit arguments with a '_' and explicit with a '*'.
Then a denotation can be associated with a pattern containing one or more
'_' and '*'.
Two denotations are compatible, if we have the same pattern after
removed the '_' from the prefix and '*' from the suffix.
Here is an example of compatible denotations
f : Int -> Int -> Int Pattern * *
g : Pi {A : Type}, A -> A -> A Pattern _ * *
h : Pi {A B : Type}, A -> B -> A Pattern _ _ * *
They are compatible, because after we remove the _ from the prefix, and * from the suffix,
all of them reduce to the empty sequence
Here is another example of compatible denotations:
f : Pi {A : Type} (a : A) {B : Type} (b : B), A Pattern _ * _ *
g : Pi (i : Int) {T : Type} (x : T), T Pattern * _ *
They are compatible, because after we remove the _ from the prefix, and * from the suffix,
we get the same sequence: * _
The following two are not compatible
f : Pi {A : Type} (a : A) {B : Type} (b : B), A Pattern _ * _ *
g : Pi {A B : Type} (a : A) (b : B), A Pattern _ _ * *
Remark: we remove the explicit suffix at mark_implicit_arguments.
*/
bool compatible_denotation(expr const & d1, expr const & d2) {
auto imp1 = get_implicit_arguments(d1);
auto imp2 = get_implicit_arguments(d2);
auto it1 = std::find(imp1.begin(), imp1.end(), false);
auto it2 = std::find(imp2.begin(), imp2.end(), false);
for (; it1 != imp1.end() && it2 != imp2.end() && *it1 == *it2; ++it1, ++it2) {}
return it1 == imp1.end() && it2 == imp2.end();
}
/**
\brief Return true iff the existing denotations (aka
overloads) for an operator op are compatible with the new
denotation d.
The compatibility is only an issue if implicit arguments are
used. If one of the denotations has implicit arguments, then
all of them should have implicit arguments, and the implicit
arguments should occur in the same positions.
*/
bool compatible_denotations(operator_info const & op, expr const & d) {
return std::all_of(op.get_denotations().begin(), op.get_denotations().end(), [&](expr const & prev_d) { return compatible_denotation(prev_d, d); });
}
/**
\brief Add a new operator and save information as object.
If the new operator does not conflict with existing operators,
then we just register it.
If it conflicts, there are two options:
1) It is an overload (we just add the internal name \c n as
new option.
2) It is a real conflict, and report the issue in the
diagnostic channel, and override the existing operator (aka notation).
*/
void add_op(operator_info new_op, expr const & d, bool led, environment const & env, io_state const & ios) {
name const & opn = new_op.get_op_name();
operator_info old_op = find_op(opn, led);
if (!old_op) {
register_new_op(new_op, d, led, ios);
} else if (old_op == new_op) {
if (compatible_denotations(old_op, d)) {
// overload
if (defined_here(old_op, led)) {
old_op.add_expr(d);
insert_expr_to_operator_entry(d, old_op);
} else {
// we must copy the operator because it was defined in
// a parent frontend.
new_op = old_op.copy();
register_new_op(new_op, d, led, ios);
}
} else {
diagnostic(ios) << "The denotation(s) for the existing notation:\n " << old_op
<< "\nhave been replaced with the new denotation:\n " << d
<< "\nbecause they conflict on how implicit arguments are used.\n";
remove_bindings(old_op);
register_new_op(new_op, d, led, ios);
}
} else {
diagnostic(ios) << "Notation has been redefined, the existing notation:\n " << old_op
<< "\nhas been replaced with:\n " << new_op << "\nbecause they conflict with each other.\n";
remove_bindings(old_op);
register_new_op(new_op, d, led, ios);
}
env->add_neutral_object(new notation_declaration(new_op, d));
}
static name mk_explicit_name(name const & n) {
if (n.is_anonymous()) {
throw exception("anonymous names cannot be used in definitions");
} else if (n.is_numeral()) {
return name(n, "explicit");
} else {
std::string new_name = "@";
new_name += n.get_string();
if (n.is_atomic())
return name(new_name);
else
return name(n.get_prefix(), new_name.c_str());
}
}
void mark_implicit_arguments(name const & n, unsigned sz, bool const * implicit, environment const & env) {
if (env->has_children())
throw exception(sstream() << "failed to mark implicit arguments, frontend object is read-only");
object const & obj = env->get_object(n);
if (obj.kind() != object_kind::Definition && obj.kind() != object_kind::Postulate && obj.kind() != object_kind::Builtin)
throw exception(sstream() << "failed to mark implicit arguments, the object '" << n << "' is not a definition or postulate");
if (has_implicit_arguments(n))
throw exception(sstream() << "the object '" << n << "' already has implicit argument information associated with it");
name explicit_version = mk_explicit_name(n);
if (env->find_object(explicit_version))
throw exception(sstream() << "failed to mark implicit arguments for '" << n << "', the frontend already has an object named '" << explicit_version << "'");
expr const & type = obj.get_type();
unsigned num_args = 0;
expr it = type;
while (is_pi(it)) { num_args++; it = abst_body(it); }
if (sz > num_args)
throw exception(sstream() << "failed to mark implicit arguments for '" << n << "', object has only " << num_args << " arguments, but trying to mark " << sz << " arguments");
// remove explicit suffix
while (sz > 0 && !implicit[sz - 1]) sz--;
if (sz == 0)
throw exception(sstream() << "failed to mark implicit arguments for '" << n << "', all arguments are explicit");
std::vector<bool> v(implicit, implicit+sz);
m_implicit_table[n] = mk_pair(v, explicit_version);
expr body = mk_constant(n);
m_explicit_names.insert(explicit_version);
env->add_neutral_object(new mark_implicit_command(n, sz, implicit));
env->auxiliary_section([&]() {
if (obj.is_axiom() || obj.is_theorem()) {
env->add_theorem(explicit_version, type, body);
} else {
env->add_definition(explicit_version, type, body);
}
});
}
bool has_implicit_arguments(name const & n) const {
if (m_implicit_table.find(n) != m_implicit_table.end())
return true;
lean_extension const * parent = get_parent();
if (parent)
return parent->has_implicit_arguments(n);
else
return false;
}
std::vector<bool> const & get_implicit_arguments(name const & n) const {
auto it = m_implicit_table.find(n);
if (it != m_implicit_table.end())
return it->second.first;
lean_extension const * parent = get_parent();
if (parent)
return parent->get_implicit_arguments(n);
else
return g_empty_vector;
}
std::vector<bool> const & get_implicit_arguments(expr const & n) const {
if (is_constant(n))
return get_implicit_arguments(const_name(n));
else
return g_empty_vector;
}
name const & get_explicit_version(name const & n) const {
auto it = m_implicit_table.find(n);
if (it != m_implicit_table.end())
return it->second.second;
lean_extension const * parent = get_parent();
if (parent)
return parent->get_explicit_version(n);
else
return name::anonymous();
}
bool is_explicit(name const & n) const {
if (m_explicit_names.find(n) != m_explicit_names.end())
return true;
lean_extension const * parent = get_parent();
if (parent)
return parent->is_explicit(n);
else
return false;
}
/**
\brief It is too expensive to normalize \c t when checking if there is a coercion for it.
So, we just do a 'quick' normalization following a chain of definitions.
*/
expr coercion_type_normalization(expr t, ro_environment const & env) const {
while (true) {
if (is_constant(t)) {
auto obj = env->find_object(const_name(t));
if (obj && obj->is_definition()) {
t = obj->get_value();
} else {
return t;
}
} else {
return t;
}
}
}
void add_coercion(expr const & f, environment const & env) {
expr type = env->type_check(f);
expr norm_type = type; // env->normalize(type);
if (!is_arrow(norm_type))
throw exception("invalid coercion declaration, a coercion must have an arrow type (i.e., a non-dependent functional type)");
expr from = coercion_type_normalization(abst_domain(norm_type), env);
expr to = coercion_type_normalization(abst_body(norm_type), env);
if (from == to)
throw exception("invalid coercion declaration, 'from' and 'to' types are the same");
if (get_coercion_core(from, to))
throw exception("invalid coercion declaration, frontend already has a coercion for the given types");
m_coercion_map[expr_pair(from, to)] = f;
m_coercion_set.insert(f);
list<expr_pair> l = get_coercions_core(from);
insert(m_type_coercions, from, cons(expr_pair(to, f), l));
env->add_neutral_object(new coercion_declaration(f));
}
optional<expr> get_coercion_core(expr const & from_type, expr const & to_type) const {
expr_pair p(from_type, to_type);
auto it = m_coercion_map.find(p);
if (it != m_coercion_map.end())
return some_expr(it->second);
lean_extension const * parent = get_parent();
if (parent)
return parent->get_coercion_core(from_type, to_type);
else
return none_expr();
}
optional<expr> get_coercion(expr const & from_type, expr const & to_type, ro_environment const & env) const {
return get_coercion_core(coercion_type_normalization(from_type, env),
coercion_type_normalization(to_type, env));
}
list<expr_pair> get_coercions_core(expr const & from_type) const {
auto r = m_type_coercions.find(from_type);
if (r != m_type_coercions.end())
return r->second;
lean_extension const * parent = get_parent();
if (parent)
return parent->get_coercions_core(from_type);
else
return list<expr_pair>();
}
list<expr_pair> get_coercions(expr const & from_type, ro_environment const & env) const {
return get_coercions_core(coercion_type_normalization(from_type, env));
}
bool is_coercion(expr const & f) const {
if (m_coercion_set.find(f) != m_coercion_set.end())
return true;
lean_extension const * parent = get_parent();
return parent && parent->is_coercion(f);
}
optional<expr> get_alias(name const & n) const {
auto it = m_aliases.find(n);
if (it != m_aliases.end())
return some_expr(it->second);
lean_extension const * parent = get_parent();
if (parent)
return parent->get_alias(n);
else
return none_expr();
}
optional<list<name>> get_aliased(expr const & e) const {
auto it = m_inv_aliases.find(e);
if (it != m_inv_aliases.end())
return optional<list<name>>(it->second);
lean_extension const * parent = get_parent();
if (parent)
return parent->get_aliased(e);
else
return optional<list<name>>();
}
void add_alias(name const & n, expr const & e, environment const & env) {
if (get_alias(n))
throw exception(sstream() << "alias '" << n << "' was already defined");
m_aliases[n] = e;
auto l = get_aliased(e);
if (l)
m_inv_aliases[e] = list<name>(n, *l);
else
m_inv_aliases[e] = list<name>(n);
env->add_neutral_object(new alias_declaration(n, e));
}
};
struct lean_extension_initializer {
unsigned m_extid;
lean_extension_initializer() {
m_extid = environment_cell::register_extension([](){ return std::unique_ptr<environment_extension>(new lean_extension()); });
}
};
static lean_extension_initializer g_lean_extension_initializer;
static lean_extension const & to_ext(ro_environment const & env) {
return env->get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
}
static lean_extension & to_ext(environment const & env) {
return env->get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
}
io_state init_frontend(environment const & env, bool no_kernel) {
io_state ios(mk_pp_formatter(env));
if (!no_kernel) {
import_kernel(env, ios);
import_nat(env, ios);
}
return ios;
}
io_state init_test_frontend(environment const & env) {
env->set_trusted_imported(true);
io_state ios = init_frontend(env);
import_int(env, ios);
import_real(env, ios);
return ios;
}
void add_infix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
to_ext(env).add_op(infix(opn, p), d, true, env, ios);
}
void add_infixl(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
to_ext(env).add_op(infixl(opn, p), d, true, env, ios);
}
void add_infixr(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
to_ext(env).add_op(infixr(opn, p), d, true, env, ios);
}
void add_prefix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
to_ext(env).add_op(prefix(opn, p), d, false, env, ios);
}
void add_postfix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
to_ext(env).add_op(postfix(opn, p), d, true, env, ios);
}
void add_mixfixl(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
to_ext(env).add_op(mixfixl(sz, opns, p), d, false, env, ios);
}
void add_mixfixr(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
to_ext(env).add_op(mixfixr(sz, opns, p), d, true, env, ios);
}
void add_mixfixc(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
to_ext(env).add_op(mixfixc(sz, opns, p), d, false, env, ios);
}
void add_mixfixo(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
to_ext(env).add_op(mixfixo(sz, opns, p), d, true, env, ios);
}
operator_info find_op_for(ro_environment const & env, expr const & n, bool unicode) {
operator_info r = to_ext(env).find_op_for(n, unicode);
if (r) {
return r;
} else if (is_constant(n)) {
optional<object> obj = env->find_object(const_name(n));
if (obj && obj->is_builtin() && obj->get_name() == const_name(n)) {
// n is a constant that is referencing a builtin object.
// If the notation is associated with the builtin object, we should try it.
// TODO(Leo): Remark: in the new approach using .Lean files, the table is populated with constants.
// So, this branch is not really needed anymore.
return to_ext(env).find_op_for(obj->get_value(), unicode);
} else {
return r;
}
} else if (is_value(n)) {
// Check whether the notation was declared for a constant referencing this builtin object.
return to_ext(env).find_op_for(mk_constant(to_value(n).get_name()), unicode);
} else {
return r;
}
}
operator_info find_nud(ro_environment const & env, name const & n) {
return to_ext(env).find_nud(n);
}
operator_info find_led(ro_environment const & env, name const & n) {
return to_ext(env).find_led(n);
}
optional<unsigned> get_lbp(ro_environment const & env, name const & n) {
return to_ext(env).get_lbp(n);
}
void mark_implicit_arguments(environment const & env, name const & n, unsigned sz, bool const * implicit) {
to_ext(env).mark_implicit_arguments(n, sz, implicit, env);
}
void mark_implicit_arguments(environment const & env, name const & n, std::initializer_list<bool> const & l) {
mark_implicit_arguments(env, n, l.size(), l.begin());
}
bool has_implicit_arguments(ro_environment const & env, name const & n) {
return to_ext(env).has_implicit_arguments(n);
}
std::vector<bool> const & get_implicit_arguments(ro_environment const & env, name const & n) {
return to_ext(env).get_implicit_arguments(n);
}
std::vector<bool> const & get_implicit_arguments(ro_environment const & env, expr const & n) {
if (is_constant(n))
return get_implicit_arguments(env, const_name(n));
else if (is_value(n))
return get_implicit_arguments(env, to_value(n).get_name());
else
return g_empty_vector;
}
name const & get_explicit_version(ro_environment const & env, name const & n) {
return to_ext(env).get_explicit_version(n);
}
bool is_explicit(ro_environment const & env, name const & n) {
return to_ext(env).is_explicit(n);
}
void add_coercion(environment const & env, expr const & f) {
to_ext(env).add_coercion(f, env);
}
optional<expr> get_coercion(ro_environment const & env, expr const & from_type, expr const & to_type) {
return to_ext(env).get_coercion(from_type, to_type, env);
}
list<expr_pair> get_coercions(ro_environment const & env, expr const & from_type) {
return to_ext(env).get_coercions(from_type, env);
}
bool is_coercion(ro_environment const & env, expr const & f) {
return to_ext(env).is_coercion(f);
}
optional<expr> get_alias(ro_environment const & env, name const & n) {
return to_ext(env).get_alias(n);
}
optional<list<name>> get_aliased(ro_environment const & env, expr const & e) {
return to_ext(env).get_aliased(e);
}
void add_alias(environment const & env, name const & n, expr const & e) {
to_ext(env).add_alias(n, e, env);
}
}

View file

@ -1,170 +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 <memory>
#include <vector>
#include "kernel/environment.h"
#include "kernel/io_state.h"
#include "library/expr_pair.h"
#include "frontends/lean/operator_info.h"
namespace lean {
/**
\brief Load kernel, nat and set pretty printer.
*/
io_state init_frontend(environment const & env, bool no_kernel = false);
/*
\brief Load kernel, nat, int, real and set pretty printer.
It is used for testing.
*/
io_state init_test_frontend(environment const & env);
/**
@name Notation for parsing and pretty printing.
*/
/*@{*/
void add_infix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d);
void add_infixl(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d);
void add_infixr(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d);
void add_prefix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d);
void add_postfix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d);
void add_mixfixl(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned precedence, expr const & d);
void add_mixfixr(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned precedence, expr const & d);
void add_mixfixc(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned precedence, expr const & d);
void add_mixfixo(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned precedence, expr const & d);
inline void add_mixfixl(environment const & env, io_state const & ios, std::initializer_list<name> const & l, unsigned p, expr const & d) {
add_mixfixl(env, ios, l.size(), l.begin(), p, d);
}
inline void add_mixfixr(environment const & env, io_state const & ios, std::initializer_list<name> const & l, unsigned p, expr const & d) {
add_mixfixr(env, ios, l.size(), l.begin(), p, d);
}
inline void add_mixfixc(environment const & env, io_state const & ios, std::initializer_list<name> const & l, unsigned p, expr const & d) {
add_mixfixc(env, ios, l.size(), l.begin(), p, d);
}
inline void add_mixfixo(environment const & env, io_state const & ios, std::initializer_list<name> const & l, unsigned p, expr const & d) {
add_mixfixo(env, ios, l.size(), l.begin(), p, d);
}
/**
\brief Return the operator (if one exists) that can appear at
the beginning of a language construct.
\remark If there isn't a nud operator starting with \c n, then
return the null operator.
\remark This is used for parsing.
*/
operator_info find_nud(ro_environment const & env, name const & n);
/**
\brief Return the operator (if one exists) that can appear
inside of a language construct.
\remark If there isn't a led operator starting with \c n, then
return the null operator.
\remark This is used for parsing.
*/
operator_info find_led(ro_environment const & env, name const & n);
/**
\brief Return the precedence (aka binding power) of the given name.
*/
optional<unsigned> get_lbp(ro_environment const & env, name const & n);
/**
\brief Return the operator (if one exists) associated with the
given expression.
\remark If an operator is not associated with \c e, then
return the null operator.
\remark This is used for pretty printing.
\remark If unicode is false, then only operators containing
safe ASCII chars are considered.
*/
operator_info find_op_for(ro_environment const & env, expr const & e, bool unicode);
/*@}*/
/**
@name Implicit arguments.
*/
/*@{*/
/**
\brief Mark the given arguments of \c n as implicit.
The bit-vector array specify the position of the implicit arguments.
*/
void mark_implicit_arguments(environment const & env, name const & n, unsigned sz, bool const * implicit);
void mark_implicit_arguments(environment const & env, name const & n, std::initializer_list<bool> const & l);
/**
\brief Return true iff \c n has implicit arguments
*/
bool has_implicit_arguments(ro_environment const & env, name const & n);
/**
\brief Return the position of the arguments that are implicit.
*/
std::vector<bool> const & get_implicit_arguments(ro_environment const & env, name const & n);
/**
\brief Return the position of the arguments that are implicit.
\remark If \c n is not a constant, then return the empty vector.
*/
std::vector<bool> const & get_implicit_arguments(ro_environment const & env, expr const & n);
/**
\brief This frontend associates an definition with each
definition (or postulate) that has implicit arguments. The
additional definition has explicit arguments, and it is called
n::explicit. The explicit version can be used when the Lean
frontend can't figure out the value for the implicit
arguments.
*/
name const & get_explicit_version(ro_environment const & env, name const & n);
/**
\brief Return true iff \c n is the name of the "explicit"
version of an object with implicit arguments
*/
bool is_explicit(ro_environment const & env, name const & n);
/*@}*/
/**
@name Coercions
We support a very basic form of coercion. It is an expression
with type T1 -> T2. This expression can be used to convert
an expression of type T1 into an expression of type T2 whenever
T2 is expected, but T1 was provided.
*/
/*@{*/
/**
\brief Add a new coercion to the frontend.
It throws an exception if f does not have type T1 -> T2, or if there is already a
coercion from T1 to T2.
*/
void add_coercion(environment const & env, expr const & f);
/**
\brief Return true iff the given expression is a coercion. That is, it was added using
\c add_coercion.
*/
bool is_coercion(ro_environment const & env, expr const & f);
/**
\brief Return a coercion from given_type to expected_type if it exists.
*/
optional<expr> get_coercion(ro_environment const & env, expr const & from_type, expr const & to_type);
/**
\brief Return the list of coercions for the given type.
The result is a list of pairs (to_type, function).
*/
list<expr_pair> get_coercions(ro_environment const & env, expr const & from_type);
/*@}*/
/**
@name Aliases
*/
/*@{*/
optional<expr> get_alias(ro_environment const & env, name const & n);
optional<list<name>> get_aliased(ro_environment const & env, expr const & e);
void add_alias(environment const & env, name const & n, expr const & e);
/*@}*/
}

View file

@ -1,523 +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 <utility>
#include <algorithm>
#include <limits>
#include "util/interrupt.h"
#include "util/freset.h"
#include "kernel/type_checker.h"
#include "kernel/type_checker_justification.h"
#include "kernel/normalizer.h"
#include "kernel/replace_visitor.h"
#include "kernel/unification_constraint.h"
#include "kernel/instantiate.h"
#include "kernel/kernel.h"
#include "library/io_state_stream.h"
#include "library/placeholder.h"
#include "library/elaborator/elaborator.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/frontend_elaborator.h"
namespace lean {
static name g_x_name("x");
static format g_assignment_fmt = format(":=");
static format g_unification_u_fmt = format("\u2248");
static format g_unification_fmt = format("=?=");
/**
\brief Internal value used to store choices for the elaborator.
This is a transient value that is only used to setup a problem
for the elaborator.
*/
struct choice_value : public value {
std::vector<expr> m_choices;
choice_value(unsigned num_fs, expr const * fs):m_choices(fs, fs + num_fs) {}
virtual ~choice_value() {}
virtual expr get_type() const { lean_unreachable(); } // LCOV_EXCL_LINE
virtual void write(serializer & ) const { lean_unreachable(); } // LCOV_EXCL_LINE
virtual name get_name() const { return name("Choice"); }
virtual bool is_atomic_pp(bool /* unicode */, bool /* coercion */) const { return false; } // NOLINT
virtual void display(std::ostream & out) const {
out << "(Choice";
for (auto c : m_choices) {
out << " (" << c << ")";
}
out << ")";
}
// Remark: we don't implement the pp methods because the lean::pp_fn formatter
// object has support for formatting choice internal values.
};
expr mk_choice(unsigned num_fs, expr const * fs) {
lean_assert(num_fs >= 2);
return mk_value(*(new choice_value(num_fs, fs)));
}
bool is_choice(expr const & e) {
return is_value(e) && dynamic_cast<choice_value const *>(&to_value(e)) != nullptr;
}
choice_value const & to_choice_value(expr const & e) {
lean_assert(is_choice(e));
return static_cast<choice_value const &>(to_value(e));
}
unsigned get_num_choices(expr const & e) {
return to_choice_value(e).m_choices.size();
}
expr const & get_choice(expr const & e, unsigned i) {
return to_choice_value(e).m_choices[i];
}
class coercion_justification_cell : public justification_cell {
context m_ctx;
expr m_src;
public:
coercion_justification_cell(context const & c, expr const & src):m_ctx(c), m_src(src) {}
virtual ~coercion_justification_cell() {}
virtual format pp_header(formatter const & fmt, options const & opts, optional<metavar_env> const & menv) const {
unsigned indent = get_pp_indent(opts);
format expr_fmt = fmt(m_ctx, instantiate_metavars(menv, m_src), false, opts);
format r;
r += format("Coercion for");
r += nest(indent, compose(line(), expr_fmt));
return r;
}
virtual void get_children(buffer<justification_cell*> &) const {}
virtual optional<expr> get_main_expr() const { return some_expr(m_src); }
context const & get_context() const { return m_ctx; }
};
class overload_justification_cell : public justification_cell {
context m_ctx;
expr m_app;
public:
overload_justification_cell(context const & c, expr const & app):m_ctx(c), m_app(app) {}
virtual ~overload_justification_cell() {}
virtual format pp_header(formatter const & fmt, options const & opts, optional<metavar_env> const & menv) const {
unsigned indent = get_pp_indent(opts);
format expr_fmt = fmt(m_ctx, instantiate_metavars(menv, m_app), false, opts);
format r;
r += format("Overloading at");
r += nest(indent, compose(line(), expr_fmt));
return r;
}
virtual void get_children(buffer<justification_cell*> &) const {}
virtual optional<expr> get_main_expr() const { return some_expr(m_app); }
context const & get_context() const { return m_ctx; }
expr const & get_app() const { return m_app; }
};
inline justification mk_coercion_justification(context const & ctx, expr const & e) {
return justification(new coercion_justification_cell(ctx, e));
}
inline justification mk_overload_justification(context const & ctx, expr const & app) {
return justification(new overload_justification_cell(ctx, app));
}
/**
\brief Actual implementation of the frontend_elaborator.
*/
class frontend_elaborator::imp {
environment m_env;
type_checker m_type_checker;
normalizer m_normalizer;
metavar_env m_menv;
buffer<unification_constraint> m_ucs;
// The following mapping is used to store the relationship
// between elaborated expressions and non-elaborated expressions.
// We need that because a frontend may associate line number information
// with the original non-elaborated expressions.
expr_map<expr> m_trace;
/**
\brief Replace placeholders and choices with metavariables.
It also introduce metavariables where coercions are needed.
*/
struct preprocessor : public replace_visitor {
imp & m_ref;
preprocessor(imp & r):m_ref(r) {}
expr instantiate(expr const & e, expr const & v) {
return ::lean::instantiate(e, v, m_ref.m_menv);
}
environment const & env() const { return m_ref.m_env; }
virtual expr visit_constant(expr const & e, context const & ctx) {
if (is_placeholder(e)) {
expr m = m_ref.m_menv->mk_metavar(ctx, visit(const_type(e), ctx));
m_ref.m_trace[m] = e;
return m;
} else {
return e;
}
}
/**
\brief Return the type of \c e if possible.
The idea is to use the type to catch the easy cases where we can solve
overloads (aka choices) and coercions during preprocessing.
*/
optional<expr> get_type(expr const & e, context const & ctx) {
try {
return some_expr(m_ref.m_type_checker.infer_type(e, ctx));
} catch (exception &) {
return none_expr();
}
}
bool is_convertible(expr const & from, expr const & to) {
try {
return m_ref.m_type_checker.is_convertible(from, to);
} catch (exception &) {
return false;
}
}
/**
\brief Make sure f_t is a Pi, if it is not, then return none_expr()
*/
optional<expr> check_pi(optional<expr> const & f_t, context const & ctx) {
if (!f_t || is_pi(*f_t)) {
return f_t;
} else {
expr r = m_ref.m_normalizer(*f_t, ctx, m_ref.m_menv);
if (is_pi(r))
return some_expr(r);
else
return none_expr();
}
}
expr add_coercion_mvar_app(list<expr_pair> const & l, expr const & a, expr const & a_t,
context const & ctx, expr const & original_a) {
buffer<expr> choices;
expr mvar = m_ref.m_menv->mk_metavar(ctx);
for (auto p : l) {
choices.push_back(p.second);
}
choices.push_back(mk_lambda(g_x_name, a_t, mk_var(0))); // add indentity function
std::reverse(choices.begin(), choices.end());
m_ref.m_ucs.push_back(mk_choice_constraint(ctx, mvar, choices.size(), choices.data(),
mk_coercion_justification(ctx, original_a)));
return mk_app(mvar, a);
}
optional<expr> find_coercion(list<expr_pair> const & l, expr const & to_type) {
for (auto p : l) {
if (p.first == to_type) {
return some_expr(p.second);
}
}
return none_expr();
}
/**
\brief Try to solve overload at preprocessing time.
*/
void choose(buffer<expr> & f_choices, buffer<optional<expr>> & f_choice_types,
buffer<expr> const & args, buffer<optional<expr>> const & arg_types,
context const & ctx) {
unsigned best_num_coercions = std::numeric_limits<unsigned>::max();
unsigned num_choices = f_choices.size();
unsigned num_args = args.size();
buffer<unsigned> delayed;
buffer<unsigned> matched;
for (unsigned j = 0; j < num_choices; j++) {
optional<expr> f_t = f_choice_types[j];
unsigned num_coercions = 0; // number of coercions needed by current choice
unsigned num_skipped_args = 0;
unsigned i = 1;
for (; i < num_args; i++) {
f_t = check_pi(f_t, ctx);
if (!f_t) {
// can't process this choice at preprocessing time
delayed.push_back(j);
break;
} else {
expr expected = abst_domain(*f_t);
optional<expr> given = arg_types[i];
if (!given) {
num_skipped_args++;
} else {
if (!has_metavar(expected) && !has_metavar(*given)) {
if (m_ref.m_type_checker.is_convertible(*given, expected, ctx)) {
// compatible
} else if (get_coercion(env(), *given, expected)) {
// compatible if using coercion
num_coercions++;
} else {
// failed, this choice does not work
break;
}
} else {
num_skipped_args++;
}
}
f_t = some_expr(instantiate(abst_body(*f_t), args[i]));
}
}
if (i == num_args) {
if (num_skipped_args > 0) {
// should keep this choice because we could not check all arguments
delayed.push_back(j);
} else if (num_coercions < best_num_coercions) {
// found best choice
best_num_coercions = num_coercions;
matched.clear();
matched.push_back(j);
} else {
matched.push_back(j);
}
}
}
matched.append(delayed);
if (matched.size() == 0) {
// TODO(Leo): must use another exception that stores the choices considered.
// We currently do nothing, and let the elaborator to sign the error
} else {
buffer<expr> to_keep;
buffer<optional<expr>> to_keep_types;
std::sort(matched.begin(), matched.end()); // we must preserve the original order
for (unsigned i : matched) {
to_keep.push_back(f_choices[i]);
to_keep_types.push_back(f_choice_types[i]);
}
f_choices.clear();
f_choice_types.clear();
f_choices.append(to_keep);
f_choice_types.append(to_keep_types);
}
}
/**
\brief Create a metavariable for representing the choice.
*/
expr mk_overload_mvar(buffer<expr> & f_choices, context const & ctx, expr const & src) {
std::reverse(f_choices.begin(), f_choices.end());
expr mvar = m_ref.m_menv->mk_metavar(ctx);
m_ref.m_ucs.push_back(mk_choice_constraint(ctx, mvar, f_choices.size(), f_choices.data(),
mk_overload_justification(ctx, src)));
return mvar;
}
virtual expr visit_app(expr const & e, context const & ctx) {
expr f = arg(e, 0);
optional<expr> f_t;
buffer<expr> args;
buffer<optional<expr>> arg_types;
args.push_back(expr()); // placeholder
arg_types.push_back(none_expr()); // placeholder
for (unsigned i = 1; i < num_args(e); i++) {
expr a = arg(e, i);
expr new_a = visit(a, ctx);
optional<expr> new_a_t = get_type(new_a, ctx);
args.push_back(new_a);
arg_types.push_back(new_a_t);
}
if (is_choice(f)) {
buffer<expr> f_choices;
buffer<optional<expr>> f_choice_types;
unsigned num_alts = get_num_choices(f);
for (unsigned i = 0; i < num_alts; i++) {
expr c = get_choice(f, i);
expr new_c = visit(c, ctx);
optional<expr> new_c_t = get_type(new_c, ctx);
f_choices.push_back(new_c);
f_choice_types.push_back(new_c_t);
}
choose(f_choices, f_choice_types, args, arg_types, ctx);
if (f_choices.size() > 1) {
args[0] = mk_overload_mvar(f_choices, ctx, e);
for (unsigned i = 1; i < args.size(); i++) {
if (arg_types[i]) {
list<expr_pair> coercions = get_coercions(env(), *(arg_types[i]));
if (coercions)
args[i] = add_coercion_mvar_app(coercions, args[i], *(arg_types[i]), ctx, arg(e, i));
}
}
return mk_app(args);
} else {
// managed to solve overload at preprocessing time
f = f_choices[0];
f_t = f_choice_types[0];
}
} else {
f = visit(f, ctx);
f_t = get_type(f, ctx);
}
buffer<expr> new_args;
new_args.push_back(f);
for (unsigned i = 1; i < num_args(e); i++) {
f_t = check_pi(f_t, ctx);
expr a = arg(e, i);
expr new_a = args[i];
optional<expr> new_a_t = arg_types[i];
if (new_a_t) {
list<expr_pair> coercions = get_coercions(env(), *new_a_t);
if (coercions) {
if (!f_t) {
new_a = add_coercion_mvar_app(coercions, new_a, *new_a_t, ctx, a);
} else {
expr expected = abst_domain(*f_t);
if (!is_convertible(*new_a_t, expected)) {
optional<expr> c = find_coercion(coercions, expected);
if (c) {
new_a = mk_app(*c, new_a); // apply coercion
} else {
new_a = add_coercion_mvar_app(coercions, new_a, *new_a_t, ctx, a);
}
}
}
}
}
new_args.push_back(new_a);
if (f_t)
f_t = some_expr(instantiate(abst_body(*f_t), new_a));
}
return mk_app(new_args);
}
virtual expr visit_let(expr const & e, context const & ctx) {
lean_assert(is_let(e));
return update_let(e, [&](optional<expr> const & t, expr const & v, expr const & b) {
optional<expr> new_t = visit(t, ctx);
expr new_v = visit(v, ctx);
if (new_t) {
optional<expr> new_v_t = get_type(new_v, ctx);
if (new_v_t && *new_t != *new_v_t) {
list<expr_pair> coercions = get_coercions(env(), *new_v_t);
if (coercions) {
new_v = add_coercion_mvar_app(coercions, new_v, *new_v_t, ctx, v);
}
}
}
{
freset<cache> reset(m_cache);
expr new_b = visit(b, extend(ctx, let_name(e), new_t, new_v));
return std::make_tuple(new_t, new_v, new_b);
}
});
}
optional<expr> visit(optional<expr> const & e, context const & ctx) {
return replace_visitor::visit(e, ctx);
}
virtual expr visit(expr const & e, context const & ctx) {
check_interrupted();
expr r = replace_visitor::visit(e, ctx);
if (!is_eqp(r, e))
m_ref.m_trace[r] = e;
return r;
}
};
metavar_env elaborate_core(options const & opts) {
elaborator elb(m_env, m_menv, m_ucs.size(), m_ucs.data(), opts);
return elb.next();
}
public:
imp(environment const & env):
m_env(env),
m_type_checker(m_env),
m_normalizer(m_env) {
}
std::pair<expr, metavar_env> elaborate(expr const & e, options const & opts) {
// std::cout << "Elaborate " << e << "\n";
clear();
expr new_e = preprocessor(*this)(e);
// std::cout << "After preprocessing\n" << new_e << "\n";
if (has_metavar(new_e)) {
m_type_checker.check(new_e, context(), m_menv, m_ucs);
// for (auto c : m_ucs) {
// formatter fmt = mk_simple_formatter();
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
// }
metavar_env new_menv = elaborate_core(opts);
return mk_pair(new_menv->instantiate_metavars(new_e), new_menv);
} else {
return mk_pair(new_e, metavar_env());
}
}
std::tuple<expr, expr, metavar_env> elaborate(name const & n, expr const & t, expr const & e,
options const & opts) {
// std::cout << "Elaborate " << t << " : " << e << "\n";
clear();
expr new_t = preprocessor(*this)(t);
expr new_e = preprocessor(*this)(e);
// std::cout << "After preprocessing\n" << new_t << "\n" << new_e << "\n";
if (has_metavar(new_e) || has_metavar(new_t)) {
m_type_checker.check(new_t, context(), m_menv, m_ucs);
expr new_e_t = m_type_checker.check(new_e, context(), m_menv, m_ucs);
m_ucs.push_back(mk_eq_constraint(context(), new_e_t, new_t,
mk_def_type_match_justification(context(), n, e)));
// for (auto c : m_ucs) {
// formatter fmt = mk_simple_formatter();
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
// }
metavar_env new_menv = elaborate_core(opts);
return std::make_tuple(new_menv->instantiate_metavars(new_t),
new_menv->instantiate_metavars(new_e),
new_menv);
} else {
return std::make_tuple(new_t, new_e, metavar_env());
}
}
expr const & get_original(expr const & e) {
expr const * r = &e;
while (true) {
auto it = m_trace.find(*r);
if (it == m_trace.end()) {
return *r;
} else {
r = &(it->second);
}
}
}
void clear() {
m_menv = metavar_env();
m_ucs.clear();
m_trace.clear();
m_type_checker.clear();
m_normalizer.clear();
}
environment const & get_environment() const {
return m_env;
}
};
frontend_elaborator::frontend_elaborator(environment const & env):m_ptr(std::make_shared<imp>(env)) {}
frontend_elaborator::~frontend_elaborator() {}
std::pair<expr, metavar_env> frontend_elaborator::operator()(expr const & e, options const & opts) {
return m_ptr->elaborate(e, opts);
}
std::tuple<expr, expr, metavar_env> frontend_elaborator::operator()(name const & n, expr const & t, expr const & e,
options const & opts) {
return m_ptr->elaborate(n, t, e, opts);
}
expr const & frontend_elaborator::get_original(expr const & e) const { return m_ptr->get_original(e); }
void frontend_elaborator::clear() { m_ptr->clear(); }
void frontend_elaborator::reset(environment const & env) { m_ptr.reset(new imp(env)); }
environment const & frontend_elaborator::get_environment() const { return m_ptr->get_environment(); }
}

View file

@ -1,79 +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 <memory>
#include <utility>
#include "kernel/environment.h"
#include "kernel/formatter.h"
namespace lean {
class frontend;
/**
\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. It is based on the general purpose elaborator defined at
library/elaborator/elaborator.h
*/
class frontend_elaborator {
class imp;
std::shared_ptr<imp> m_ptr;
static void print(imp * ptr);
public:
explicit frontend_elaborator(environment const & env);
~frontend_elaborator();
/**
\brief Elaborate the given expression.
*/
std::pair<expr, metavar_env> operator()(expr const & e, options const & opts);
/**
\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::tuple<expr, expr, metavar_env> operator()(name const & n, expr const & t, expr const & e, options const & opts);
/**
\brief If \c e is an expression instantiated by the elaborator, then it
returns the expression (the one with "holes") used to generate \c e.
Otherwise, it just returns \c e.
*/
expr const & get_original(expr const & e) const;
environment const & get_environment() const;
void clear();
void reset(environment const & env);
};
/**
\brief Create a choice expression for the given functions.
It is used to mark which functions can be used in a particular application.
The elaborator decides which one should be used based on the type of the arguments.
\pre num_fs >= 2
*/
expr mk_choice(unsigned num_fs, expr const * fs);
/**
\brief Return true iff \c e is an expression created using \c mk_choice.
*/
bool is_choice(expr const & e);
/**
\brief Return the number of alternatives in a choice expression.
We have that <tt>get_num_choices(mk_choice(n, fs)) == n</tt>.
\pre is_choice(e)
*/
unsigned get_num_choices(expr const & e);
/**
\brief Return the (i+1)-th alternative of a choice expression.
\pre is_choice(e)
\pre i < get_num_choices(e)
*/
expr const & get_choice(expr const & e, unsigned i);
}

View file

@ -1,14 +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 <limits>
namespace lean {
constexpr unsigned g_heq_precedence = 50;
constexpr unsigned g_arrow_precedence = 25;
constexpr unsigned g_cartesian_product_precedence = 30;
constexpr unsigned g_app_precedence = std::numeric_limits<unsigned>::max();
}

View file

@ -1,231 +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 "util/rc.h"
#include "library/io_state_stream.h"
#include "frontends/lean/operator_info.h"
#include "frontends/lean/frontend.h"
namespace lean {
/** \brief Actual implementation of operator_info */
struct operator_info::imp {
void dealloc() { delete this; }
MK_LEAN_RC();
fixity m_fixity;
unsigned m_precedence;
list<name> m_op_parts; // operator parts, > 1 only if the operator is mixfix.
list<expr> m_exprs; // possible interpretations for the operator.
imp(name const & op, fixity f, unsigned p):
m_rc(1), m_fixity(f), m_precedence(p), m_op_parts(cons(op, list<name>())) {}
imp(unsigned num_parts, name const * parts, fixity f, unsigned p):
m_rc(1), m_fixity(f), m_precedence(p), m_op_parts(to_list<name const *>(parts, parts + num_parts)) {
lean_assert(num_parts > 0);
}
imp(imp const & s):
m_rc(1), m_fixity(s.m_fixity), m_precedence(s.m_precedence), m_op_parts(s.m_op_parts), m_exprs(s.m_exprs) {
}
bool is_eq(imp const & other) const {
return
m_fixity == other.m_fixity &&
m_precedence == other.m_precedence &&
m_op_parts == other.m_op_parts;
}
};
operator_info::operator_info(imp * p):m_ptr(p) {}
operator_info::operator_info(operator_info const & info):m_ptr(info.m_ptr) { if (m_ptr) m_ptr->inc_ref(); }
operator_info::operator_info(operator_info && info):m_ptr(info.m_ptr) { info.m_ptr = nullptr; }
operator_info::~operator_info() { if (m_ptr) m_ptr->dec_ref(); }
operator_info & operator_info::operator=(operator_info const & s) { LEAN_COPY_REF(s); }
operator_info & operator_info::operator=(operator_info && s) { LEAN_MOVE_REF(s); }
void operator_info::add_expr(expr const & d) { lean_assert(m_ptr); m_ptr->m_exprs = cons(d, m_ptr->m_exprs); }
bool operator_info::is_overloaded() const { return m_ptr && !is_nil(m_ptr->m_exprs) && !is_nil(cdr(m_ptr->m_exprs)); }
list<expr> const & operator_info::get_denotations() const { lean_assert(m_ptr); return m_ptr->m_exprs; }
fixity operator_info::get_fixity() const { lean_assert(m_ptr); return m_ptr->m_fixity; }
unsigned operator_info::get_precedence() const { lean_assert(m_ptr); return m_ptr->m_precedence; }
name const & operator_info::get_op_name() const { lean_assert(m_ptr); return car(m_ptr->m_op_parts); }
list<name> const & operator_info::get_op_name_parts() const { lean_assert(m_ptr); return m_ptr->m_op_parts; }
bool operator_info::is_safe_ascii() const {
auto l = get_op_name_parts();
return std::all_of(l.begin(), l.end(), [](name const & p) { return p.is_safe_ascii(); });
}
operator_info operator_info::copy() const { return operator_info(new imp(*m_ptr)); }
bool operator==(operator_info const & op1, operator_info const & op2) {
if ((op1.m_ptr == nullptr) != (op2.m_ptr == nullptr))
return false;
if (op1.m_ptr)
return op1.m_ptr->is_eq(*(op2.m_ptr));
else
return true;
}
operator_info infix(name const & op, unsigned precedence) {
return operator_info(new operator_info::imp(op, fixity::Infix, precedence));
}
operator_info infixr(name const & op, unsigned precedence) {
return operator_info(new operator_info::imp(op, fixity::Infixr, precedence));
}
operator_info infixl(name const & op, unsigned precedence) {
return operator_info(new operator_info::imp(op, fixity::Infixl, precedence));
}
operator_info prefix(name const & op, unsigned precedence) {
return operator_info(new operator_info::imp(op, fixity::Prefix, precedence));
}
operator_info postfix(name const & op, unsigned precedence) {
return operator_info(new operator_info::imp(op, fixity::Postfix, precedence));
}
operator_info mixfixl(unsigned num_parts, name const * parts, unsigned precedence) {
lean_assert(num_parts > 1); return operator_info(new operator_info::imp(num_parts, parts, fixity::Mixfixl, precedence));
}
operator_info mixfixr(unsigned num_parts, name const * parts, unsigned precedence) {
lean_assert(num_parts > 1); return operator_info(new operator_info::imp(num_parts, parts, fixity::Mixfixr, precedence));
}
operator_info mixfixc(unsigned num_parts, name const * parts, unsigned precedence) {
lean_assert(num_parts > 1); return operator_info(new operator_info::imp(num_parts, parts, fixity::Mixfixc, precedence));
}
operator_info mixfixo(unsigned num_parts, name const * parts, unsigned precedence) {
lean_assert(num_parts > 1); return operator_info(new operator_info::imp(num_parts, parts, fixity::Mixfixo, precedence));
}
char const * to_string(fixity f) {
switch (f) {
case fixity::Infix: return "infix";
case fixity::Infixl: return "infixl";
case fixity::Infixr: return "infixr";
case fixity::Prefix: return "prefix";
case fixity::Postfix: return "postfix";
case fixity::Mixfixl: return "mixfixl";
case fixity::Mixfixr: return "mixfixr";
case fixity::Mixfixc: return "mixfixc";
case fixity::Mixfixo: return "mixfixo";
}
lean_unreachable(); // LCOV_EXCL_LINE
}
format pp(operator_info const & o) {
format r;
switch (o.get_fixity()) {
case fixity::Infix:
case fixity::Infixl:
case fixity::Infixr:
r = highlight_command(format(to_string(o.get_fixity())));
if (o.get_precedence() > 1)
r += format{space(), format(o.get_precedence())};
r += format{space(), format(o.get_op_name())};
return r;
case fixity::Prefix:
case fixity::Postfix:
case fixity::Mixfixl:
case fixity::Mixfixr:
case fixity::Mixfixc:
case fixity::Mixfixo:
r = highlight_command(format("notation"));
if (o.get_precedence() > 1)
r += format{space(), format(o.get_precedence())};
switch (o.get_fixity()) {
case fixity::Prefix:
r += format{space(), format(o.get_op_name()), space(), format("_")};
return r;
case fixity::Postfix:
r += format{space(), format("_"), space(), format(o.get_op_name())};
return r;
case fixity::Mixfixl:
for (auto p : o.get_op_name_parts())
r += format{space(), format(p), space(), format("_")};
return r;
case fixity::Mixfixr:
for (auto p : o.get_op_name_parts())
r += format{space(), format("_"), space(), format(p)};
return r;
case fixity::Mixfixc: {
auto parts = o.get_op_name_parts();
r += format{space(), format(head(parts))};
for (auto p : tail(parts))
r += format{space(), format("_"), space(), format(p)};
return r;
}
case fixity::Mixfixo:
for (auto p : o.get_op_name_parts())
r += format{space(), format("_"), space(), format(p)};
r += format{space(), format("_")};
return r;
default: lean_unreachable(); // LCOV_EXCL_LINE
}
}
lean_unreachable(); // LCOV_EXCL_LINE
}
char const * notation_declaration::keyword() const {
return to_string(m_op.get_fixity());
}
void notation_declaration::write(serializer & s) const {
auto parts = m_op.get_op_name_parts();
s << "Notation" << length(parts);
for (auto n : parts)
s << n;
s << static_cast<char>(m_op.get_fixity()) << m_op.get_precedence() << m_expr;
}
static void read_notation(environment const & env, io_state const & ios, deserializer & d) {
buffer<name> parts;
unsigned num = d.read_unsigned();
for (unsigned i = 0; i < num; i++)
parts.push_back(read_name(d));
fixity fx = static_cast<fixity>(d.read_char());
unsigned p = d.read_unsigned();
expr e = read_expr(d);
switch (fx) {
case fixity::Infix: lean_assert(parts.size() == 1); add_infix(env, ios, parts[0], p, e); return;
case fixity::Infixl: lean_assert(parts.size() == 1); add_infixl(env, ios, parts[0], p, e); return;
case fixity::Infixr: lean_assert(parts.size() == 1); add_infixr(env, ios, parts[0], p, e); return;
case fixity::Prefix: lean_assert(parts.size() == 1); add_prefix(env, ios, parts[0], p, e); return;
case fixity::Postfix: lean_assert(parts.size() == 1); add_postfix(env, ios, parts[0], p, e); return;
case fixity::Mixfixl: add_mixfixl(env, ios, parts.size(), parts.data(), p, e); return;
case fixity::Mixfixr: add_mixfixr(env, ios, parts.size(), parts.data(), p, e); return;
case fixity::Mixfixc: add_mixfixc(env, ios, parts.size(), parts.data(), p, e); return;
case fixity::Mixfixo: add_mixfixo(env, ios, parts.size(), parts.data(), p, e); return;
}
}
static object_cell::register_deserializer_fn notation_ds("Notation", read_notation);
std::ostream & operator<<(std::ostream & out, operator_info const & o) {
out << pp(o);
return out;
}
io_state_stream const & operator<<(io_state_stream const & out, operator_info const & o) {
out.get_stream() << mk_pair(pp(o), out.get_options());
return out;
}
char const * alias_declaration::keyword() const { return "alias"; }
void alias_declaration::write(serializer & s) const { s << "alias" << m_name << m_expr; }
static void read_alias(environment const & env, io_state const &, deserializer & d) {
name n = read_name(d);
expr e = read_expr(d);
add_alias(env, n, e);
}
static object_cell::register_deserializer_fn add_alias_ds("alias", read_alias);
}

View file

@ -1,143 +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 <algorithm>
#include "util/name.h"
#include "util/list.h"
#include "util/sexpr/format.h"
#include "kernel/io_state.h"
#include "kernel/object.h"
namespace lean {
/**
\brief Operator fixity.
Prefix: ID _
Infix: _ ID _
Infixl: _ ID _ (left associative)
Infixr: _ ID _ (right associative)
Postfix: _ ID
Mixfixl: ID _ ID _ ... ID _ (has at least two parts)
Mixfixr: _ ID _ ID ... _ ID (has at least two parts)
Mixfixc: ID _ ID _ ... ID _ ID (has at least two parts)
Mixfixo: _ ID _ ... ID _ (has at least two parts)
*/
enum class fixity { Prefix, Infix, Infixl, Infixr, Postfix, Mixfixl, Mixfixr, Mixfixc, Mixfixo };
/**
\brief Data-structure for storing user defined operator
information. This information is used during parsing and
pretty-printing.
*/
class operator_info {
struct imp;
imp * m_ptr;
explicit operator_info(imp * p);
public:
operator_info():m_ptr(nullptr) {}
operator_info(operator_info const & info);
operator_info(operator_info && info);
~operator_info();
operator_info & operator=(operator_info const & o);
operator_info & operator=(operator_info && o);
friend void swap(operator_info & o1, operator_info & o2) { std::swap(o1.m_ptr, o2.m_ptr); }
explicit operator bool() const { return m_ptr != nullptr; }
friend operator_info infix(name const & op, unsigned precedence);
friend operator_info infixl(name const & op, unsigned precedence);
friend operator_info infixr(name const & op, unsigned precedence);
friend operator_info prefix(name const & op, unsigned precedence);
friend operator_info postfix(name const & op, unsigned precedence);
friend operator_info mixfixl(unsigned num_parts, name const * parts, unsigned precedence);
friend operator_info mixfixr(unsigned num_parts, name const * parts, unsigned precedence);
friend operator_info mixfixc(unsigned num_parts, name const * parts, unsigned precedence);
friend operator_info mixfixo(unsigned num_parts, name const * parts, unsigned precedence);
/** \brief Associate a denotation (expression) with this operator. */
void add_expr(expr const & n);
/** \brief Return true iff the operator is overloaded. */
bool is_overloaded() const;
/**
\brief Return the list of denotations for this operator.
The list has size greater than 1 iff the operator has been
overloaded.
These are the possible interpretations for the operator.
*/
list<expr> const & get_denotations() const;
/** \brief Return the operator fixity. */
fixity get_fixity() const;
/** \brief Return the operator precedence. */
unsigned get_precedence() const;
/** \brief Return the operator name. For mixfix operators the result corresponds to the first part. */
name const & get_op_name() const;
/** \brief Return the operators parts. */
list<name> const & get_op_name_parts() const;
/** \brief Return true if all parts of the operator use only safe ASCII characters */
bool is_safe_ascii() const;
/** \brief Return a copy of the operator. */
operator_info copy() const;
friend bool operator==(operator_info const & op1, operator_info const & op2);
friend bool operator!=(operator_info const & op1, operator_info const & op2) { return !(op1 == op2); }
};
operator_info infix(name const & op, unsigned precedence);
operator_info infixl(name const & op, unsigned precedence);
operator_info infixr(name const & op, unsigned precedence);
operator_info prefix(name const & op, unsigned precedence);
operator_info postfix(name const & op, unsigned precedence);
operator_info mixfixl(unsigned num_parts, name const * parts, unsigned precedence);
operator_info mixfixr(unsigned num_parts, name const * parts, unsigned precedence);
operator_info mixfixc(unsigned num_parts, name const * parts, unsigned precedence);
operator_info mixfixo(unsigned num_parts, name const * parts, unsigned precedence);
inline operator_info mixfixl(std::initializer_list<name> const & l, unsigned precedence) { return mixfixl(l.size(), l.begin(), precedence); }
inline operator_info mixfixr(std::initializer_list<name> const & l, unsigned precedence) { return mixfixr(l.size(), l.begin(), precedence); }
inline operator_info mixfixc(std::initializer_list<name> const & l, unsigned precedence) { return mixfixc(l.size(), l.begin(), precedence); }
inline operator_info mixfixo(std::initializer_list<name> const & l, unsigned precedence) { return mixfixo(l.size(), l.begin(), precedence); }
format pp(operator_info const & o);
std::ostream & operator<<(std::ostream & out, operator_info const & o);
io_state_stream const & operator<<(io_state_stream const & out, operator_info const & o);
/**
\brief Create object for tracking notation/operator declarations.
This object is mainly used for recording the declaration.
*/
class notation_declaration : public neutral_object_cell {
operator_info m_op;
expr m_expr;
public:
notation_declaration(operator_info const & op, expr const & n):m_op(op), m_expr(n) {}
virtual ~notation_declaration() {}
virtual char const * keyword() const;
operator_info get_op() const { return m_op; }
expr const & get_expr() const { return m_expr; }
virtual void write(serializer & s) const;
};
class alias_declaration : public neutral_object_cell {
name m_name;
expr m_expr;
public:
alias_declaration(name const & n, expr const & e):m_name(n), m_expr(e) {}
virtual ~alias_declaration() {}
virtual char const * keyword() const;
name const & get_alias_name() const { return m_name; }
expr const & get_expr() const { return m_expr; }
virtual void write(serializer & s) const;
};
}

View file

@ -1,55 +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 "util/sstream.h"
#include "library/io_state_stream.h"
#include "frontends/lean/parser.h"
#include "frontends/lean/pp.h"
#include "frontends/lean/parser_imp.h"
namespace lean {
parser::parser(environment const & env, io_state const & ios, std::istream & in, char const * strm_name, script_state * S, bool use_exceptions, bool interactive) {
parser_imp::show_prompt(interactive, ios);
m_ptr.reset(new parser_imp(env, ios, in, strm_name, S, use_exceptions, interactive));
m_ptr->m_this = m_ptr;
}
parser::~parser() {
}
bool parser::operator()() {
return m_ptr->parse_commands();
}
expr parser::parse_expr() {
return m_ptr->parse_expr_main();
}
io_state parser::get_io_state() const {
return m_ptr->m_io_state;
}
bool parse_commands(environment const & env, io_state & ios, std::istream & in, char const * strm_name, script_state * S, bool use_exceptions, bool interactive) {
parser p(env, ios, in, strm_name, S, use_exceptions, interactive);
bool r = p();
ios = p.get_io_state();
return r;
}
bool parse_commands(environment const & env, io_state & ios, char const * fname, script_state * S, bool use_exceptions, bool interactive) {
std::ifstream in(fname);
if (in.bad() || in.fail())
throw exception(sstream() << "failed to open file '" << fname << "'");
return parse_commands(env, ios, in, fname, S, use_exceptions, interactive);
}
expr parse_expr(environment const & env, io_state & ios, std::istream & in, char const * strm_name, script_state * S, bool use_exceptions) {
parser p(env, ios, in, strm_name, S, use_exceptions);
expr r = p.parse_expr();
ios = p.get_io_state();
return r;
}
}

View file

@ -1,37 +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 <iostream>
#include "util/lua.h"
#include "kernel/environment.h"
#include "kernel/io_state.h"
namespace lean {
class script_state;
class parser_imp;
/** \brief Functional object for parsing commands and expressions */
class parser {
private:
std::shared_ptr<parser_imp> m_ptr;
public:
parser(environment const & env, io_state const & st, std::istream & in, char const * strm_name, script_state * S, bool use_exceptions = true, bool interactive = false);
~parser();
/** \brief Parse a sequence of commands */
bool operator()();
/** \brief Parse a single expression */
expr parse_expr();
io_state get_io_state() const;
};
bool parse_commands(environment const & env, io_state & st, std::istream & in, char const * strm_name, script_state * S = nullptr, bool use_exceptions = true, bool interactive = false);
bool parse_commands(environment const & env, io_state & st, char const * fname, script_state * S = nullptr, bool use_exceptions = true, bool interactive = false);
expr parse_expr(environment const & env, io_state & st, std::istream & in, char const * strm_name, script_state * S = nullptr, bool use_exceptions = true);
void open_macros(lua_State * L);
}

View file

@ -1,158 +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 "kernel/kernel.h"
#include "library/placeholder.h"
#include "library/io_state_stream.h"
#include "frontends/lean/parser_calc.h"
#include "frontends/lean/parser_imp.h"
#include "frontends/lean/operator_info.h"
#include "frontends/lean/frontend.h"
namespace lean {
bool calc_proof_parser::supports(expr const & op1) const {
return
std::find_if(m_supported_operators.begin(), m_supported_operators.end(), [&](op_data const & op2) {
return op1 == op2.m_fn;
})
!= m_supported_operators.end();
}
void calc_proof_parser::add_supported_operator(op_data const & op1) {
if (supports(op1.m_fn))
throw exception("operator already supported in the calculational proof manager");
m_supported_operators = cons(op1, m_supported_operators);
}
optional<trans_data> calc_proof_parser::find_trans_data(expr const & op1, expr const & op2) const {
auto it = std::find_if(m_trans_ops.begin(), m_trans_ops.end(),
[&](std::tuple<expr, expr, trans_data> const & entry) { return std::get<0>(entry) == op1 && std::get<1>(entry) == op2; });
if (it == m_trans_ops.end())
return optional<trans_data>();
else
return optional<trans_data>(std::get<2>(*it));
}
void calc_proof_parser::add_trans_step(expr const & op1, expr const & op2, trans_data const & d) {
if (!supports(op1) || !supports(op2) || !supports(d.m_rop))
throw exception("invalid transitivity step in calculational proof manager, operator not supported");
if (find_trans_data(op1, op2))
throw exception("transitivity step is already supported in the calculational proof manager");
if (d.m_num_args < 5)
throw exception("transitivity step must have at least 5 arguments");
m_trans_ops.emplace_front(op1, op2, d);
}
calc_proof_parser::calc_proof_parser() {
expr imp = mk_implies_fn();
expr eq = mk_eq_fn();
expr neq = mk_neq_fn();
add_supported_operator(op_data(imp, 2));
add_supported_operator(op_data(eq, 3));
add_supported_operator(op_data(neq, 3));
add_trans_step(eq, eq, trans_data(mk_trans_fn(), 6, eq));
add_trans_step(eq, imp, trans_data(mk_eq_imp_trans_fn(), 5, imp));
add_trans_step(imp, eq, trans_data(mk_imp_eq_trans_fn(), 5, imp));
add_trans_step(imp, imp, trans_data(mk_imp_trans_fn(), 5, imp));
add_trans_step(eq, neq, trans_data(mk_eq_ne_trans_fn(), 6, neq));
add_trans_step(neq, eq, trans_data(mk_ne_eq_trans_fn(), 6, neq));
}
optional<expr> calc_proof_parser::find_op(operator_info const & op, pos_info const & p) const {
if (!op)
return none_expr();
for (auto d : op.get_denotations()) {
// TODO(Leo): I'm ignoring overloading.
if (supports(d))
return some_expr(d);
}
throw parser_error("invalid calculational proof, operator is not supported", p);
return none_expr();
}
expr calc_proof_parser::parse_op(parser_imp & imp) const {
environment const & env = imp.get_environment();
auto p = imp.pos();
name id = imp.check_identifier_next("invalid calculational proof, identifier expected");
if (auto r = find_op(find_led(env, id), p))
return *r;
if (auto r = find_op(find_nud(env, id), p))
return *r;
expr e = imp.get_name_ref(id, p, false /* do not process implicit args */);
if (!supports(e))
throw parser_error("invalid calculational proof, operator is not supported", p);
return e;
}
static expr parse_step_pr(parser_imp & imp, expr const & lhs) {
auto p = imp.pos();
imp.check_colon_next("invalid calculational proof, ':' expected");
if (imp.curr_is_lcurly()) {
imp.next();
expr eq_pr = imp.parse_expr();
imp.check_rcurly_next("invalid calculational proof, '}' expected");
// Using axiom Subst {A : TypeU} {a b : A} {P : A → Bool} (H1 : P a) (H2 : a == b) : P b.
return imp.save(mk_subst_th(imp.save(mk_placeholder(), p),
imp.save(mk_placeholder(), p),
imp.save(mk_placeholder(), p),
imp.save(mk_placeholder(), p), // let elaborator compute the first four arguments
imp.save(mk_refl_th(imp.save(mk_placeholder(), p), lhs), p),
eq_pr), p);
} else {
return imp.parse_expr();
}
}
/**
\brief Parse
calc expr op expr : proof1
... op expr : proof2
... op expr : proofn
*/
expr calc_proof_parser::parse(parser_imp & imp) const {
auto p = imp.pos();
expr first = imp.parse_expr();
if (!is_app(first))
throw parser_error("invalid calculational proof, first expression must be an application", p);
expr op = arg(first, 0);
if (!supports(op))
throw parser_error("invalid calculational proof, first expression is not an application of a supported operator", p);
if (num_args(first) < 3)
throw parser_error("invalid calculational proof, first expression must be an application of a binary operator (modulo implicit arguments)", p);
unsigned num = num_args(first);
expr lhs = arg(first, num - 2);
expr rhs = arg(first, num - 1);
expr result = parse_step_pr(imp, lhs);
while (imp.curr() == scanner::token::Ellipsis) {
imp.next();
p = imp.pos();
expr new_op = parse_op(imp);
auto tdata = find_trans_data(op, new_op);
if (!tdata)
throw parser_error("invalid calculational proof, operators cannot be combined", p);
expr new_rhs = imp.parse_expr();
expr step_pr = parse_step_pr(imp, rhs);
buffer<expr> args;
args.push_back(tdata->m_fn);
for (unsigned i = 0; i < tdata->m_num_args - 5; i++) {
// transitivity step has implicit arguments
args.push_back(imp.save(mk_placeholder(), p));
}
args.push_back(lhs);
args.push_back(rhs);
args.push_back(new_rhs);
args.push_back(result);
args.push_back(step_pr);
result = imp.save(mk_app(args), p);
op = tdata->m_rop;
rhs = new_rhs;
}
return result;
}
}

View file

@ -1,82 +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 "util/list.h"
#include "util/optional.h"
#include "kernel/expr.h"
#include "frontends/lean/parser_types.h"
namespace lean {
class parser_imp;
class operator_info;
/**
\brief Information for a operator available in the
calculational proof module. It must have at least two arguments.
It may have implicit ones. We assume the implicit arguments
occur in the beginning.
*/
struct op_data {
expr m_fn;
unsigned m_num_args;
op_data(expr const & f, unsigned n): m_fn(f), m_num_args(n) {}
};
/**
\brief Information about a transitivity-like proof step.
The m_fn function must have at least 5 arguments.
We assume the implicit ones occur in the beginning.
The last five are a, b, c and a proof for 'a op1 b' and 'b op2 c'.
The function m_fn produces a proof for 'a rop b'.
The resultant operator must be in the list of supported operators.
*/
struct trans_data {
expr m_fn;
unsigned m_num_args; // number of arguments of m_fn .
expr m_rop; // resultant operator
trans_data(expr const & f, unsigned n, expr const & rop):
m_fn (f), m_num_args(n), m_rop(rop) {
}
};
/**
\brief Calculational proof support in the Lean parser.
It parses expression of the form:
calc expr op expr : proof1
... op expr : proof2
... op expr : proofn
*/
class calc_proof_parser {
/**
\brief List of supported operators in calculational proofs.
*/
list<op_data> m_supported_operators;
/**
\brief Maps to operators to the corresponding transitivity operator.
For example, the pair (=, =>) maps to
Theorem EqImpTrans {a b c : Bool} (H1 : a == b) (H2 : b c) : a c
:= assume Ha, MP H2 (EqMP H1 Ha).
*/
list<std::tuple<expr, expr, trans_data>> m_trans_ops;
optional<trans_data> find_trans_data(expr const & op1, expr const & op2) const;
optional<expr> find_op(operator_info const & op, pos_info const & p) const;
expr parse_op(parser_imp & imp) const;
public:
calc_proof_parser();
bool supports(expr const & op1) const;
void add_supported_operator(op_data const & op);
void add_trans_step(expr const & op1, expr const & op2, trans_data const & d);
expr parse(parser_imp & p) const;
};
}

View file

@ -1,978 +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 <string>
#include "util/sstream.h"
#include "util/lean_path.h"
#include "util/sexpr/option_declarations.h"
#include "kernel/find_fn.h"
#include "kernel/kernel_exception.h"
#include "kernel/normalizer.h"
#include "kernel/type_checker.h"
#include "library/placeholder.h"
#include "library/io_state_stream.h"
#include "library/simplifier/rewrite_rule_set.h"
#include "frontends/lean/parser_imp.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/pp.h"
#include "frontends/lean/environment_scope.h"
namespace lean {
// ==========================================
// Builtin commands
static name g_alias_kwd("alias");
static name g_definition_kwd("definition");
static name g_variable_kwd("variable");
static name g_variables_kwd("variables");
static name g_theorem_kwd("theorem");
static name g_axiom_kwd("axiom");
static name g_universe_kwd("universe");
static name g_eval_kwd("eval");
static name g_check_kwd("check");
static name g_infix_kwd("infix");
static name g_infixl_kwd("infixl");
static name g_infixr_kwd("infixr");
static name g_notation_kwd("notation");
static name g_set_option_kwd("set_option");
static name g_set_opaque_kwd("set_opaque");
static name g_options_kwd("options");
static name g_env_kwd("environment");
static name g_import_kwd("import");
static name g_help_kwd("help");
static name g_coercion_kwd("coercion");
static name g_exit_kwd("exit");
static name g_print_kwd("print");
static name g_pop_kwd("pop_scope");
static name g_scope_kwd("scope");
static name g_builtin_kwd("builtin");
static name g_namespace_kwd("namespace");
static name g_end_kwd("end");
static name g_using_kwd("using");
static name g_rewrite_set_kwd("rewrite_set");
static name g_add_rewrite_kwd("add_rewrite");
static name g_enable_rewrite_kwd("enable_rewrite");
static name g_disable_rewrite_kwd("disable_rewrite");
/** \brief Table/List with all builtin command keywords */
static list<name> g_command_keywords = {g_definition_kwd, g_variable_kwd, g_variables_kwd, g_theorem_kwd,
g_axiom_kwd, g_universe_kwd, g_eval_kwd,
g_check_kwd, g_infix_kwd, g_infixl_kwd, g_infixr_kwd, g_notation_kwd,
g_set_option_kwd, g_set_opaque_kwd, g_env_kwd, g_options_kwd,
g_import_kwd, g_help_kwd, g_coercion_kwd,
g_exit_kwd, g_print_kwd, g_pop_kwd, g_scope_kwd, g_alias_kwd, g_builtin_kwd,
g_namespace_kwd, g_end_kwd, g_using_kwd, g_rewrite_set_kwd, g_add_rewrite_kwd,
g_enable_rewrite_kwd, g_disable_rewrite_kwd};
// ==========================================
list<name> const & parser_imp::get_command_keywords() {
return g_command_keywords;
}
/**
\brief Register implicit arguments for the definition or
postulate named \c n. The fourth element in the tuple parameters
is a flag indiciating whether the argument is implicit or not.
*/
void parser_imp::register_implicit_arguments(name const & n, parameter_buffer & parameters) {
bool found = false;
buffer<bool> imp_args;
for (unsigned i = 0; i < parameters.size(); i++) {
imp_args.push_back(parameters[i].m_implicit);
if (imp_args.back())
found = true;
}
if (found)
mark_implicit_arguments(m_env, n, imp_args.size(), imp_args.data());
}
/** \brief Throw an exception if \c e contains a metavariable */
void parser_imp::check_no_metavar(expr const & e, metavar_env const &, char const * msg) {
if (has_metavar(e))
throw unsolved_metavar_exception(msg, e);
}
void parser_imp::check_no_metavar(std::pair<expr, metavar_env> const & p, char const * msg) {
check_no_metavar(p.first, p.second, msg);
}
/**
\brief Return a fully qualified name (i.e., include current namespace)
*/
name parser_imp::mk_full_name(name const & n) {
return m_namespace_prefixes.back() + n;
}
/** \brief Auxiliary method used for parsing definitions and theorems. */
void parser_imp::parse_def_core(bool is_definition) {
next();
expr pre_type, pre_val;
name id = check_identifier_next("invalid definition, identifier expected");
parameter_buffer parameters;
if (curr_is_colon()) {
next();
pre_type = parse_expr();
if (!is_definition && curr_is_period()) {
pre_val = save(mk_placeholder(), pos());
} else {
check_assign_next("invalid definition, ':=' expected");
pre_val = parse_expr();
}
} else if (is_definition && curr_is_assign()) {
auto p = pos();
next();
pre_type = save(mk_placeholder(), p);
pre_val = parse_expr();
} else {
mk_scope scope(*this);
parse_definition_parameters(parameters);
expr type_body;
if (curr_is_colon()) {
next();
type_body = parse_expr();
} else {
auto p = pos();
type_body = save(mk_placeholder(), p);
}
pre_type = mk_abstraction(expr_kind::Pi, parameters, type_body);
if (!is_definition && curr_is_period()) {
pre_val = mk_abstraction(expr_kind::Lambda, parameters, mk_placeholder());
} else {
check_assign_next("invalid definition, ':=' expected");
expr val_body = parse_expr();
pre_val = mk_abstraction(expr_kind::Lambda, parameters, val_body);
}
}
auto r = elaborate(id, pre_type, pre_val);
expr type = std::get<0>(r);
expr val = std::get<1>(r);
metavar_env menv = std::get<2>(r);
if (has_metavar(type)) {
type = apply_tactics(type, menv);
val = menv->instantiate_metavars(val); // val may contain metavariables instantiated in the previous step
}
check_no_metavar(type, menv, "invalid definition, type still contains metavariables after elaboration");
if (has_metavar(val))
val = apply_tactics(val, menv);
check_no_metavar(val, menv, "invalid definition, value still contains metavariables after elaboration");
lean_assert(!has_metavar(val));
name full_id = mk_full_name(id);
if (is_definition) {
m_env->add_definition(full_id, type, val);
if (m_verbose)
regular(m_io_state) << " Defined: " << full_id << endl;
} else {
m_env->add_theorem(full_id, type, val);
if (m_verbose)
regular(m_io_state) << " Proved: " << full_id << endl;
}
register_implicit_arguments(full_id, parameters);
}
/**
\brief Parse a Definition. It has one of the following two forms:
1) 'definition' ID ':' expr ':=' expr
2) 'definition' ID parameters ':' expr ':=' expr
*/
void parser_imp::parse_definition() {
parse_def_core(true);
}
/**
\brief Parse a Theorem. It has one of the following two forms:
1) 'theorem' ID ':' expr ':=' expr
2) 'theorem' ID parameters ':' expr ':=' expr
*/
void parser_imp::parse_theorem() {
parse_def_core(false);
}
/** \brief Auxiliary method for parsing Variable and axiom declarations. */
void parser_imp::parse_variable_core(bool is_var) {
next();
name id = check_identifier_next("invalid variable/axiom declaration, identifier expected");
expr pre_type;
parameter_buffer parameters;
if (curr_is_colon()) {
next();
pre_type = parse_expr();
} else {
mk_scope scope(*this);
parse_var_decl_parameters(parameters);
check_colon_next("invalid variable/axiom declaration, ':' expected");
expr type_body = parse_expr();
pre_type = mk_abstraction(expr_kind::Pi, parameters, type_body);
}
auto p = elaborate(pre_type);
expr type = p.first;
metavar_env menv = p.second;
if (has_metavar(type))
type = apply_tactics(type, menv);
check_no_metavar(type, menv, "invalid variable/axiom, type still contains metavariables after elaboration");
name full_id = mk_full_name(id);
if (is_var)
m_env->add_var(full_id, type);
else
m_env->add_axiom(full_id, type);
if (m_verbose)
regular(m_io_state) << " Assumed: " << full_id << endl;
register_implicit_arguments(full_id, parameters);
}
/** \brief Parse one of the two forms:
1) 'variable' ID ':' type
2) 'variable' ID parameters ':' type
*/
void parser_imp::parse_variable() {
parse_variable_core(true);
}
/** \brief Parse the form:
'variables' ID+ ':' type
*/
void parser_imp::parse_variables() {
next();
mk_scope scope(*this);
parameter_buffer parameters;
parse_simple_parameters(parameters, false, false);
for (auto p : parameters) {
name full_id = mk_full_name(p.m_name);
if (m_env->find_object(full_id))
throw already_declared_exception(m_env, full_id);
}
for (auto p : parameters) {
name full_id = mk_full_name(p.m_name);
expr const & type = p.m_type;
m_env->add_var(full_id, type);
if (m_verbose)
regular(m_io_state) << " Assumed: " << full_id << endl;
}
}
/** \brief Parse one of the two forms:
1) 'axiom' ID ':' type
2) 'axiom' ID parameters ':' type
*/
void parser_imp::parse_axiom() {
parse_variable_core(false);
}
/** \brief Parse 'Eval' expr */
void parser_imp::parse_eval() {
next();
expr v = elaborate(parse_expr()).first;
normalizer norm(m_env);
expr r = norm(v);
regular(m_io_state) << r << endl;
}
/** \brief Return true iff \c obj is an object that should be ignored by the Show command */
bool parser_imp::is_hidden_object(object const & obj) const {
return (obj.is_definition() && is_explicit(m_env, obj.get_name())) || !supported_by_pp(obj);
}
/** \brief Parse
'print' expr
'print' Environment [num]
'print' Environment all
'print' Options
'print' [string]
*/
void parser_imp::parse_print() {
next();
if (curr() == scanner::token::CommandId) {
name opt_id = curr_name();
next();
if (opt_id == g_env_kwd) {
buffer<object> to_display;
bool all = false;
unsigned end = m_env->get_num_objects(false);
unsigned i;
if (curr_is_nat()) {
i = parse_unsigned("invalid argument, value does not fit in a machine integer");
} else if (curr_is_identifier() && curr_name() == "all") {
next();
i = std::numeric_limits<unsigned>::max();
all = true;
} else {
i = std::numeric_limits<unsigned>::max();
}
unsigned it = end;
unsigned num_imports = 0;
while (it != 0 && i != 0) {
--it;
auto obj = m_env->get_object(it, false);
if (is_begin_import(obj)) {
lean_assert(num_imports > 0);
num_imports--;
if (num_imports == 0)
to_display.push_back(obj);
} else if (is_begin_builtin_import(obj)) {
lean_assert(num_imports > 0);
num_imports--;
} else if (is_end_import(obj)) {
num_imports++;
} else if (is_hidden_object(obj)) {
// skip
} else if (num_imports == 0 || all) {
to_display.push_back(obj);
--i;
}
}
std::reverse(to_display.begin(), to_display.end());
for (auto obj : to_display) {
if (is_begin_import(obj)) {
regular(m_io_state) << "import \"" << *get_imported_module(obj) << "\"" << endl;
} else {
regular(m_io_state) << obj << endl;
}
}
} else if (opt_id == g_options_kwd) {
regular(m_io_state) << pp(m_io_state.get_options()) << endl;
} else if (opt_id == g_rewrite_set_kwd) {
name rsid;
if (curr_is_identifier()) {
rsid = curr_name();
next();
} else {
rsid = get_default_rewrite_rule_set_id();
}
rewrite_rule_set rs = get_rewrite_rule_set(m_env, rsid);
regular(m_io_state) << rs << endl;
} else {
throw parser_error("invalid Show command, expression, 'Options' or 'Environment' expected", m_last_cmd_pos);
}
} else if (curr() == scanner::token::StringVal) {
std::string msg = curr_string();
next();
regular(m_io_state) << msg << endl;
} else {
expr v = elaborate(parse_expr()).first;
regular(m_io_state) << v << endl;
}
}
/** \brief Parse 'Check' expr */
void parser_imp::parse_check() {
next();
auto p = elaborate(parse_expr());
check_no_metavar(p, "invalid expression, it still contains metavariables after elaboration");
expr v = p.first;
expr t = type_check(v, m_env);
formatter fmt = m_io_state.get_formatter();
options opts = m_io_state.get_options();
unsigned indent = get_pp_indent(opts);
format r = group(format{fmt(v, opts), space(), colon(), nest(indent, compose(line(), fmt(t, opts)))});
regular(m_io_state) << mk_pair(r, opts) << endl;
}
/** \brief Return the (optional) precedence of a user-defined operator. */
unsigned parser_imp::parse_precedence() {
if (curr_is_nat()) {
return parse_unsigned("invalid operator definition, precedence does not fit in a machine integer");
} else {
return 0;
}
}
/** \brief Throw an error if the current token is not an identifier. If it is, move to next token. */
name parser_imp::parse_op_id() {
return check_identifier_next("invalid operator definition, identifier expected");
}
/**
\brief Parse prefix/postfix/infix/infixl/infixr user operator
definitions. These definitions have the form:
- fixity [Num] ID ':' ID
*/
void parser_imp::parse_op(fixity fx) {
next();
unsigned prec = parse_precedence();
name op_id = parse_op_id();
check_colon_next("invalid operator definition, ':' expected");
auto name_pos = pos();
name name_id = check_identifier_next("invalid operator definition, identifier expected");
expr d = get_name_ref(name_id, name_pos, false);
switch (fx) {
case fixity::Infix: add_infix(m_env, m_io_state, op_id, prec, d); break;
case fixity::Infixl: add_infixl(m_env, m_io_state, op_id, prec, d); break;
case fixity::Infixr: add_infixr(m_env, m_io_state, op_id, prec, d); break;
default: lean_unreachable(); // LCOV_EXCL_LINE
}
}
/**
\brief Parse notation declaration unified format
'notation' [Num] parts ':' ID
*/
void parser_imp::parse_notation_decl() {
auto p = pos();
next();
unsigned prec = parse_precedence();
bool first = true;
bool prev_placeholder = false;
bool first_placeholder = false;
buffer<name> parts;
while (true) {
if (first) {
if (curr_is_placeholder()) {
prev_placeholder = true;
first_placeholder = true;
next();
} else {
parts.push_back(check_identifier_next("invalid notation declaration, identifier or '_' expected"));
prev_placeholder = false;
first_placeholder = false;
}
first = false;
} else {
if (curr_is_colon()) {
next();
if (parts.size() == 0) {
throw parser_error("invalid notation declaration, it must have at least one identifier", p);
}
auto id_pos = pos();
name name_id = check_identifier_next("invalid notation declaration, identifier expected");
expr d = get_name_ref(name_id, id_pos, false);
if (parts.size() == 1) {
if (first_placeholder && prev_placeholder) {
// infix: _ ID _
add_infix(m_env, m_io_state, parts[0], prec, d);
} else if (first_placeholder) {
// postfix: _ ID
add_postfix(m_env, m_io_state, parts[0], prec, d);
} else if (prev_placeholder) {
// prefix: ID _
add_prefix(m_env, m_io_state, parts[0], prec, d);
} else {
throw parser_error("invalid notation declaration, at least one placeholder expected", p);
}
} else {
if (first_placeholder && prev_placeholder) {
// mixfixo: _ ID ... ID _
add_mixfixo(m_env, m_io_state, parts.size(), parts.data(), prec, d);
} else if (first_placeholder) {
// mixfixr: _ ID ... ID
add_mixfixr(m_env, m_io_state, parts.size(), parts.data(), prec, d);
} else if (prev_placeholder) {
// mixfixl: ID _ ... ID _
add_mixfixl(m_env, m_io_state, parts.size(), parts.data(), prec, d);
} else {
// mixfixc: ID _ ... _ ID
add_mixfixc(m_env, m_io_state, parts.size(), parts.data(), prec, d);
}
}
return;
} else {
if (prev_placeholder) {
parts.push_back(check_identifier_next("invalid notation declaration, identifier or ':' expected"));
prev_placeholder = false;
} else {
check_placeholder_next("invalid notation declaration, '_' or ':' expected");
prev_placeholder = true;
}
}
}
}
}
/** Parse 'set_option' [id] [value] */
void parser_imp::parse_set_option() {
next();
auto id_pos = pos();
name id = check_identifier_next("invalid set options, identifier (i.e., option name) expected");
auto decl_it = get_option_declarations().find(id);
if (decl_it == get_option_declarations().end()) {
// add "lean" prefix
name lean_id = name("lean") + id;
decl_it = get_option_declarations().find(lean_id);
if (decl_it == get_option_declarations().end()) {
throw parser_error(sstream() << "unknown option '" << id << "', type 'help options.' for list of available options", id_pos);
} else {
id = lean_id;
}
}
option_kind k = decl_it->second.kind();
switch (curr()) {
case scanner::token::Id:
if (k != BoolOption)
throw parser_error(sstream() << "invalid option value, given option is not Boolean", pos());
if (curr_name() == "true")
m_io_state.set_option(id, true);
else if (curr_name() == "false")
m_io_state.set_option(id, false);
else
throw parser_error("invalid Boolean option value, 'true' or 'false' expected", pos());
next();
break;
case scanner::token::StringVal:
if (k != StringOption)
throw parser_error("invalid option value, given option is not a string", pos());
m_io_state.set_option(id, curr_string());
next();
break;
case scanner::token::IntVal:
if (k != IntOption && k != UnsignedOption)
throw parser_error("invalid option value, given option is not an integer", pos());
m_io_state.set_option(id, parse_unsigned("invalid option value, value does not fit in a machine integer"));
break;
case scanner::token::DecimalVal:
if (k != DoubleOption)
throw parser_error("invalid option value, given option is not floating point value", pos());
m_io_state.set_option(id, parse_double());
break;
default:
throw parser_error("invalid option value, 'true', 'false', string, integer or decimal value expected", pos());
}
updt_options();
if (m_verbose)
regular(m_io_state) << " Set: " << id << endl;
}
/** Parse 'set_opaque' [id] [true/false] */
void parser_imp::parse_set_opaque() {
next();
auto p = pos();
name id;
if (curr() == scanner::token::Exists) {
id = "exists";
} else {
check_identifier("invalid set opaque, identifier expected");
id = curr_name();
}
next();
expr d = get_name_ref(id, p, false);
name real_id;
if (is_constant(d))
real_id = const_name(d);
else if (is_value(d))
real_id = to_value(d).get_name();
else
throw parser_error(sstream() << "invalid set opaque, identifier '" << id << "' cannot be set opaque", p);
auto val_pos = pos();
name val = check_identifier_next("invalid opaque flag, true/false expected");
if (val == "true")
m_env->set_opaque(real_id, true);
else if (val == "false")
m_env->set_opaque(real_id, false);
else
throw parser_error("invalid opaque flag, true/false expected", val_pos);
}
optional<std::string> parser_imp::find_lua_file(std::string const & fname) {
try {
return some(find_file(fname, {".lua"}));
} catch (...) {
return optional<std::string>();
}
}
void parser_imp::parse_import() {
auto p = pos();
next();
unsigned num = 0;
while (curr_is_identifier() || curr_is_string()) {
std::string fname;
if (curr_is_identifier()) {
fname = name_to_file(curr_name());
next();
} else {
fname = curr_string();
next();
}
bool r = false;
if (auto lua_fname = find_lua_file(fname)) {
if (!m_script_state)
throw parser_error(sstream() << "failed to import Lua file '" << *lua_fname << "', parser does not have an intepreter",
m_last_cmd_pos);
r = m_script_state->import_explicit(lua_fname->c_str());
} else {
r = m_env->import(fname, m_io_state);
}
if (m_verbose && r) {
regular(m_io_state) << " Imported '" << fname << "'" << endl;
}
num++;
}
if (num == 0)
throw parser_error("invalid import command, string (i.e., file name) or identifier expected", p);
}
void parser_imp::parse_help() {
next();
if (curr() == scanner::token::CommandId) {
name opt_id = curr_name();
next();
if (opt_id == g_options_kwd) {
regular(m_io_state) << "Available options:" << endl;
for (auto p : get_option_declarations()) {
auto opt = p.second;
regular(m_io_state) << " " << opt.get_name() << " (" << opt.kind() << ") "
<< opt.get_description() << " (default: " << opt.get_default_value() << ")" << endl;
}
} else {
throw parser_error("invalid help command", m_last_cmd_pos);
}
} else {
regular(m_io_state) << "Available commands:" << endl
<< " alias [id] : [expr] define an alias for the given expression" << endl
<< " axiom [id] : [type] assert/postulate a new axiom" << endl
<< " check [expr] type check the given expression" << endl
<< " definition [id] : [type] := [expr] define a new element" << endl
<< " end end the current scope/namespace" << endl
<< " eval [expr] evaluate the given expression" << endl
<< " exit exit" << endl
<< " help display this message" << endl
<< " help options display available options" << endl
<< " help notation describe commands for defining infix, mixfix, postfix operators" << endl
<< " import [string] load the given file" << endl
<< " pop::scope discard the current scope" << endl
<< " print [expr] pretty print the given expression" << endl
<< " print Options print current the set of assigned options" << endl
<< " print [string] print the given string" << endl
<< " print Environment print objects in the environment, if [Num] provided, then show only the last [Num] objects" << endl
<< " print Environment [num] show the last num objects in the environment" << endl
<< " scope create a scope" << endl
<< " set::option [id] [value] set option [id] with value [value]" << endl
<< " set::opaque [id] [bool] set the given definition as opaque/transparent" << endl
<< " theorem [id] : [type] := [expr] define a new theorem" << endl
<< " variable [id] : [type] declare/postulate an element of the given type" << endl
<< " universe [id] >= [level] declare a new universe constraint" << endl
<< " using [id]_1 [id]_2? create an alias for object starting with the prefix [id]_1 using the [id]_2" << endl;
#if !defined(LEAN_WINDOWS)
regular(m_io_state) << "Type Ctrl-D to exit" << endl;
#endif
}
}
/** \brief Parse 'Coercion' expr */
void parser_imp::parse_coercion() {
next();
expr coercion = parse_expr();
add_coercion(m_env, coercion);
if (m_verbose)
regular(m_io_state) << " Coercion " << coercion << endl;
}
void parser_imp::reset_env(environment env) {
m_env = env;
m_elaborator.reset(env);
m_io_state.set_formatter(mk_pp_formatter(env));
}
void parser_imp::parse_cmd_macro(name cmd_id, pos_info const & p) {
lean_assert(m_cmd_macros && m_cmd_macros->find(cmd_id) != m_cmd_macros->end());
next();
auto m = m_cmd_macros->find(cmd_id)->second;
macro_arg_stack args;
parse_macro(m.m_arg_kinds, m.m_fn, m.m_precedence, args, p);
}
static name g_geq_unicode("\u2265"); // ≥
static name g_geq(">=");
void parser_imp::parse_universe() {
next();
name id = check_identifier_next("invalid universe constraint, identifier expected");
if (curr_is_identifier()) {
name geq = check_identifier_next("invalid universe constraint, '>=' expected");
if (geq != g_geq && geq != g_geq_unicode)
throw parser_error("invalid universe constraint, '>=' expected", m_last_cmd_pos);
level lvl = parse_level();
m_env->add_uvar_cnstr(id, lvl);
} else {
m_env->add_uvar_cnstr(id);
}
}
void parser_imp::parse_alias() {
next();
name id = check_identifier_next("invalid alias declaration, identifier expected");
check_colon_next("invalid alias declaration, ':' expected");
expr e = parse_expr();
add_alias(m_env, id, e);
}
void parser_imp::parse_builtin() {
next();
auto id_pos = pos();
name id = check_identifier_next("invalid builtin declaration, identifier expected");
name full_id = mk_full_name(id);
auto d = get_builtin(full_id);
if (!d)
throw parser_error(sstream() << "unknown builtin '" << full_id << "'", id_pos);
expr b = d->first;
if (d->second) {
m_env->add_builtin_set(b);
return;
}
parameter_buffer parameters;
expr type;
if (curr_is_colon()) {
next();
auto p = elaborate(parse_expr());
check_no_metavar(p, "invalid builtin declaration, type still contains metavariables after elaboration");
type = p.first;
} else {
mk_scope scope(*this);
parse_var_decl_parameters(parameters);
check_colon_next("invalid builtin declaration, ':' expected");
expr type_body = parse_expr();
auto p = elaborate(mk_abstraction(expr_kind::Pi, parameters, type_body));
check_no_metavar(p, "invalid declaration, type still contains metavariables after elaboration");
type = p.first;
}
if (to_value(b).get_type() != type) {
diagnostic(m_io_state) << "Error, builtin expected type: " << to_value(b).get_type() << ", given: " << type << "\n";
throw parser_error(sstream() << "given type does not match builtin type", id_pos);
}
m_env->add_builtin(d->first);
if (m_verbose)
regular(m_io_state) << " Added: " << full_id << endl;
register_implicit_arguments(full_id, parameters);
}
void parser_imp::parse_scope() {
next();
m_scope_kinds.push_back(scope_kind::Scope);
reset_env(m_env->mk_child());
m_using_decls.push();
}
void parser_imp::parse_pop() {
next();
if (m_scope_kinds.empty())
throw parser_error("main scope cannot be removed", m_last_cmd_pos);
if (m_scope_kinds.back() != scope_kind::Scope)
throw parser_error("invalid pop command, it is not inside of a scope", m_last_cmd_pos);
if (!m_env->has_parent())
throw parser_error("main scope cannot be removed", m_last_cmd_pos);
m_scope_kinds.pop_back();
reset_env(m_env->parent());
m_using_decls.pop();
m_script_state->apply([&](lua_State * L) { lua_gc(L, LUA_GCCOLLECT, 0); });
}
void parser_imp::parse_namespace() {
next();
name id = check_identifier_next("invalid namespace declaration, identifier expected");
m_scope_kinds.push_back(scope_kind::Namespace);
m_namespace_prefixes.push_back(m_namespace_prefixes.back() + id);
m_using_decls.push();
}
void parser_imp::parse_end() {
next();
if (m_scope_kinds.empty())
throw parser_error("invalid 'end', not inside of a scope or namespace", m_last_cmd_pos);
scope_kind k = m_scope_kinds.back();
m_scope_kinds.pop_back();
m_script_state->apply([&](lua_State * L) { lua_gc(L, LUA_GCCOLLECT, 0); });
switch (k) {
case scope_kind::Scope: {
if (!m_env->has_parent())
throw parser_error("main scope cannot be removed", m_last_cmd_pos);
auto new_objects = export_local_objects(m_env);
reset_env(m_env->parent());
for (auto const & obj : new_objects) {
if (obj.is_theorem())
m_env->add_theorem(obj.get_name(), obj.get_type(), obj.get_value());
else
m_env->add_definition(obj.get_name(), obj.get_type(), obj.get_value(), obj.is_opaque());
}
break;
}
case scope_kind::Namespace:
if (m_namespace_prefixes.size() <= 1)
throw parser_error("invalid end namespace command, there are no open namespaces", m_last_cmd_pos);
m_namespace_prefixes.pop_back();
}
m_using_decls.pop();
}
static name replace_prefix(name const & n, name const & prefix, name const & new_prefix) {
if (n == prefix)
return new_prefix;
name p = replace_prefix(n.get_prefix(), prefix, new_prefix);
if (n.is_string())
return name(p, n.get_string());
else
return name(p, n.get_numeral());
}
void parser_imp::parse_using() {
next();
name prefix = check_identifier_next("invalid using command, identifier expected");
name new_prefix;
if (curr_is_identifier()) {
new_prefix = curr_name();
next();
}
buffer<std::pair<name, name>> to_add;
for (auto it = m_env->begin_objects(); it != m_env->end_objects(); ++it) {
if (it->has_type() && it->has_name() && supported_by_pp(*it) && is_prefix_of(prefix, it->get_name())) {
auto n = replace_prefix(it->get_name(), prefix, new_prefix);
if (!n.is_anonymous())
to_add.emplace_back(n, it->get_name());
}
}
for (auto p : to_add) {
auto n = p.first;
if (m_verbose) {
auto it = m_using_decls.find(n);
if (it != m_using_decls.end())
diagnostic(m_io_state) << "warning: " << n << " will shadow " << it->second << endl;
auto obj = m_env->find_object(n);
if (obj && n != obj->get_name())
diagnostic(m_io_state) << "warning: " << n << " will shadow " << obj->get_name() << endl;
}
m_using_decls.insert(n, p.second);
}
if (m_verbose)
regular(m_io_state) << " Using: " << prefix;
if (new_prefix)
regular(m_io_state) << " as " << new_prefix;
regular(m_io_state) << endl;
}
void parser_imp::parse_rewrite_set() {
next();
name id = check_identifier_next("invalid rewrite set declaration, identifier expected");
mk_rewrite_rule_set(m_env, id);
}
void parser_imp::parse_ids_and_rsid(buffer<name> & ids, name & rsid) {
while (curr_is_identifier()) {
auto p = pos();
name id = curr_name();
expr d = get_name_ref(id, p, false);
if (is_constant(d))
ids.push_back(const_name(d));
else if (is_value(d))
ids.push_back(to_value(d).get_name());
else
throw parser_error(sstream() << "'" << id << "' does not name a theorem or axiom", p);
next();
}
if (ids.empty())
throw parser_error("invalid command, at least one identifier expected", m_last_cmd_pos);
if (curr_is_colon()) {
next();
rsid = check_identifier_next("invalid command, rewrite set name expected");
} else {
rsid = get_default_rewrite_rule_set_id();
}
}
void parser_imp::parse_add_rewrite() {
next();
buffer<name> th_names;
name rsid;
parse_ids_and_rsid(th_names, rsid);
for (auto id : th_names) {
add_rewrite_rules(m_env, rsid, id);
}
}
void parser_imp::parse_enable_rewrite(bool flag) {
next();
buffer<name> ids;
name rsid;
parse_ids_and_rsid(ids, rsid);
for (auto id : ids) {
enable_rewrite_rules(m_env, rsid, id, flag);
}
}
/** \brief Parse a Lean command. */
bool parser_imp::parse_command() {
m_elaborator.clear();
m_expr_pos_info.clear();
m_tactic_hints.clear();
m_last_cmd_pos = pos();
name const & cmd_id = curr_name();
if (cmd_id == g_definition_kwd) {
parse_definition();
} else if (cmd_id == g_variable_kwd) {
parse_variable();
} else if (cmd_id == g_variables_kwd) {
parse_variables();
} else if (cmd_id == g_theorem_kwd) {
parse_theorem();
} else if (cmd_id == g_axiom_kwd) {
parse_axiom();
} else if (cmd_id == g_eval_kwd) {
parse_eval();
} else if (cmd_id == g_print_kwd) {
parse_print();
} else if (cmd_id == g_check_kwd) {
parse_check();
} else if (cmd_id == g_infix_kwd) {
parse_op(fixity::Infix);
} else if (cmd_id == g_infixl_kwd) {
parse_op(fixity::Infixl);
} else if (cmd_id == g_infixr_kwd) {
parse_op(fixity::Infixr);
} else if (cmd_id == g_notation_kwd) {
parse_notation_decl();
} else if (cmd_id == g_set_option_kwd) {
parse_set_option();
} else if (cmd_id == g_set_opaque_kwd) {
parse_set_opaque();
} else if (cmd_id == g_import_kwd) {
parse_import();
} else if (cmd_id == g_help_kwd) {
parse_help();
} else if (cmd_id == g_coercion_kwd) {
parse_coercion();
} else if (cmd_id == g_exit_kwd) {
next();
return false;
} else if (cmd_id == g_scope_kwd) {
parse_scope();
} else if (cmd_id == g_pop_kwd) {
parse_pop();
} else if (cmd_id == g_universe_kwd) {
parse_universe();
} else if (cmd_id == g_alias_kwd) {
parse_alias();
} else if (cmd_id == g_builtin_kwd) {
parse_builtin();
} else if (cmd_id == g_namespace_kwd) {
parse_namespace();
} else if (cmd_id == g_end_kwd) {
parse_end();
} else if (cmd_id == g_using_kwd) {
parse_using();
} else if (cmd_id == g_rewrite_set_kwd) {
parse_rewrite_set();
} else if (cmd_id == g_add_rewrite_kwd) {
parse_add_rewrite();
} else if (cmd_id == g_enable_rewrite_kwd) {
parse_enable_rewrite(true);
} else if (cmd_id == g_disable_rewrite_kwd) {
parse_enable_rewrite(false);
} else if (m_cmd_macros && m_cmd_macros->find(cmd_id) != m_cmd_macros->end()) {
parse_cmd_macro(cmd_id, m_last_cmd_pos);
} else {
next();
throw parser_error(sstream() << "invalid command '" << cmd_id << "'", m_last_cmd_pos);
}
return true;
}
}

View file

@ -1,98 +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 <utility>
#include "kernel/for_each_fn.h"
#include "library/io_state_stream.h"
#include "library/parser_nested_exception.h"
#include "library/error_handling/error_handling.h"
#include "frontends/lean/parser_imp.h"
namespace lean {
void parser_imp::display_error_pos(unsigned line, unsigned pos) {
regular(m_io_state) << m_strm_name << ":" << line << ":";
if (pos != static_cast<unsigned>(-1))
regular(m_io_state) << pos << ":";
regular(m_io_state) << " error:";
}
void parser_imp::display_error_pos(pos_info const & p) { display_error_pos(p.first, p.second); }
void parser_imp::display_error(char const * msg, unsigned line, unsigned pos) {
display_error_pos(line, pos);
regular(m_io_state) << " " << msg << endl;
}
std::pair<unsigned, unsigned> parser_imp::lean_pos_info_provider::get_pos_info(expr const & e) const {
expr const & o = m_parser->m_elaborator.get_original(e);
auto it = m_parser->m_expr_pos_info.find(o);
if (it == m_parser->m_expr_pos_info.end())
return m_pos;
return it->second;
}
char const * parser_imp::lean_pos_info_provider::get_file_name() const {
return m_parser->m_strm_name.c_str();
}
void parser_imp::display_error(tactic_cmd_error const & ex) {
display_error(ex.what(), ex.m_pos.first, ex.m_pos.second);
display_proof_state(ex.m_state);
}
void parser_imp::display_error(exception const & ex) {
lean_pos_info_provider pos_provider(m_this.lock(), m_last_cmd_pos);
::lean::display_error(m_io_state, &pos_provider, ex);
}
void parser_imp::display_error(script_exception const & ex) {
lean_pos_info_provider pos_provider(m_this.lock(), m_last_script_pos);
::lean::display_error(m_io_state, &pos_provider, ex);
}
#define CATCH(ShowError, ThrowError) \
m_found_errors = true; \
if (!m_use_exceptions && m_show_errors) { ShowError ; } \
sync(); \
if (m_use_exceptions) { ThrowError ; }
/**
\brief Execute the given function \c f, and handle exceptions occurring
when executing \c f.
The parameter \c s is an error synchronization procedure.
*/
void parser_imp::protected_call(std::function<void()> && f, std::function<void()> && sync) {
try {
f();
} catch (tactic_cmd_error & ex) {
CATCH(display_error(ex),
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second));
} catch (parser_exception & ex) {
CATCH(regular(m_io_state) << ex.what() << endl,
throw);
} catch (parser_error & ex) {
CATCH(display_error(ex.what(), ex.m_pos.first, ex.m_pos.second),
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second));
} catch (interrupted & ex) {
reset_interrupt();
if (m_verbose)
regular(m_io_state) << "!!!Interrupted!!!" << endl;
sync();
if (m_use_exceptions)
throw;
} catch (script_exception & ex) {
reset_interrupt();
CATCH(display_error(ex),
throw parser_nested_exception(std::shared_ptr<exception>(ex.clone()),
std::shared_ptr<pos_info_provider>(new lean_pos_info_provider(m_this.lock(), m_last_script_pos))));
} catch (exception & ex) {
reset_interrupt();
CATCH(display_error(ex),
throw parser_nested_exception(std::shared_ptr<exception>(ex.clone()),
std::shared_ptr<pos_info_provider>(new lean_pos_info_provider(m_this.lock(), m_last_cmd_pos))));
}
}
}

View file

@ -1,32 +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 "util/exception.h"
#include "kernel/expr.h"
#include "kernel/context.h"
#include "library/tactic/proof_state.h"
#include "frontends/lean/parser_types.h"
namespace lean {
/** \brief Exception used to track parsing erros, it does not leak outside of this class. */
struct parser_error : public exception {
pos_info m_pos;
parser_error(char const * msg, pos_info const & p):exception(msg), m_pos(p) {}
parser_error(sstream const & msg, pos_info const & p):exception(msg), m_pos(p) {}
virtual exception * clone() const { return new parser_error(m_msg.c_str(), m_pos); }
virtual void rethrow() const { throw *this; }
};
/** \brief Exception used to report error in the tactic frontend available in the Lean parser. */
struct tactic_cmd_error : public parser_error {
proof_state m_state;
tactic_cmd_error(char const * msg, pos_info const & p, proof_state const & s):parser_error(msg, p), m_state(s) {}
tactic_cmd_error(sstream const & msg, pos_info const & p, proof_state const & s):parser_error(msg, p), m_state(s) {}
virtual exception * clone() const { return new tactic_cmd_error(m_msg.c_str(), m_pos, m_state); }
virtual void rethrow() const { throw *this; }
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,238 +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 <utility>
#include <string>
#include <vector>
#include "library/io_state_stream.h"
#include "library/parser_nested_exception.h"
#include "frontends/lean/parser_imp.h"
#include "frontends/lean/parser_macros.h"
#include "frontends/lean/parser_calc.h"
#ifndef LEAN_DEFAULT_PARSER_SHOW_ERRORS
#define LEAN_DEFAULT_PARSER_SHOW_ERRORS true
#endif
namespace lean {
// ==========================================
// Parser configuration options
static name g_parser_show_errors {"lean", "parser", "show_errors"};
RegisterBoolOption(g_parser_show_errors, LEAN_DEFAULT_PARSER_SHOW_ERRORS, "(lean parser) display error messages in the regular output channel");
bool get_parser_show_errors(options const & opts) { return opts.get_bool(g_parser_show_errors, LEAN_DEFAULT_PARSER_SHOW_ERRORS); }
// ==========================================
void parser_imp::code_with_callbacks(std::function<void()> && f) {
m_script_state->apply([&](lua_State * L) {
set_io_state set1(L, m_io_state);
set_environment set2(L, m_env);
m_script_state->exec_unprotected([&]() {
f();
});
});
}
parser_imp::mk_scope::mk_scope(parser_imp & fn):
m_fn(fn),
m_scope(fn.m_local_decls),
m_old_num_local_decls(fn.m_num_local_decls) {
}
parser_imp::mk_scope::~mk_scope() {
m_fn.m_num_local_decls = m_old_num_local_decls;
}
calc_proof_parser & parser_imp::get_calc_proof_parser() {
if (!m_calc_proof_parser)
m_calc_proof_parser.reset(new calc_proof_parser());
return *m_calc_proof_parser;
}
pos_info parser_imp::pos_of(expr const & e, pos_info default_pos) {
auto it = m_expr_pos_info.find(e);
if (it == m_expr_pos_info.end())
return default_pos;
else
return it->second;
}
void parser_imp::check_next(scanner::token t, char const * msg) {
if (curr() == t)
next();
else
throw parser_error(msg, pos());
}
/**
\brief Throws a parser error if the current token is not a
string. If it is, move to the next token.
*/
std::string parser_imp::check_string_next(char const * msg) {
if (curr() != scanner::token::StringVal)
throw parser_error(msg, pos());
std::string r = curr_string();
next();
return r;
}
unsigned parser_imp::parse_unsigned(char const * msg) {
lean_assert(curr_is_nat());
mpz pval = curr_num().get_numerator();
if (!pval.is_unsigned_int()) {
throw parser_error(msg, pos());
} else {
unsigned r = pval.get_unsigned_int();
next();
return r;
}
}
double parser_imp::parse_double() {
return 0.0;
}
[[ noreturn ]] void parser_imp::not_implemented_yet() {
// TODO(Leo)
throw parser_error("not implemented yet", pos());
}
void parser_imp::updt_options() {
m_verbose = get_verbose(m_io_state.get_options());
m_show_errors = get_parser_show_errors(m_io_state.get_options());
}
/**
\brief Parse a block of Lua code. If as_expr is true, then
it appends the string "return " in front of the script.
*/
void parser_imp::parse_script(bool as_expr) {
m_last_script_pos = mk_pair(m_scanner.get_script_block_line(), m_scanner.get_script_block_pos());
if (!m_script_state)
throw exception("failed to execute Lua script, parser does not have a Lua interpreter");
std::string script_code = m_scanner.get_str_val();
if (as_expr) {
script_code = "return " + script_code;
}
next();
using_script([&](lua_State * L) {
dostring(L, script_code.c_str());
});
}
void parser_imp::parse_script_expr() { return parse_script(true); }
std::pair<expr, metavar_env> parser_imp::elaborate(expr const & e) {
return m_elaborator(e, m_io_state.get_options());
}
std::tuple<expr, expr, metavar_env> parser_imp::elaborate(name const & n, expr const & t, expr const & e) {
return m_elaborator(n, t, e, m_io_state.get_options());
}
void parser_imp::sync_command() {
// Keep consuming tokens until we find a Command or End-of-file
show_prompt();
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof && curr() != scanner::token::Period)
next();
if (curr_is_period())
next();
}
parser_imp::parser_imp(environment const & env, io_state const & st, std::istream & in, char const * strm_name,
script_state * S, bool use_exceptions, bool interactive):
m_env(env),
m_io_state(st),
m_scanner(in, strm_name),
m_strm_name(strm_name),
m_elaborator(env),
m_use_exceptions(use_exceptions),
m_interactive(interactive),
m_script_state(S),
m_set_parser(m_script_state, this) {
m_namespace_prefixes.push_back(name());
m_check_identifiers = true;
updt_options();
m_found_errors = false;
m_num_local_decls = 0;
m_scanner.set_command_keywords(get_command_keywords());
if (m_script_state) {
m_script_state->apply([&](lua_State * L) {
m_expr_macros = &get_expr_macros(L);
m_tactic_macros = &get_tactic_macros(L);
m_cmd_macros = &get_cmd_macros(L);
for (auto const & p : *m_cmd_macros) {
m_scanner.add_command_keyword(p.first);
}
});
} else {
m_expr_macros = nullptr;
m_tactic_macros = nullptr;
m_cmd_macros = nullptr;
}
// Initialize m_curr with some value. We need that because scan()
// may fail, and m_curr will remain uninitialized.
// In thi case, Valgrind would report unit var errors.
m_curr = scanner::token::Id;
protected_call([&]() { scan(); },
[&]() { sync_command(); });
}
parser_imp::~parser_imp() {}
void parser_imp::show_prompt(bool interactive, io_state const & ios) {
if (interactive) {
regular(ios) << "# ";
regular(ios).flush();
}
}
void parser_imp::show_prompt() {
show_prompt(m_interactive, m_io_state);
}
void parser_imp::show_tactic_prompt() {
if (m_interactive) {
regular(m_io_state) << "## ";
regular(m_io_state).flush();
}
}
/** \brief Parse a sequence of commands. This method also perform error management. */
bool parser_imp::parse_commands() {
bool done = false;
while (!done) {
protected_call([&]() {
check_interrupted();
switch (curr()) {
case scanner::token::CommandId: if (!parse_command()) done = true; break;
case scanner::token::ScriptBlock: parse_script(); break;
case scanner::token::Period: show_prompt(); next(); break;
case scanner::token::Eof: done = true; break;
default:
throw parser_error("Command expected", pos());
}
},
[&]() { sync_command(); });
}
return !m_found_errors;
}
/** \brief Parse an expression. */
expr parser_imp::parse_expr_main() {
try {
auto p = elaborate(parse_expr());
check_no_metavar(p, "invalid expression, it still contains metavariables after elaboration");
return p.first;
} catch (parser_error & ex) {
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second);
} catch (exception & ex) {
throw parser_nested_exception(std::shared_ptr<exception>(ex.clone()),
std::shared_ptr<pos_info_provider>(new lean_pos_info_provider(m_this.lock(), m_last_cmd_pos)));
}
}
};

View file

@ -1,470 +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 <utility>
#include <string>
#include <vector>
#include "util/name_map.h"
#include "util/scoped_map.h"
#include "util/script_exception.h"
#include "util/script_state.h"
#include "kernel/expr_maps.h"
#include "kernel/io_state.h"
#include "kernel/environment.h"
#include "library/kernel_bindings.h"
#include "library/tactic/tactic.h"
#include "library/elaborator/elaborator_exception.h"
#include "library/unsolved_metavar_exception.h"
#include "frontends/lean/scanner.h"
#include "frontends/lean/parser_types.h"
#include "frontends/lean/parser_error.h"
#include "frontends/lean/operator_info.h"
#include "frontends/lean/frontend_elaborator.h"
// Lean encodes
// proj1 i t
// as
// proj1 (proj2 (proj2 ... t) ...)
// So, a big \c i may make Lean run out of memory.
// The default limit is 10000. I don't believe anybody needs to create a tuple with
// more than 10000 entries
#ifndef LEAN_MAX_PROJECTION
#define LEAN_MAX_PROJECTION 10000
#endif
namespace lean {
class parser_imp;
class calc_proof_parser;
bool get_parser_verbose(options const & opts);
bool get_parser_show_errors(options const & opts);
/** \brief Auxiliary object that stores a reference to the parser object inside the Lua State */
struct set_parser {
script_state::weak_ref m_state;
parser_imp * m_prev;
set_parser(script_state * S, parser_imp * ptr);
~set_parser();
};
/**
\brief Actual implementation for the default Lean parser
\remark It is an instance of a Pratt parser
(http://en.wikipedia.org/wiki/Pratt_parser) described in the paper
"Top down operator precedence". This algorithm is super simple,
and it is easy to support user-defined infix/prefix/postfix/mixfix
operators.
*/
class parser_imp {
friend class parser;
friend int mk_cmd_macro(lua_State * L);
typedef scoped_map<name, unsigned, name_hash, name_eq> local_decls;
typedef name_map<expr> builtins;
typedef expr_map<pos_info> expr_pos_info;
typedef expr_map<tactic> tactic_hints; // a mapping from placeholder to tactic
typedef scoped_map<name, name, name_hash, name_eq> using_decls;
enum class scope_kind { Scope, Namespace };
std::weak_ptr<parser_imp> m_this;
environment m_env;
io_state m_io_state;
scanner m_scanner;
std::string m_strm_name; // input stream name
frontend_elaborator m_elaborator;
macros const * m_expr_macros;
macros const * m_cmd_macros;
macros const * m_tactic_macros;
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;
pos_info m_last_script_pos;
tactic_hints m_tactic_hints;
using_decls m_using_decls;
std::vector<name> m_namespace_prefixes;
std::vector<scope_kind> m_scope_kinds;
std::unique_ptr<calc_proof_parser> m_calc_proof_parser;
// If true then return error when parsing identifiers and it is not local or global.
// We set this flag off when parsing tactics. The apply_tac may reference
// a hypothesis in the proof state. This hypothesis is not visible until we
// execute the tactic.
bool m_check_identifiers;
script_state * m_script_state;
set_parser m_set_parser;
bool m_verbose;
bool m_show_errors;
template<typename F>
typename std::result_of<F(lua_State * L)>::type using_script(F && f) {
return m_script_state->apply([&](lua_State * L) {
set_io_state set1(L, m_io_state);
set_environment set2(L, m_env);
return f(L);
});
}
void code_with_callbacks(std::function<void()> && f);
/**
\brief Auxiliar struct for creating/destroying a new scope for
local declarations.
*/
struct mk_scope {
parser_imp & m_fn;
local_decls::mk_scope m_scope;
unsigned m_old_num_local_decls;
mk_scope(parser_imp & fn);
~mk_scope();
};
calc_proof_parser & get_calc_proof_parser();
public:
environment const & get_environment() const { return m_env; }
/** \brief Return the current position information */
pos_info pos() const { return mk_pair(m_scanner.get_line(), m_scanner.get_pos()); }
/** \brief Return the position associated with \c e. If there is none, then return \c default_pos. */
pos_info pos_of(expr const & e, pos_info default_pos);
/** \brief Associate position \c p with \c e and return \c e */
expr save(expr const & e, pos_info p) { m_expr_pos_info[e] = p; return e; }
/** \brief Read the next token. */
void scan() { m_curr = m_scanner.scan(); }
/** \brief Return the current token */
scanner::token curr() const { return m_curr; }
/** \brief Read the next token if the current one is not End-of-file. */
void next() { if (m_curr != scanner::token::Eof) scan(); }
/** \brief Return the name associated with the current token. */
name const & curr_name() const { return m_scanner.get_name_val(); }
/** \brief Return the numeral associated with the current token. */
mpq const & curr_num() const { return m_scanner.get_num_val(); }
/** \brief Return the string associated with the current token. */
std::string const & curr_string() const { return m_scanner.get_str_val(); }
/**
\brief Check if the current token is \c t, and move to the
next one. If the current token is not \c t, it throws a parser error.
*/
void check_next(scanner::token t, char const * msg);
/** \brief Return true iff the current token is an identifier */
bool curr_is_identifier() const { return curr() == scanner::token::Id; }
/** \brief Return true iff the current token is a string literal */
bool curr_is_string() const { return curr() == scanner::token::StringVal; }
/** \brief Return true iff the current token is a '_" */
bool curr_is_placeholder() const { return curr() == scanner::token::Placeholder; }
/** \brief Return true iff the current token is a natural number */
bool curr_is_nat() const { return curr() == scanner::token::IntVal && m_scanner.get_num_val() >= 0; }
/** \brief Return true iff the current token is a '(' */
bool curr_is_lparen() const { return curr() == scanner::token::LeftParen; }
/** \brief Return true iff the current token is a '{' */
bool curr_is_lcurly() const { return curr() == scanner::token::LeftCurlyBracket; }
/** \brief Return true iff the current token is a ':' */
bool curr_is_colon() const { return curr() == scanner::token::Colon; }
/** \brief Return true iff the current token is a ',' */
bool curr_is_comma() const { return curr() == scanner::token::Comma; }
/** \brief Return true iff the current token is a ':=' */
bool curr_is_assign() const { return curr() == scanner::token::Assign; }
/** \brief Return true iff the current token is an 'in' token */
bool curr_is_in() const { return curr() == scanner::token::In; }
/** \brief Return true iff the current token is '.' */
bool curr_is_period() const { return curr() == scanner::token::Period; }
/** \brief Throws a parser error if the current token is not an identifier. */
void check_identifier(char const * msg) { if (!curr_is_identifier()) throw parser_error(msg, pos()); }
/**
\brief Throws a parser error if the current token is not an
identifier. If it is, move to the next token.
*/
name check_identifier_next(char const * msg) { check_identifier(msg); name r = curr_name(); next(); return r; }
/** \brief Throws a parser error if the current token is not '_'. If it is, move to the next token. */
void check_placeholder_next(char const * msg) { check_next(scanner::token::Placeholder, msg); }
/** \brief Throws a parser error if the current token is not ':'. If it is, move to the next token. */
void check_colon_next(char const * msg) { check_next(scanner::token::Colon, msg); }
/** \brief Throws a parser error if the current token is not ','. If it is, move to the next token. */
void check_comma_next(char const * msg) { check_next(scanner::token::Comma, msg); }
/** \brief Throws a parser error if the current token is not ')'. If it is, move to the next token. */
void check_rparen_next(char const * msg) { check_next(scanner::token::RightParen, msg); }
/** \brief Throws a parser error if the current token is not '}'. If it is, move to the next token. */
void check_rcurly_next(char const * msg) { check_next(scanner::token::RightCurlyBracket, msg); }
/** \brief Throws a parser error if the current token is not ':='. If it is, move to the next token. */
void check_assign_next(char const * msg) { check_next(scanner::token::Assign, msg); }
/** \brief Throws a parser error if the current token is not '.'. If it is, move to the next token. */
void check_period_next(char const * msg) { check_next(scanner::token::Period, msg); }
std::string check_string_next(char const * msg);
/**
@name Error handling
*/
/*@{*/
private:
void display_error_pos(unsigned line, unsigned pos);
void display_error_pos(pos_info const & p);
void display_error(char const * msg, unsigned line, unsigned pos);
struct lean_pos_info_provider : public pos_info_provider {
std::shared_ptr<parser_imp> m_parser;
pos_info m_pos;
lean_pos_info_provider(std::shared_ptr<parser_imp> const & p, pos_info const & pos):m_parser(p), m_pos(pos) {}
virtual std::pair<unsigned, unsigned> get_pos_info(expr const & e) const;
virtual std::pair<unsigned, unsigned> get_some_pos() const { return m_pos; }
virtual char const * get_file_name() const;
};
void display_error(tactic_cmd_error const & ex);
void display_error(script_exception const & ex);
void display_error(exception const & ex);
public:
void protected_call(std::function<void()> && f, std::function<void()> && sync);
/*@}*/
public:
unsigned parse_unsigned(char const * msg);
double parse_double();
private:
[[ noreturn ]] void not_implemented_yet();
void updt_options();
void parse_script(bool as_expr = false);
void parse_script_expr();
/**
@name Elaborator interface
*/
/*@{*/
private:
std::pair<expr, metavar_env> elaborate(expr const & e);
std::tuple<expr, expr, metavar_env> elaborate(name const & n, expr const & t, expr const & e);
/*@}*/
/**
@name Parse Universe levels
*/
/*@{*/
private:
level parse_level_max();
level parse_level_nud_id();
level parse_level_nud_int();
level parse_level_lparen();
level parse_level_nud();
level parse_level_led_plus(level const & left);
level parse_level_led_cup(level const & left);
level parse_level_led(level const & left);
unsigned curr_level_lbp();
public:
/** \brief Parse a universe level */
level parse_level(unsigned rbp = 0);
/*@}*/
/**
@name Parse Expressions
*/
/*@{*/
private:
unsigned get_implicit_vector_size(expr const & d);
std::vector<bool> const & get_smallest_implicit_vector(list<expr> const & ds);
unsigned get_smallest_implicit_vector_size(list<expr> const & ds);
expr mk_fun(operator_info const & op, pos_info const & pos);
expr mk_application(operator_info const & op, pos_info const & pos, unsigned num_args, expr const * args);
expr mk_application(operator_info const & op, pos_info const & pos, std::initializer_list<expr> const & l);
expr mk_application(operator_info const & op, pos_info const & pos, expr const & arg);
expr mk_application(operator_info const & op, pos_info const & pos, buffer<expr> const & args);
expr parse_prefix(operator_info const & op);
expr parse_postfix(expr const & left, operator_info const & op, pos_info const & op_pos);
expr parse_infix(expr const & left, operator_info const & op, pos_info const & op_pos);
expr parse_infixl(expr const & left, operator_info const & op, pos_info const & op_pos);
expr parse_infixr(expr const & left, operator_info const & op, pos_info const & op_pos);
void check_op_part(name const & op_part);
void parse_mixfix_args(list<name> const & ops, unsigned prec, buffer<expr> & args);
expr parse_mixfixl(operator_info const & op);
expr parse_mixfixr(expr const & left, operator_info const & op, pos_info const & op_pos);
expr parse_mixfixo(expr const & left, operator_info const & op, pos_info const & op_pos);
expr parse_mixfixc(operator_info const & op);
void propagate_position(expr const & e, pos_info p);
bool is_curr_begin_expr() const;
bool is_curr_begin_tactic() const;
typedef buffer<std::pair<macro_arg_kind, void*>> macro_arg_stack;
struct macro_result {
optional<expr> m_expr;
optional<tactic> m_tactic;
macro_result(expr const & e):m_expr(e) {}
macro_result(tactic const & t):m_tactic(t) {}
macro_result() {}
};
macro_result parse_macro(list<macro_arg_kind> const & arg_kinds, luaref const & fn, unsigned prec, macro_arg_stack & args,
pos_info const & p);
expr parse_expr_macro(name const & id, pos_info const & p);
expr parse_led_id(expr const & left);
expr parse_heq(expr const & left);
expr parse_arrow(expr const & left);
expr parse_cartesian_product(expr const & left);
expr parse_lparen();
void parse_names(buffer<std::pair<pos_info, name>> & result);
void register_binding(name const & n);
void parse_simple_parameters(parameter_buffer & result, bool implicit_decl, bool suppress_type);
expr mk_abstraction(expr_kind k, parameter_buffer const & parameters, expr const & body);
expr parse_abstraction(expr_kind k);
expr parse_lambda();
expr parse_pi();
expr parse_sig();
expr parse_exists();
expr parse_let();
expr parse_type(bool level_expected);
expr parse_pair();
expr parse_proj(bool first);
tactic parse_tactic_macro(name tac_id, pos_info const & p);
expr parse_show_expr();
expr parse_have_expr();
expr parse_calc();
expr parse_nud_id();
expr parse_nud();
expr parse_led(expr const & left);
expr mk_app_left(expr const & left, expr const & arg);
unsigned curr_lbp();
expr parse_nat_int();
expr parse_decimal();
expr parse_string();
expr parse_by_expr();
public:
/**
\brief Try to find an object (Definition or Postulate) named \c
id in the frontend/environment. If there isn't one, then tries
to check if \c id is a builtin symbol. If it is not throws an error.
If \c implicit_args is true, then we also parse explicit arguments until
we have a placeholder forall implicit ones.
*/
expr get_name_ref(name const & id, pos_info const & p, bool implicit_args = true);
/**
\brief Parse a sequence of <tt>'(' ID ... ID ':' expr ')'</tt>.
This is used when parsing lambda, Pi, forall/exists expressions and
definitions.
\remark If implicit_decls is true, then we allow declarations
with curly braces. These declarations are used to tag implicit
arguments. Such as:
<code> Definition f {A : Type} (a b : A) : A := ... </code>
\see parse_simple_parameters
*/
void parse_parameters(parameter_buffer & result, bool implicit_decls, bool suppress_type);
/** \brief Parse parameters for expressions such as: lambda, pi, forall, exists */
void parse_expr_parameters(parameter_buffer & result);
void parse_var_decl_parameters(parameter_buffer & result);
void parse_definition_parameters(parameter_buffer & result);
/** \brief Parse a tactic expression, it can be
1) A Lua Script Block expression that returns a tactic
2) '(' expr ')' where expr is a proof term
3) identifier that is the name of a tactic or proof term.
*/
tactic parse_tactic_expr();
/** \brief Parse \c _ a hole that must be filled by the elaborator. */
expr parse_placeholder();
/** \brief Parse an expression */
expr parse_expr(unsigned rbp = 0);
/*@}*/
/**
@name Tactics
*/
/*@{*/
private:
/** \brief Return true iff the current token is a tactic command */
bool curr_is_tactic_cmd() const;
void display_proof_state(proof_state s);
void display_proof_state_if_interactive(proof_state s);
typedef std::vector<proof_state_seq> proof_state_seq_stack;
std::pair<proof_state, proof_state_seq> apply_tactic(proof_state const & s, tactic const & t, pos_info const & p);
expr mk_proof_for(proof_state const & s, pos_info const & p, context const & ctx, expr const & expected_type);
void back_cmd(/* inout */ proof_state_seq_stack & stack, /* inout */ proof_state & s);
void tactic_cmd(/* inout */ proof_state_seq_stack & stack, /* inout */ proof_state & s);
expr done_cmd(proof_state const & s, context const & ctx, expr const & expected_type);
expr parse_tactic_cmds(proof_state s, context const & ctx, expr const & expected_type);
std::pair<optional<tactic>, pos_info> get_tactic_for(expr const & mvar);
std::pair<expr, context> get_metavar_ctx_and_type(expr const & mvar, metavar_env const & menv);
expr apply_tactics(expr const & val, metavar_env & menv);
/*@}*/
private:
/**
@name Parse Commands
*/
/*@{*/
static list<name> const & get_command_keywords();
void register_implicit_arguments(name const & n, parameter_buffer & parameters);
void check_no_metavar(expr const & e, metavar_env const & menv, char const * msg);
void check_no_metavar(std::pair<expr, metavar_env> const & p, char const * msg);
name mk_full_name(name const & n);
void parse_def_core(bool is_definition);
void parse_definition();
void parse_theorem();
void parse_variable_core(bool is_var);
void parse_variable();
void parse_variables();
void parse_axiom();
void parse_eval();
bool is_hidden_object(object const & obj) const;
void parse_print();
void parse_check();
unsigned parse_precedence();
name parse_op_id();
void parse_op(fixity fx);
void parse_notation_decl();
void parse_set_option();
void parse_set_opaque();
optional<std::string> find_lua_file(std::string const & fname);
void parse_import();
void parse_help();
void parse_coercion();
void reset_env(environment env);
void parse_scope();
void parse_pop();
void parse_cmd_macro(name cmd_id, pos_info const & p);
void parse_universe();
void parse_alias();
void parse_builtin();
void parse_namespace();
void parse_end();
void parse_using();
void parse_rewrite_set();
void parse_ids_and_rsid(buffer<name> & ids, name & rsid);
void parse_add_rewrite();
void parse_enable_rewrite(bool flag);
bool parse_command();
void sync_command();
/*@}*/
public:
parser_imp(environment const & env, io_state const & st, std::istream & in, char const * strm_name,
script_state * S, bool use_exceptions, bool interactive);
~parser_imp();
static void show_prompt(bool interactive, io_state const & ios);
void show_prompt();
void show_tactic_prompt();
/** \brief Parse a sequence of commands. This method also perform error management. */
bool parse_commands();
/** \brief Parse an expression. */
expr parse_expr_main();
};
}

View file

@ -1,119 +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 "library/io_state_stream.h"
#include "frontends/lean/parser_imp.h"
namespace lean {
// ==========================================
// Support for parsing levels
static name g_max_name("max");
static name g_cup_name("\u2294");
static name g_plus_name("+");
static unsigned g_level_plus_prec = 10;
static unsigned g_level_cup_prec = 5;
// ==========================================
/*
Parse Universe levels
*/
level parser_imp::parse_level_max() {
auto p = pos();
next();
buffer<level> lvls;
while (curr_is_identifier() || curr_is_nat()) {
lvls.push_back(parse_level());
}
if (lvls.size() < 2)
throw parser_error("invalid level expression, max must have at least two arguments", p);
level r = lvls[0];
for (unsigned i = 1; i < lvls.size(); i++)
r = max(r, lvls[i]);
return r;
}
level parser_imp::parse_level_nud_id() {
name id = curr_name();
if (id == g_max_name) {
return parse_level_max();
} else {
next();
return m_env->get_uvar(id);
}
}
level parser_imp::parse_level_nud_int() {
auto p = pos();
mpz val = curr_num().get_numerator();
next();
if (val < 0)
throw parser_error("invalid level expression, value is negative", p);
if (!val.is_unsigned_int())
throw parser_error("invalid level expression, value does not fit in a machine integer", p);
return level() + val.get_unsigned_int();
}
level parser_imp::parse_level_lparen() {
next();
level r = parse_level();
check_rparen_next("invalid level expression, ')' expected");
return r;
}
level parser_imp::parse_level_nud() {
switch (curr()) {
case scanner::token::Id: return parse_level_nud_id();
case scanner::token::IntVal: return parse_level_nud_int();
case scanner::token::LeftParen: return parse_level_lparen();
default:
throw parser_error("invalid level expression", pos());
}
}
level parser_imp::parse_level_led_plus(level const & left) {
auto p = pos();
next();
level right = parse_level(g_level_plus_prec);
if (!is_lift(right) || !lift_of(right).is_bottom())
throw parser_error("invalid level expression, right hand side of '+' (aka universe lift operator) must be a numeral", p);
return left + lift_offset(right);
}
level parser_imp::parse_level_led_cup(level const & left) {
next();
level right = parse_level(g_level_cup_prec);
return max(left, right);
}
level parser_imp::parse_level_led(level const & left) {
switch (curr()) {
case scanner::token::Id:
if (curr_name() == g_plus_name) return parse_level_led_plus(left);
else if (curr_name() == g_cup_name) return parse_level_led_cup(left);
default:
throw parser_error("invalid level expression", pos());
}
}
unsigned parser_imp::curr_level_lbp() {
switch (curr()) {
case scanner::token::Id: {
name const & id = curr_name();
if (id == g_plus_name) return g_level_plus_prec;
else if (id == g_cup_name) return g_level_cup_prec;
else return 0;
}
default: return 0;
}
}
level parser_imp::parse_level(unsigned rbp) {
level left = parse_level_nud();
while (rbp < curr_level_lbp()) {
left = parse_level_led(left);
}
return left;
}
}

View file

@ -1,143 +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 "library/io_state_stream.h"
#include "frontends/lean/parser_imp.h"
#include "frontends/lean/notation.h"
namespace lean {
static char g_set_parser_key;
/** \brief Return a reference to the parser object */
parser_imp * get_parser(lua_State * L) {
lua_pushlightuserdata(L, static_cast<void *>(&g_set_parser_key));
lua_gettable(L, LUA_REGISTRYINDEX);
if (lua_islightuserdata(L, -1)) {
auto r = static_cast<parser_imp*>(lua_touserdata(L, -1));
lua_pop(L, 1);
return r;
}
lua_pop(L, 1);
return nullptr;
}
set_parser::set_parser(script_state * S, parser_imp * ptr) {
if (S) {
m_state = S->to_weak_ref();
S->apply([&](lua_State * L) {
m_prev = get_parser(L);
lua_pushlightuserdata(L, static_cast<void *>(&g_set_parser_key));
lua_pushlightuserdata(L, ptr);
lua_settable(L, LUA_REGISTRYINDEX);
});
}
}
set_parser::~set_parser() {
if (!m_state.expired()) {
script_state S(m_state);
S.apply([&](lua_State * L) {
lua_pushlightuserdata(L, static_cast<void *>(&g_set_parser_key));
lua_pushlightuserdata(L, m_prev);
lua_settable(L, LUA_REGISTRYINDEX);
});
}
}
static char g_parser_expr_macros_key;
static char g_parser_tactic_macros_key;
static char g_parser_cmd_macros_key;
DECL_UDATA(macros)
macros & get_macros(lua_State * L, char * key) {
lua_pushlightuserdata(L, static_cast<void *>(key));
lua_gettable(L, LUA_REGISTRYINDEX);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_pushlightuserdata(L, static_cast<void *>(key));
push_macros(L, macros());
lua_settable(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, static_cast<void *>(key));
lua_gettable(L, LUA_REGISTRYINDEX);
}
lean_assert(is_macros(L, -1));
macros & r = to_macros(L, -1);
lua_pop(L, 1);
return r;
}
macros & get_expr_macros(lua_State * L) { return get_macros(L, &g_parser_expr_macros_key); }
macros & get_tactic_macros(lua_State * L) { return get_macros(L, &g_parser_tactic_macros_key); }
macros & get_cmd_macros(lua_State * L) { return get_macros(L, &g_parser_cmd_macros_key); }
void mk_macro(lua_State * L, macros & mtable) {
int nargs = lua_gettop(L);
name macro_name = to_name_ext(L, 1);
unsigned prec = nargs == 4 ? lua_tointeger(L, 4) : g_app_precedence;
luaL_checktype(L, 3, LUA_TFUNCTION); // user-fun
buffer<macro_arg_kind> arg_kind_buffer;
int n = objlen(L, 2);
for (int i = 1; i <= n; i++) {
lua_rawgeti(L, 2, i);
arg_kind_buffer.push_back(static_cast<macro_arg_kind>(luaL_checkinteger(L, -1)));
lua_pop(L, 1);
}
list<macro_arg_kind> arg_kinds = to_list(arg_kind_buffer.begin(), arg_kind_buffer.end());
mtable.insert(mk_pair(macro_name, macro(arg_kinds, luaref(L, 3), prec)));
}
int mk_macro(lua_State * L) {
mk_macro(L, get_expr_macros(L));
return 0;
}
int mk_cmd_macro(lua_State * L) {
mk_macro(L, get_cmd_macros(L));
name macro_name = to_name_ext(L, 1);
parser_imp * ptr = get_parser(L);
if (!ptr)
throw exception("invalid cmd_macro, it is not in the context of a Lean parser");
// Make sure macro_name is a CommandId.
ptr->m_scanner.add_command_keyword(macro_name);
if (ptr->m_curr == scanner::token::Id && ptr->curr_name() == macro_name) {
ptr->m_curr = scanner::token::CommandId;
}
return 0;
}
int mk_tactic_macro(lua_State * L) {
mk_macro(L, get_tactic_macros(L));
return 0;
}
static const struct luaL_Reg macros_m[] = {
{"__gc", macros_gc}, // never throws
{0, 0}
};
void open_macros(lua_State * L) {
luaL_newmetatable(L, macros_mt);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
setfuncs(L, macros_m, 0);
SET_GLOBAL_FUN(macros_pred, "is_macros");
SET_GLOBAL_FUN(mk_macro, "macro");
SET_GLOBAL_FUN(mk_cmd_macro, "cmd_macro");
SET_GLOBAL_FUN(mk_tactic_macro, "tactic_macro");
lua_newtable(L);
SET_ENUM("Expr", macro_arg_kind::Expr);
SET_ENUM("Exprs", macro_arg_kind::Exprs);
SET_ENUM("Parameters", macro_arg_kind::Parameters);
SET_ENUM("Id", macro_arg_kind::Id);
SET_ENUM("Ids", macro_arg_kind::Ids);
SET_ENUM("String", macro_arg_kind::String);
SET_ENUM("Int", macro_arg_kind::Int);
SET_ENUM("Comma", macro_arg_kind::Comma);
SET_ENUM("Assign", macro_arg_kind::Assign);
SET_ENUM("Tactic", macro_arg_kind::Tactic);
SET_ENUM("Tactics", macro_arg_kind::Tactics);
lua_setglobal(L, "macro_arg");
}
}

View file

@ -1,14 +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 "util/lua.h"
#include "frontends/lean/parser_types.h"
namespace lean {
macros & get_expr_macros(lua_State * L);
macros & get_tactic_macros(lua_State * L);
macros & get_cmd_macros(lua_State * L);
}

View file

@ -1,295 +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 <utility>
#include <algorithm>
#include "kernel/for_each_fn.h"
#include "kernel/type_checker.h"
#include "kernel/type_checker_justification.h"
#include "kernel/unification_constraint.h"
#include "library/io_state_stream.h"
#include "library/expr_lt.h"
#include "library/elaborator/elaborator.h"
#include "frontends/lean/parser_imp.h"
namespace lean {
static name g_apply("apply");
static name g_done("done");
static name g_back("back");
static name g_abort("abort");
static name g_assumption("assumption");
static list<name> g_tactic_cmds = { g_apply, g_done, g_back, g_abort, g_assumption };
bool parser_imp::curr_is_tactic_cmd() const {
return curr_is_identifier() && std::find(g_tactic_cmds.begin(), g_tactic_cmds.end(), curr_name()) != g_tactic_cmds.end();
}
void parser_imp::display_proof_state(proof_state s) {
regular(m_io_state) << "Proof state:\n" << s << endl;
}
void parser_imp::display_proof_state_if_interactive(proof_state s) {
if (m_interactive)
display_proof_state(s);
}
/**
\brief Apply tactic \c t to state \c s.
When \c t succeeds, it returns the head and tail of the output state sequence.
Throws an exception if the tactic fails, and use \c p to sign the error position.
*/
std::pair<proof_state, proof_state_seq> parser_imp::apply_tactic(proof_state const & s, tactic const & t, pos_info const & p) {
proof_state_seq::maybe_pair r;
code_with_callbacks([&]() {
// t may have call-backs we should set ios in the script_state
proof_state_seq seq = t(m_env, m_io_state, s);
r = seq.pull();
});
if (r) {
return mk_pair(r->first, r->second);
} else {
throw tactic_cmd_error("tactic failed", p, s);
}
}
/**
\brief Try to create a proof for the input state \c s.
It basically applies the proof_builder if \c s does not contain
any goals left. The position information is used to throw an exception
if \c s is not a final state.
The resultant proof must have type \c expected_type in the context \c ctx.
*/
expr parser_imp::mk_proof_for(proof_state const & s, pos_info const & p, context const & ctx, expr const & expected_type) {
if (s.is_proof_final_state()) {
assignment a(s.get_menv().copy());
proof_map m;
expr pr = s.get_proof_builder()(m, a);
if (has_metavar(pr)) {
// Some tactics generate metavariables that can only be instantiated by type inference elaboration.
// Example: apply_tactic.
metavar_env menv = s.get_menv().copy();
buffer<unification_constraint> ucs;
expr pr_type = type_checker(m_env).check(pr, ctx, menv, ucs);
ucs.push_back(mk_convertible_constraint(ctx, pr_type, expected_type, mk_type_match_justification(ctx, expected_type, pr)));
elaborator elb(m_env, menv, ucs.size(), ucs.data(), m_io_state.get_options());
metavar_env new_menv = elb.next();
pr = new_menv->instantiate_metavars(pr);
if (has_metavar(pr))
throw exception("synthesized proof object has unsolved metavars");
}
return pr;
} else {
throw tactic_cmd_error("failed to create proof for the following proof state", p, s);
}
}
/**
\brief Execute the \c back (backtrack) tactic command. It
succeeds if the stack \c stack contains proof states. When it
succeeds, \c s is updated with the next state in the state
sequence on the top of \c stack. The new state is also removed
from the stack.
*/
void parser_imp::back_cmd(/* inout */ proof_state_seq_stack & stack, /* inout */ proof_state & s) {
auto p = pos();
next();
while (!stack.empty()) {
check_interrupted();
lazy_list<proof_state> seq = stack.back();
stack.pop_back();
proof_state_seq::maybe_pair r;
code_with_callbacks([&]() {
r = seq.pull();
});
if (r) {
stack.push_back(r->second);
s = r->first;
return;
}
}
throw tactic_cmd_error("failed to backtrack", p, s);
}
/**
\brief Execute the tactic.
This command just applies the tactic to the input state \c s.
If it succeeds, \c s is assigned to the head of the output
state sequence produced by the tactic. The rest/tail of the
output state sequence is stored on the top of the stack \c
stack.
*/
void parser_imp::tactic_cmd(/* inout */ proof_state_seq_stack & stack, /* inout */ proof_state & s) {
auto tac_pos = pos();
tactic t = parse_tactic_expr();
auto r = apply_tactic(s, t, tac_pos);
s = r.first;
stack.push_back(r.second);
}
/**
\brief Execute the \c done tactic command. It succeeds if
a proof for \c s can be built.
*/
expr parser_imp::done_cmd(proof_state const & s, context const & ctx, expr const & expected_type) {
auto p = pos();
next();
return mk_proof_for(s, p, ctx, expected_type);
}
/**
\brief Parse tactic command sequence for solving input state \c s.
The proof for \c s must have type \c expected_type in the context \c ctx.
*/
expr parser_imp::parse_tactic_cmds(proof_state s, context const & ctx, expr const & expected_type) {
proof_state_seq_stack stack;
optional<expr> pr;
enum class status { Continue, Done, Eof, Abort };
status st = status::Continue;
while (st == status::Continue) {
protected_call(
[&]() {
auto p = pos();
check_interrupted();
name id;
switch (curr()) {
case scanner::token::Period:
display_proof_state_if_interactive(s);
show_tactic_prompt();
next();
break;
case scanner::token::Eof:
st = status::Eof;
break;
case scanner::token::Id:
id = curr_name();
if (id == g_back) {
back_cmd(stack, s);
} else if (id == g_done) {
pr = done_cmd(s, ctx, expected_type);
if (pr)
st = status::Done;
} else if (id == g_abort) {
next();
st = status::Abort;
} else {
tactic_cmd(stack, s);
}
break;
case scanner::token::ScriptBlock:
tactic_cmd(stack, s);
break;
case scanner::token::CommandId:
st = status::Abort;
break;
default:
next();
throw tactic_cmd_error("invalid tactic command, identifier expected", p, s);
}
},
[&]() {
// Keep consuming tokens until we find a Command or End-of-file or Tactic command
show_tactic_prompt();
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof &&
curr() != scanner::token::Period && !curr_is_tactic_cmd())
next();
if (curr_is_period())
next();
});
}
switch (st) {
case status::Done: return *pr;
case status::Eof: throw tactic_cmd_error("invalid tactic command, unexpected end of file", pos(), s);
case status::Abort: throw tactic_cmd_error("failed to prove theorem, proof has been aborted", pos(), s);
default: lean_unreachable(); // LCOV_EXCL_LINE
}
}
/**
\brief Return a 'hint' tactic (if it exists) for the given metavariable.
If the metavariable is not associated with any hint, then return the
null tactic. The method also returns the position of the hint.
*/
std::pair<optional<tactic>, pos_info> parser_imp::get_tactic_for(expr const & mvar) {
expr placeholder = m_elaborator.get_original(mvar);
auto it = m_tactic_hints.find(placeholder);
if (it != m_tactic_hints.end()) {
return mk_pair(some_tactic(it->second), pos_of(placeholder, pos()));
} else {
return mk_pair(none_tactic(), pos_of(placeholder, pos()));
}
}
std::pair<expr, context> parser_imp::get_metavar_ctx_and_type(expr const & mvar, metavar_env const & menv) {
expr mvar_type = menv->instantiate_metavars(menv->get_type(mvar));
buffer<context_entry> new_entries;
for (auto e : menv->get_context(mvar)) {
optional<expr> d = e.get_domain();
optional<expr> b = e.get_body();
if (d) d = menv->instantiate_metavars(*d);
if (b) b = menv->instantiate_metavars(*b);
if (d)
new_entries.emplace_back(e.get_name(), *d, b);
else
new_entries.emplace_back(e.get_name(), d, *b);
}
context mvar_ctx(new_entries.size(), new_entries.data());
return mk_pair(mvar_type, mvar_ctx);
}
/**
\brief Try to fill the 'holes' in \c val using tactics.
The metavar_env \c menv contains the definition of the metavariables occurring in \c val.
If a 'hole' is associated with a "hint tactic" ('show-by' and 'by' constructs),
then this will be the tactic used to "fill" the hole. Otherwise,
a tactic command sequence is expected in the input stream being parsed.
*/
expr parser_imp::apply_tactics(expr const & val, metavar_env & menv) {
buffer<expr> mvars;
for_each(val, [&](expr const & e, unsigned) {
if (is_metavar(e)) {
expr m = e;
if (has_local_context(m))
m = mk_metavar(metavar_name(m));
if (std::find(mvars.begin(), mvars.end(), m) == mvars.end())
mvars.push_back(m);
}
return true;
});
std::sort(mvars.begin(), mvars.end(), [](expr const & e1, expr const & e2) { return is_lt(e1, e2, false); });
for (auto mvar : mvars) {
auto p = get_metavar_ctx_and_type(mvar, menv);
expr mvar_type = p.first;
context mvar_ctx = p.second;
if (has_metavar(mvar_type))
throw unsolved_metavar_exception(sstream() << "failed to synthesize metavars, type of " << metavar_name(mvar) << " still contains metavariables", val);
try {
proof_state s = to_proof_state(m_env, mvar_ctx, mvar_type);
std::pair<optional<tactic>, pos_info> hint_and_pos = get_tactic_for(mvar);
if (hint_and_pos.first) {
// metavariable has an associated tactic hint
s = apply_tactic(s, *(hint_and_pos.first), hint_and_pos.second).first;
menv->assign(mvar, mk_proof_for(s, hint_and_pos.second, mvar_ctx, mvar_type));
} else {
if (curr_is_period()) {
display_proof_state_if_interactive(s);
show_tactic_prompt();
next();
}
expr mvar_val = parse_tactic_cmds(s, mvar_ctx, mvar_type);
menv->assign(mvar, mvar_val);
}
} catch (type_is_not_proposition_exception &) {
throw unsolved_metavar_exception(sstream() << "failed to synthesize metavar " << metavar_name(mvar) << ", it could not be synthesized by type inference and its type is not a proposition",
val);
}
}
return menv->instantiate_metavars(val);
}
}

View file

@ -1,37 +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 <utility>
#include "util/name.h"
#include "util/buffer.h"
#include "util/list.h"
#include "util/luaref.h"
#include "util/name_map.h"
#include "kernel/expr.h"
namespace lean {
typedef std::pair<unsigned, unsigned> pos_info;
/** \brief Parameter data */
struct parameter {
pos_info m_pos;
name m_name;
expr m_type;
bool m_implicit;
parameter(pos_info const & p, name const & n, expr const & t, bool i):m_pos(p), m_name(n), m_type(t), m_implicit(i) {}
parameter():m_pos(0, 0), m_implicit(false) {}
};
typedef buffer<parameter> parameter_buffer;
enum class macro_arg_kind { Expr, Exprs, Parameters, Id, Ids, Int, String, Comma, Assign, Tactic, Tactics };
struct macro {
list<macro_arg_kind> m_arg_kinds;
luaref m_fn;
unsigned m_precedence;
macro(list<macro_arg_kind> const & as, luaref const & fn, unsigned prec):m_arg_kinds(as), m_fn(fn), m_precedence(prec) {}
};
typedef name_map<macro> macros;
}

File diff suppressed because it is too large Load diff

View file

@ -1,19 +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 "util/sexpr/options.h"
#include "kernel/context.h"
#include "kernel/formatter.h"
namespace lean {
class frontend;
class environment;
formatter mk_pp_formatter(ro_environment const & env);
std::ostream & operator<<(std::ostream & out, frontend const & fe);
/** \brief Return true iff the given object is supported by this pretty printer. */
bool supported_by_pp(object const & obj);
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Copyright (c) 2013-2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
@ -7,161 +7,11 @@ Author: Leonardo de Moura
#include <sstream>
#include "util/lua.h"
#include "util/script_state.h"
#include "util/sexpr/options.h"
#include "kernel/io_state.h"
#include "library/kernel_bindings.h"
#include "library/io_state_stream.h"
#include "frontends/lean/parser.h"
#include "frontends/lean/frontend.h"
#include "frontends/lean/pp.h"
namespace lean {
/** \see parse_lean_expr */
static int parse_lean_expr_core(lua_State * L, rw_shared_environment const & env, io_state & st) {
char const * src = luaL_checkstring(L, 1);
std::istringstream in(src);
script_state S = to_script_state(L);
expr r;
S.exec_unprotected([&]() {
r = parse_expr(env, st, in, "[string]", &S);
});
return push_expr(L, r);
void open_frontend_lean(lua_State *) {
// TODO(Leo)
}
/** \see parse_lean_expr */
static int parse_lean_expr_core(lua_State * L, rw_shared_environment const & env) {
io_state * io = get_io_state(L);
if (io == nullptr) {
io_state s(get_global_options(L), mk_pp_formatter(env));
return parse_lean_expr_core(L, env, s);
} else {
return parse_lean_expr_core(L, env, *io);
}
}
static int parse_lean_expr(lua_State * L) {
/*
The parse_lean_expr function in the Lua API supports different calling arguments:
1. (string) : simplest form, it is just a string in Lean default syntax.
The string is parsed using the global environment in the Lua registry.
If the Lua registry does not contain an environment, then commands fails.
It also uses the global state object in the registry. If there is no
state object, it will tries to create one using the global options and formatter
in the registry.
It returns a Lean expression.
2. (string, env) : similar to the previous one, but the environment is explicitly
provided.
3. (string, env, options, formatter?) : Everything is explicitly provided in this
version. We also support a variation where the formmater is omitted.
*/
int nargs = get_nonnil_top(L);
if (nargs == 1) {
rw_shared_environment env(L); // get global environment
return parse_lean_expr_core(L, env);
} else {
rw_shared_environment env(L, 2);
if (nargs == 2) {
return parse_lean_expr_core(L, env);
} else {
options opts = to_options(L, 3);
formatter fmt = nargs == 3 ? mk_pp_formatter(env) : to_formatter(L, 4);
io_state st(opts, fmt);
return parse_lean_expr_core(L, env, st);
}
}
}
/** \see parse_lean_cmds */
static void parse_lean_cmds_core(lua_State * L, rw_shared_environment const & env, io_state & st) {
char const * src = luaL_checkstring(L, 1);
std::istringstream in(src);
script_state S = to_script_state(L);
S.exec_unprotected([&]() {
parse_commands(env, st, in, "[string]", &S);
});
}
/** \see parse_lean_cmds */
static void parse_lean_cmds_core(lua_State * L, rw_shared_environment const & env) {
io_state * io = get_io_state(L);
if (io == nullptr) {
io_state s(get_global_options(L), mk_pp_formatter(env));
parse_lean_cmds_core(L, env, s);
set_global_options(L, s.get_options());
} else {
parse_lean_cmds_core(L, env, *io);
set_global_options(L, io->get_options());
}
}
static int parse_lean_cmds(lua_State * L) {
/*
The parse_lean_cmds function reads a sequence of Lean commands from the input string.
The supported calling arguments are equal to the ones used in parse_lean_expr.
The main difference is the function result. When calling with explicit options
the function returns an updated set of options. Otherwise it does not return anything.
*/
int nargs = get_nonnil_top(L);
if (nargs == 1) {
rw_shared_environment env(L); // get global environment
parse_lean_cmds_core(L, env);
return 0;
} else {
rw_shared_environment env(L, 2);
if (nargs == 2) {
parse_lean_cmds_core(L, env);
return 0;
} else {
options opts = to_options(L, 3);
formatter fmt = nargs == 3 ? mk_pp_formatter(env) : to_formatter(L, 4);
io_state st(opts, fmt);
parse_lean_cmds_core(L, env, st);
push_options(L, st.get_options());
return 1;
}
}
}
static bool g_default_trust_imported = false;
void set_default_trust_imported_for_lua(bool f) {
g_default_trust_imported = f;
}
static int mk_environment(lua_State * L) {
int nargs = lua_gettop(L);
environment env;
if (nargs == 0)
env->set_trusted_imported(g_default_trust_imported);
else
env->set_trusted_imported(lua_toboolean(L, 1));
io_state ios = init_frontend(env);
return push_environment(L, env);
}
static int mk_lean_formatter(lua_State * L) {
return push_formatter(L, mk_pp_formatter(to_environment(L, 1)));
}
static int is_explicit(lua_State * L) {
ro_shared_environment env(L, 1);
lua_pushboolean(L, is_explicit(env, to_name_ext(L, 2)));
return 1;
}
void open_frontend_lean(lua_State * L) {
open_macros(L);
SET_GLOBAL_FUN(mk_environment, "environment");
SET_GLOBAL_FUN(mk_lean_formatter, "lean_formatter");
SET_GLOBAL_FUN(parse_lean_expr, "parse_lean");
SET_GLOBAL_FUN(parse_lean_cmds, "parse_lean_cmds");
SET_GLOBAL_FUN(is_explicit, "is_explicit");
}
void register_frontend_lean_module() {
script_state::register_module(open_frontend_lean);
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Copyright (c) 2013-2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
@ -9,5 +9,4 @@ Author: Leonardo de Moura
namespace lean {
void open_frontend_lean(lua_State * L);
void register_frontend_lean_module();
void set_default_trust_imported_for_lua(bool f);
}

View file

@ -1,487 +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 <cstdio>
#include <string>
#include <algorithm>
#include "util/debug.h"
#include "util/exception.h"
#include "frontends/lean/scanner.h"
namespace lean {
static name g_lambda_unicode("\u03BB");
static name g_pi_unicode("\u2200");
static name g_arrow_unicode("\u2192");
static name g_lambda_name("fun");
static name g_type_name("Type");
static name g_pi_name("forall");
static name g_let_name("let");
static name g_in_name("in");
static name g_heq_name("==");
static name g_arrow_name("->");
static name g_exists_name("exists");
static name g_Exists_name("Exists");
static name g_exists_unicode("\u2203");
static name g_placeholder_name("_");
static name g_using_name("using");
static name g_have_name("have");
static name g_show_name("show");
static name g_by_name("by");
static name g_sig_name("sig");
static name g_pair_name("pair");
static name g_proj1_name("proj1");
static name g_proj2_name("proj2");
static name g_cartesian_product_unicode("\u2A2F");
static name g_cartesian_product("#");
static char g_normalized[256];
/** \brief Auxiliary class for initializing global variable \c g_normalized. */
class init_normalized_table {
void set(int i, char v) { g_normalized[static_cast<unsigned char>(i)] = v; }
public:
init_normalized_table() {
// by default all characters are in group c,
for (int i = 0; i <= 255; i++)
set(i, 'c');
// digits normalize to '0'
for (int i = '0'; i <= '9'; i++)
set(i, '0');
// characters that can be used to create ids of group a
for (int i = 'a'; i <= 'z'; i++)
set(i, 'a');
for (int i = 'A'; i <= 'Z'; i++)
set(i, 'a');
set('_', 'a');
set('\'', 'a');
set('@', 'a');
set('-', '-');
// characters that can be used to create ids of group b
for (unsigned char b : {'=', '<', '>', '^', '|', '&', '~', '+', '*', '/', '\\', '$', '%', '?', ';', '[', ']', '#'})
set(b, 'b');
// punctuation
set('(', '(');
set(')', ')');
set('{', '{');
set('}', '}');
set(':', ':');
set('.', '.');
set(',', ',');
// spaces
set(' ', ' ');
set('\t', ' ');
set('\r', ' ');
// new line
set('\n', '\n');
// double quotes for strings
set('\"', '\"');
// eof
set(255, -1);
}
};
static init_normalized_table g_init_normalized_table;
char normalize(char c) {
return g_normalized[static_cast<unsigned char>(c)];
}
scanner::scanner(std::istream& stream, char const * strm_name):
m_spos(0),
m_curr(0),
m_line(1),
m_pos(0),
m_stream(stream),
m_stream_name(strm_name),
m_script_line(1),
m_script_pos(0) {
next();
}
scanner::~scanner() {
}
void scanner::add_command_keyword(name const & n) {
m_commands = cons(n, m_commands);
}
void scanner::throw_exception(char const * msg) {
throw parser_exception(msg, m_stream_name.c_str(), m_line, m_spos);
}
void scanner::next() {
lean_assert(m_curr != EOF);
m_curr = m_stream.get();
m_spos++;
}
bool scanner::check_next(char c) {
lean_assert(m_curr != EOF);
bool r = m_stream.get() == c;
m_stream.unget();
return r;
}
bool scanner::check_next_is_digit() {
lean_assert(m_curr != EOF);
char c = m_stream.get();
bool r = '0' <= c && c <= '9';
m_stream.unget();
return r;
}
void scanner::read_single_line_comment() {
while (true) {
if (curr() == '\n') {
new_line();
next();
return;
} else if (curr() == EOF) {
return;
} else {
next();
}
}
}
bool scanner::is_command(name const & n) const {
return std::any_of(m_commands.begin(), m_commands.end(), [&](name const & c) { return c == n; });
}
/** \brief Auxiliary function for #read_a_symbol */
name scanner::mk_name(name const & curr, std::string const & buf, bool only_digits) {
if (curr.is_anonymous()) {
lean_assert(!only_digits);
return name(buf.c_str());
} else if (only_digits) {
mpz val(buf.c_str());
if (!val.is_unsigned_int())
throw_exception("invalid hierarchical name, numeral is too big");
return name(curr, val.get_unsigned_int());
} else {
return name(curr, buf.c_str());
}
}
scanner::token scanner::read_a_symbol() {
lean_assert(normalize(curr()) == 'a');
m_buffer.clear();
m_buffer += curr();
m_name_val = name();
next();
bool only_digits = false;
while (true) {
if (normalize(curr()) == 'a') {
if (only_digits)
throw_exception("invalid hierarchical name, digit expected");
m_buffer += curr();
next();
} else if (normalize(curr()) == '0') {
m_buffer += curr();
next();
} else if (curr() == ':' && check_next(':')) {
next();
lean_assert(curr() == ':');
next();
m_name_val = mk_name(m_name_val, m_buffer, only_digits);
m_buffer.clear();
only_digits = (normalize(curr()) == '0');
} else {
m_name_val = mk_name(m_name_val, m_buffer, only_digits);
if (m_name_val == g_lambda_name) {
return token::Lambda;
} else if (m_name_val == g_pi_name) {
return token::Pi;
} else if (m_name_val == g_exists_name) {
return token::Exists;
} else if (m_name_val == g_type_name) {
return token::Type;
} else if (m_name_val == g_let_name) {
return token::Let;
} else if (m_name_val == g_in_name) {
return token::In;
} else if (m_name_val == g_sig_name) {
return token::Sig;
} else if (m_name_val == g_pair_name) {
return token::Pair;
} else if (m_name_val == g_proj1_name) {
return token::Proj1;
} else if (m_name_val == g_proj2_name) {
return token::Proj2;
} else if (m_name_val == g_placeholder_name) {
return token::Placeholder;
} else if (m_name_val == g_have_name) {
return token::Have;
} else if (m_name_val == g_show_name) {
return token::Show;
} else if (m_name_val == g_by_name) {
return token::By;
} else if (m_name_val == g_Exists_name) {
m_name_val = g_exists_name;
return token::Id;
} else {
return is_command(m_name_val) ? token::CommandId : token::Id;
}
}
}
}
scanner::token scanner::read_b_symbol(char prev) {
lean_assert(normalize(curr()) == 'b' || curr() == '-');
m_buffer.clear();
if (prev != 0)
m_buffer += prev;
m_buffer += curr();
next();
while (true) {
if (normalize(curr()) == 'b' || curr() == '-') {
m_buffer += curr();
next();
} else {
m_name_val = name(m_buffer.c_str());
if (m_name_val == g_arrow_name)
return token::Arrow;
else if (m_name_val == g_cartesian_product)
return token::CartesianProduct;
else if (m_name_val == g_heq_name)
return token::HEq;
else
return token::Id;
}
}
}
scanner::token scanner::read_c_symbol() {
lean_assert(normalize(curr()) == 'c');
m_buffer.clear();
m_buffer += curr();
next();
while (true) {
if (normalize(curr()) == 'c') {
m_buffer += curr();
next();
} else {
m_name_val = name(m_buffer.c_str());
if (m_name_val == g_arrow_unicode)
return token::Arrow;
if (m_name_val == g_cartesian_product_unicode)
return token::CartesianProduct;
else if (m_name_val == g_lambda_unicode)
return token::Lambda;
else if (m_name_val == g_pi_unicode)
return token::Pi;
else if (m_name_val == g_exists_unicode)
return token::Exists;
else
return token::Id;
}
}
}
scanner::token scanner::read_number(bool pos) {
lean_assert('0' <= curr() && curr() <= '9');
mpq q(1);
m_num_val = curr() - '0';
next();
bool is_decimal = false;
while (true) {
char c = curr();
if ('0' <= c && c <= '9') {
m_num_val = 10*m_num_val + (c - '0');
if (is_decimal)
q *= 10;
next();
} else if (c == '.') {
// Num. is not a decimal. It should be at least Num.0
if (check_next_is_digit()) {
if (is_decimal)
break;
is_decimal = true;
next();
} else {
break;
}
} else {
break;
}
}
if (is_decimal)
m_num_val /= q;
if (!pos)
m_num_val.neg();
return is_decimal ? token::DecimalVal : token::IntVal;
}
scanner::token scanner::read_string() {
lean_assert(curr() == '\"');
next();
m_buffer.clear();
while (true) {
char c = curr();
if (c == EOF) {
throw_exception("unexpected end of string");
} else if (c == '\"') {
next();
return token::StringVal;
} else if (c == '\n') {
new_line();
} else if (c == '\\') {
next();
c = curr();
if (c == EOF)
throw_exception("unexpected end of string");
if (c != '\\' && c != '\"' && c != 'n')
throw_exception("invalid escape sequence");
if (c == 'n')
c = '\n';
}
m_buffer += c;
next();
}
}
scanner::token scanner::read_script_block() {
m_script_line = m_line;
m_script_pos = m_pos;
m_buffer.clear();
while (true) {
char c1 = curr();
if (c1 == EOF)
throw_exception("unexpected end of script");
next();
if (c1 == '*') {
char c2 = curr();
if (c2 == EOF)
throw_exception("unexpected end of script");
next();
if (c2 == ')') {
return token::ScriptBlock;
} else {
if (c2 == '\n')
new_line();
m_buffer += c1;
m_buffer += c2;
}
} else {
if (c1 == '\n')
new_line();
m_buffer += c1;
}
}
}
scanner::token scanner::scan() {
while (true) {
char c = curr();
m_pos = m_spos;
switch (normalize(c)) {
case ' ': next(); break;
case '\n': next(); new_line(); break;
case ':': next();
if (curr() == '=') {
next();
return token::Assign;
} else {
return token::Colon;
}
case ',': next(); return token::Comma;
case '.':
next();
if (curr() == '.') {
next();
if (curr() != '.')
throw_exception("invalid character sequence, '...' ellipsis expected");
next();
return token::Ellipsis;
} else {
return token::Period;
}
case '(':
next();
if (curr() == '*') {
next();
return read_script_block();
} else {
return token::LeftParen;
}
case ')': next(); return token::RightParen;
case '{': next(); return token::LeftCurlyBracket;
case '}': next(); return token::RightCurlyBracket;
case 'a': return read_a_symbol();
case 'b': return read_b_symbol(0);
case 'c': return read_c_symbol();
case '-':
next();
if (normalize(curr()) == '0') {
return read_number(false);
} else if (curr() == '-') {
read_single_line_comment();
break;
} else if (normalize(curr()) == 'b') {
return read_b_symbol('-');
} else {
m_name_val = name("-");
return token::Id;
}
break;
case '0': return read_number(true);
case '\"': return read_string();
case -1: return token::Eof;
default: lean_unreachable(); // LCOV_EXCL_LINE
}
}
}
std::ostream & operator<<(std::ostream & out, scanner::token const & t) {
switch (t) {
case scanner::token::LeftParen: out << "("; break;
case scanner::token::RightParen: out << ")"; break;
case scanner::token::LeftCurlyBracket: out << "{"; break;
case scanner::token::RightCurlyBracket: out << "}"; break;
case scanner::token::Colon: out << ":"; break;
case scanner::token::Comma: out << ","; break;
case scanner::token::Period: out << "."; break;
case scanner::token::Lambda: out << g_lambda_unicode; break;
case scanner::token::Pi: out << g_pi_unicode; break;
case scanner::token::Exists: out << g_exists_unicode; break;
case scanner::token::Arrow: out << g_arrow_unicode; break;
case scanner::token::Let: out << "let"; break;
case scanner::token::In: out << "in"; break;
case scanner::token::Id: out << "Id"; break;
case scanner::token::CommandId: out << "CId"; break;
case scanner::token::IntVal: out << "Int"; break;
case scanner::token::DecimalVal: out << "Dec"; break;
case scanner::token::StringVal: out << "String"; break;
case scanner::token::Assign: out << ":="; break;
case scanner::token::Type: out << "Type"; break;
case scanner::token::Sig: out << "sig"; break;
case scanner::token::Proj1: out << "proj1"; break;
case scanner::token::Proj2: out << "proj2"; break;
case scanner::token::Pair: out << "pair"; break;
case scanner::token::Placeholder: out << "_"; break;
case scanner::token::ScriptBlock: out << "Script"; break;
case scanner::token::CartesianProduct: out << "#"; break;
case scanner::token::Have: out << "have"; break;
case scanner::token::Show: out << "show"; break;
case scanner::token::By: out << "by"; break;
case scanner::token::Ellipsis: out << "..."; break;
case scanner::token::HEq: out << "=="; break;
case scanner::token::Eof: out << "EOF"; break;
}
return out;
}
}

View file

@ -1,80 +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 <iostream>
#include <vector>
#include <string>
#include "util/name.h"
#include "util/list.h"
#include "util/numerics/mpq.h"
namespace lean {
/**
\brief Lean scanner.
*/
class scanner {
public:
enum class token {
LeftParen, RightParen, LeftCurlyBracket, RightCurlyBracket, Colon, Comma, Period, Lambda, Pi, Arrow,
HEq, Sig, Pair, Proj1, Proj2, Let, In, Exists, Id, CommandId, IntVal, DecimalVal, StringVal, Assign, Type, Placeholder,
Have, Show, By, ScriptBlock, Ellipsis, CartesianProduct, Eof
};
protected:
int m_spos; // position in the current line of the stream
char m_curr; // current char;
int m_line; // line
int m_pos; // start position of the token
std::istream & m_stream;
std::string m_stream_name;
int m_script_line; // hack for saving beginning of script block line and pos
int m_script_pos;
mpq m_num_val;
name m_name_val;
std::string m_buffer;
list<name> m_commands;
void throw_exception(char const * msg);
char curr() const { return m_curr; }
void new_line() { m_line++; m_spos = 0; }
void next();
bool check_next(char c);
bool check_next_is_digit();
void read_single_line_comment();
name mk_name(name const & curr, std::string const & buf, bool only_digits);
token read_a_symbol();
token read_b_symbol(char prev);
token read_c_symbol();
token read_number(bool pos);
token read_string();
token read_script_block();
bool is_command(name const & n) const;
public:
scanner(std::istream& stream, char const * strm_name);
~scanner();
/** \brief Register a new command keyword. */
void add_command_keyword(name const & n);
void set_command_keywords(list<name> const & l) { m_commands = l; }
int get_line() const { return m_line; }
int get_pos() const { return m_pos; }
token scan();
name const & get_name_val() const { return m_name_val; }
mpq const & get_num_val() const { return m_num_val; }
std::string const & get_str_val() const { return m_buffer; }
int get_script_block_line() const { return m_script_line; }
int get_script_block_pos() const { return m_script_pos; }
};
std::ostream & operator<<(std::ostream & out, scanner::token const & t);
}

View file

@ -1,110 +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
*/
#ifdef LEAN_USE_READLINE
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <unistd.h>
#include <algorithm>
#endif
#include <string>
#include "frontends/lean/shell.h"
#include "frontends/lean/pp.h"
namespace lean {
#ifdef LEAN_USE_READLINE
// Hackish wrapper for implementing a streambuf on top of the readline library
class readline_streambuf : public std::streambuf {
std::string m_buffer;
std::streamsize m_curr;
std::streamsize m_high;
protected:
virtual int_type underflow() {
while (m_curr == m_high) {
char * line = readline("");
if (!line) {
// EOF received
return traits_type::eof();
} else if (strlen(line) == 0) {
// ignore blank line
m_buffer.push_back('\n');
free(line);
} else {
add_history(line);
m_buffer += line;
m_buffer.push_back('\n');
free(line);
m_high = m_buffer.size();
}
}
return traits_type::to_int_type(m_buffer[m_curr]);
}
virtual int_type uflow() {
int_type r = underflow();
if (r != traits_type::eof())
m_curr++;
return r;
}
virtual int_type pbackfail(int_type c) {
if (m_curr == 0)
return traits_type::eof();
m_curr--;
if (c != traits_type::eof())
m_buffer[m_curr] = c;
return traits_type::eof() + 1; // something different from eof()
}
virtual std::streamsize showmanyc() {
return m_high - m_curr;
}
public:
readline_streambuf():
m_curr(0), m_high(0) {
setbuf(0, 0);
}
};
struct readline_config {
FILE * m_devnull;
readline_config() {
// By default, the readline library echos the input in the standard output.
// We can change this behavior by setting rl_outstream to /dev/null
m_devnull = fopen("/dev/null", "a");
rl_outstream = m_devnull;
}
~readline_config() {
fclose(m_devnull);
}
};
static readline_config g_readline_config;
#endif
shell::shell(environment const & env, io_state const & ios, script_state * S):m_env(env), m_io_state(ios), m_script_state(S) {
}
shell::shell(environment const & env, script_state * S):m_env(env), m_io_state(mk_pp_formatter(m_env)), m_script_state(S) {
}
shell::~shell() {
}
bool shell::operator()() {
#ifdef LEAN_USE_READLINE
readline_streambuf buf;
std::istream is(&buf);
parser p(m_env, m_io_state, is, "[stdin]", m_script_state, false, true);
#else
parser p(m_env, m_io_state, std::cin, "[stdin]", m_script_state, false, true);
#endif
return p();
}
}

View file

@ -1,24 +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 "frontends/lean/parser.h"
namespace lean {
/** \brief Implements the Read Eval Print loop */
class shell {
environment m_env;
io_state m_io_state;
script_state * m_script_state;
public:
shell(environment const & env, io_state const & st, script_state * S);
shell(environment const & env, script_state * S);
~shell();
bool operator()();
io_state get_io_state() const { return m_io_state; }
};
}