Add comments to parser.cpp
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
ce43c1cbae
commit
b2c877b0c3
1 changed files with 208 additions and 26 deletions
|
@ -19,6 +19,8 @@ Author: Leonardo de Moura
|
||||||
#include "printer.h"
|
#include "printer.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
|
// ==========================================
|
||||||
|
// Builtin commands
|
||||||
static name g_definition_kwd("Definition");
|
static name g_definition_kwd("Definition");
|
||||||
static name g_variable_kwd("Variable");
|
static name g_variable_kwd("Variable");
|
||||||
static name g_theorem_kwd("Theorem");
|
static name g_theorem_kwd("Theorem");
|
||||||
|
@ -36,6 +38,11 @@ static name g_postfix_kwd("Postfix");
|
||||||
static name g_mixfixl_kwd("Mixfixl");
|
static name g_mixfixl_kwd("Mixfixl");
|
||||||
static name g_mixfixr_kwd("Mixfixr");
|
static name g_mixfixr_kwd("Mixfixr");
|
||||||
static name g_mixfixc_kwd("Mixfixc");
|
static name g_mixfixc_kwd("Mixfixc");
|
||||||
|
/** \brief Table/List with all builtin command keywords */
|
||||||
|
static list<name> g_command_keywords = {g_definition_kwd, g_variable_kwd, g_theorem_kwd, g_axiom_kwd, g_universe_kwd, g_eval_kwd,
|
||||||
|
g_show_kwd, g_check_kwd, g_env_kwd, g_infix_kwd, g_infixl_kwd, g_infixr_kwd, g_prefix_kwd,
|
||||||
|
g_postfix_kwd, g_mixfixl_kwd, g_mixfixr_kwd, g_mixfixc_kwd};
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// Support for parsing levels
|
// Support for parsing levels
|
||||||
|
@ -46,13 +53,22 @@ static unsigned g_level_plus_prec = 10;
|
||||||
static unsigned g_level_cup_prec = 5;
|
static unsigned g_level_cup_prec = 5;
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Auxiliary constant used to mark applications of overloaded operators.
|
||||||
static name g_overload_name(name(name(name(0u), "parser"), "overload"));
|
static name g_overload_name(name(name(name(0u), "parser"), "overload"));
|
||||||
static expr g_overload = mk_constant(g_overload_name);
|
static expr g_overload = mk_constant(g_overload_name);
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
static list<name> g_command_keywords = {g_definition_kwd, g_variable_kwd, g_theorem_kwd, g_axiom_kwd, g_universe_kwd, g_eval_kwd, g_show_kwd, g_check_kwd, g_env_kwd, g_infix_kwd, g_infixl_kwd, g_infixr_kwd, g_prefix_kwd, g_postfix_kwd, g_mixfixl_kwd, g_mixfixr_kwd, g_mixfixc_kwd};
|
/**
|
||||||
|
\brief Functional object for parsing
|
||||||
|
|
||||||
/** \brief Functional object for parsing */
|
\remark It is an instance of a Pratt parser
|
||||||
struct parser_fn {
|
(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_fn {
|
||||||
typedef scoped_map<name, unsigned, name_hash, name_eq> local_decls;
|
typedef scoped_map<name, unsigned, name_hash, name_eq> local_decls;
|
||||||
typedef std::unordered_map<name, expr, name_hash, name_eq> builtins;
|
typedef std::unordered_map<name, expr, name_hash, name_eq> builtins;
|
||||||
frontend m_frontend;
|
frontend m_frontend;
|
||||||
|
@ -72,6 +88,10 @@ struct parser_fn {
|
||||||
parser_error(char const * msg):exception(msg) {}
|
parser_error(char const * msg):exception(msg) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Auxiliar struct for creating/destroying a new scope for
|
||||||
|
local declarations.
|
||||||
|
*/
|
||||||
struct mk_scope {
|
struct mk_scope {
|
||||||
parser_fn & m_fn;
|
parser_fn & m_fn;
|
||||||
local_decls::mk_scope m_scope;
|
local_decls::mk_scope m_scope;
|
||||||
|
@ -80,16 +100,27 @@ struct parser_fn {
|
||||||
~mk_scope() { m_fn.m_num_local_decls = m_old_num_local_decls; }
|
~mk_scope() { m_fn.m_num_local_decls = m_old_num_local_decls; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** \brief Read the next token. */
|
||||||
void scan() { m_curr = m_scanner.scan(); }
|
void scan() { m_curr = m_scanner.scan(); }
|
||||||
|
/** \brief Return the current token */
|
||||||
scanner::token curr() const { return m_curr; }
|
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(); }
|
void next() { if (m_curr != scanner::token::Eof) scan(); }
|
||||||
|
/** \brief Keep consume tokens until we find a Command or End-of-file. */
|
||||||
void sync() {
|
void sync() {
|
||||||
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof)
|
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof)
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Return the name associated with the current token. */
|
||||||
name const & curr_name() const { return m_scanner.get_name_val(); }
|
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(); }
|
mpq const & curr_num() const { return m_scanner.get_num_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) {
|
void check_next(scanner::token t, char const * msg) {
|
||||||
if (curr() == t)
|
if (curr() == t)
|
||||||
next();
|
next();
|
||||||
|
@ -97,23 +128,42 @@ struct parser_fn {
|
||||||
throw parser_error(msg);
|
throw parser_error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Return true iff the current toke is an identifier */
|
||||||
bool curr_is_identifier() const { return curr() == scanner::token::Id; }
|
bool curr_is_identifier() const { return curr() == scanner::token::Id; }
|
||||||
|
/** \brief Return true iff the current toke is an integer */
|
||||||
bool curr_is_int() const { return curr() == scanner::token::IntVal; }
|
bool curr_is_int() const { return curr() == scanner::token::IntVal; }
|
||||||
|
/** \brief Return true iff the current toke is a '(' */
|
||||||
bool curr_is_lparen() const { return curr() == scanner::token::LeftParen; }
|
bool curr_is_lparen() const { return curr() == scanner::token::LeftParen; }
|
||||||
|
/** \brief Return true iff the current toke is a ':' */
|
||||||
bool curr_is_colon() const { return curr() == scanner::token::Colon; }
|
bool curr_is_colon() const { return curr() == scanner::token::Colon; }
|
||||||
|
/** \brief Return true iff the current toke is a ',' */
|
||||||
bool curr_is_comma() const { return curr() == scanner::token::Comma; }
|
bool curr_is_comma() const { return curr() == scanner::token::Comma; }
|
||||||
|
/** \brief Return true iff the current toke is an 'in' token */
|
||||||
bool curr_is_in() const { return curr() == scanner::token::In; }
|
bool curr_is_in() const { return curr() == scanner::token::In; }
|
||||||
|
|
||||||
|
/** \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); }
|
void check_identifier(char const * msg) { if (!curr_is_identifier()) throw parser_error(msg); }
|
||||||
|
/**
|
||||||
|
\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; }
|
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_colon_next(char const * msg) { check_next(scanner::token::Colon, msg); }
|
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); }
|
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_lparen_next(char const * msg) { check_next(scanner::token::LeftParen, msg); }
|
void check_lparen_next(char const * msg) { check_next(scanner::token::LeftParen, 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); }
|
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_assign_next(char const * msg) { check_next(scanner::token::Assign, msg); }
|
void check_assign_next(char const * msg) { check_next(scanner::token::Assign, msg); }
|
||||||
|
/** \brief Throws a parser error if the current token is not an identifier named \c op. */
|
||||||
void check_name(name const & op, char const * msg) { if(!curr_is_identifier() || curr_name() != op) throw parser_error(msg); }
|
void check_name(name const & op, char const * msg) { if(!curr_is_identifier() || curr_name() != op) throw parser_error(msg); }
|
||||||
|
/** \brief Throws a parser error if the current token is not an identifier named \c op. If it is, move to the next token. */
|
||||||
void check_name_next(name const & op, char const * msg) { check_name(op, msg); next(); }
|
void check_name_next(name const & op, char const * msg) { check_name(op, msg); next(); }
|
||||||
|
|
||||||
|
/** \brief Initialize \c m_builtins table with Lean builtin symbols that do not have notation associated with them. */
|
||||||
void init_builtins() {
|
void init_builtins() {
|
||||||
m_builtins["Bool"] = Bool;
|
m_builtins["Bool"] = Bool;
|
||||||
m_builtins["true"] = True;
|
m_builtins["true"] = True;
|
||||||
|
@ -123,27 +173,15 @@ struct parser_fn {
|
||||||
m_builtins["Int"] = Int;
|
m_builtins["Int"] = Int;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser_fn(frontend & fe, std::istream & in, std::ostream * out, std::ostream * err, bool use_exceptions):
|
|
||||||
m_frontend(fe),
|
|
||||||
m_scanner(in),
|
|
||||||
m_formatter(mk_pp_formatter(fe)),
|
|
||||||
m_out(out),
|
|
||||||
m_err(err),
|
|
||||||
m_use_exceptions(use_exceptions) {
|
|
||||||
m_found_errors = false;
|
|
||||||
m_num_local_decls = 0;
|
|
||||||
m_scanner.set_command_keywords(g_command_keywords);
|
|
||||||
init_builtins();
|
|
||||||
scan();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ noreturn ]] void not_implemented_yet() {
|
[[ noreturn ]] void not_implemented_yet() {
|
||||||
// TODO
|
// TODO
|
||||||
throw parser_error("not implemented yet");
|
throw parser_error("not implemented yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
/**
|
||||||
// Universe Level parsing
|
@name Parse Universe levels
|
||||||
|
*/
|
||||||
|
/*@{*/
|
||||||
level parse_level_max() {
|
level parse_level_max() {
|
||||||
next();
|
next();
|
||||||
buffer<level> lvls;
|
buffer<level> lvls;
|
||||||
|
@ -221,6 +259,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a universe level */
|
||||||
level parse_level(unsigned rbp = 0) {
|
level parse_level(unsigned rbp = 0) {
|
||||||
level left = parse_level_nud();
|
level left = parse_level_nud();
|
||||||
while (rbp < curr_level_lbp()) {
|
while (rbp < curr_level_lbp()) {
|
||||||
|
@ -228,8 +267,21 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
// ==========================================
|
/*@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@name Parse Expressions
|
||||||
|
*/
|
||||||
|
/*@{*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Return the function associated with the given operator.
|
||||||
|
If the operator has been overloaded, it returns an expression
|
||||||
|
of the form (overload f_k ... (overload f_2 f_1) ...)
|
||||||
|
where f_i's are different options.
|
||||||
|
After we finish parsing, the procedure #elaborate will
|
||||||
|
resolve/decide which f_i should be used.
|
||||||
|
*/
|
||||||
expr mk_fun(operator_info const & op) {
|
expr mk_fun(operator_info const & op) {
|
||||||
list<name> const & fs = op.get_internal_names();
|
list<name> const & fs = op.get_internal_names();
|
||||||
lean_assert(!is_nil(fs));
|
lean_assert(!is_nil(fs));
|
||||||
|
@ -237,38 +289,49 @@ struct parser_fn {
|
||||||
expr r = mk_constant(*it);
|
expr r = mk_constant(*it);
|
||||||
++it;
|
++it;
|
||||||
for (; it != fs.end(); ++it)
|
for (; it != fs.end(); ++it)
|
||||||
r = mk_app(g_overload, r, mk_constant(*it));
|
r = mk_app(g_overload, mk_constant(*it), r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a user defined prefix operator. */
|
||||||
expr parse_prefix(operator_info const & op) {
|
expr parse_prefix(operator_info const & op) {
|
||||||
expr arg = parse_expr(op.get_precedence());
|
expr arg = parse_expr(op.get_precedence());
|
||||||
return mk_app(mk_fun(op), arg);
|
return mk_app(mk_fun(op), arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a user defined postfix operator. */
|
||||||
expr parse_postfix(expr const & left, operator_info const & op) {
|
expr parse_postfix(expr const & left, operator_info const & op) {
|
||||||
return mk_app(mk_fun(op), left);
|
return mk_app(mk_fun(op), left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a user defined infix operator. */
|
||||||
expr parse_infix(expr const & left, operator_info const & op) {
|
expr parse_infix(expr const & left, operator_info const & op) {
|
||||||
expr right = parse_expr(op.get_precedence()+1);
|
expr right = parse_expr(op.get_precedence()+1);
|
||||||
return mk_app(mk_fun(op), left, right);
|
return mk_app(mk_fun(op), left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a user defined infix-left operator. */
|
||||||
expr parse_infixl(expr const & left, operator_info const & op) {
|
expr parse_infixl(expr const & left, operator_info const & op) {
|
||||||
expr right = parse_expr(op.get_precedence());
|
expr right = parse_expr(op.get_precedence());
|
||||||
return mk_app(mk_fun(op), left, right);
|
return mk_app(mk_fun(op), left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a user defined infix-right operator. */
|
||||||
expr parse_infixr(expr const & left, operator_info const & op) {
|
expr parse_infixr(expr const & left, operator_info const & op) {
|
||||||
expr right = parse_expr(op.get_precedence()-1);
|
expr right = parse_expr(op.get_precedence()-1);
|
||||||
return mk_app(mk_fun(op), left, right);
|
return mk_app(mk_fun(op), left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Throws an error if the current token is not an identifier named \c op_part.
|
||||||
|
If it is, move to the next toke. The error message assumes
|
||||||
|
this method has been used when parsing mixfix operators.
|
||||||
|
*/
|
||||||
void check_op_part(name const & op_part) {
|
void check_op_part(name const & op_part) {
|
||||||
check_name_next(op_part, "invalid expression, identifier expected");
|
check_name_next(op_part, "invalid mixfix operator application, identifier expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Auxiliary function for #parse_mixfixl and #parse_mixfixr */
|
||||||
void parse_mixfix_args(list<name> const & ops, unsigned prec, buffer<expr> & args) {
|
void parse_mixfix_args(list<name> const & ops, unsigned prec, buffer<expr> & args) {
|
||||||
auto it = ops.begin();
|
auto it = ops.begin();
|
||||||
++it;
|
++it;
|
||||||
|
@ -279,6 +342,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse user defined mixfixl operator. It has the form: ID _ ... ID _ */
|
||||||
expr parse_mixfixl(operator_info const & op) {
|
expr parse_mixfixl(operator_info const & op) {
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
args.push_back(mk_fun(op));
|
args.push_back(mk_fun(op));
|
||||||
|
@ -287,6 +351,7 @@ struct parser_fn {
|
||||||
return mk_app(args.size(), args.data());
|
return mk_app(args.size(), args.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse user defined mixfixr operator. It has the form: _ ID ... _ ID */
|
||||||
expr parse_mixfixr(expr const & left, operator_info const & op) {
|
expr parse_mixfixr(expr const & left, operator_info const & op) {
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
args.push_back(mk_fun(op));
|
args.push_back(mk_fun(op));
|
||||||
|
@ -295,6 +360,7 @@ struct parser_fn {
|
||||||
return mk_app(args.size(), args.data());
|
return mk_app(args.size(), args.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse user defined mixfixc operator. It has the form: ID _ ID ... _ ID */
|
||||||
expr parse_mixfixc(operator_info const & op) {
|
expr parse_mixfixc(operator_info const & op) {
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
args.push_back(mk_fun(op));
|
args.push_back(mk_fun(op));
|
||||||
|
@ -311,6 +377,11 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\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.
|
||||||
|
*/
|
||||||
expr get_name_ref(name const & id) {
|
expr get_name_ref(name const & id) {
|
||||||
object const & obj = m_frontend.find_object(id);
|
object const & obj = m_frontend.find_object(id);
|
||||||
if (obj) {
|
if (obj) {
|
||||||
|
@ -330,6 +401,14 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse an identifier that has a "null denotation" (See
|
||||||
|
paper: "Top down operator precedence"). A nud identifier is a
|
||||||
|
token that appears at the beginning of a language construct.
|
||||||
|
In Lean, local declarations (i.e., local functions), user
|
||||||
|
defined prefix, mixfixl and mixfixc operators, and global
|
||||||
|
functions can begin a language construct.
|
||||||
|
*/
|
||||||
expr parse_nud_id() {
|
expr parse_nud_id() {
|
||||||
name id = curr_name();
|
name id = curr_name();
|
||||||
next();
|
next();
|
||||||
|
@ -351,6 +430,15 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse an identifier that has a "left denotation" (See
|
||||||
|
paper: "Top down operator precedence"). A left identifier is a
|
||||||
|
token that appears inside of a construct (to left of the rest
|
||||||
|
of the construct). In Lean, local declarations (i.e., function
|
||||||
|
application arguments), user defined infix, infixl, infixr,
|
||||||
|
mixfixr and global values (as function application arguments)
|
||||||
|
can appear inside of a construct.
|
||||||
|
*/
|
||||||
expr parse_led_id(expr const & left) {
|
expr parse_led_id(expr const & left) {
|
||||||
name id = curr_name();
|
name id = curr_name();
|
||||||
next();
|
next();
|
||||||
|
@ -373,18 +461,22 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse <tt>expr '=' expr</tt>. */
|
||||||
expr parse_eq(expr const & left) {
|
expr parse_eq(expr const & left) {
|
||||||
next();
|
next();
|
||||||
expr right = parse_expr(g_eq_precedence);
|
expr right = parse_expr(g_eq_precedence);
|
||||||
return mk_eq(left, right);
|
return mk_eq(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse <tt>expr '->' expr</tt>. */
|
||||||
expr parse_arrow(expr const & left) {
|
expr parse_arrow(expr const & left) {
|
||||||
next();
|
next();
|
||||||
|
// The -1 is a trick to get right associativity in Pratt's parsers
|
||||||
expr right = parse_expr(g_arrow_precedence-1);
|
expr right = parse_expr(g_arrow_precedence-1);
|
||||||
return mk_arrow(left, right);
|
return mk_arrow(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse <tt>'(' expr ')'</tt>. */
|
||||||
expr parse_lparen() {
|
expr parse_lparen() {
|
||||||
next();
|
next();
|
||||||
expr r = parse_expr();
|
expr r = parse_expr();
|
||||||
|
@ -392,6 +484,10 @@ struct parser_fn {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse a sequence of identifiers <tt>ID*</tt>. Store the
|
||||||
|
result in \c result.
|
||||||
|
*/
|
||||||
void parse_names(buffer<name> & result) {
|
void parse_names(buffer<name> & result) {
|
||||||
while (curr_is_identifier()) {
|
while (curr_is_identifier()) {
|
||||||
result.push_back(curr_name());
|
result.push_back(curr_name());
|
||||||
|
@ -399,6 +495,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Register the name \c n as a local declaration. */
|
||||||
void register_binding(name const & n) {
|
void register_binding(name const & n) {
|
||||||
unsigned lvl = m_num_local_decls;
|
unsigned lvl = m_num_local_decls;
|
||||||
m_local_decls.insert(n, lvl);
|
m_local_decls.insert(n, lvl);
|
||||||
|
@ -406,8 +503,11 @@ struct parser_fn {
|
||||||
lean_assert(m_local_decls.find(n)->second == lvl);
|
lean_assert(m_local_decls.find(n)->second == lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse <tt>ID ... ID ':' expr</tt>, where the expression
|
||||||
|
represents the type of the identifiers.
|
||||||
|
*/
|
||||||
void parse_simple_bindings(buffer<std::pair<name, expr>> & result) {
|
void parse_simple_bindings(buffer<std::pair<name, expr>> & result) {
|
||||||
// ID ... ID : type
|
|
||||||
buffer<name> names;
|
buffer<name> names;
|
||||||
parse_names(names);
|
parse_names(names);
|
||||||
check_colon_next("invalid binder, ':' expected");
|
check_colon_next("invalid binder, ':' expected");
|
||||||
|
@ -422,6 +522,11 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse a sequence of <tt>'(' ID ... ID ':' expr ')'</tt>.
|
||||||
|
|
||||||
|
This is used when parsing lambda, Pi, forall/exists expressions and definitions.
|
||||||
|
*/
|
||||||
void parse_bindings(buffer<std::pair<name, expr>> & result) {
|
void parse_bindings(buffer<std::pair<name, expr>> & result) {
|
||||||
if (curr_is_identifier()) {
|
if (curr_is_identifier()) {
|
||||||
parse_simple_bindings(result);
|
parse_simple_bindings(result);
|
||||||
|
@ -438,6 +543,10 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Create a lambda/Pi abstraction, using the giving binders
|
||||||
|
and body.
|
||||||
|
*/
|
||||||
expr mk_abstraction(bool is_lambda, buffer<std::pair<name, expr>> const & bindings, expr const & body) {
|
expr mk_abstraction(bool is_lambda, buffer<std::pair<name, expr>> const & bindings, expr const & body) {
|
||||||
expr result = body;
|
expr result = body;
|
||||||
unsigned i = bindings.size();
|
unsigned i = bindings.size();
|
||||||
|
@ -451,6 +560,7 @@ struct parser_fn {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse lambda/Pi abstraction. */
|
||||||
expr parse_abstraction(bool is_lambda) {
|
expr parse_abstraction(bool is_lambda) {
|
||||||
next();
|
next();
|
||||||
mk_scope scope(*this);
|
mk_scope scope(*this);
|
||||||
|
@ -461,14 +571,17 @@ struct parser_fn {
|
||||||
return mk_abstraction(is_lambda, bindings, result);
|
return mk_abstraction(is_lambda, bindings, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse lambda abstraction. */
|
||||||
expr parse_lambda() {
|
expr parse_lambda() {
|
||||||
return parse_abstraction(true);
|
return parse_abstraction(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse Pi abstraction. */
|
||||||
expr parse_pi() {
|
expr parse_pi() {
|
||||||
return parse_abstraction(false);
|
return parse_abstraction(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse Let expression. */
|
||||||
expr parse_let() {
|
expr parse_let() {
|
||||||
next();
|
next();
|
||||||
mk_scope scope(*this);
|
mk_scope scope(*this);
|
||||||
|
@ -494,6 +607,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse an integer value. */
|
||||||
expr parse_int() {
|
expr parse_int() {
|
||||||
expr r = mk_int_value(m_scanner.get_num_val().get_numerator());
|
expr r = mk_int_value(m_scanner.get_num_val().get_numerator());
|
||||||
next();
|
next();
|
||||||
|
@ -508,6 +622,7 @@ struct parser_fn {
|
||||||
not_implemented_yet();
|
not_implemented_yet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse <tt>'Type'</tt> and <tt>'Type' level</tt> expressions. */
|
||||||
expr parse_type() {
|
expr parse_type() {
|
||||||
next();
|
next();
|
||||||
if (curr_is_identifier() || curr_is_int()) {
|
if (curr_is_identifier() || curr_is_int()) {
|
||||||
|
@ -517,6 +632,9 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Auxiliary method used when processing the beginning of an expression.
|
||||||
|
*/
|
||||||
expr parse_nud() {
|
expr parse_nud() {
|
||||||
switch (curr()) {
|
switch (curr()) {
|
||||||
case scanner::token::Id: return parse_nud_id();
|
case scanner::token::Id: return parse_nud_id();
|
||||||
|
@ -533,6 +651,9 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Auxiliary method used when processing the 'inside' of an expression.
|
||||||
|
*/
|
||||||
expr parse_led(expr const & left) {
|
expr parse_led(expr const & left) {
|
||||||
switch (curr()) {
|
switch (curr()) {
|
||||||
case scanner::token::Id: return parse_led_id(left);
|
case scanner::token::Id: return parse_led_id(left);
|
||||||
|
@ -547,6 +668,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Return the binding power of the current token (when parsing expression). */
|
||||||
unsigned curr_lbp() {
|
unsigned curr_lbp() {
|
||||||
switch (curr()) {
|
switch (curr()) {
|
||||||
case scanner::token::Id: {
|
case scanner::token::Id: {
|
||||||
|
@ -572,6 +694,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse an expression */
|
||||||
expr parse_expr(unsigned rbp = 0) {
|
expr parse_expr(unsigned rbp = 0) {
|
||||||
expr left = parse_nud();
|
expr left = parse_nud();
|
||||||
while (rbp < curr_lbp()) {
|
while (rbp < curr_lbp()) {
|
||||||
|
@ -579,12 +702,18 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
expr elaborate(expr const & e) {
|
expr elaborate(expr const & e) {
|
||||||
// TODO
|
// TODO
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@name Parse Commands
|
||||||
|
*/
|
||||||
|
/*@{*/
|
||||||
|
/** \brief Auxiliary method used for parsing definitions and theorems. */
|
||||||
void parse_def_core(bool is_definition) {
|
void parse_def_core(bool is_definition) {
|
||||||
next();
|
next();
|
||||||
expr type, val;
|
expr type, val;
|
||||||
|
@ -611,14 +740,29 @@ struct parser_fn {
|
||||||
m_frontend.add_theorem(id, type, val);
|
m_frontend.add_theorem(id, type, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse a Definition. It has one of the following two forms:
|
||||||
|
|
||||||
|
1) 'Definition' ID ':' expr ':=' expr
|
||||||
|
|
||||||
|
2) 'Definition' ID bindings ':' expr ':=' expr
|
||||||
|
*/
|
||||||
void parse_definition() {
|
void parse_definition() {
|
||||||
parse_def_core(true);
|
parse_def_core(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse a Theorem. It has one of the following two forms:
|
||||||
|
|
||||||
|
1) 'Theorem' ID ':' expr ':=' expr
|
||||||
|
|
||||||
|
2) 'Theorem' ID bindings ':' expr ':=' expr
|
||||||
|
*/
|
||||||
void parse_theorem() {
|
void parse_theorem() {
|
||||||
parse_def_core(false);
|
parse_def_core(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse 'Variable' ID ':' expr */
|
||||||
void parse_variable() {
|
void parse_variable() {
|
||||||
next();
|
next();
|
||||||
name id = check_identifier_next("invalid variable declaration, identifier expected");
|
name id = check_identifier_next("invalid variable declaration, identifier expected");
|
||||||
|
@ -627,6 +771,7 @@ struct parser_fn {
|
||||||
m_frontend.add_var(id, type);
|
m_frontend.add_var(id, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse 'Axiom' ID ':' expr */
|
||||||
void parse_axiom() {
|
void parse_axiom() {
|
||||||
next();
|
next();
|
||||||
name id = check_identifier_next("invalid axiom, identifier expected");
|
name id = check_identifier_next("invalid axiom, identifier expected");
|
||||||
|
@ -635,6 +780,7 @@ struct parser_fn {
|
||||||
m_frontend.add_axiom(id, type);
|
m_frontend.add_axiom(id, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse 'Eval' expr */
|
||||||
void parse_eval() {
|
void parse_eval() {
|
||||||
next();
|
next();
|
||||||
expr v = elaborate(parse_expr());
|
expr v = elaborate(parse_expr());
|
||||||
|
@ -642,12 +788,14 @@ struct parser_fn {
|
||||||
(*m_out) << m_formatter(r) << std::endl;
|
(*m_out) << m_formatter(r) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse 'Show' expr */
|
||||||
void parse_show() {
|
void parse_show() {
|
||||||
next();
|
next();
|
||||||
expr v = elaborate(parse_expr());
|
expr v = elaborate(parse_expr());
|
||||||
(*m_out) << m_formatter(v) << std::endl;
|
(*m_out) << m_formatter(v) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse 'Check' expr */
|
||||||
void parse_check() {
|
void parse_check() {
|
||||||
next();
|
next();
|
||||||
expr v = elaborate(parse_expr());
|
expr v = elaborate(parse_expr());
|
||||||
|
@ -655,12 +803,14 @@ struct parser_fn {
|
||||||
(*m_out) << m_formatter(t) << std::endl;
|
(*m_out) << m_formatter(t) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse 'Environment' */
|
||||||
void parse_env() {
|
void parse_env() {
|
||||||
next();
|
next();
|
||||||
(*m_out) << m_frontend << std::endl;
|
(*m_out) << m_frontend << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned parse_precision() {
|
/** \brief Return the (optional) precedence of a user-defined operator. */
|
||||||
|
unsigned parse_precedence() {
|
||||||
if (curr() == scanner::token::IntVal) {
|
if (curr() == scanner::token::IntVal) {
|
||||||
mpz pval = curr_num().get_numerator();
|
mpz pval = curr_num().get_numerator();
|
||||||
if (!pval.is_unsigned_int())
|
if (!pval.is_unsigned_int())
|
||||||
|
@ -673,13 +823,20 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Throw an error if the current token is not an identifier. If it is, move to next token. */
|
||||||
name parse_op_id() {
|
name parse_op_id() {
|
||||||
return check_identifier_next("invalid operator definition, identifier expected");
|
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 parse_op(fixity fx) {
|
void parse_op(fixity fx) {
|
||||||
next();
|
next();
|
||||||
unsigned prec = parse_precision();
|
unsigned prec = parse_precedence();
|
||||||
name op_id = parse_op_id();
|
name op_id = parse_op_id();
|
||||||
name id = parse_op_id();
|
name id = parse_op_id();
|
||||||
switch (fx) {
|
switch (fx) {
|
||||||
|
@ -692,9 +849,15 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Parse mixfix/mixfixl/mixfixr user operator definition.
|
||||||
|
These definitions have the form:
|
||||||
|
|
||||||
|
- fixity [NUM] ID ID+ ID
|
||||||
|
*/
|
||||||
void parse_mixfix_op(fixity fx) {
|
void parse_mixfix_op(fixity fx) {
|
||||||
next();
|
next();
|
||||||
unsigned prec = parse_precision();
|
unsigned prec = parse_precedence();
|
||||||
buffer<name> parts;
|
buffer<name> parts;
|
||||||
parts.push_back(parse_op_id());
|
parts.push_back(parse_op_id());
|
||||||
parts.push_back(parse_op_id());
|
parts.push_back(parse_op_id());
|
||||||
|
@ -712,6 +875,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a Lean command. */
|
||||||
void parse_command() {
|
void parse_command() {
|
||||||
name const & cmd_id = curr_name();
|
name const & cmd_id = curr_name();
|
||||||
if (cmd_id == g_definition_kwd) parse_definition();
|
if (cmd_id == g_definition_kwd) parse_definition();
|
||||||
|
@ -732,6 +896,7 @@ struct parser_fn {
|
||||||
else if (cmd_id == g_mixfixc_kwd) parse_mixfix_op(fixity::Mixfixc);
|
else if (cmd_id == g_mixfixc_kwd) parse_mixfix_op(fixity::Mixfixc);
|
||||||
else { lean_unreachable(); }
|
else { lean_unreachable(); }
|
||||||
}
|
}
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
void error(char const * msg, unsigned line, unsigned pos) {
|
void error(char const * msg, unsigned line, unsigned pos) {
|
||||||
lean_assert(m_err);
|
lean_assert(m_err);
|
||||||
|
@ -743,6 +908,22 @@ struct parser_fn {
|
||||||
sync();
|
sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
parser_fn(frontend & fe, std::istream & in, std::ostream * out, std::ostream * err, bool use_exceptions):
|
||||||
|
m_frontend(fe),
|
||||||
|
m_scanner(in),
|
||||||
|
m_formatter(mk_pp_formatter(fe)),
|
||||||
|
m_out(out),
|
||||||
|
m_err(err),
|
||||||
|
m_use_exceptions(use_exceptions) {
|
||||||
|
m_found_errors = false;
|
||||||
|
m_num_local_decls = 0;
|
||||||
|
m_scanner.set_command_keywords(g_command_keywords);
|
||||||
|
init_builtins();
|
||||||
|
scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Parse a sequence of commands. This method also perform error management. */
|
||||||
bool parse_commands() {
|
bool parse_commands() {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
@ -771,6 +952,7 @@ struct parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Parse an expression. */
|
||||||
expr parse_expr_main() {
|
expr parse_expr_main() {
|
||||||
try {
|
try {
|
||||||
return elaborate(parse_expr());
|
return elaborate(parse_expr());
|
||||||
|
|
Loading…
Reference in a new issue