From f7af705367803465c15aff61537431de3bbe0ca1 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 22 Apr 2023 21:57:06 +0800 Subject: [PATCH] ... --- src/ceval.h | 8 ++++-- src/common.h | 2 +- src/compiler.h | 13 +++++----- src/io.h | 65 +++++++++++++++++++++++++++++------------------- src/obj.h | 3 +++ src/opcodes.h | 4 +-- src/pocketpy.h | 25 ++++++++++++++----- tests/70_file.py | 18 ++++++++++++-- 8 files changed, 93 insertions(+), 45 deletions(-) diff --git a/src/ceval.h b/src/ceval.h index 23ae3e46..d28e8f5f 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -521,8 +521,12 @@ __NEXT_STEP:; } DISPATCH(); /*****************************************/ // // TODO: using "goto" inside with block may cause __exit__ not called - // TARGET(WITH_ENTER) call(frame->pop_value(this), __enter__, no_arg()); DISPATCH(); - // TARGET(WITH_EXIT) call(frame->pop_value(this), __exit__, no_arg()); DISPATCH(); + TARGET(WITH_ENTER) + call_method(POPX(), __enter__); + DISPATCH(); + TARGET(WITH_EXIT) + call_method(POPX(), __exit__); + DISPATCH(); /*****************************************/ TARGET(ASSERT) { PyObject* obj = TOP(); diff --git a/src/common.h b/src/common.h index b69aa839..39575557 100644 --- a/src/common.h +++ b/src/common.h @@ -46,7 +46,7 @@ #if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__) #define PK_ENABLE_FILEIO 0 #else -#define PK_ENABLE_FILEIO 0 // TODO: refactor this +#define PK_ENABLE_FILEIO 1 #endif // This is the maximum number of arguments in a function declaration diff --git a/src/compiler.h b/src/compiler.h index e6df0990..bc732eb2 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -795,17 +795,16 @@ __SUBSCR_END: consume_end_stmt(); } break; case TK("with"): { - // TODO: reimpl this EXPR(false); - ctx()->emit(OP_POP_TOP, BC_NOARG, prev().line); consume(TK("as")); consume(TK("@id")); - // emit(OP_STORE_NAME, index); - // emit(OP_LOAD_NAME_REF, index); - // emit(OP_WITH_ENTER); + StrName name(prev().str()); + ctx()->emit(OP_STORE_NAME, name.index, prev().line); + ctx()->emit(OP_LOAD_NAME, name.index, prev().line); + ctx()->emit(OP_WITH_ENTER, BC_NOARG, prev().line); compile_block_body(); - // emit(OP_LOAD_NAME_REF, index); - // emit(OP_WITH_EXIT); + ctx()->emit(OP_LOAD_NAME, name.index, prev().line); + ctx()->emit(OP_WITH_EXIT, BC_NOARG, prev().line); } break; /*************************************************/ // TODO: refactor goto/label use special $ syntax diff --git a/src/io.h b/src/io.h index 63814418..d1b3ed96 100644 --- a/src/io.h +++ b/src/io.h @@ -2,6 +2,7 @@ #include "ceval.h" #include "cffi.h" +#include "common.h" #if PK_ENABLE_FILEIO @@ -11,7 +12,7 @@ namespace pkpy{ inline Str _read_file_cwd(const Str& name, bool* ok){ - std::filesystem::path path(name.c_str()); + std::filesystem::path path(name.sv()); bool exists = std::filesystem::exists(path); if(!exists){ *ok = false; @@ -31,13 +32,17 @@ struct FileIO { Str mode; std::fstream _fs; + bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; } + FileIO(VM* vm, Str file, Str mode): file(file), mode(mode) { - if(mode == "rt" || mode == "r"){ - _fs.open(file, std::ios::in); - }else if(mode == "wt" || mode == "w"){ - _fs.open(file, std::ios::out); - }else if(mode == "at" || mode == "a"){ - _fs.open(file, std::ios::app); + if(mode == "rt" || mode == "r" || mode == "rb"){ + _fs.open(file.sv(), std::ios::in); + }else if(mode == "wt" || mode == "w" || mode == "wb"){ + _fs.open(file.sv(), std::ios::out); + }else if(mode == "at" || mode == "a" || mode == "ab"){ + _fs.open(file.sv(), std::ios::app); + }else{ + vm->ValueError("invalid mode"); } if(!_fs.is_open()) vm->IOError(strerror(errno)); } @@ -51,14 +56,19 @@ struct FileIO { vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){ FileIO& io = CAST(FileIO&, args[0]); - std::string buffer; - io._fs >> buffer; + Bytes buffer; + io._fs >> buffer._data; + if(io.is_text()) return VAR(Str(buffer._data)); return VAR(buffer); }); vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){ FileIO& io = CAST(FileIO&, args[0]); - io._fs << CAST(Str&, args[1]); + if(io.is_text()) io._fs << CAST(Str&, args[1]); + else{ + Bytes& buffer = CAST(Bytes&, args[1]); + io._fs << buffer._data; + } return vm->None; }); @@ -80,35 +90,40 @@ struct FileIO { inline void add_module_io(VM* vm){ PyObject* mod = vm->new_module("io"); - PyObject* type = FileIO::register_class(vm, mod); - vm->bind_builtin_func<2>("open", [type](VM* vm, ArgsView args){ - return vm->call(type, args); + FileIO::register_class(vm, mod); + vm->bind_builtin_func<2>("open", [](VM* vm, ArgsView args){ + static StrName m_io("io"); + static StrName m_FileIO("FileIO"); + return vm->call(vm->_modules[m_io]->attr(m_FileIO), args[0], args[1]); }); } inline void add_module_os(VM* vm){ PyObject* mod = vm->new_module("os"); + PyObject* path_obj = vm->heap.gcnew(vm->tp_object, {}); + mod->attr().set("path", path_obj); + // Working directory is shared by all VMs!! vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){ return VAR(std::filesystem::current_path().string()); }); vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){ - std::filesystem::path path(CAST(Str&, args[0]).c_str()); + std::filesystem::path path(CAST(Str&, args[0]).sv()); std::filesystem::current_path(path); return vm->None; }); vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){ - std::filesystem::path path(CAST(Str&, args[0]).c_str()); + std::filesystem::path path(CAST(Str&, args[0]).sv()); std::filesystem::directory_iterator di; try{ di = std::filesystem::directory_iterator(path); }catch(std::filesystem::filesystem_error& e){ - Str msg = e.what(); + std::string msg = e.what(); auto pos = msg.find_last_of(":"); - if(pos != Str::npos) msg = msg.substr(pos + 1); - vm->IOError(msg.lstrip()); + if(pos != std::string::npos) msg = msg.substr(pos + 1); + vm->IOError(Str(msg).lstrip()); } List ret; for(auto& p: di) ret.push_back(VAR(p.path().filename().string())); @@ -116,36 +131,36 @@ inline void add_module_os(VM* vm){ }); vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){ - std::filesystem::path path(CAST(Str&, args[0]).c_str()); + std::filesystem::path path(CAST(Str&, args[0]).sv()); bool ok = std::filesystem::remove(path); if(!ok) vm->IOError("operation failed"); return vm->None; }); vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){ - std::filesystem::path path(CAST(Str&, args[0]).c_str()); + std::filesystem::path path(CAST(Str&, args[0]).sv()); bool ok = std::filesystem::create_directory(path); if(!ok) vm->IOError("operation failed"); return vm->None; }); vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){ - std::filesystem::path path(CAST(Str&, args[0]).c_str()); + std::filesystem::path path(CAST(Str&, args[0]).sv()); bool ok = std::filesystem::remove(path); if(!ok) vm->IOError("operation failed"); return vm->None; }); - vm->bind_func<-1>(mod, "path_join", [](VM* vm, ArgsView args){ + vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){ std::filesystem::path path; for(int i=0; ibind_func<1>(mod, "path_exists", [](VM* vm, ArgsView args){ - std::filesystem::path path(CAST(Str&, args[0]).c_str()); + vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); bool exists = std::filesystem::exists(path); return VAR(exists); }); diff --git a/src/obj.h b/src/obj.h index 2dbd7e3e..f023500b 100644 --- a/src/obj.h +++ b/src/obj.h @@ -62,6 +62,9 @@ struct Bytes{ int size() const noexcept { return _data.size(); } int operator[](int i) const noexcept { return (int)(uint8_t)_data[i]; } + + bool operator==(const Bytes& rhs) const noexcept { return _data == rhs._data; } + bool operator!=(const Bytes& rhs) const noexcept { return _data != rhs._data; } }; using Super = std::pair; diff --git a/src/opcodes.h b/src/opcodes.h index c14f17fd..1cfce753 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -102,8 +102,8 @@ OPCODE(BEGIN_CLASS) OPCODE(END_CLASS) OPCODE(STORE_CLASS_ATTR) /**************************/ -// OPCODE(WITH_ENTER) -// OPCODE(WITH_EXIT) +OPCODE(WITH_ENTER) +OPCODE(WITH_EXIT) /**************************/ OPCODE(ASSERT) OPCODE(EXCEPTION_MATCH) diff --git a/src/pocketpy.h b/src/pocketpy.h index 6bd1bf44..4da662dd 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -353,15 +353,15 @@ inline void init_builtins(VM* _vm) { }); _vm->bind_method<1>("str", "__eq__", [](VM* vm, ArgsView args) { - if(is_type(args[0], vm->tp_str) && is_type(args[1], vm->tp_str)) - return VAR(CAST(Str&, args[0]) == CAST(Str&, args[1])); - return VAR(args[0] == args[1]); + const Str& self = CAST(Str&, args[0]); + if(!is_type(args[1], vm->tp_str)) return VAR(false); + return VAR(self == CAST(Str&, args[1])); }); _vm->bind_method<1>("str", "__ne__", [](VM* vm, ArgsView args) { - if(is_type(args[0], vm->tp_str) && is_type(args[1], vm->tp_str)) - return VAR(CAST(Str&, args[0]) != CAST(Str&, args[1])); - return VAR(args[0] != args[1]); + const Str& self = CAST(Str&, args[0]); + if(!is_type(args[1], vm->tp_str)) return VAR(true); + return VAR(self != CAST(Str&, args[1])); }); _vm->bind_method<1>("str", "__getitem__", [](VM* vm, ArgsView args) { @@ -609,6 +609,19 @@ inline void init_builtins(VM* _vm) { return VAR(Str(self._data)); }); + _vm->bind_method<1>("bytes", "__eq__", [](VM* vm, ArgsView args) { + const Bytes& self = CAST(Bytes&, args[0]); + if(!is_type(args[1], vm->tp_bytes)) return VAR(false); + const Bytes& other = CAST(Bytes&, args[1]); + return VAR(self == other); + }); + + _vm->bind_method<1>("bytes", "__ne__", [](VM* vm, ArgsView args) { + const Bytes& self = CAST(Bytes&, args[0]); + if(!is_type(args[1], vm->tp_bytes)) return VAR(true); + const Bytes& other = CAST(Bytes&, args[1]); + return VAR(self != other); + }); } #ifdef _WIN32 diff --git a/tests/70_file.py b/tests/70_file.py index d9fdde27..0662ac5a 100644 --- a/tests/70_file.py +++ b/tests/70_file.py @@ -18,6 +18,20 @@ with open('123.txt', 'a') as f: with open('123.txt', 'r') as f: assert f.read() == '123456' + '测试' -assert os.path_exists('123.txt') +assert os.path.exists('123.txt') os.remove('123.txt') -assert not os.path_exists('123.txt') +assert not os.path.exists('123.txt') + + +with open('123.bin', 'wb') as f: + f.write('123'.encode()) + f.write('测试'.encode()) + +with open('123.bin', 'rb') as f: + b = f.read() + assert isinstance(b, bytes) + assert b == '123测试'.encode() + +assert os.path.exists('123.bin') +os.remove('123.bin') +assert not os.path.exists('123.bin') \ No newline at end of file