diff --git a/.gitignore b/.gitignore index 113e3775..60c2171b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ plugins/flutter/example/web/lib/pocketpy.wasm plugins/flutter/src/pocketpy.h plugins/macos/pocketpy/pocketpy.h plugins/godot/godot-cpp/ -123.txt src/_generated.h profile.sh test diff --git a/amalgamate.py b/amalgamate.py index 39a74d4a..b2564c5b 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -9,7 +9,7 @@ pipeline = [ ["common.h", "memory.h", "str.h", "tuplelist.h", "namedict.h", "error.h"], ["obj.h", "parser.h", "codeobject.h", "frame.h"], ["vm.h", "ref.h", "ceval.h", "compiler.h", "repl.h"], - ["iter.h", "cffi.h", "_generated.h", "pocketpy.h"] + ["iter.h", "cffi.h", "io.h", "_generated.h", "pocketpy.h"] ] copied = set() diff --git a/preprocess.py b/preprocess.py index 492e8bd1..deffcff7 100644 --- a/preprocess.py +++ b/preprocess.py @@ -20,7 +20,7 @@ def generate_python_sources(): #include namespace pkpy{ - std::map kPythonLibs = { + std::map kPythonLibs = { ''' for key, value in sources.items(): header += ' '*8 + '{"' + key + '", "' + value + '"},' diff --git a/src/ceval.h b/src/ceval.h index 0b8638e4..b933c893 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -330,7 +330,7 @@ PyVar VM::run_frame(Frame* frame){ if(frame->_data.size() != 1) throw std::runtime_error("_data.size() != 1 in EVAL/JSON_MODE"); return frame->pop_value(this); } -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(!frame->_data.empty()) throw std::runtime_error("_data.size() != 0 in EXEC_MODE"); #endif return None; diff --git a/src/common.h b/src/common.h index 58e30017..3d4cf977 100644 --- a/src/common.h +++ b/src/common.h @@ -26,16 +26,16 @@ #include #include #include -#include -#define PK_VERSION "0.9.2" +#define PK_VERSION "0.9.3" +#define PK_EXTRA_CHECK 0 +#define PK_ENABLE_FILEIO 1 #if defined(__EMSCRIPTEN__) || defined(__arm__) || defined(__i386__) typedef int32_t i64; typedef float f64; #define S_TO_INT std::stoi #define S_TO_FLOAT std::stof -#define PKPY_USE_32_BITS #else typedef int64_t i64; typedef double f64; @@ -78,8 +78,4 @@ struct Type { const float kLocalsLoadFactor = 0.67f; const float kInstAttrLoadFactor = 0.67f; const float kTypeAttrLoadFactor = 0.5f; - -// do extra check for debug -// #define PK_EXTRA_CHECK - } // namespace pkpy \ No newline at end of file diff --git a/src/frame.h b/src/frame.h index 088de356..4b456d33 100644 --- a/src/frame.h +++ b/src/frame.h @@ -58,7 +58,7 @@ struct Frame { } inline PyVar pop(){ -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(_data.empty()) throw std::runtime_error("_data.empty() is true"); #endif PyVar v = std::move(_data.back()); @@ -67,7 +67,7 @@ struct Frame { } inline void _pop(){ -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(_data.empty()) throw std::runtime_error("_data.empty() is true"); #endif _data.pop_back(); @@ -88,14 +88,14 @@ struct Frame { } inline PyVar& top(){ -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(_data.empty()) throw std::runtime_error("_data.empty() is true"); #endif return _data.back(); } inline PyVar& top_1(){ -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(_data.size() < 2) throw std::runtime_error("_data.size() < 2"); #endif return _data[_data.size()-2]; diff --git a/src/io.h b/src/io.h new file mode 100644 index 00000000..9f24435f --- /dev/null +++ b/src/io.h @@ -0,0 +1,129 @@ +#pragma once + +#include "ceval.h" +#include "cffi.h" + +#if PK_ENABLE_FILEIO + +#include +#include + +namespace pkpy{ + +struct FileIO { + PY_CLASS(FileIO, io, FileIO) + + Str file; + Str mode; + std::fstream _fs; + + 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(!_fs.is_open()) vm->IOError(strerror(errno)); + } + + static void _register(VM* vm, PyVar mod, PyVar type){ + vm->bind_static_method<2>(type, "__new__", [](VM* vm, Args& args){ + return VAR_T(FileIO, + vm, CAST(Str, args[0]), CAST(Str, args[1]) + ); + }); + + vm->bind_method<0>(type, "read", [](VM* vm, Args& args){ + FileIO& io = CAST(FileIO&, args[0]); + std::string buffer; + io._fs >> buffer; + return VAR(buffer); + }); + + vm->bind_method<1>(type, "write", [](VM* vm, Args& args){ + FileIO& io = CAST(FileIO&, args[0]); + io._fs << CAST(Str&, args[1]); + return vm->None; + }); + + vm->bind_method<0>(type, "close", [](VM* vm, Args& args){ + FileIO& io = CAST(FileIO&, args[0]); + io._fs.close(); + return vm->None; + }); + + vm->bind_method<0>(type, "__exit__", [](VM* vm, Args& args){ + FileIO& io = CAST(FileIO&, args[0]); + io._fs.close(); + return vm->None; + }); + + vm->bind_method<0>(type, "__enter__", CPP_LAMBDA(vm->None)); + } +}; + +void add_module_io(VM* vm){ + PyVar mod = vm->new_module("io"); + PyVar type = FileIO::register_class(vm, mod); + vm->bind_builtin_func<2>("open", [type](VM* vm, const Args& args){ + return vm->call(type, args); + }); +} + +void add_module_os(VM* vm){ + PyVar mod = vm->new_module("os"); + vm->bind_func<0>(mod, "getcwd", [](VM* vm, const Args& args){ + return VAR(std::filesystem::current_path().string()); + }); + + vm->bind_func<1>(mod, "listdir", [](VM* vm, const Args& args){ + std::filesystem::path path(CAST(Str&, args[0]).c_str()); + std::filesystem::directory_iterator di; + try{ + di = std::filesystem::directory_iterator(path); + }catch(std::filesystem::filesystem_error& e){ + Str msg = e.what(); + auto pos = msg.find_last_of(":"); + if(pos != Str::npos) msg = msg.substr(pos + 1); + vm->IOError(msg.lstrip()); + } + List ret; + for(auto& p: di) ret.push_back(VAR(p.path().filename().string())); + return VAR(ret); + }); + + vm->bind_func<1>(mod, "remove", [](VM* vm, const Args& args){ + std::filesystem::path path(CAST(Str&, args[0]).c_str()); + bool ok = std::filesystem::remove(path); + if(!ok) vm->IOError("operation failed"); + return vm->None; + }); + + vm->bind_func<1>(mod, "mkdir", [](VM* vm, const Args& args){ + std::filesystem::path path(CAST(Str&, args[0]).c_str()); + bool ok = std::filesystem::create_directory(path); + if(!ok) vm->IOError("operation failed"); + return vm->None; + }); + + vm->bind_func<1>(mod, "rmdir", [](VM* vm, const Args& args){ + std::filesystem::path path(CAST(Str&, args[0]).c_str()); + bool ok = std::filesystem::remove(path); + if(!ok) vm->IOError("operation failed"); + return vm->None; + }); +} + +} // namespace pkpy + + +#else + +namespace pkpy{ +void add_module_io(VM* vm){} +void add_module_os(VM* vm){} +} // namespace pkpy + +#endif \ No newline at end of file diff --git a/src/pocketpy.h b/src/pocketpy.h index 5ef4a8c4..7f3605a2 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -5,6 +5,7 @@ #include "repl.h" #include "iter.h" #include "cffi.h" +#include "io.h" #include "_generated.h" namespace pkpy { @@ -546,7 +547,6 @@ void init_builtins(VM* _vm) { #define __EXPORT #endif - void add_module_time(VM* vm){ PyVar mod = vm->new_module("time"); vm->bind_func<0>(mod, "time", [](VM* vm, Args& args) { @@ -609,69 +609,6 @@ void add_module_dis(VM* vm){ }); } -struct FileIO { - PY_CLASS(FileIO, io, FileIO) - - Str file; - Str mode; - std::fstream _fs; - - 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(!_fs.is_open()) vm->IOError(strerror(errno)); - } - - static void _register(VM* vm, PyVar mod, PyVar type){ - vm->bind_static_method<2>(type, "__new__", [](VM* vm, Args& args){ - return VAR_T(FileIO, - vm, CAST(Str, args[0]), CAST(Str, args[1]) - ); - }); - - vm->bind_method<0>(type, "read", [](VM* vm, Args& args){ - FileIO& io = CAST(FileIO&, args[0]); - std::string buffer; - io._fs >> buffer; - return VAR(buffer); - }); - - vm->bind_method<1>(type, "write", [](VM* vm, Args& args){ - FileIO& io = CAST(FileIO&, args[0]); - io._fs << CAST(Str&, args[1]); - return vm->None; - }); - - vm->bind_method<0>(type, "close", [](VM* vm, Args& args){ - FileIO& io = CAST(FileIO&, args[0]); - io._fs.close(); - return vm->None; - }); - - vm->bind_method<0>(type, "__exit__", [](VM* vm, Args& args){ - FileIO& io = CAST(FileIO&, args[0]); - io._fs.close(); - return vm->None; - }); - - vm->bind_method<0>(type, "__enter__", CPP_LAMBDA(vm->None)); - } -}; -void add_module_io(VM* vm){ - PyVar mod = vm->new_module("io"); - PyVar type = FileIO::register_class(vm, mod); - vm->bind_builtin_func<2>("open", [type](VM* vm, const Args& args){ - return vm->call(type, args); - }); -} - -void add_module_os(VM* vm){} - struct ReMatch { PY_CLASS(ReMatch, re, Match) diff --git a/src/vm.h b/src/vm.h index 0b9f4541..7da9e490 100644 --- a/src/vm.h +++ b/src/vm.h @@ -79,7 +79,7 @@ public: } inline Frame* top_frame() const { -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(callstack.empty()) UNREACHABLE(); #endif return callstack.top().get(); @@ -170,14 +170,14 @@ public: template inline PyVar new_object(const PyVar& type, const T& _value) { -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(!is_type(type, tp_type)) UNREACHABLE(); #endif return make_sp>>(OBJ_GET(Type, type), _value); } template inline PyVar new_object(const PyVar& type, T&& _value) { -#ifdef PK_EXTRA_CHECK +#if PK_EXTRA_CHECK if(!is_type(type, tp_type)) UNREACHABLE(); #endif return make_sp>>(OBJ_GET(Type, type), std::move(_value)); diff --git a/tests/70_file.py b/tests/70_file.py index 55aa6383..73ff4eaa 100644 --- a/tests/70_file.py +++ b/tests/70_file.py @@ -1,3 +1,9 @@ +try: + import os + import io +except ImportError: + exit(0) + a = open('123.txt', 'wt') a.write('123') a.write('456') @@ -11,3 +17,5 @@ with open('123.txt', 'a') as f: with open('123.txt', 'r') as f: assert f.read() == '123456' + '测试' + +os.remove('123.txt')