feat(library/system/io,shell/lean): add --run switch

This commit is contained in:
Gabriel Ebner 2017-04-07 14:16:00 +02:00 committed by Leonardo de Moura
parent e1fae7b761
commit e2fa363423
12 changed files with 284 additions and 109 deletions

View file

@ -13,6 +13,7 @@ inductive io.error
structure io.terminal (m : Type → Type → Type) :=
(put_str : string → m io.error unit)
(get_line : m io.error string)
(cmdline_args : list string)
inductive io.mode
| read | write | read_write | append
@ -77,6 +78,9 @@ put_str s >> put_str "\n"
def get_line : io string :=
interface.term.get_line
def cmdline_args : io (list string) :=
return interface.term.cmdline_args
def print {α} [has_to_string α] (s : α) : io unit :=
put_str ∘ to_string $ s

View file

@ -6,6 +6,7 @@ Author: Leonardo de Moura
*/
#include <algorithm>
#include <string>
#include "library/eval_helper.h"
#include "util/timeit.h"
#include "util/sstream.h"
#include "util/sexpr/option_declarations.h"
@ -423,34 +424,6 @@ static environment compile_expr(environment const & env, name const & n, level_p
return vm_compile(new_env, new_env.get(n));
}
static void eval_core(vm_state & s, name const & main, bool is_io) {
vm_decl d = *s.get_decl(main);
if (!is_io && d.get_arity() > 0)
throw exception("eval result is a function");
if (is_io) {
s.push(mk_vm_simple(0)); // "world state"
s.push(mk_io_interface());
}
s.invoke_fn(main);
if (is_io) {
if (d.get_arity() == 1) {
/* main returned a closure, it did not process initial_state yet.
So, we force the execution. */
s.apply();
}
vm_obj r = s.top();
if (optional<vm_obj> error = is_io_error(r)) {
std::string msg = io_error_to_string(*error);
throw exception(msg);
}
}
}
static optional<expr> find_io_interface_local(expr const & e) {
return find(e, [&](expr const & e, unsigned) {
return is_local(e) && is_constant(mlocal_type(e), get_io_interface_name());
});
}
static environment eval_cmd(parser & p) {
auto pos = p.pos();
@ -458,60 +431,69 @@ static environment eval_cmd(parser & p) {
std::tie(e, ls) = parse_local_expr(p, "_eval");
if (has_metavar(e))
throw parser_error("invalid eval command, expression contains metavariables", pos);
type_context tc(p.env(), transparency_mode::All);
expr type0 = tc.infer(e);
expr type = type0; // TODO(Leo): tc.whnf(type0);
bool is_io = is_constant(get_app_fn(type), get_io_name());
bool is_string = false;
if (optional<expr> io_interface = find_io_interface_local(e)) {
if (!is_io)
throw parser_error("invalid eval command, it depends on io.interface local variable, but it is not an io action", pos);
e = Fun(*io_interface, e);
type = Pi(*io_interface, type);
} else if (is_io) {
throw parser_error("invalid eval command, failed to locate io.interface local variable", pos);
auto type = tc.infer(e);
/* Check if resultant type has an instance of has_to_string */
try {
expr has_to_string_type = mk_app(tc, get_has_to_string_name(), type);
optional<expr> to_string_instance = tc.mk_class_instance(has_to_string_type);
if (to_string_instance) {
/* Modify the 'program' to (to_string e) */
e = mk_app(tc, get_to_string_name(), type, *to_string_instance, e);
type = tc.infer(e);
}
} catch (exception &) {}
// Close under locals
collected_locals locals;
collect_locals(e, locals);
for (auto & l : locals.get_collected()) {
e = Fun(l, e);
type = Pi(l, type);
}
if (!is_io) {
/* Check if resultant type has an instance of has_to_string */
try {
expr has_to_string_type = mk_app(tc, get_has_to_string_name(), type0);
optional<expr> to_string_instance = tc.mk_class_instance(has_to_string_type);
if (to_string_instance) {
/* Modify the 'program' to (to_string e) */
e = mk_app(tc, get_to_string_name(), type0, *to_string_instance, e);
type = tc.infer(e);
is_string = true;
}
} catch (exception &) {}
}
name main("_main");
environment new_env = compile_expr(p.env(), main, ls, type, e, pos);
vm_state s(new_env, p.get_options());
name fn_name = "_main";
auto new_env = compile_expr(p.env(), fn_name, ls, type, e, pos);
auto out = p.mk_message(p.cmd_pos(), INFORMATION);
out.set_caption("eval result");
vm_state::profiler prof(s, p.get_options());
// TODO(gabriel): capture output
scope_traces_as_messages scope_traces(p.get_stream_name(), p.cmd_pos());
bool should_report = false;
auto run = [&] {
eval_helper fn(new_env, p.get_options(), fn_name);
fn.dependency_injection();
try {
if (!fn.try_exec()) {
auto r = fn.invoke_fn();
should_report = true;
if (is_constant(fn.get_type(), get_string_name())) {
out << to_string(r);
} else {
display(out.get_text_stream().get_stream(), r);
}
}
} catch (throwable & t) {
p.mk_message(p.cmd_pos(), ERROR).set_exception(t).report();
}
if (fn.get_profiler().enabled()) {
out << "\n";
fn.get_profiler().get_snapshots().display(out.get_text_stream().get_stream());
should_report = true;
}
};
if (p.profiling()) {
timeit timer(out.get_text_stream().get_stream(), "eval time");
eval_core(s, main, is_io);
run();
should_report = true;
} else {
eval_core(s, main, is_io);
run();
}
if (is_io) {
// do not print anything
} else if (is_string) {
vm_obj r = s.get(0);
out << to_string(r);
} else {
/* if it is not IO nor a string, then display object on top of the stack using vm_obj display method */
vm_obj r = s.get(0);
display(out.get_text_stream().get_stream(), r);
}
if (prof.enabled()) {
out << "\n";
prof.get_snapshots().display(out.get_text_stream().get_stream());
}
out.report();
if (should_report) out.report();
return p.env();
}

View file

@ -18,6 +18,7 @@ add_library(library OBJECT deep_copy.cpp expr_lt.cpp io_state.cpp
locals.cpp normalize.cpp discr_tree.cpp
mt_task_queue.cpp st_task_queue.cpp
library_task_builder.cpp
eval_helper.cpp
messages.cpp message_builder.cpp module_mgr.cpp comp_val.cpp
documentation.cpp check.cpp arith_instance.cpp parray.cpp process.cpp
pipe.cpp handle.cpp)

View file

@ -0,0 +1,89 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include "library/eval_helper.h"
#include "library/tactic/tactic_state.h"
namespace lean {
eval_helper::eval_helper(environment const & env, options const & opts, name const & fn) :
m_env(env), m_opts(opts), m_tc(env, opts, transparency_mode::None),
m_vms(env, opts), m_prof(m_vms, opts), m_fn(fn) {
auto d = env.get(m_fn);
m_ty = m_tc.whnf(d.get_type());
if (auto vm_decl = m_vms.get_decl(m_fn)) {
m_arity = vm_decl->get_arity();
} else {
throw exception(sstream() << "no vm declaration found for " << m_fn);
}
m_io_iface = m_tc.push_local(
"_vm_io_iface", mk_constant(get_io_interface_name(), {}),
mk_inst_implicit_binder_info());
}
void eval_helper::dependency_injection() {
while (is_pi(m_ty)) {
auto arg_ty = m_tc.whnf(binding_domain(m_ty));
optional<expr> arg;
if (is_constant(get_app_fn(arg_ty), get_io_interface_name())) {
m_args.push_back(mk_io_interface(m_cmdline_args));
arg = m_io_iface;
}
if (arg) {
m_ty = m_tc.whnf(instantiate(binding_body(m_ty), *arg));
} else {
break;
}
}
}
vm_obj eval_helper::invoke_fn() {
std::reverse(m_args.begin(), m_args.end());
return m_vms.invoke(m_fn, m_args.size(), m_args.data());
}
optional<vm_obj> eval_helper::try_exec_io() {
if (is_constant(get_app_fn(m_ty), get_io_name()) && app_arg(app_fn(m_ty)) == m_io_iface) {
m_args.push_back(mk_vm_simple(0)); // "world state"
auto r = invoke_fn();
if (auto error = is_io_error(r)) {
throw exception(io_error_to_string(*error));
} else if (auto result = is_io_result(r)) {
return result;
} else {
throw exception("unexpected vm result of io expression");
}
}
return optional<vm_obj>();
}
optional<vm_obj> eval_helper::try_exec_tac() {
if (is_constant(get_app_fn(m_ty), get_tactic_name())) {
auto tac_st = mk_tactic_state_for(m_env, m_opts, m_fn, m_tc.mctx(), m_tc.lctx(), mk_true());
m_vms.push(tactic::to_obj(tac_st));
auto r = invoke_fn();
if (tactic::is_result_success(r)) {
return optional<vm_obj>(tactic::get_result_value(r));
} else if (auto ex = tactic::is_exception(m_vms, r)) {
throw formatted_exception(std::get<1>(*ex), std::get<0>(*ex));
} else {
throw exception("tactic failed");
}
}
return optional<vm_obj>();
}
optional<vm_obj> eval_helper::try_exec() {
if (auto res = try_exec_io()) return res;
if (auto res = try_exec_tac()) return res;
return optional<vm_obj>();
}
}

53
src/library/eval_helper.h Normal file
View file

@ -0,0 +1,53 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include "library/vm/vm_io.h"
#include "library/type_context.h"
#include "library/vm/vm.h"
#include "library/constants.h"
#include <string>
#include <vector>
namespace lean {
class eval_helper {
environment m_env;
options m_opts;
type_context m_tc;
std::vector<std::string> m_cmdline_args;
buffer<vm_obj> m_args;
vm_state m_vms;
vm_state::profiler m_prof;
name m_fn;
expr m_ty;
unsigned m_arity;
expr m_io_iface;
public:
eval_helper(environment const & env, options const & opts, name const & fn);
void set_cmdline_args(std::vector<std::string> const & cmdline_args) {
m_cmdline_args = cmdline_args;
}
void dependency_injection();
vm_obj invoke_fn();
expr const & get_type() const { return m_ty; }
vm_state::profiler & get_profiler() { return m_prof; }
optional<vm_obj> try_exec_io();
optional<vm_obj> try_exec_tac();
optional<vm_obj> try_exec();
};
}

View file

@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <string>
#include <vector>
#include <cstdio>
#include <iostream>
#include "util/sstream.h"
@ -47,17 +48,26 @@ static vm_obj io_get_line(vm_obj const &) {
return mk_io_result(to_obj(str));
}
static vm_obj cmdline_args_to_obj(std::vector<std::string> const & ss) {
buffer<vm_obj> objs;
for (auto & s : ss) objs.push_back(to_obj(s));
return to_obj(objs);
}
/*
structure io.terminal (m : Type Type Type) :=
(put_str : string m io.error unit)
(get_line : m io.error string)
(cmdline_args : list string)
*/
static vm_obj mk_terminal() {
vm_obj fields[2] = {
static vm_obj mk_terminal(std::vector<std::string> const & cmdline_args) {
constexpr size_t num_fields = 3;
vm_obj fields[num_fields] = {
mk_native_closure(io_put_str),
mk_native_closure(io_get_line)
mk_native_closure(io_get_line),
cmdline_args_to_obj(cmdline_args),
};
return mk_vm_constructor(0, 2, fields);
return mk_vm_constructor(0, num_fields, fields);
}
struct vm_handle : public vm_external {
@ -397,18 +407,23 @@ class io.interface :=
(fs : io.file_system handle m)
(process : io.process io.error handle m)
*/
vm_obj mk_io_interface() {
vm_obj fields[8] = {
vm_obj mk_io_interface(std::vector<std::string> const & cmdline_args) {
constexpr size_t num_fields = 8;
vm_obj fields[num_fields] = {
mk_native_closure(io_m), /* TODO(Leo): delete after we improve code generator */
mk_native_closure(io_monad),
mk_native_closure(io_catch),
mk_native_closure(io_fail),
mk_native_closure(io_iterate),
mk_terminal(),
mk_terminal(cmdline_args),
mk_fs(),
mk_process()
mk_process(),
};
return mk_vm_constructor(0, 8, fields);
return mk_vm_constructor(0, num_fields, fields);
}
vm_obj mk_io_interface() {
return mk_io_interface({});
}
optional<vm_obj> is_io_result(vm_obj const & o) {

View file

@ -6,12 +6,14 @@ Author: Leonardo de Moura
*/
#pragma once
#include <string>
#include <vector>
#include "library/vm/vm.h"
#include "library/handle.h"
namespace lean {
vm_obj mk_io_result(vm_obj const & r);
vm_obj mk_io_interface();
vm_obj mk_io_interface(std::vector<std::string> const & cmdline_args);
/* The io monad produces a result object, or an error.
If `o` is a result, then we return the result value. */
optional<vm_obj> is_io_result(vm_obj const & o);

View file

@ -12,6 +12,7 @@ Author: Leonardo de Moura
#include <string>
#include <utility>
#include <vector>
#include "library/eval_helper.h"
#include "util/timer.h"
#include "util/task_builder.h"
#include "util/realpath.h"
@ -95,6 +96,7 @@ static void display_help(std::ostream & out) {
std::cout << " --help -h display this message\n";
std::cout << " --version -v display version number\n";
std::cout << " --githash display the git commit hash number used to build this binary\n";
std::cout << " --run executes the 'main' definition\n";
std::cout << " --path display the path used for finding Lean libraries and extensions\n";
std::cout << " --doc=file -r generate module documentation based on module doc strings\n";
std::cout << " --make create olean files\n";
@ -129,6 +131,7 @@ static void display_help(std::ostream & out) {
static struct option g_long_options[] = {
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"run", required_argument, 0, 'a'},
{"smt2", no_argument, 0, 'Y'},
{"path", no_argument, 0, 'p'},
{"githash", no_argument, 0, 'g'},
@ -350,6 +353,7 @@ int main(int argc, char ** argv) {
optional<std::string> export_txt;
optional<std::string> doc;
optional<std::string> server_in;
optional<std::string> run_arg;
std::string native_output;
while (true) {
int c = getopt_long(argc, argv, g_opt_str, g_long_options, NULL);
@ -368,6 +372,9 @@ int main(int argc, char ** argv) {
case 'h':
display_help(std::cout);
return 0;
case 'a':
run_arg = optarg;
break;
case 'Y':
smt2 = true;
break;
@ -449,6 +456,10 @@ int main(int argc, char ** argv) {
display_help(std::cerr);
return 1;
}
if (run_arg) {
// treat run_arg as the last arg
break;
}
}
if (auto max_memory = opts.get_unsigned(get_max_memory_opt_name(),
@ -516,6 +527,49 @@ int main(int argc, char ** argv) {
}
try {
std::shared_ptr<task_queue> tq;
#if defined(LEAN_MULTI_THREAD)
if (num_threads == 0) {
tq = std::make_shared<st_task_queue>();
} else {
tq = std::make_shared<mt_task_queue>(num_threads);
}
#else
tq = std::make_shared<st_task_queue>();
#endif
set_task_queue(tq.get());
fs_module_vfs vfs;
module_mgr mod_mgr(&vfs, lt.get_root(), env, ios);
if (run_arg) {
auto mod = mod_mgr.get_module(lrealpath(run_arg->c_str()));
if (!mod) throw exception(sstream() << "could not load " << *run_arg);
auto main_env = get(get(mod->m_result).m_loaded_module->m_env);
auto main_opts = get(mod->m_result).m_opts;
eval_helper fn(main_env, main_opts, "main");
fn.set_cmdline_args({argv + optind, argv + argc});
type_context tc(main_env, main_opts);
scope_trace_env scope2(main_env, main_opts, tc);
try {
fn.dependency_injection();
if (fn.try_exec()) {
return 0;
} else {
throw exception(sstream() << *run_arg << ": cannot execute main function with type "
<< ios.get_formatter_factory()(main_env, main_opts, tc)(fn.get_type()));
}
} catch (std::exception & ex) {
std::cerr << ex.what() << std::endl;
return 1;
}
}
mod_mgr.set_save_olean(make_mode);
std::vector<std::string> args(argv + optind, argv + argc);
if (recursive) {
if (args.empty()) args.push_back(".");
@ -538,27 +592,11 @@ int main(int argc, char ** argv) {
std::vector<std::string> module_args;
for (auto & f : args) module_args.push_back(lrealpath(f.c_str()));
std::shared_ptr<task_queue> tq;
#if defined(LEAN_MULTI_THREAD)
if (num_threads == 0) {
tq = std::make_shared<st_task_queue>();
} else {
tq = std::make_shared<mt_task_queue>(num_threads);
}
#else
tq = std::make_shared<st_task_queue>();
#endif
set_task_queue(tq.get());
fs_module_vfs vfs;
if (!recursive) {
for (auto & mod_id : module_args)
vfs.m_modules_to_load_from_source.insert(mod_id);
}
module_mgr mod_mgr(&vfs, lt.get_root(), env, ios);
mod_mgr.set_save_olean(make_mode);
bool ok = true;
if (only_deps) {

View file

@ -2,11 +2,7 @@
#"a"
#"\n"
#"\\"
aaa\
aaa'

View file

@ -1,7 +1,5 @@
onetwothree
---------
in
in
in
@ -10,7 +8,6 @@ out
out
out
---------
in
in
in

View file

@ -1,3 +1,2 @@
"t1"
"t2"

View file

@ -1,4 +1,3 @@
hello
world
from Lean