blueloveTH 2 ani în urmă
părinte
comite
f7af705367
8 a modificat fișierele cu 93 adăugiri și 45 ștergeri
  1. 6 2
      src/ceval.h
  2. 1 1
      src/common.h
  3. 6 7
      src/compiler.h
  4. 40 25
      src/io.h
  5. 3 0
      src/obj.h
  6. 2 2
      src/opcodes.h
  7. 19 6
      src/pocketpy.h
  8. 16 2
      tests/70_file.py

+ 6 - 2
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();

+ 1 - 1
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

+ 6 - 7
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

+ 40 - 25
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<DummyInstance>(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; i<args.size(); i++){
-            path /= CAST(Str&, args[i]).c_str();
+            path /= CAST(Str&, args[i]).sv();
         }
         return VAR(path.string());
     });
 
-    vm->bind_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);
     });

+ 3 - 0
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<PyObject*, Type>;

+ 2 - 2
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)

+ 19 - 6
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

+ 16 - 2
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')