411 lines
13 KiB
C++
411 lines
13 KiB
C++
|
/*
|
||
|
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 <unordered_map>
|
||
|
#include "frontend.h"
|
||
|
#include "scanner.h"
|
||
|
#include "scoped_map.h"
|
||
|
#include "exception.h"
|
||
|
#include "builtin.h"
|
||
|
#include "arith.h"
|
||
|
|
||
|
namespace lean {
|
||
|
static name g_definition_kwd("Definition");
|
||
|
static name g_variable_kwd("Variable");
|
||
|
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_show_kwd("Show");
|
||
|
static name g_infix_kwd("Infix");
|
||
|
static name g_infixl_kwd("Infixl");
|
||
|
static name g_infixr_kwd("Infixr");
|
||
|
static name g_prefix_kwd("Prefix");
|
||
|
static name g_postfix_kwd("Postfix");
|
||
|
static name g_mixfixl_kwd("Mixfixl");
|
||
|
static name g_mixfixr_kwd("Mixfixr");
|
||
|
static name g_mixfixc_kwd("Mixfixc");
|
||
|
|
||
|
static name g_overload_name(name(name(name(0u), "parser"), "overload"));
|
||
|
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_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 */
|
||
|
struct parser_fn {
|
||
|
typedef scoped_map<name, unsigned, name_hash, name_eq> local_decls;
|
||
|
typedef std::unordered_map<name, expr, name_hash, name_eq> builtins;
|
||
|
frontend m_frontend;
|
||
|
scanner m_scanner;
|
||
|
std::ostream & m_err;
|
||
|
scanner::token m_curr;
|
||
|
bool m_use_exceptions;
|
||
|
bool m_found_errors;
|
||
|
local_decls m_local_decls;
|
||
|
builtins m_builtins;
|
||
|
|
||
|
/** \brief Exception used to track parsing erros, it does not leak outside of this class. */
|
||
|
struct parser_error : public exception {
|
||
|
parser_error(char const * msg):exception(msg) {}
|
||
|
};
|
||
|
|
||
|
void scan() { m_curr = m_scanner.scan(); }
|
||
|
scanner::token curr() const { return m_curr; }
|
||
|
void next() { if (m_curr != scanner::token::Eof) scan(); }
|
||
|
void sync() {
|
||
|
while (curr() != scanner::token::CommandId && curr() != scanner::token::Eof)
|
||
|
next();
|
||
|
}
|
||
|
name const & curr_name() const { return m_scanner.get_name_val(); }
|
||
|
|
||
|
void check_next(scanner::token t, char const * msg) {
|
||
|
if (curr() == t)
|
||
|
next();
|
||
|
else
|
||
|
throw parser_error(msg);
|
||
|
}
|
||
|
|
||
|
bool curr_is_identifier() const { return curr() == scanner::token::Id; }
|
||
|
|
||
|
void check_identifier(char const * msg) { if (!curr_is_identifier()) throw parser_error(msg); }
|
||
|
name check_identifier_next(char const * msg) { check_identifier(msg); name r = curr_name(); next(); return r; }
|
||
|
void check_colon_next(char const * msg) { check_next(scanner::token::Colon, msg); }
|
||
|
void check_rparen_next(char const * msg) { check_next(scanner::token::RightParen, msg); }
|
||
|
|
||
|
void init_builtins() {
|
||
|
m_builtins["Bool"] = Bool;
|
||
|
m_builtins["true"] = True;
|
||
|
m_builtins["false"] = False;
|
||
|
m_builtins["\u22A4"] = True;
|
||
|
m_builtins["\u22A5"] = False;
|
||
|
m_builtins["Int"] = Int;
|
||
|
}
|
||
|
|
||
|
parser_fn(frontend & fe, std::istream & in, std::ostream & err, bool use_exceptions):
|
||
|
m_frontend(fe),
|
||
|
m_scanner(in),
|
||
|
m_err(err),
|
||
|
m_use_exceptions(use_exceptions) {
|
||
|
m_found_errors = false;
|
||
|
m_scanner.set_command_keywords(g_command_keywords);
|
||
|
init_builtins();
|
||
|
scan();
|
||
|
}
|
||
|
|
||
|
[[ noreturn ]] void not_implemented_yet() {
|
||
|
throw parser_error("not implemented yet");
|
||
|
}
|
||
|
|
||
|
expr mk_fun(operator_info const & op) {
|
||
|
list<name> const & fs = op.get_internal_names();
|
||
|
lean_assert(!is_nil(fs));
|
||
|
auto it = fs.begin();
|
||
|
expr r = mk_constant(*it);
|
||
|
++it;
|
||
|
for (; it != fs.end(); ++it)
|
||
|
r = mk_app(g_overload, r, mk_constant(*it));
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
expr parse_prefix(operator_info const & op) {
|
||
|
expr arg = parse_expr(op.get_precedence());
|
||
|
return mk_app(mk_fun(op), arg);
|
||
|
}
|
||
|
|
||
|
expr parse_postfix(expr const & left, operator_info const & op) {
|
||
|
return mk_app(mk_fun(op), left);
|
||
|
}
|
||
|
|
||
|
expr parse_infix(expr const & left, operator_info const & op) {
|
||
|
expr right = parse_expr(op.get_precedence()+1);
|
||
|
return mk_app(mk_fun(op), left, right);
|
||
|
}
|
||
|
|
||
|
expr parse_infixl(expr const & left, operator_info const & op) {
|
||
|
expr right = parse_expr(op.get_precedence());
|
||
|
return mk_app(mk_fun(op), left, right);
|
||
|
}
|
||
|
|
||
|
expr parse_infixr(expr const & left, operator_info const & op) {
|
||
|
expr right = parse_expr(op.get_precedence()-1);
|
||
|
return mk_app(mk_fun(op), left, right);
|
||
|
}
|
||
|
|
||
|
expr parse_mixfixl(operator_info const & op) {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr parse_mixfixr(expr const & left, operator_info const & op) {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr parse_mixfixc(operator_info const & op) {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr get_name_ref(name const & id) {
|
||
|
object const & obj = m_frontend.find_object(id);
|
||
|
if (obj) {
|
||
|
object_kind k = obj.kind();
|
||
|
if (k == object_kind::Definition || k == object_kind::Postulate)
|
||
|
return mk_constant(obj.get_name());
|
||
|
else
|
||
|
throw parser_error("invalid object reference, object is not an expression.");
|
||
|
}
|
||
|
else {
|
||
|
auto it = m_builtins.find(id);
|
||
|
if (it != m_builtins.end()) {
|
||
|
return it->second;
|
||
|
} else {
|
||
|
throw parser_error("unknown identifier");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expr parse_nud_id() {
|
||
|
name id = curr_name();
|
||
|
next();
|
||
|
auto it = m_local_decls.find(id);
|
||
|
if (it != m_local_decls.end()) {
|
||
|
return mk_var(m_local_decls.size() - it->second - 1);
|
||
|
} else {
|
||
|
operator_info op = m_frontend.find_nud(id);
|
||
|
if (op) {
|
||
|
switch (op.get_fixity()) {
|
||
|
case fixity::Prefix: return parse_prefix(op);
|
||
|
case fixity::Mixfixl: return parse_mixfixl(op);
|
||
|
case fixity::Mixfixc: return parse_mixfixc(op);
|
||
|
default: lean_unreachable(); return expr();
|
||
|
}
|
||
|
} else {
|
||
|
return get_name_ref(id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expr parse_led_id(expr const & left) {
|
||
|
name id = curr_name();
|
||
|
next();
|
||
|
auto it = m_local_decls.find(id);
|
||
|
if (it != m_local_decls.end()) {
|
||
|
return mk_app(left, mk_var(m_local_decls.size() - it->second - 1));
|
||
|
} else {
|
||
|
operator_info op = m_frontend.find_led(id);
|
||
|
if (op) {
|
||
|
switch (op.get_fixity()) {
|
||
|
case fixity::Infix: return parse_infix(left, op);
|
||
|
case fixity::Infixl: return parse_infixl(left, op);
|
||
|
case fixity::Infixr: return parse_infixr(left, op);
|
||
|
case fixity::Mixfixr: return parse_mixfixr(left, op);
|
||
|
default: lean_unreachable(); return expr();
|
||
|
}
|
||
|
} else {
|
||
|
return mk_app(left, get_name_ref(id));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expr parse_lparen() {
|
||
|
next();
|
||
|
expr r = parse_expr();
|
||
|
check_rparen_next("invalid expression, ')' expected");
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
expr parse_lambda() {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr parse_pi() {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr parse_int() {
|
||
|
expr r = mk_int_value(m_scanner.get_num_val().get_numerator());
|
||
|
next();
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
expr parse_decimal() {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr parse_string() {
|
||
|
not_implemented_yet();
|
||
|
}
|
||
|
|
||
|
expr parse_type() {
|
||
|
next();
|
||
|
// TODO universe
|
||
|
return Type();
|
||
|
}
|
||
|
|
||
|
expr parse_nud() {
|
||
|
switch (curr()) {
|
||
|
case scanner::token::Id: return parse_nud_id();
|
||
|
case scanner::token::LeftParen: return parse_lparen();
|
||
|
case scanner::token::Lambda: return parse_lambda();
|
||
|
case scanner::token::Pi: return parse_pi();
|
||
|
case scanner::token::IntVal: return parse_int();
|
||
|
case scanner::token::DecimalVal: return parse_decimal();
|
||
|
case scanner::token::StringVal: return parse_string();
|
||
|
case scanner::token::Type: return parse_type();
|
||
|
default:
|
||
|
throw parser_error("invalid expression, unexpected token");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expr parse_led(expr const & left) {
|
||
|
switch (curr()) {
|
||
|
case scanner::token::Id: return parse_led_id(left);
|
||
|
case scanner::token::LeftParen: return mk_app(left, parse_lparen());
|
||
|
case scanner::token::IntVal: return mk_app(left, parse_int());
|
||
|
case scanner::token::DecimalVal: return mk_app(left, parse_decimal());
|
||
|
case scanner::token::StringVal: return mk_app(left, parse_string());
|
||
|
case scanner::token::Type: return mk_app(left, parse_type());
|
||
|
default: return left;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned curr_lbp() {
|
||
|
switch (curr()) {
|
||
|
case scanner::token::Id: {
|
||
|
name const & id = curr_name();
|
||
|
auto it = m_local_decls.find(id);
|
||
|
if (it != m_local_decls.end()) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
operator_info op = m_frontend.find_led(id);
|
||
|
if (op)
|
||
|
return op.get_precedence();
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
case scanner::token::LeftParen: case scanner::token::IntVal: case scanner::token::DecimalVal:
|
||
|
case scanner::token::StringVal: case scanner::token::Type:
|
||
|
return 1;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expr parse_expr(unsigned rbp = 0) {
|
||
|
expr left = parse_nud();
|
||
|
while (rbp < curr_lbp()) {
|
||
|
left = parse_led(left);
|
||
|
}
|
||
|
return left;
|
||
|
}
|
||
|
|
||
|
expr elaborate(expr const & e) {
|
||
|
// TODO
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
void parse_definition() {
|
||
|
next();
|
||
|
name id = check_identifier_next("invalid definition, identifier expected");
|
||
|
check_colon_next("invalid definition, ':' expected");
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void parse_variable() {
|
||
|
next();
|
||
|
name id = check_identifier_next("invalid variable declaration, identifier expected");
|
||
|
check_colon_next("invalid variable declaration, ':' expected");
|
||
|
expr type = elaborate(parse_expr());
|
||
|
m_frontend.add_var(id, type);
|
||
|
}
|
||
|
|
||
|
void parse_theorem() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void parse_axiom() {
|
||
|
next();
|
||
|
name id = check_identifier_next("invalid axiom, identifier expected");
|
||
|
check_colon_next("invalid axiom, ':' expected");
|
||
|
expr type = elaborate(parse_expr());
|
||
|
m_frontend.add_axiom(id, type);
|
||
|
}
|
||
|
|
||
|
void parse_eval() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void parse_show() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void parse_op(fixity fx) {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void parse_mixfix_op(fixity fx) {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
void parse_command() {
|
||
|
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_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_show_kwd) parse_show();
|
||
|
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_prefix_kwd) parse_op(fixity::Prefix);
|
||
|
else if (cmd_id == g_postfix_kwd) parse_op(fixity::Postfix);
|
||
|
else if (cmd_id == g_mixfixl_kwd) parse_mixfix_op(fixity::Mixfixl);
|
||
|
else if (cmd_id == g_mixfixr_kwd) parse_mixfix_op(fixity::Mixfixr);
|
||
|
else if (cmd_id == g_mixfixc_kwd) parse_mixfix_op(fixity::Mixfixc);
|
||
|
else { lean_unreachable(); }
|
||
|
}
|
||
|
|
||
|
void error(char const * msg, unsigned line, unsigned pos) {
|
||
|
m_err << "Error (line: " << line << ", pos: " << pos << ") " << msg << std::endl;
|
||
|
}
|
||
|
|
||
|
void error(char const * msg) {
|
||
|
error(msg, m_scanner.get_line(), m_scanner.get_pos());
|
||
|
sync();
|
||
|
}
|
||
|
|
||
|
bool parse_commands() {
|
||
|
while (true) {
|
||
|
try {
|
||
|
switch (curr()) {
|
||
|
case scanner::token::CommandId: parse_command(); break;
|
||
|
case scanner::token::Eof: return !m_found_errors;
|
||
|
default:
|
||
|
throw parser_error("Command expected");
|
||
|
}
|
||
|
} catch (parser_error & ex) {
|
||
|
m_found_errors = true;
|
||
|
if (m_use_exceptions) {
|
||
|
throw parser_exception(ex.what(), m_scanner.get_line(), m_scanner.get_pos());
|
||
|
} else {
|
||
|
error(ex.what());
|
||
|
}
|
||
|
} catch (exception & ex) {
|
||
|
m_found_errors = true;
|
||
|
if (m_use_exceptions) {
|
||
|
throw;
|
||
|
} else {
|
||
|
error(ex.what());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
bool parse_commands(frontend & fe, std::istream & in, std::ostream & err, bool use_exceptions) {
|
||
|
return parser_fn(fe, in, err, use_exceptions).parse_commands();
|
||
|
}
|
||
|
}
|