Add interrupt to parser. Add elaborator to parser. Add placeholder support in the scanner.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
02b72acc2f
commit
ece6e6ca6a
9 changed files with 257 additions and 81 deletions
|
@ -18,6 +18,7 @@ Author: Leonardo de Moura
|
||||||
#include "expr_maps.h"
|
#include "expr_maps.h"
|
||||||
#include "sstream.h"
|
#include "sstream.h"
|
||||||
#include "kernel_exception.h"
|
#include "kernel_exception.h"
|
||||||
|
#include "elaborator.h"
|
||||||
#include "lean_frontend.h"
|
#include "lean_frontend.h"
|
||||||
#include "lean_parser.h"
|
#include "lean_parser.h"
|
||||||
#include "lean_scanner.h"
|
#include "lean_scanner.h"
|
||||||
|
@ -31,7 +32,27 @@ Author: Leonardo de Moura
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LEAN_DEFAULT_PARSER_SHOW_ERRORS
|
||||||
|
#define LEAN_DEFAULT_PARSER_SHOW_ERRORS true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LEAN_DEFAULT_PARSER_VERBOSE
|
||||||
|
#define LEAN_DEFAULT_PARSER_VERBOSE true
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
|
// ==========================================
|
||||||
|
// Parser configuration options
|
||||||
|
static name g_parser_verbose {"lean", "parser", "verbose"};
|
||||||
|
static name g_parser_show_errors {"lean", "parser", "show_errors"};
|
||||||
|
|
||||||
|
RegisterBoolOption(g_parser_verbose, LEAN_DEFAULT_PARSER_VERBOSE, "(lean parser) disable/enable parser verbose messages");
|
||||||
|
RegisterBoolOption(g_parser_show_errors, LEAN_DEFAULT_PARSER_SHOW_ERRORS, "(lean parser) display error messages in the regular output channel");
|
||||||
|
|
||||||
|
bool get_parser_verbose(options const & opts) { return opts.get_bool(g_parser_verbose, LEAN_DEFAULT_PARSER_VERBOSE); }
|
||||||
|
bool get_parser_show_errors(options const & opts) { return opts.get_bool(g_parser_show_errors, LEAN_DEFAULT_PARSER_SHOW_ERRORS); }
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// Builtin commands
|
// Builtin commands
|
||||||
static name g_definition_kwd("Definition");
|
static name g_definition_kwd("Definition");
|
||||||
|
@ -72,19 +93,13 @@ 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 expr g_overload = mk_constant(g_overload_name);
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
// A name that can't be created by the user.
|
// A name that can't be created by the user.
|
||||||
// It is used as placeholder for parsing A -> B expressions which
|
// It is used as placeholder for parsing A -> B expressions which
|
||||||
// are syntax sugar for (Pi (_ : A), B)
|
// are syntax sugar for (Pi (_ : A), B)
|
||||||
static name g_unused(name(0u), "parser");
|
static name g_unused(name(0u), "parser");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Functional object for parsing
|
\brief Actual implementation for the parser functional object
|
||||||
|
|
||||||
\remark It is an instance of a Pratt parser
|
\remark It is an instance of a Pratt parser
|
||||||
(http://en.wikipedia.org/wiki/Pratt_parser) described in the paper
|
(http://en.wikipedia.org/wiki/Pratt_parser) described in the paper
|
||||||
|
@ -92,13 +107,14 @@ static name g_unused(name(0u), "parser");
|
||||||
and it is easy to support user-defined infix/prefix/postfix/mixfix
|
and it is easy to support user-defined infix/prefix/postfix/mixfix
|
||||||
operators.
|
operators.
|
||||||
*/
|
*/
|
||||||
class parser_fn {
|
class parser::imp {
|
||||||
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;
|
||||||
typedef std::pair<unsigned, unsigned> pos_info;
|
typedef std::pair<unsigned, unsigned> pos_info;
|
||||||
typedef expr_map<pos_info> expr_pos_info;
|
typedef expr_map<pos_info> expr_pos_info;
|
||||||
frontend m_frontend;
|
frontend m_frontend;
|
||||||
scanner m_scanner;
|
scanner m_scanner;
|
||||||
|
elaborator m_elaborator;
|
||||||
scanner::token m_curr;
|
scanner::token m_curr;
|
||||||
bool m_use_exceptions;
|
bool m_use_exceptions;
|
||||||
bool m_interactive;
|
bool m_interactive;
|
||||||
|
@ -108,6 +124,12 @@ class parser_fn {
|
||||||
builtins m_builtins;
|
builtins m_builtins;
|
||||||
expr_pos_info m_expr_pos_info;
|
expr_pos_info m_expr_pos_info;
|
||||||
pos_info m_last_cmd_pos;
|
pos_info m_last_cmd_pos;
|
||||||
|
// Reference to temporary parser used to process import command.
|
||||||
|
// We need this reference to be able to interrupt it.
|
||||||
|
interruptable_ptr<parser> m_import_parser;
|
||||||
|
|
||||||
|
bool m_verbose;
|
||||||
|
bool m_show_errors;
|
||||||
|
|
||||||
/** \brief Exception used to track parsing erros, it does not leak outside of this class. */
|
/** \brief Exception used to track parsing erros, it does not leak outside of this class. */
|
||||||
struct parser_error : public exception {
|
struct parser_error : public exception {
|
||||||
|
@ -121,10 +143,10 @@ class parser_fn {
|
||||||
local declarations.
|
local declarations.
|
||||||
*/
|
*/
|
||||||
struct mk_scope {
|
struct mk_scope {
|
||||||
parser_fn & m_fn;
|
imp & m_fn;
|
||||||
local_decls::mk_scope m_scope;
|
local_decls::mk_scope m_scope;
|
||||||
unsigned m_old_num_local_decls;
|
unsigned m_old_num_local_decls;
|
||||||
mk_scope(parser_fn & fn):m_fn(fn), m_scope(fn.m_local_decls), m_old_num_local_decls(fn.m_num_local_decls) {}
|
mk_scope(imp & fn):m_fn(fn), m_scope(fn.m_local_decls), m_old_num_local_decls(fn.m_num_local_decls) {}
|
||||||
~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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,11 +171,6 @@ class parser_fn {
|
||||||
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. */
|
/** \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() {
|
|
||||||
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof)
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief Return the name associated with the current token. */
|
/** \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(); }
|
||||||
|
@ -364,7 +381,7 @@ class parser_fn {
|
||||||
expr r = *it;
|
expr r = *it;
|
||||||
++it;
|
++it;
|
||||||
for (; it != fs.end(); ++it)
|
for (; it != fs.end(); ++it)
|
||||||
r = mk_app(g_overload, *it, r);
|
r = mk_app(mk_overload_marker(), *it, r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,22 +784,36 @@ class parser_fn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Create a fresh metavariable. */
|
||||||
|
expr mk_metavar() {
|
||||||
|
// TODO
|
||||||
|
return expr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Parse \c _ a hole that must be filled by the elaborator. */
|
||||||
|
expr parse_placeholder() {
|
||||||
|
auto p = pos();
|
||||||
|
next();
|
||||||
|
return save(mk_metavar(), p);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Auxiliary method used when processing the beginning of an expression.
|
\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();
|
||||||
case scanner::token::LeftParen: return parse_lparen();
|
case scanner::token::LeftParen: return parse_lparen();
|
||||||
case scanner::token::Lambda: return parse_lambda();
|
case scanner::token::Lambda: return parse_lambda();
|
||||||
case scanner::token::Pi: return parse_pi();
|
case scanner::token::Pi: return parse_pi();
|
||||||
case scanner::token::Forall: return parse_forall();
|
case scanner::token::Forall: return parse_forall();
|
||||||
case scanner::token::Exists: return parse_exists();
|
case scanner::token::Exists: return parse_exists();
|
||||||
case scanner::token::Let: return parse_let();
|
case scanner::token::Let: return parse_let();
|
||||||
case scanner::token::IntVal: return parse_int();
|
case scanner::token::IntVal: return parse_int();
|
||||||
case scanner::token::DecimalVal: return parse_decimal();
|
case scanner::token::DecimalVal: return parse_decimal();
|
||||||
case scanner::token::StringVal: return parse_string();
|
case scanner::token::StringVal: return parse_string();
|
||||||
case scanner::token::Type: return parse_type();
|
case scanner::token::Placeholder: return parse_placeholder();
|
||||||
|
case scanner::token::Type: return parse_type();
|
||||||
default:
|
default:
|
||||||
throw parser_error("invalid expression, unexpected token", pos());
|
throw parser_error("invalid expression, unexpected token", pos());
|
||||||
}
|
}
|
||||||
|
@ -793,15 +824,16 @@ class parser_fn {
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
case scanner::token::Eq: return parse_eq(left);
|
case scanner::token::Eq: return parse_eq(left);
|
||||||
case scanner::token::Arrow: return parse_arrow(left);
|
case scanner::token::Arrow: return parse_arrow(left);
|
||||||
case scanner::token::LeftParen: return mk_app(left, parse_lparen());
|
case scanner::token::LeftParen: return mk_app(left, parse_lparen());
|
||||||
case scanner::token::IntVal: return mk_app(left, parse_int());
|
case scanner::token::IntVal: return mk_app(left, parse_int());
|
||||||
case scanner::token::DecimalVal: return mk_app(left, parse_decimal());
|
case scanner::token::DecimalVal: return mk_app(left, parse_decimal());
|
||||||
case scanner::token::StringVal: return mk_app(left, parse_string());
|
case scanner::token::StringVal: return mk_app(left, parse_string());
|
||||||
case scanner::token::Type: return mk_app(left, parse_type());
|
case scanner::token::Placeholder: return mk_app(left, parse_placeholder());
|
||||||
default: return left;
|
case scanner::token::Type: return mk_app(left, parse_type());
|
||||||
|
default: return left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,10 +905,12 @@ class parser_fn {
|
||||||
}
|
}
|
||||||
if (is_definition) {
|
if (is_definition) {
|
||||||
m_frontend.add_definition(id, type, val);
|
m_frontend.add_definition(id, type, val);
|
||||||
regular(m_frontend) << " Defined: " << id << endl;
|
if (m_verbose)
|
||||||
|
regular(m_frontend) << " Defined: " << id << endl;
|
||||||
} else {
|
} else {
|
||||||
m_frontend.add_theorem(id, type, val);
|
m_frontend.add_theorem(id, type, val);
|
||||||
regular(m_frontend) << " Proved: " << id << endl;
|
if (m_verbose)
|
||||||
|
regular(m_frontend) << " Proved: " << id << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,7 +943,8 @@ class parser_fn {
|
||||||
check_colon_next("invalid variable declaration, ':' expected");
|
check_colon_next("invalid variable declaration, ':' expected");
|
||||||
expr type = elaborate(parse_expr());
|
expr type = elaborate(parse_expr());
|
||||||
m_frontend.add_var(id, type);
|
m_frontend.add_var(id, type);
|
||||||
regular(m_frontend) << " Assumed: " << id << endl;
|
if (m_verbose)
|
||||||
|
regular(m_frontend) << " Assumed: " << id << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \brief Parse 'Axiom' ID ':' expr */
|
/** \brief Parse 'Axiom' ID ':' expr */
|
||||||
|
@ -919,7 +954,8 @@ class parser_fn {
|
||||||
check_colon_next("invalid axiom, ':' expected");
|
check_colon_next("invalid axiom, ':' expected");
|
||||||
expr type = elaborate(parse_expr());
|
expr type = elaborate(parse_expr());
|
||||||
m_frontend.add_axiom(id, type);
|
m_frontend.add_axiom(id, type);
|
||||||
regular(m_frontend) << " Assumed: " << id << endl;
|
if (m_verbose)
|
||||||
|
regular(m_frontend) << " Assumed: " << id << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \brief Parse 'Eval' expr */
|
/** \brief Parse 'Eval' expr */
|
||||||
|
@ -1080,7 +1116,9 @@ class parser_fn {
|
||||||
default:
|
default:
|
||||||
throw parser_error("invalid option value, 'true', 'false', string, integer or decimal value expected", pos());
|
throw parser_error("invalid option value, 'true', 'false', string, integer or decimal value expected", pos());
|
||||||
}
|
}
|
||||||
regular(m_frontend) << " Set option: " << id << endl;
|
updt_options();
|
||||||
|
if (m_verbose)
|
||||||
|
regular(m_frontend) << " Set option: " << id << endl;
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1090,7 +1128,17 @@ class parser_fn {
|
||||||
std::ifstream in(fname);
|
std::ifstream in(fname);
|
||||||
if (!in.is_open())
|
if (!in.is_open())
|
||||||
throw parser_error(sstream() << "invalid import command, failed to open file '" << fname << "'", m_last_cmd_pos);
|
throw parser_error(sstream() << "invalid import command, failed to open file '" << fname << "'", m_last_cmd_pos);
|
||||||
::lean::parse_commands(m_frontend, in, m_use_exceptions, false);
|
try {
|
||||||
|
if (m_verbose)
|
||||||
|
regular(m_frontend) << "Importing file '" << fname << "'" << endl;
|
||||||
|
parser import_parser(m_frontend, in, true /* use exceptions */, false /* not interactive */);
|
||||||
|
scoped_set_interruptable_ptr<parser> set(m_import_parser, &import_parser);
|
||||||
|
import_parser();
|
||||||
|
} catch (interrupted &) {
|
||||||
|
throw;
|
||||||
|
} catch (exception &) {
|
||||||
|
throw parser_error(sstream() << "failed to import file '" << fname << "'", m_last_cmd_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_help() {
|
void parse_help() {
|
||||||
|
@ -1132,7 +1180,9 @@ class parser_fn {
|
||||||
|
|
||||||
/** \brief Parse a Lean command. */
|
/** \brief Parse a Lean command. */
|
||||||
void parse_command() {
|
void parse_command() {
|
||||||
m_frontend.reset_interrupt();
|
if (m_interactive && !m_use_exceptions)
|
||||||
|
reset_interrupt();
|
||||||
|
m_elaborator.clear();
|
||||||
m_expr_pos_info.clear();
|
m_expr_pos_info.clear();
|
||||||
m_last_cmd_pos = pos();
|
m_last_cmd_pos = pos();
|
||||||
name const & cmd_id = curr_name();
|
name const & cmd_id = curr_name();
|
||||||
|
@ -1186,12 +1236,26 @@ class parser_fn {
|
||||||
sync();
|
sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updt_options() {
|
||||||
|
m_verbose = get_parser_verbose(m_frontend.get_state().get_options());
|
||||||
|
m_show_errors = get_parser_show_errors(m_frontend.get_state().get_options());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Keep consuming tokens until we find a Command or End-of-file. */
|
||||||
|
void sync() {
|
||||||
|
show_prompt();
|
||||||
|
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof)
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
parser_fn(frontend & fe, std::istream & in, bool use_exceptions, bool interactive):
|
imp(frontend & fe, std::istream & in, bool use_exceptions, bool interactive):
|
||||||
m_frontend(fe),
|
m_frontend(fe),
|
||||||
m_scanner(in),
|
m_scanner(in),
|
||||||
|
m_elaborator(fe),
|
||||||
m_use_exceptions(use_exceptions),
|
m_use_exceptions(use_exceptions),
|
||||||
m_interactive(interactive) {
|
m_interactive(interactive) {
|
||||||
|
updt_options();
|
||||||
m_found_errors = false;
|
m_found_errors = false;
|
||||||
m_num_local_decls = 0;
|
m_num_local_decls = 0;
|
||||||
m_scanner.set_command_keywords(g_command_keywords);
|
m_scanner.set_command_keywords(g_command_keywords);
|
||||||
|
@ -1223,32 +1287,29 @@ public:
|
||||||
}
|
}
|
||||||
} catch (parser_error & ex) {
|
} catch (parser_error & ex) {
|
||||||
m_found_errors = true;
|
m_found_errors = true;
|
||||||
|
if (m_show_errors)
|
||||||
|
display_error(ex.what(), ex.m_pos.first, ex.m_pos.second);
|
||||||
if (m_use_exceptions) {
|
if (m_use_exceptions) {
|
||||||
throw parser_exception(ex.what(), ex.m_pos.first, ex.m_pos.second);
|
throw parser_exception(ex.what(), ex.m_pos.first, ex.m_pos.second);
|
||||||
} else {
|
|
||||||
display_error(ex.what(), ex.m_pos.first, ex.m_pos.second);
|
|
||||||
}
|
}
|
||||||
} catch (kernel_exception & ex) {
|
} catch (kernel_exception & ex) {
|
||||||
m_found_errors = true;
|
m_found_errors = true;
|
||||||
if (m_use_exceptions) {
|
if (m_show_errors)
|
||||||
throw;
|
|
||||||
} else {
|
|
||||||
display_error(ex);
|
display_error(ex);
|
||||||
}
|
if (m_use_exceptions)
|
||||||
} catch (interrupted & ex) {
|
|
||||||
if (m_use_exceptions) {
|
|
||||||
throw;
|
throw;
|
||||||
} else {
|
} catch (interrupted & ex) {
|
||||||
|
if (m_verbose)
|
||||||
regular(m_frontend) << "\n!!!Interrupted!!!" << endl;
|
regular(m_frontend) << "\n!!!Interrupted!!!" << endl;
|
||||||
sync();
|
sync();
|
||||||
}
|
if (m_use_exceptions)
|
||||||
|
throw;
|
||||||
} catch (exception & ex) {
|
} catch (exception & ex) {
|
||||||
m_found_errors = true;
|
m_found_errors = true;
|
||||||
if (m_use_exceptions) {
|
if (m_show_errors)
|
||||||
throw;
|
|
||||||
} else {
|
|
||||||
display_error(ex.what());
|
display_error(ex.what());
|
||||||
}
|
if (m_use_exceptions)
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1261,12 +1322,45 @@ public:
|
||||||
throw parser_exception(ex.what(), ex.m_pos.first, ex.m_pos.second);
|
throw parser_exception(ex.what(), ex.m_pos.first, ex.m_pos.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_interrupt(bool flag) {
|
||||||
|
m_frontend.set_interrupt(flag);
|
||||||
|
m_elaborator.set_interrupt(flag);
|
||||||
|
m_import_parser.set_interrupt(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_interrupt() {
|
||||||
|
set_interrupt(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
bool parse_commands(frontend & fe, std::istream & in, bool use_exceptions, bool interactive) {
|
|
||||||
parser_fn::show_prompt(interactive, fe);
|
parser::parser(frontend fe, std::istream & in, bool use_exceptions, bool interactive) {
|
||||||
return parser_fn(fe, in, use_exceptions, interactive).parse_commands();
|
parser::imp::show_prompt(interactive, fe);
|
||||||
|
m_ptr.reset(new imp(fe, in, use_exceptions, interactive));
|
||||||
}
|
}
|
||||||
bool parse_commands_from_stdin(frontend & fe) {
|
|
||||||
|
parser::~parser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parser::operator()() {
|
||||||
|
return m_ptr->parse_commands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void parser::set_interrupt(bool flag) {
|
||||||
|
m_ptr->set_interrupt(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr parser::parse_expr() {
|
||||||
|
return m_ptr->parse_expr_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
shell::shell(frontend & fe):m_frontend(fe) {
|
||||||
|
}
|
||||||
|
|
||||||
|
shell::~shell() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shell::operator()() {
|
||||||
#ifdef LEAN_USE_READLINE
|
#ifdef LEAN_USE_READLINE
|
||||||
bool errors = false;
|
bool errors = false;
|
||||||
char * input;
|
char * input;
|
||||||
|
@ -1276,15 +1370,22 @@ bool parse_commands_from_stdin(frontend & fe) {
|
||||||
return errors;
|
return errors;
|
||||||
add_history(input);
|
add_history(input);
|
||||||
std::istringstream strm(input);
|
std::istringstream strm(input);
|
||||||
if (!parse_commands(fe, strm, false, false))
|
{
|
||||||
errors = true;
|
parser p(m_frontend, strm, false, false);
|
||||||
|
scoped_set_interruptable_ptr<parser> set(m_parser, &p);
|
||||||
|
if (!p())
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
free(input);
|
free(input);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return parse_commands(fe, std::cin, false, true) ? 0 : 1;
|
parser p(m_frontend, std::cin, false, true);
|
||||||
|
scoped_set_interruptable_ptr<parser> set(m_parser, &p);
|
||||||
|
return p();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
expr parse_expr(frontend const & fe, std::istream & in) {
|
|
||||||
return parser_fn(const_cast<frontend&>(fe), in, true, false).parse_expr_main();
|
void shell::set_interrupt(bool flag) {
|
||||||
|
m_parser.set_interrupt(flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,47 @@ Author: Leonardo de Moura
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "lean_frontend.h"
|
#include "lean_frontend.h"
|
||||||
|
#include "interruptable_ptr.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
bool parse_commands(frontend & fe, std::istream & in, bool use_exceptions = true, bool interactive = false);
|
/** \brief Functional object for parsing commands and expressions */
|
||||||
bool parse_commands_from_stdin(frontend & fe);
|
class parser {
|
||||||
expr parse_expr(frontend const & fe, std::istream & in);
|
class imp;
|
||||||
|
std::unique_ptr<imp> m_ptr;
|
||||||
|
public:
|
||||||
|
parser(frontend fe, std::istream & in, bool use_exceptions = true, bool interactive = false);
|
||||||
|
~parser();
|
||||||
|
|
||||||
|
/** \brief Parse a sequence of commands */
|
||||||
|
bool operator()();
|
||||||
|
|
||||||
|
/** \brief Parse a single expression */
|
||||||
|
expr parse_expr();
|
||||||
|
|
||||||
|
void set_interrupt(bool flag);
|
||||||
|
void interrupt() { set_interrupt(true); }
|
||||||
|
void reset_interrupt() { set_interrupt(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \brief Implements the Read Eval Print loop */
|
||||||
|
class shell {
|
||||||
|
frontend m_frontend;
|
||||||
|
interruptable_ptr<parser> m_parser;
|
||||||
|
public:
|
||||||
|
shell(frontend & fe);
|
||||||
|
~shell();
|
||||||
|
|
||||||
|
bool operator()();
|
||||||
|
|
||||||
|
void set_interrupt(bool flag);
|
||||||
|
void interrupt() { set_interrupt(true); }
|
||||||
|
void reset_interrupt() { set_interrupt(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool parse_commands(frontend & fe, std::istream & in, bool use_exceptions = true, bool interactive = false) {
|
||||||
|
return parser(fe, in, use_exceptions, interactive)();
|
||||||
|
}
|
||||||
|
inline expr parse_expr(frontend const & fe, std::istream & in) {
|
||||||
|
return parser(fe, in).parse_expr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ static name g_forall_name("forall");
|
||||||
static name g_forall_unicode("\u2200");
|
static name g_forall_unicode("\u2200");
|
||||||
static name g_exists_name("exists");
|
static name g_exists_name("exists");
|
||||||
static name g_exists_unicode("\u2203");
|
static name g_exists_unicode("\u2203");
|
||||||
|
static name g_placeholder_name("_");
|
||||||
|
|
||||||
static char g_normalized[256];
|
static char g_normalized[256];
|
||||||
|
|
||||||
|
@ -215,6 +216,8 @@ scanner::token scanner::read_a_symbol() {
|
||||||
return token::Let;
|
return token::Let;
|
||||||
else if (m_name_val == g_in_name)
|
else if (m_name_val == g_in_name)
|
||||||
return token::In;
|
return token::In;
|
||||||
|
else if (m_name_val == g_placeholder_name)
|
||||||
|
return token::Placeholder;
|
||||||
else
|
else
|
||||||
return is_command(m_name_val) ? token::CommandId : token::Id;
|
return is_command(m_name_val) ? token::CommandId : token::Id;
|
||||||
}
|
}
|
||||||
|
@ -392,6 +395,7 @@ std::ostream & operator<<(std::ostream & out, scanner::token const & t) {
|
||||||
case scanner::token::Eq: out << "="; break;
|
case scanner::token::Eq: out << "="; break;
|
||||||
case scanner::token::Assign: out << ":="; break;
|
case scanner::token::Assign: out << ":="; break;
|
||||||
case scanner::token::Type: out << "Type"; break;
|
case scanner::token::Type: out << "Type"; break;
|
||||||
|
case scanner::token::Placeholder: out << "_"; break;
|
||||||
case scanner::token::Eof: out << "EOF"; break;
|
case scanner::token::Eof: out << "EOF"; break;
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -19,7 +19,7 @@ class scanner {
|
||||||
public:
|
public:
|
||||||
enum class token {
|
enum class token {
|
||||||
LeftParen, RightParen, LeftCurlyBracket, RightCurlyBracket, Colon, Comma, Period, Lambda, Pi, Arrow,
|
LeftParen, RightParen, LeftCurlyBracket, RightCurlyBracket, Colon, Comma, Period, Lambda, Pi, Arrow,
|
||||||
Let, In, Forall, Exists, Id, CommandId, IntVal, DecimalVal, StringVal, Eq, Assign, Type, Eof
|
Let, In, Forall, Exists, Id, CommandId, IntVal, DecimalVal, StringVal, Eq, Assign, Type, Placeholder, Eof
|
||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
int m_spos; // position in the current line of the stream
|
int m_spos; // position in the current line of the stream
|
||||||
|
|
|
@ -12,6 +12,16 @@ Author: Leonardo de Moura
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
|
static name g_overload_name(name(name(name(0u), "library"), "overload"));
|
||||||
|
static expr g_overload = mk_constant(g_overload_name);
|
||||||
|
|
||||||
|
bool is_overload_marker(expr const & e) {
|
||||||
|
return e == g_overload;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr mk_overload_marker() {
|
||||||
|
return g_overload;
|
||||||
|
}
|
||||||
|
|
||||||
expr elaborator::lookup(context const & c, unsigned i) {
|
expr elaborator::lookup(context const & c, unsigned i) {
|
||||||
auto p = lookup_ext(c, i);
|
auto p = lookup_ext(c, i);
|
||||||
|
@ -21,7 +31,7 @@ expr elaborator::lookup(context const & c, unsigned i) {
|
||||||
return lift_free_vars(def.get_domain(), length(c) - length(def_c));
|
return lift_free_vars(def.get_domain(), length(c) - length(def_c));
|
||||||
}
|
}
|
||||||
|
|
||||||
elaborator::elaborator(environment & env):
|
elaborator::elaborator(environment const & env):
|
||||||
m_env(env), m_metaenv(env) {
|
m_env(env), m_metaenv(env) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ namespace lean {
|
||||||
the value of implicit arguments.
|
the value of implicit arguments.
|
||||||
*/
|
*/
|
||||||
class elaborator {
|
class elaborator {
|
||||||
environment & m_env;
|
environment const & m_env;
|
||||||
metavar_env m_metaenv;
|
metavar_env m_metaenv;
|
||||||
|
|
||||||
expr lookup(context const & c, unsigned i);
|
expr lookup(context const & c, unsigned i);
|
||||||
void unify(expr const & e1, expr const & e2, context const & ctx);
|
void unify(expr const & e1, expr const & e2, context const & ctx);
|
||||||
|
@ -24,8 +24,20 @@ class elaborator {
|
||||||
expr process(expr const & e, context const & ctx);
|
expr process(expr const & e, context const & ctx);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
elaborator(environment & env);
|
elaborator(environment const & env);
|
||||||
metavar_env & menv() { return m_metaenv; }
|
metavar_env & menv() { return m_metaenv; }
|
||||||
|
|
||||||
expr operator()(expr const & e);
|
expr operator()(expr const & e);
|
||||||
|
|
||||||
|
void clear() { m_metaenv.clear(); }
|
||||||
|
|
||||||
|
void set_interrupt(bool flag) { m_metaenv.set_interrupt(flag); }
|
||||||
|
void interrupt() { set_interrupt(true); }
|
||||||
|
void reset_interrupt() { set_interrupt(false); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** \brief Return true iff \c e is a special constant used to mark application of overloads. */
|
||||||
|
bool is_overload_marker(expr const & e);
|
||||||
|
/** \brief Return the overload marker */
|
||||||
|
expr mk_overload_marker();
|
||||||
}
|
}
|
||||||
|
|
|
@ -445,6 +445,10 @@ void metavar_env::set_interrupt(bool flag) {
|
||||||
m_interrupted = flag;
|
m_interrupted = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void metavar_env::clear() {
|
||||||
|
m_cells.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void metavar_env::display(std::ostream & out) const {
|
void metavar_env::display(std::ostream & out) const {
|
||||||
for (unsigned i = 0; i < m_cells.size(); i++) {
|
for (unsigned i = 0; i < m_cells.size(); i++) {
|
||||||
out << "?" << i << " --> ";
|
out << "?" << i << " --> ";
|
||||||
|
|
|
@ -146,6 +146,11 @@ public:
|
||||||
*/
|
*/
|
||||||
context const & get_context(expr const & m);
|
context const & get_context(expr const & m);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Clear/Reset the state.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
void set_interrupt(bool flag);
|
void set_interrupt(bool flag);
|
||||||
|
|
||||||
void display(std::ostream & out) const;
|
void display(std::ostream & out) const;
|
||||||
|
|
|
@ -7,19 +7,20 @@
|
||||||
#include "lean_parser.h"
|
#include "lean_parser.h"
|
||||||
using namespace lean;
|
using namespace lean;
|
||||||
|
|
||||||
static interruptable_ptr<frontend> g_lean_frontend;
|
static interruptable_ptr<shell> g_lean_shell;
|
||||||
|
|
||||||
static void on_ctrl_c(int) {
|
static void on_ctrl_c(int) {
|
||||||
g_lean_frontend.set_interrupt(true);
|
g_lean_shell.set_interrupt(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lean_shell() {
|
bool lean_shell() {
|
||||||
std::cout << "Lean (version " << LEAN_VERSION_MAJOR << "." << LEAN_VERSION_MINOR << ")\n";
|
std::cout << "Lean (version " << LEAN_VERSION_MAJOR << "." << LEAN_VERSION_MINOR << ")\n";
|
||||||
std::cout << "Type Ctrl-D to exit or 'Help.' for help."<< std::endl;
|
std::cout << "Type Ctrl-D to exit or 'Help.' for help."<< std::endl;
|
||||||
frontend f;
|
frontend f;
|
||||||
scoped_set_interruptable_ptr<frontend> set(g_lean_frontend, &f);
|
shell sh(f);
|
||||||
|
scoped_set_interruptable_ptr<shell> set(g_lean_shell, &sh);
|
||||||
signal(SIGINT, on_ctrl_c);
|
signal(SIGINT, on_ctrl_c);
|
||||||
return parse_commands_from_stdin(f);
|
return sh();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
|
@ -30,7 +31,8 @@ int main(int argc, char ** argv) {
|
||||||
frontend f;
|
frontend f;
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
std::ifstream in(argv[i]);
|
std::ifstream in(argv[i]);
|
||||||
if (!parse_commands(f, in))
|
parser p(f, in);
|
||||||
|
if (!p())
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
return ok ? 0 : 1;
|
return ok ? 0 : 1;
|
||||||
|
|
Loading…
Reference in a new issue