Uniform notation declarations.
This commit is contained in:
parent
5aae838e1c
commit
a9c6088d11
4 changed files with 162 additions and 28 deletions
|
@ -192,7 +192,7 @@ struct frontend::imp {
|
|||
void add_mixfixl(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixl(sz, opns, p), d, false); }
|
||||
void add_mixfixr(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixr(sz, opns, p), d, true); }
|
||||
void add_mixfixc(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixc(sz, opns, p), d, false); }
|
||||
void add_mixfixo(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixo(sz, opns, p), d, false); }
|
||||
void add_mixfixo(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixo(sz, opns, p), d, true); }
|
||||
|
||||
void mark_implicit_arguments(name const & n, unsigned sz, bool const * implicit) {
|
||||
if (has_children())
|
||||
|
|
|
@ -122,12 +122,56 @@ char const * to_string(fixity f) {
|
|||
|
||||
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() != 0)
|
||||
if (o.get_precedence() > 1)
|
||||
r += format{space(), format(o.get_precedence())};
|
||||
for (auto p : o.get_op_name_parts())
|
||||
r += format{space(), format(p)};
|
||||
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(); break;
|
||||
}
|
||||
}
|
||||
lean_unreachable();
|
||||
return format();
|
||||
}
|
||||
|
||||
char const * notation_declaration::keyword() const {
|
||||
|
|
|
@ -77,11 +77,12 @@ 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_notation_kwd("Notation");
|
||||
/** \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_infix_kwd, g_infixl_kwd, g_infixr_kwd, g_prefix_kwd,
|
||||
g_postfix_kwd, g_mixfixl_kwd, g_mixfixr_kwd, g_mixfixc_kwd, g_echo_kwd,
|
||||
g_set_kwd, g_env_kwd, g_options_kwd, g_import_kwd, g_help_kwd};
|
||||
g_set_kwd, g_env_kwd, g_options_kwd, g_import_kwd, g_help_kwd, g_notation_kwd};
|
||||
// ==========================================
|
||||
|
||||
// ==========================================
|
||||
|
@ -201,19 +202,21 @@ class parser::imp {
|
|||
throw parser_error(msg, pos());
|
||||
}
|
||||
|
||||
/** \brief Return true iff the current toke is an identifier */
|
||||
/** \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 toke is an integer */
|
||||
/** \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 an integer */
|
||||
bool curr_is_int() const { return curr() == scanner::token::IntVal; }
|
||||
/** \brief Return true iff the current toke is a '(' */
|
||||
/** \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 toke is a '{' */
|
||||
/** \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 toke is a ':' */
|
||||
/** \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 toke is a ',' */
|
||||
/** \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 toke is an 'in' token */
|
||||
/** \brief Return true iff the current token is an 'in' token */
|
||||
bool curr_is_in() const { return curr() == scanner::token::In; }
|
||||
|
||||
/** \brief Throws a parser error if the current token is not an identifier. */
|
||||
|
@ -223,6 +226,8 @@ class parser::imp {
|
|||
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. */
|
||||
|
@ -235,10 +240,7 @@ class parser::imp {
|
|||
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 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, pos()); }
|
||||
/** \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(); }
|
||||
|
||||
/**
|
||||
\brief Throws a parser error if the current token is not a
|
||||
string. If it is, move to the next token.
|
||||
|
@ -474,11 +476,13 @@ class parser::imp {
|
|||
this method has been used when parsing mixfix operators.
|
||||
*/
|
||||
void check_op_part(name const & op_part) {
|
||||
check_name_next(op_part, "invalid mixfix operator application, identifier expected");
|
||||
if(!curr_is_identifier() || curr_name() != op_part)
|
||||
throw parser_error(sstream() << "invalid mixfix operator application, '" << op_part << "' expected", pos());
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Auxiliary function for #parse_mixfixl and #parse_mixfixr
|
||||
\brief Auxiliary function for #parse_mixfixl and #parse_mixfixo
|
||||
|
||||
It parses (ID _)*
|
||||
*/
|
||||
|
@ -506,7 +510,14 @@ class parser::imp {
|
|||
auto p = pos();
|
||||
buffer<expr> args;
|
||||
args.push_back(left);
|
||||
parse_mixfix_args(op.get_op_name_parts(), op.get_precedence(), args);
|
||||
auto parts = op.get_op_name_parts();
|
||||
auto it = parts.begin();
|
||||
++it;
|
||||
while (it != parts.end()) {
|
||||
args.push_back(parse_expr(op.get_precedence()));
|
||||
check_op_part(*it);
|
||||
++it;
|
||||
}
|
||||
return mk_application(op, p, args);
|
||||
}
|
||||
|
||||
|
@ -515,6 +526,7 @@ class parser::imp {
|
|||
auto p = pos();
|
||||
buffer<expr> args;
|
||||
args.push_back(left);
|
||||
args.push_back(parse_expr(op.get_precedence()));
|
||||
parse_mixfix_args(op.get_op_name_parts(), op.get_precedence(), args);
|
||||
return mk_application(op, p, args);
|
||||
}
|
||||
|
@ -633,6 +645,7 @@ class parser::imp {
|
|||
case fixity::Infixr: return parse_infixr(left, op);
|
||||
case fixity::Mixfixr: return parse_mixfixr(left, op);
|
||||
case fixity::Mixfixo: return parse_mixfixo(left, op);
|
||||
case fixity::Postfix: return parse_postfix(left, op);
|
||||
default: lean_unreachable(); return expr();
|
||||
}
|
||||
} else {
|
||||
|
@ -1165,7 +1178,7 @@ class parser::imp {
|
|||
\brief Parse prefix/postfix/infix/infixl/infixr user operator
|
||||
definitions. These definitions have the form:
|
||||
|
||||
- fixity [Num] ID ':' expr
|
||||
- fixity [Num] ID ':' ID
|
||||
*/
|
||||
void parse_op(fixity fx) {
|
||||
next();
|
||||
|
@ -1211,12 +1224,88 @@ class parser::imp {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Parse notation declaration unified format
|
||||
|
||||
'Notation' [Num] parts ':' ID
|
||||
*/
|
||||
void parse_notation_decl() {
|
||||
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", pos());
|
||||
}
|
||||
name name_id = check_identifier_next("invalid notation declaration, identifier expected");
|
||||
expr d = mk_constant(name_id);
|
||||
if (parts.size() == 1) {
|
||||
if (first_placeholder && prev_placeholder) {
|
||||
// infix: _ ID _
|
||||
m_frontend.add_infix(parts[0], prec, d);
|
||||
} else if (first_placeholder) {
|
||||
// postfix: _ ID
|
||||
m_frontend.add_postfix(parts[0], prec, d);
|
||||
} else if (prev_placeholder) {
|
||||
// prefix: ID _
|
||||
m_frontend.add_prefix(parts[0], prec, d);
|
||||
} else {
|
||||
lean_unreachable();
|
||||
}
|
||||
} else {
|
||||
if (first_placeholder && prev_placeholder) {
|
||||
// mixfixo: _ ID ... ID _
|
||||
m_frontend.add_mixfixo(parts.size(), parts.data(), prec, d);
|
||||
} else if (first_placeholder) {
|
||||
// mixfixr: _ ID ... ID
|
||||
m_frontend.add_mixfixr(parts.size(), parts.data(), prec, d);
|
||||
} else if (prev_placeholder) {
|
||||
// mixfixl: ID _ ... ID _
|
||||
m_frontend.add_mixfixl(parts.size(), parts.data(), prec, d);
|
||||
} else {
|
||||
// mixfixc: ID _ ... _ ID
|
||||
m_frontend.add_mixfixc(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 'Echo' [string] */
|
||||
void parse_echo() {
|
||||
next();
|
||||
std::string msg = check_string_next("invalid echo command, string expected");
|
||||
regular(m_frontend) << msg << endl;
|
||||
}
|
||||
|
||||
/** Parse 'Set' [id] [value] */
|
||||
void parse_set() {
|
||||
next();
|
||||
auto id_pos = pos();
|
||||
|
@ -1337,6 +1426,7 @@ class parser::imp {
|
|||
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 if (cmd_id == g_notation_kwd) parse_notation_decl();
|
||||
else if (cmd_id == g_echo_kwd) parse_echo();
|
||||
else if (cmd_id == g_set_kwd) parse_set();
|
||||
else if (cmd_id == g_import_kwd) parse_import();
|
||||
|
|
|
@ -354,9 +354,9 @@ class pp_fn {
|
|||
\brief Return the precedence of the given expression
|
||||
*/
|
||||
unsigned get_operator_precedence(expr const & e) {
|
||||
if (is_constant(e)) {
|
||||
operator_info op = get_operator(e);
|
||||
return op ? op.get_precedence() : 0;
|
||||
if (op) {
|
||||
return op.get_precedence();
|
||||
} else if (is_eq(e)) {
|
||||
return g_eq_precedence;
|
||||
} else if (is_arrow(e)) {
|
||||
|
@ -547,9 +547,9 @@ class pp_fn {
|
|||
r_format += format{p_arg.first, space(), format(*it)};
|
||||
} else {
|
||||
r_format += format{p_arg.first, space(), format(*it), line()};
|
||||
++it;
|
||||
}
|
||||
r_weight += p_arg.second;
|
||||
++it;
|
||||
}
|
||||
return mk_result(group(r_format), r_weight);
|
||||
}
|
||||
|
@ -562,7 +562,7 @@ class pp_fn {
|
|||
for (unsigned i = 0; i < num; i++) {
|
||||
result p_arg = pp_mixfix_child(op, app.get_arg(i), depth);
|
||||
unsigned sz = it->size();
|
||||
if (i > 1) r_format += space();
|
||||
if (i > 0) r_format += space();
|
||||
r_format += format{format(*it), nest(sz+1, format{line(), p_arg.first})};
|
||||
r_weight += p_arg.second;
|
||||
++it;
|
||||
|
|
Loading…
Reference in a new issue