/* Copyright (c) 2017 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Sebastian Ullrich */ #include #include "kernel/abstract.h" #include "library/constants.h" #include "library/attribute_manager.h" #include "library/tactic/elaborator_exception.h" #include "library/string.h" #include "library/vm/vm_expr.h" #include "library/vm/vm_parser.h" #include "library/quote.h" #include "library/placeholder.h" #include "frontends/lean/elaborator.h" namespace lean { static environment add_user_command(environment const & env, name const & d) { auto type = env.get(d).get_type(); bool takes_meta_infos = false; if (is_binding(type) && is_constant(binding_domain(type), {"interactive", "decl_meta_info"})) { takes_meta_infos = true; type = binding_body(type); } std::string tk; if (is_binding(type) && is_app_of(binding_domain(type), get_interactive_parse_name(), 3)) { auto const & parser = app_arg(binding_domain(type)); if (is_app_of(parser, get_lean3_parser_tk_name(), 1)) { if (auto lit = to_string(app_arg(parser))) { tk = *lit; type = binding_body(type); } else { throw elaborator_exception(app_arg(parser), "invalid user-defined command, token must be a name literal"); } } } else { throw exception("invalid user-defined command, must take `interactive.parse (lean.parser.tk c)` parameter, " "optionally preceded by `interactive.decl_meta_info` parameter"); } if (!(is_app_of(type, get_lean3_parser_name(), 1) && is_constant(app_arg(type), get_unit_name()))) { throw exception("invalid user-defined command, must return type `lean.parser unit`"); } expr dummy = mk_true(); auto run = [=](parser & p, cmd_meta const & meta) { expr parser = mk_constant(d); // we don't want to reflect `meta` into an expr, so abstract the parameter and pass it as a vm_obj arg if (takes_meta_infos) { parser = mk_app(parser, mk_var(0)); } // `parse (tk c)` arg parser = mk_app(parser, mk_constant(get_unit_star_name())); for (expr t = type; is_pi(t); t = binding_body(t)) { expr arg_type = binding_domain(t); if (is_app_of(arg_type, get_interactive_parse_name())) { parser = mk_app(parser, parse_interactive_param(p, arg_type)); } else { expr e = p.parse_expr(get_max_prec()); if (!closed(e) || has_local(e)) { throw elaborator_exception(e, "invalid argument to user-defined command, must be closed term"); } parser = mk_app(parser, e); } } buffer args; if (takes_meta_infos) { parser = mk_lambda("a", mk_expr_placeholder(), parser); args.push_back(to_obj(meta)); } parser = p.elaborate("_user_command", {}, parser).first; run_parser(p, parser, args); return p.env(); }; if (takes_meta_infos) { return add_command(env, tk, cmd_info(tk, "description", run)); } else { return add_command(env, tk, cmd_info(tk, "description", [=](parser & p) { return run(p, {}); })); } } struct user_command_modification : public modification { LEAN_MODIFICATION("USR_CMD") name m_name; user_command_modification() {} user_command_modification(name const & name) : m_name(name) {} void perform(environment & env) const override { env = add_user_command(env, m_name); } void serialize(serializer & s) const override { s << m_name; } static std::shared_ptr deserialize(deserializer & d) { return std::make_shared(read_name(d)); } }; void initialize_user_command() { user_command_modification::init(); register_system_attribute(basic_attribute( "user_command", "user-defined command", [](environment const & env, io_state const &, name const & d, unsigned, bool persistent) { if (persistent) { return module::add_and_perform(env, std::make_shared(d)); } else { throw exception("[user_command] cannot be used locally"); } })); } void finalize_user_command() { user_command_modification::finalize(); } }