environment coercion_cmd(parser & p) { auto pos = p.pos(); expr f = p.parse_expr(); if (!is_constant(f)) throw parser_error("invalid 'coercion' command, constant expected", pos); if (p.curr_is_token(g_colon)) { p.next(); pos = p.pos(); expr C = p.parse_expr(); if (!is_constant(C)) throw parser_error("invalid 'coercion' command, constant expected", pos); return add_coercion(p.env(), const_name(f), const_name(C), p.ios()); } else { return add_coercion(p.env(), const_name(f), p.ios()); } }
environment find_cmd(parser & p) { expr e; level_param_names ls; { bool save_options = true; parser::local_scope scope(p, save_options); p.set_option(get_elaborator_ignore_instances_name(), true); std::tie(e, ls) = parse_local_expr(p); } buffer<std::string> pos_names, neg_names; parse_filters(p, pos_names, neg_names); environment env = p.env(); auto tc = mk_opaque_type_checker(env, p.mk_ngen()); flycheck_information info(p.regular_stream()); if (info.enabled()) { p.display_information_pos(p.cmd_pos()); } p.regular_stream() << "find_decl result:\n"; unsigned max_steps = get_find_max_steps(p.get_options()); bool cheap = !get_find_expensive(p.get_options()); bool found = false; env.for_each_declaration([&](declaration const & d) { if (std::all_of(pos_names.begin(), pos_names.end(), [&](std::string const & pos) { return is_part_of(pos, d.get_name()); }) && std::all_of(neg_names.begin(), neg_names.end(), [&](std::string const & neg) { return !is_part_of(neg, d.get_name()); }) && match_pattern(*tc.get(), e, d, max_steps, cheap)) { found = true; p.regular_stream() << " " << get_decl_short_name(d.get_name(), env) << " : " << d.get_type() << endl; } }); if (!found) p.regular_stream() << "no matches\n"; return env; }
static name parse_quoted_symbol_or_token(parser & p, buffer<token_entry> & new_tokens, bool & used_default, notation_entry_group grp) { used_default = false; if (p.curr_is_quoted_symbol()) { environment const & env = p.env(); auto pp_tk = p.get_name_val(); auto tks = utf8_trim(pp_tk.to_string()); auto tkcs = tks.c_str(); check_not_forbidden(tkcs); p.next(); if (p.curr_is_token(get_colon_tk())) { p.next(); unsigned prec = parse_precedence(p); new_tokens.push_back(mk_token_entry(tkcs, prec, grp)); } else if (!get_precedence(env, tkcs, grp)) { new_tokens.push_back(mk_token_entry(tkcs, LEAN_DEFAULT_PRECEDENCE, grp)); used_default = true; } return pp_tk; } else if (p.curr_is_keyword()) { auto tk = p.get_token_info().token(); check_not_forbidden(tk.to_string().c_str()); p.next(); return tk; } else { throw parser_error("invalid notation declaration, symbol expected", p.pos()); } }
environment check_cmd(parser & p) { expr e = p.parse_expr(); list<expr> ctx = locals_to_context(e, p); level_param_names ls = to_level_param_names(collect_univ_params(e)); level_param_names new_ls; std::tie(e, new_ls) = p.elaborate_relaxed(e, ctx); auto tc = mk_type_checker_with_hints(p.env(), p.mk_ngen(), true); expr type = tc->check(e, append(ls, new_ls)); auto reg = p.regular_stream(); formatter const & fmt = reg.get_formatter(); options opts = p.ios().get_options(); unsigned indent = get_pp_indent(opts); format r = group(format{fmt(e), space(), colon(), nest(indent, compose(line(), fmt(type)))}); reg << mk_pair(r, opts) << endl; return p.env(); }
environment namespace_cmd(parser & p) { auto pos = p.pos(); name n = p.check_atomic_id_next("invalid namespace declaration, atomic identifier expected"); if (is_root_namespace(n)) throw parser_error(sstream() << "invalid namespace name, '" << n << "' is reserved", pos); return push_scope(p.env(), p.ios(), n); }
// using [class] id (id ...) (renaming id->id id->id) (hiding id ... id) environment using_cmd(parser & p) { environment env = p.env(); while (true) { name cls = parse_class(p); bool decls = cls.is_anonymous() || cls == g_decls || cls == g_declarations; auto pos = p.pos(); name ns = p.check_id_next("invalid 'using' command, identifier expected"); optional<name> real_ns = to_valid_namespace_name(env, ns); if (!real_ns) throw parser_error(sstream() << "invalid namespace name '" << ns << "'", pos); ns = *real_ns; env = using_namespace(env, p.ios(), ns, cls); if (decls) { // Remark: we currently to not allow renaming and hiding of universe levels buffer<name> exceptions; bool found_explicit = false; while (p.curr_is_token(g_lparen)) { p.next(); if (p.curr_is_token_or_id(g_renaming)) { p.next(); while (p.curr_is_identifier()) { name from_id = p.get_name_val(); p.next(); p.check_token_next(g_arrow, "invalid 'using' command renaming, '->' expected"); name to_id = p.check_id_next("invalid 'using' command renaming, identifier expected"); check_identifier(p, env, ns, from_id); exceptions.push_back(from_id); env = add_expr_alias(env, to_id, ns+from_id); } } else if (p.curr_is_token_or_id(g_hiding)) { p.next(); while (p.curr_is_identifier()) { name id = p.get_name_val(); p.next(); check_identifier(p, env, ns, id); exceptions.push_back(id); } } else if (p.curr_is_identifier()) { found_explicit = true; while (p.curr_is_identifier()) { name id = p.get_name_val(); p.next(); check_identifier(p, env, ns, id); env = add_expr_alias(env, id, ns+id); } } else { throw parser_error("invalid 'using' command option, identifier, 'hiding' or 'renaming' expected", p.pos()); } if (found_explicit && !exceptions.empty()) throw parser_error("invalid 'using' command option, mixing explicit and implicit 'using' options", p.pos()); p.check_token_next(g_rparen, "invalid 'using' command option, ')' expected"); } if (!found_explicit) env = add_aliases(env, ns, name(), exceptions.size(), exceptions.data()); } if (!p.curr_is_token(g_lbracket) && !p.curr_is_identifier()) break; } return env; }
environment opaque_hint_cmd(parser & p) { environment env = p.env(); bool found = false; while (p.curr_is_token(g_lparen)) { p.next(); bool hiding; auto pos = p.pos(); if (p.curr_is_token_or_id(g_hiding)) hiding = true; else if (p.curr_is_token_or_id(g_exposing)) hiding = false; else throw parser_error("invalid 'opaque_hint', 'hiding' or 'exposing' expected", pos); p.next(); while (!p.curr_is_token(g_rparen)) { if (p.curr_is_token(g_module)) { found = true; p.next(); env = set_hide_main_opaque(env, hiding); } else { name c = p.check_constant_next("invalid 'opaque_hint', constant expected"); found = true; if (hiding) env = hide_definition(env, c); else env = expose_definition(env, c); } } p.next(); } if (!found) throw exception("invalid empty 'opaque_hint' command"); return env; }
environment set_line_cmd(parser & p) { if (!p.curr_is_numeral()) throw parser_error("invalid #setline command, numeral expected", p.pos()); unsigned r = p.get_small_nat(); p.set_line(r); p.next(); return p.env(); }
static environment mixfix_cmd(parser & p, mixfix_kind k, bool overload, notation_entry_group grp, bool persistent) { notation_modifiers mods; mods.parse(p); flet<bool> set_allow_local(g_allow_local, !persistent); auto nt = parse_mixfix_notation(p, k, overload, grp, mods.m_parse_only, mods.m_priority); environment env = p.env(); if (nt.second) env = add_user_token(env, *nt.second, persistent); env = add_notation(env, nt.first, persistent); return env; }
static environment notation_cmd_core(parser & p, bool overload, notation_entry_group grp, bool persistent) { notation_modifiers mods; mods.parse(p); flet<bool> set_allow_local(g_allow_local, !persistent); environment env = p.env(); buffer<token_entry> new_tokens; auto ne = parse_notation_core(p, overload, grp, new_tokens, mods.m_parse_only, mods.m_priority); for (auto const & te : new_tokens) env = add_user_token(env, te, persistent); env = add_notation(env, ne, persistent); return env; }
static unsigned parse_precedence_core(parser & p) { auto pos = p.pos(); if (p.curr_is_numeral()) { return p.parse_small_nat(); } else { environment env = p.env(); env = open_prec_aliases(env); parser::local_scope scope(p, env); expr pre_val = p.parse_expr(get_max_prec()); pre_val = mk_typed_expr(mk_constant(get_num_name()), pre_val); expr val = std::get<0>(p.elaborate(pre_val, list<expr>())); val = normalize(p.env(), val); if (optional<mpz> mpz_val = to_num_core(val)) { if (!mpz_val->is_unsigned_int()) throw parser_error("invalid 'precedence', argument does not fit in a machine integer", pos); return mpz_val->get_unsigned_int(); } else { throw parser_error("invalid 'precedence', argument does not evaluate to a numeral", pos); } } }
environment print_cmd(parser & p) { if (p.curr() == scanner::token_kind::String) { p.regular_stream() << p.get_str_val() << endl; p.next(); } else if (p.curr_is_token_or_id(g_raw)) { p.next(); expr e = p.parse_expr(); p.regular_stream() << e << endl; } else if (p.curr_is_token_or_id(g_options)) { p.next(); p.regular_stream() << p.ios().get_options() << endl; } else { throw parser_error("invalid print command", p.pos()); } return p.env(); }
environment set_option_cmd(parser & p) { auto id_pos = p.pos(); name id = p.check_id_next("invalid set option, identifier (i.e., option name) expected"); auto decl_it = get_option_declarations().find(id); if (decl_it == get_option_declarations().end()) { // add "lean" prefix name lean_id = name("lean") + id; decl_it = get_option_declarations().find(lean_id); if (decl_it == get_option_declarations().end()) { throw parser_error(sstream() << "unknown option '" << id << "', type 'help options.' for list of available options", id_pos); } else { id = lean_id; } } option_kind k = decl_it->second.kind(); if (k == BoolOption) { if (p.curr_is_token_or_id(g_true)) p.set_option(id, true); else if (p.curr_is_token_or_id(g_false)) p.set_option(id, false); else throw parser_error("invalid Boolean option value, 'true' or 'false' expected", p.pos()); p.next(); } else if (k == StringOption) { if (!p.curr_is_string()) throw parser_error("invalid option value, given option is not a string", p.pos()); p.set_option(id, p.get_str_val()); p.next(); } else if (k == DoubleOption) { p.set_option(id, p.parse_double()); } else if (k == UnsignedOption || k == IntOption) { p.set_option(id, p.parse_small_nat()); } else { throw parser_error("invalid option value, 'true', 'false', string, integer or decimal value expected", p.pos()); } p.updt_options(); return p.env(); }
environment end_scoped_cmd(parser & p) { if (in_section(p.env())) p.pop_local_scope(); return pop_scope(p.env()); }
environment section_cmd(parser & p) { p.push_local_scope(); return push_scope(p.env(), p.ios()); }
static action parse_action(parser & p, name const & prev_token, unsigned default_prec, buffer<expr> & locals, buffer<token_entry> & new_tokens, notation_entry_group grp) { if (p.curr_is_token(get_colon_tk())) { p.next(); if (p.curr_is_numeral() || p.curr_is_token_or_id(get_max_tk())) { unsigned prec = parse_precedence(p); return mk_expr_action(prec); } else if (p.curr_is_token_or_id(get_prev_tk())) { p.next(); return mk_expr_action(get_precedence(p.env(), new_tokens, prev_token)); } else if (p.curr_is_token_or_id(get_scoped_tk())) { p.next(); return mk_scoped_expr_action(mk_var(0)); } else { p.check_token_next(get_lparen_tk(), "invalid notation declaration, '(', numeral or 'scoped' expected"); if (p.curr_is_token_or_id(get_foldl_tk()) || p.curr_is_token_or_id(get_foldr_tk())) { bool is_fold_right = p.curr_is_token_or_id(get_foldr_tk()); p.next(); auto prec = parse_optional_precedence(p); name sep = parse_quoted_symbol_or_token(p, new_tokens, grp); expr rec; { parser::local_scope scope(p); p.check_token_next(get_lparen_tk(), "invalid fold notation argument, '(' expected"); parse_notation_local(p, locals); parse_notation_local(p, locals); p.check_token_next(get_comma_tk(), "invalid fold notation argument, ',' expected"); rec = parse_notation_expr(p, locals); p.check_token_next(get_rparen_tk(), "invalid fold notation argument, ')' expected"); locals.pop_back(); locals.pop_back(); } optional<expr> ini; if (!p.curr_is_token(get_rparen_tk()) && !p.curr_is_quoted_symbol()) ini = parse_notation_expr(p, locals); optional<name> terminator; if (!p.curr_is_token(get_rparen_tk())) terminator = parse_quoted_symbol_or_token(p, new_tokens, grp); p.check_token_next(get_rparen_tk(), "invalid fold notation argument, ')' expected"); return mk_exprs_action(sep, rec, ini, terminator, is_fold_right, prec ? *prec : 0); } else if (p.curr_is_token_or_id(get_scoped_tk())) { p.next(); auto prec = parse_optional_precedence(p); expr rec; { parser::local_scope scope(p); parse_notation_local(p, locals); p.check_token_next(get_comma_tk(), "invalid scoped notation argument, ',' expected"); rec = parse_notation_expr(p, locals); locals.pop_back(); } p.check_token_next(get_rparen_tk(), "invalid scoped notation argument, ')' expected"); return mk_scoped_expr_action(rec, prec ? *prec : 0); } else { throw parser_error("invalid notation declaration, 'foldl', 'foldr' or 'scoped' expected", p.pos()); } } } else { return mk_expr_action(default_prec); } }
static environment precedence_cmd(parser & p) { std::string tk = parse_symbol(p, "invalid precedence declaration, quoted symbol or identifier expected"); p.check_token_next(get_colon_tk(), "invalid precedence declaration, ':' expected"); unsigned prec = parse_precedence(p); return add_user_token(p.env(), tk.c_str(), prec); }
static notation_entry parse_notation_core(parser & p, bool overload, notation_entry_group grp, buffer<token_entry> & new_tokens, bool parse_only, unsigned priority) { buffer<expr> locals; buffer<transition> ts; parser::local_scope scope(p); bool is_nud = true; optional<parse_table> pt; optional<parse_table> reserved_pt; if (p.curr_is_numeral()) { lean_assert(p.curr_is_numeral()); mpz num = p.get_num_val().get_numerator(); p.next(); p.check_token_next(get_assign_tk(), "invalid numeral notation, `:=` expected"); auto e_pos = p.pos(); expr e = p.parse_expr(); check_notation_expr(e, e_pos); return notation_entry(num, e, overload, parse_only); } else if (p.curr_is_identifier()) { parse_notation_local(p, locals); is_nud = false; pt = get_led_table(p.env()); if (grp != notation_entry_group::Reserve) reserved_pt = get_reserved_led_table(p.env()); } else { pt = get_nud_table(p.env()); if (grp != notation_entry_group::Reserve) reserved_pt = get_reserved_nud_table(p.env()); } bool used_default = false; while ((grp != notation_entry_group::Reserve && !p.curr_is_token(get_assign_tk())) || (grp == notation_entry_group::Reserve && !p.curr_is_command() && !p.curr_is_eof())) { name pp_tk = parse_quoted_symbol_or_token(p, new_tokens, used_default, grp).to_string(); name tk = utf8_trim(pp_tk.to_string()); if (auto at = find_next(reserved_pt, tk)) { // Remark: we are ignoring multiple actions in the reserved notation table transition const & trans = head(at).first; action const & a = trans.get_action(); reserved_pt = head(at).second; if (!p.curr_is_quoted_symbol()) pp_tk = trans.get_pp_token(); switch (a.kind()) { case notation::action_kind::Skip: if (!p.curr_is_quoted_symbol() && !p.curr_is_keyword() && !p.curr_is_token(get_assign_tk())) { if (g_allow_local && !p.curr_is_token_or_id(get_binders_tk())) { ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk)); break; } p.check_token_or_id_next(get_binders_tk(), "invalid notation declaration, quoted-symbol, keyword or `:=` expected " "(declaration prefix matches reserved notation)"); } ts.push_back(transition(tk, a, pp_tk)); break; case notation::action_kind::Binder: if (g_allow_local && !p.curr_is_token_or_id(get_binder_tk())) { ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk)); break; } p.check_token_or_id_next(get_binder_tk(), "invalid notation declaration, 'binder' expected " "(declaration prefix matches reserved notation)"); ts.push_back(transition(tk, a, pp_tk)); break; case notation::action_kind::Binders: if (g_allow_local && !p.curr_is_token_or_id(get_binders_tk())) { ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk)); break; } p.check_token_or_id_next(get_binders_tk(), "invalid notation declaration, 'binders' expected " "(declaration prefix matches reserved notation)"); ts.push_back(transition(tk, a, pp_tk)); break; case notation::action_kind::Expr: case notation::action_kind::Exprs: case notation::action_kind::ScopedExpr: case notation::action_kind::Ext: { if (g_allow_local && !p.curr_is_identifier()) { ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk)); break; } name n = p.check_id_next("invalid notation declaration, identifier expected " "(declaration prefix matches reserved notation)"); if (p.curr_is_token(get_colon_tk())) { if (g_allow_local) { unsigned default_prec = get_default_prec(pt, tk); action a = parse_action(p, tk, default_prec, locals, new_tokens, grp); expr local_type = mk_Prop(); // type used in notation local declarations, it is irrelevant expr l = mk_local(n, local_type); p.add_local(l); locals.push_back(l); ts.push_back(transition(tk, a, pp_tk)); break; } else { throw parser_error("invalid notation declaration, invalid ':' occurrence " "(declaration prefix matches reserved notation)", p.pos()); } } else { expr local_type = mk_Prop(); // type used in notation local declarations, it is irrelevant expr l = mk_local(n, local_type); p.add_local(l); locals.push_back(l); ts.push_back(transition(tk, a, pp_tk)); break; } }} } else { reserved_pt = optional<parse_table>(); ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk)); } pt = find_match(pt, ts.back()); } // for atomic notation where binding power was not set, we set it to max if (used_default && ts.size() == 1 && ts.back().get_action().kind() == notation::action_kind::Skip) { lean_assert(!new_tokens.empty()); new_tokens.back().m_prec = get_max_prec(); } expr n; if (grp == notation_entry_group::Reserve) { // reserve notation commands do not have a denotation lean_assert(p.curr_is_command() || p.curr_is_eof()); expr dummy = mk_Prop(); // any expression without free variables will do n = dummy; } else { lean_assert(p.curr_is_token(get_assign_tk())); p.next(); if (ts.empty()) throw parser_error("invalid notation declaration, empty notation is not allowed", p.pos()); n = parse_notation_expr(p, locals); } return notation_entry(is_nud, to_list(ts.begin(), ts.end()), n, overload, priority, grp, parse_only); }