lean4-htt/src/library/vm/vm_io.cpp
2018-10-02 08:44:05 -07:00

412 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#ifdef _MSC_VER
#include <direct.h>
#define getcwd _getcwd
#define PATH_MAX _MAX_PATH
#else
#include <unistd.h>
#endif
#ifdef __linux__
#include <linux/limits.h>
#endif
#include <util/unit.h>
#include "runtime/sstream.h"
#include "library/handle.h"
#include "library/io_state.h"
#include "library/constants.h"
#include "library/process.h"
#include "library/tactic/tactic_state.h"
#include "library/vm/vm.h"
#include "library/vm/vm_array.h"
#include "library/vm/vm_string.h"
#include "library/vm/vm_nat.h"
#include "library/vm/vm_option.h"
#include "library/vm/vm_io.h"
namespace lean {
static vm_obj const REAL_WORLD = mk_vm_simple(0);
vm_obj mk_io_result(vm_obj const & r) {
return mk_vm_constructor(0, r, REAL_WORLD);
}
vm_obj get_io_result(vm_obj const & r) {
return cfield(r, 0);
}
vm_obj run_io(vm_obj const & act) {
auto r = invoke(act, REAL_WORLD);
return cfield(r, 0);
}
vm_obj mk_ioe_result(vm_obj const & r) {
return mk_io_result(mk_vm_constructor(1, r));
}
vm_obj mk_ioe_failure(vm_obj const & e) {
return mk_io_result(mk_vm_constructor(0, e));
}
vm_obj mk_ioe_failure(std::string const & s) {
return mk_ioe_failure(to_obj(s));
}
vm_obj mk_ioe_failure(sstream const & s) {
return mk_ioe_failure(s.str());
}
static vm_obj io_put_str(vm_obj const & str, vm_obj const &) {
if ((get_global_ios().get_regular_stream() << to_string(str)).bad())
return mk_ioe_failure("io.put_str failed");
else
return mk_ioe_result(mk_vm_unit());
}
static vm_obj io_get_line(vm_obj const &) {
if (get_global_ios().get_options().get_bool("server"))
return mk_ioe_failure("io.get_line: cannot read from stdin in server mode");
std::string str;
std::getline(std::cin, str);
if (std::cin.bad())
return mk_ioe_failure("io.get_line failed");
else
return mk_ioe_result(to_obj(str));
}
struct vm_handle : public vm_external {
handle_ref m_handle;
vm_handle(handle_ref const & h):m_handle(h) {}
vm_handle(handle_ref && h):m_handle(std::move(h)) {}
virtual ~vm_handle() {}
virtual void dealloc() override { delete this; }
virtual vm_external * clone(vm_clone_fn const &) override { return new vm_handle(m_handle); }
virtual vm_external * ts_clone(vm_clone_fn const &) override { lean_unreachable(); }
};
static handle_ref const & to_handle(vm_obj const & o) {
lean_vm_check(dynamic_cast<vm_handle*>(to_external(o)));
return static_cast<vm_handle*>(to_external(o))->m_handle;
}
static vm_obj to_obj(handle_ref && h) {
return mk_vm_external(new vm_handle(std::move(h)));
}
/*
struct vm_child : public vm_external {
std::shared_ptr<child> m_child;
vm_child(std::shared_ptr<child> && h):m_child(std::move(h)) {}
vm_child(std::shared_ptr<child> const & h):m_child(h) {}
virtual ~vm_child() {}
virtual void dealloc() override { delete this; }
virtual vm_external * clone(vm_clone_fn const &) override { return new vm_child(m_child); }
virtual vm_external * ts_clone(vm_clone_fn const &) override { lean_unreachable(); }
};
std::shared_ptr<child> const & to_child(vm_obj const & o) {
lean_vm_check(dynamic_cast<vm_child*>(to_external(o)));
return static_cast<vm_child*>(to_external(o))->m_child;
}
static vm_obj to_obj(std::shared_ptr<child> && h) {
return mk_vm_external(new vm_child(std::move(h)));
}
*/
/*
inductive io.mode
| read | write | read_write | append
*/
char const * to_c_io_mode(vm_obj const & mode, vm_obj const & bin) {
bool is_bin = to_bool(bin);
switch (cidx(mode)) {
case 0: return is_bin ? "rb" : "r";
case 1: return is_bin ? "wb" : "w";
case 2: return is_bin ? "r+b" : "r+";
case 3: return is_bin ? "ab" : "a";
}
lean_vm_check(false);
lean_unreachable();
}
/* (mk_file_handle : string → io.mode → bool → ioe handle) */
static vm_obj fs_mk_file_handle(vm_obj const & fname, vm_obj const & m, vm_obj const & bin, vm_obj const &) {
FILE * h = fopen(to_string(fname).c_str(), to_c_io_mode(m, bin));
if (h != nullptr)
return mk_ioe_result(to_obj(std::make_shared<handle>(h)));
else
return mk_ioe_failure(sstream() << "failed to open file '" << to_string(fname) << "'");
}
static vm_obj mk_handle_has_been_closed_error() {
return mk_ioe_failure("invalid io action, handle has been closed");
}
static vm_obj fs_is_eof(vm_obj const & h, vm_obj const &) {
handle_ref const & href = to_handle(h);
if (href->is_closed()) return mk_handle_has_been_closed_error();
bool r = feof(href->m_file) != 0;
return mk_ioe_result(mk_vm_bool(r));
}
static vm_obj fs_flush(vm_obj const & h, vm_obj const &) {
handle_ref const & href = to_handle(h);
if (href->is_closed()) {
return mk_handle_has_been_closed_error();
}
try {
href->flush();
return mk_ioe_result(mk_vm_unit());
} catch (handle_exception e) {
return mk_ioe_failure("flush failed");
}
}
static vm_obj fs_close(vm_obj const & h, vm_obj const &) {
handle_ref const & href = to_handle(h);
if (href->is_closed()) {
return mk_handle_has_been_closed_error();
}
if (href->is_stdin())
return mk_ioe_failure("close failed, stdin cannot be closed");
if (href->is_stdout())
return mk_ioe_failure("close failed, stdout cannot be closed");
if (href->is_stderr())
return mk_ioe_failure("close failed, stderr cannot be closed");
try {
href->close();
return mk_ioe_result(mk_vm_unit());
} catch (handle_exception e) {
return mk_ioe_failure("close failed");
}
}
static vm_obj fs_read(vm_obj const & h, vm_obj const & n, vm_obj const &) {
handle_ref const & href = to_handle(h);
if (href->is_closed()) return mk_handle_has_been_closed_error();
buffer<char> tmp;
unsigned num = force_to_unsigned(n); /* TODO(Leo): handle size_t */
tmp.resize(num, 0);
size_t sz = fread(tmp.data(), 1, num, href->m_file);
if (ferror(href->m_file)) {
clearerr(href->m_file);
return mk_ioe_failure("read failed");
}
return mk_ioe_result(to_obj(std::string(tmp.data(), sz)));
}
static vm_obj fs_write(vm_obj const & h, vm_obj const & b, vm_obj const &) {
handle_ref const & href = to_handle(h);
if (href->is_closed()) {
return mk_handle_has_been_closed_error();
}
try {
href->write(to_string(b));
return mk_ioe_result(mk_vm_unit());
} catch (handle_exception e) {
return mk_ioe_failure("write failed");
}
}
static vm_obj fs_get_line(vm_obj const & h, vm_obj const &) {
handle_ref const & href = to_handle(h);
if (href->is_closed()) {
return mk_handle_has_been_closed_error();
}
std::string r;
while (true) {
int c = fgetc(href->m_file);
if (ferror(href->m_file)) {
clearerr(href->m_file);
return mk_ioe_failure("get_line failed");
}
if (c == EOF)
break;
r.push_back(static_cast<char>(c));
if (c == '\n')
break;
}
return mk_ioe_result(to_obj(r));
}
static vm_obj fs_stdin(vm_obj const &) {
return mk_io_result(to_obj(std::make_shared<handle>(stdin)));
}
static vm_obj fs_stdout(vm_obj const &) {
return mk_io_result(to_obj(std::make_shared<handle>(stdout)));
}
static vm_obj fs_stderr(vm_obj const &) {
return mk_io_result(to_obj(std::make_shared<handle>(stderr)));
}
/*
stdio to_stdio(vm_obj const & o) {
switch (cidx(o)) {
case 0:
return stdio::PIPED;
case 1:
return stdio::INHERIT;
case 2:
return stdio::NUL;
default:
lean_unreachable()
}
}
static vm_obj io_process_wait(vm_obj const & ch, vm_obj const &) {
return mk_io_result(mk_vm_nat(to_child(ch)->wait()));
}
*/
/* (iterate : Π (α β : Type), α → (α → io (sum α β)) → io β) */
static vm_obj io_iterate(vm_obj const &, vm_obj const &, vm_obj const & a, vm_obj const & fn, vm_obj const &) {
vm_obj r = a;
while (true) {
vm_obj sum = cfield(invoke(fn, r, REAL_WORLD), 0);
if (cidx(sum) == 1) {
return mk_io_result(cfield(sum, 0));
} else {
r = cfield(sum, 0);
}
}
}
/*
static vm_obj io_get_env(vm_obj const & k, vm_obj const &) {
if (auto v = getenv(to_string(k).c_str())) {
return mk_io_result(mk_vm_some(to_obj(std::string(v))));
} else {
return mk_io_result(mk_vm_none());
}
}
static vm_obj io_get_cwd(vm_obj const &) {
char buffer[PATH_MAX];
auto cwd = getcwd(buffer, sizeof(buffer));
if (cwd) {
return mk_io_result(to_obj(std::string(cwd)));
} else {
return mk_ioe_failure("get_cwd failed");
}
}
static vm_obj io_set_cwd(vm_obj const & cwd, vm_obj const &) {
if (chdir(to_string(cwd).c_str()) == 0) {
return mk_io_result(mk_vm_unit());
} else {
return mk_ioe_failure("set_cwd failed");
}
}
*/
static std::vector<std::string> * g_cmdline_args = nullptr;
static vm_obj to_list(buffer<vm_obj> const & ls) {
vm_obj obj = mk_vm_simple(0);
for (unsigned i = ls.size(); i > 0; i--)
obj = mk_vm_constructor(1, ls[i - 1], obj);
return obj;
}
static vm_obj io_cmdline_args() {
buffer<vm_obj> objs;
for (auto & s : *g_cmdline_args) objs.push_back(to_obj(s));
return to_list(objs);
}
void set_io_cmdline_args(std::vector<std::string> const & args) {
*g_cmdline_args = args;
}
optional<vm_obj> is_ioe_result(vm_obj const & o) {
auto r = cfield(o, 0);
if (cidx(o) == 0)
return some(cfield(o, 0));
else
return optional<vm_obj>();
}
optional<vm_obj> is_ioe_error(vm_obj const & o) {
auto r = cfield(o, 0);
if (cidx(o) == 1)
return some(cfield(o, 0));
else
return optional<vm_obj>();
}
/*
MK_THREAD_LOCAL_GET_DEF(vm_obj, get_rand_gen);
vm_obj io_set_rand_gen(vm_obj const & g, vm_obj const &) {
get_rand_gen() = g;
return mk_io_result(mk_vm_unit());
}
vm_obj io_rand(vm_obj const & lo, vm_obj const & hi, vm_obj const &) {
vm_obj & gen = get_rand_gen();
if (is_simple(gen)) {
if (optional<unsigned> lo1 = try_to_unsigned(lo)) {
if (optional<unsigned> hi1 = try_to_unsigned(hi)) {
unsigned r = 0;
if (*lo1 < *hi1) {
r = *lo1 + (std::rand() % (*hi1 - *lo1)); // NOLINT
}
return mk_io_result(mk_vm_nat(r));
}
}
mpz const & lo1 = vm_nat_to_mpz1(lo);
mpz const & hi1 = vm_nat_to_mpz2(hi);
mpz r(0);
if (lo1 < hi1) {
r = lo1 + (mpz(std::rand()) % (hi1 - lo1)); // NOLINT
}
return mk_io_result(mk_vm_nat(r));
} else {
return mk_ioe_failure("not implemented yet, io_rand_nat primitive has been deleted");
}
}
*/
void initialize_vm_io() {
DECLARE_VM_BUILTIN(name({"io", "prim", "put_str"}), io_put_str);
DECLARE_VM_BUILTIN(name({"io", "prim", "get_line"}), io_get_line);
DECLARE_VM_BUILTIN(name({"io", "prim", "iterate"}), io_iterate);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "mk"}), fs_mk_file_handle);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "is_eof"}), fs_is_eof);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "flush"}), fs_flush);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "close"}), fs_close);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "read"}), fs_read);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "write"}), fs_write);
DECLARE_VM_BUILTIN(name({"io", "prim", "handle", "get_line"}), fs_get_line);
DECLARE_VM_BUILTIN(name({"io", "stdin"}), fs_stdin);
DECLARE_VM_BUILTIN(name({"io", "stdout"}), fs_stdout);
DECLARE_VM_BUILTIN(name({"io", "stderr"}), fs_stderr);
DECLARE_VM_BUILTIN(name({"io", "cmdline_args"}), io_cmdline_args);
g_cmdline_args = new std::vector<std::string>();
}
void finalize_vm_io() {
delete g_cmdline_args;
}
}