diff --git a/3rd/numpy/tests/CMakeLists.txt b/3rd/numpy/CMakeLists.txt similarity index 51% rename from 3rd/numpy/tests/CMakeLists.txt rename to 3rd/numpy/CMakeLists.txt index 0f66b7fc..a966c3c1 100644 --- a/3rd/numpy/tests/CMakeLists.txt +++ b/3rd/numpy/CMakeLists.txt @@ -1,17 +1,13 @@ cmake_minimum_required(VERSION 3.10) -project(numpy_bindings) +project(numpy) # Set C++ standard set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# Add pocketpy as a subdirectory -add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../.." "${CMAKE_CURRENT_BINARY_DIR}/pocketpy") # Include pybind11 and numpy -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../../include") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include") +include_directories("${CMAKE_CURRENT_LIST_DIR}/../../include") +include_directories("${CMAKE_CURRENT_LIST_DIR}/include") # Control xtensor warnings (OFF by default) option(SHOW_XTENSOR_WARNINGS "Show warnings from xtensor" OFF) @@ -32,14 +28,17 @@ if(NOT SHOW_XTENSOR_WARNINGS) endif() # Add numpy source and test files -file (GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../src/numpy.cpp") -file (GLOB TESTS "${CMAKE_CURRENT_SOURCE_DIR}/test_numpy.cpp") +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp) -# Create numpy executables -add_executable(numpy_bindings ${TESTS} ${SOURCES}) +# Create numpy dynamic library +add_library(${PROJECT_NAME} SHARED ${SOURCES}) -# Set VS debugger working directory (if relevant) -set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY CMAKE_CURRENT_LIST_DIR) +# Define PY_DYNAMIC_MODULE for numpy +target_compile_definitions(${PROJECT_NAME} PRIVATE PY_DYNAMIC_MODULE) # Link numpy with pocketpy -target_link_libraries(${PROJECT_NAME} PRIVATE pocketpy) \ No newline at end of file +target_link_libraries( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/../..//build/Release/pocketpy.lib +) diff --git a/3rd/numpy/README.md b/3rd/numpy/README.md index e869995f..4cd0b2ed 100644 --- a/3rd/numpy/README.md +++ b/3rd/numpy/README.md @@ -1,49 +1,6 @@ -# numpy +# How to build -### How to run **numpy** module programs with **gsoc-2024-dev** [pybind11](https://github.com/pocketpy/gsoc-2024-dev/tree/main/pybind11) - -1. Prepare the python code file with the numpy operations you want to run. \ -\ - For example : let's try out numpy [arange](https://numpy.org/doc/stable/reference/generated/numpy.arange.html) function in `test_numpy.py` -```py - import numpy_bindings as np - - def test_arange(n): - a = np.arange(n) - print(a.sum()) - - test_arange(100) - ``` - -2. Read the script and execute it in `test_numpy.cpp`. -```cpp - #include - #include - #include - #include - - namespace py = pybind11; - using namespace pybind11; - - int main() { - py::scoped_interpreter guard{}; - std::ifstream file("test_numpy.py"); - std::stringstream buffer; - buffer << file.rdbuf(); - std::string script = buffer.str(); - py::exec(script); - - return 0; - } ``` - -3. Build the project at root to generate the executable at `build/gsoc2024`. -```sh - cmake -B build - cmake --build build -``` -4. Now run the executable to get the output. -```sh - |base| gsoc-2024-dev ±|main ✗|→ build/gsoc2024 - 4950 +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build --config Release ``` diff --git a/3rd/numpy/src/numpy.cpp b/3rd/numpy/src/numpy.cpp index bc15429e..0271b9f9 100644 --- a/3rd/numpy/src/numpy.cpp +++ b/3rd/numpy/src/numpy.cpp @@ -2869,7 +2869,7 @@ void array_creation_registry(py::module_& m) { } -PYBIND11_EMBEDDED_MODULE(numpy_bindings, m) { +PYBIND11_MODULE(numpy, m) { m.doc() = "Python bindings for pkpy::numpy::ndarray using pybind11"; m.attr("bool_") = "bool"; diff --git a/include/pocketpy/export.h b/include/pocketpy/export.h index d2a254fb..36f97ae4 100644 --- a/include/pocketpy/export.h +++ b/include/pocketpy/export.h @@ -3,8 +3,12 @@ // clang-format off #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) - //define something for Windows (32-bit and 64-bit, this part is common) - #define PK_EXPORT __declspec(dllexport) + // define something for Windows (32-bit and 64-bit, this part is common) + #ifdef PY_DYNAMIC_MODULE + #define PK_EXPORT __declspec(dllimport) + #else + #define PK_EXPORT __declspec(dllexport) + #endif #define PY_SYS_PLATFORM 0 #define PY_SYS_PLATFORM_STRING "win32" #elif __EMSCRIPTEN__ diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index e1a3882f..6e89513b 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -14,7 +14,6 @@ // 4. stack balance guanrantee // 5. stack effect of each opcode // 6. py_TypeInfo -// 7. Direct assignment of py_NIL, py_True, py_False, py_None. They are slow. typedef struct VM { Frame* top_frame; diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 8d5df06e..cb5dbe3f 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -70,15 +70,6 @@ typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE PY_RETURN; /// + `SINGLE_MODE`: for REPL or jupyter notebook execution. enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE }; -/// A shorthand for `True`. -PK_EXPORT extern py_GlobalRef py_True; -/// A shorthand for `False`. -PK_EXPORT extern py_GlobalRef py_False; -/// A shorthand for `None`. -PK_EXPORT extern py_GlobalRef py_None; -/// A shorthand for `nil`. `nil` is not a valid python object. -PK_EXPORT extern py_GlobalRef py_NIL; - /************* Global Setup *************/ /// Initialize pocketpy and the default VM. @@ -145,6 +136,15 @@ PK_EXPORT void py_newlocals(py_OutRef); /************* Values Creation *************/ +/// A shorthand for `True`. +PK_EXPORT py_GlobalRef py_True(); +/// A shorthand for `False`. +PK_EXPORT py_GlobalRef py_False(); +/// A shorthand for `None`. +PK_EXPORT py_GlobalRef py_None(); +/// A shorthand for `nil`. `nil` is not a valid python object. +PK_EXPORT py_GlobalRef py_NIL(); + /// Create an `int` object. PK_EXPORT void py_newint(py_OutRef, py_i64); /// Create a `float` object. diff --git a/include/pybind11/internal/types.h b/include/pybind11/internal/types.h index 2ef49f25..d5354824 100644 --- a/include/pybind11/internal/types.h +++ b/include/pybind11/internal/types.h @@ -51,14 +51,14 @@ class none : public object { PKBIND_TYPE_IMPL(object, none, tp_NoneType); // note: none is global instance, so we use ref_t. - none() : object(py_None, ref_t{}) {} + none() : object(py_None(), ref_t{}) {} }; class bool_ : public object { PKBIND_TYPE_IMPL(object, bool_, tp_bool); // same as none, bool is a singleton. - bool_(bool value) : object(value ? py_True : py_False, ref_t{}) {} + bool_(bool value) : object(value ? py_True() : py_False(), ref_t{}) {} explicit operator bool () { return py_tobool(ptr()); } }; diff --git a/src/interpreter/dll.c b/src/interpreter/dll.c index c60b8ec2..4c2fc990 100644 --- a/src/interpreter/dll.c +++ b/src/interpreter/dll.c @@ -22,7 +22,10 @@ int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN { if(dll == NULL) return 0; py_module_initialize_t f_init = (py_module_initialize_t)dlsym(dll, f_init_name); #endif - if(f_init == NULL) return 0; + if(f_init == NULL) { + RuntimeError("%s() not found in '%s'", f_init_name, path); + return -1; + } bool success = f_init(); if(!success) return -1; return 1; diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index fed282c8..14d539d8 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -52,23 +52,23 @@ static void py_TypeInfo__ctor(py_TypeInfo* self, }; self->module = module; - self->annotations = *py_NIL; + self->annotations = *py_NIL(); } void VM__ctor(VM* self) { self->top_frame = NULL; - ModuleDict__ctor(&self->modules, NULL, *py_NIL); + ModuleDict__ctor(&self->modules, NULL, *py_NIL()); TypeList__ctor(&self->types); - self->builtins = *py_NIL; - self->main = *py_NIL; + self->builtins = *py_NIL(); + self->main = *py_NIL(); self->callbacks.importfile = pk_default_importfile; self->callbacks.print = pk_default_print; - self->last_retval = *py_NIL; - self->curr_exception = *py_NIL; + self->last_retval = *py_NIL(); + self->curr_exception = *py_NIL(); self->is_curr_exc_handled = false; self->ctx = NULL; @@ -329,7 +329,7 @@ py_Type pk_newtype(const char* name, if(base_ti && base_ti->is_sealed) { c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name)); } - py_TypeInfo__ctor(ti, py_name(name), index, base, base_ti, module ? *module : *py_NIL); + py_TypeInfo__ctor(ti, py_name(name), index, base, base_ti, module ? *module : *py_NIL()); if(!dtor && base) dtor = base_ti->dtor; ti->dtor = dtor; ti->is_python = is_python; diff --git a/src/modules/array2d.c b/src/modules/array2d.c index cf595440..6502cf99 100644 --- a/src/modules/array2d.c +++ b/src/modules/array2d.c @@ -106,7 +106,7 @@ static bool array2d_get(int argc, py_Ref argv) { PY_CHECK_ARG_TYPE(1, tp_int); PY_CHECK_ARG_TYPE(2, tp_int); if(argc == 3) { - default_ = py_None; + default_ = py_None(); } else if(argc == 4) { default_ = py_arg(3); } else { diff --git a/src/modules/json.c b/src/modules/json.c index 55152435..c3fdab68 100644 --- a/src/modules/json.c +++ b/src/modules/json.c @@ -21,9 +21,9 @@ static bool json_dumps(int argc, py_Ref argv) { void pk__add_module_json() { py_Ref mod = py_newmodule("json"); - py_setdict(mod, py_name("null"), py_None); - py_setdict(mod, py_name("true"), py_True); - py_setdict(mod, py_name("false"), py_False); + py_setdict(mod, py_name("null"), py_None()); + py_setdict(mod, py_name("true"), py_True()); + py_setdict(mod, py_name("false"), py_False()); py_bindfunc(mod, "loads", json_loads); py_bindfunc(mod, "dumps", json_dumps); diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 8a39eba9..28842f33 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -162,7 +162,7 @@ void CodeObject__dtor(CodeObject* self) { void Function__ctor(Function* self, FuncDecl_ decl, py_TValue* module) { PK_INCREF(decl); self->decl = decl; - self->module = module ? *module : *py_NIL; + self->module = module ? *module : *py_NIL(); self->clazz = NULL; self->closure = NULL; self->cfunc = NULL; diff --git a/src/public/internal.c b/src/public/internal.c index fe7ab656..64bae2b4 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -11,13 +11,9 @@ VM* pk_current_vm; -py_GlobalRef py_True; -py_GlobalRef py_False; -py_GlobalRef py_None; -py_GlobalRef py_NIL; - static VM pk_default_vm; static VM* pk_all_vm[16]; +static py_TValue _True, _False, _None, _NIL; void py_initialize() { if(pk_current_vm){ @@ -30,18 +26,18 @@ void py_initialize() { pk_current_vm = pk_all_vm[0] = &pk_default_vm; // initialize some convenient references - static py_TValue _True, _False, _None, _NIL; py_newbool(&_True, true); py_newbool(&_False, false); py_newnone(&_None); py_newnil(&_NIL); - py_True = &_True; - py_False = &_False; - py_None = &_None; - py_NIL = &_NIL; VM__ctor(&pk_default_vm); } +py_GlobalRef py_True() { return &_True; } +py_GlobalRef py_False() { return &_False; } +py_GlobalRef py_None() { return &_None; } +py_GlobalRef py_NIL() { return &_NIL; } + void py_finalize() { for(int i = 1; i < 16; i++) { VM* vm = pk_all_vm[i]; @@ -169,7 +165,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { py_Ref cls_var = py_tpfindmagic(py_totype(self), name); if(cls_var) { self[0] = *cls_var; - self[1] = *py_NIL; + self[1] = *py_NIL(); return true; } return false; @@ -197,7 +193,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { } case tp_staticmethod: self[0] = *py_getslot(cls_var, 0); - self[1] = *py_NIL; + self[1] = *py_NIL(); break; case tp_classmethod: self[0] = *py_getslot(cls_var, 0); diff --git a/src/public/modules.c b/src/public/modules.c index 6be8c503..aa57ba42 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -676,11 +676,11 @@ py_TValue pk_builtins__register() { // some patches py_bindmagic(tp_NoneType, __repr__, NoneType__repr__); - *py_tpgetmagic(tp_NoneType, __hash__) = *py_None; + *py_tpgetmagic(tp_NoneType, __hash__) = *py_None(); py_bindmagic(tp_ellipsis, __repr__, ellipsis__repr__); - *py_tpgetmagic(tp_ellipsis, __hash__) = *py_None; + *py_tpgetmagic(tp_ellipsis, __hash__) = *py_None(); py_bindmagic(tp_NotImplementedType, __repr__, NotImplementedType__repr__); - *py_tpgetmagic(tp_NotImplementedType, __hash__) = *py_None; + *py_tpgetmagic(tp_NotImplementedType, __hash__) = *py_None(); return *builtins; } diff --git a/src/public/py_dict.c b/src/public/py_dict.c index 62d7cf12..04619000 100644 --- a/src/public/py_dict.c +++ b/src/public/py_dict.c @@ -389,7 +389,7 @@ static bool dict_update(int argc, py_Ref argv) { static bool dict_get(int argc, py_Ref argv) { Dict* self = py_touserdata(argv); if(argc > 3) return TypeError("get() takes at most 3 arguments (%d given)", argc); - py_Ref default_val = argc == 3 ? py_arg(2) : py_None; + py_Ref default_val = argc == 3 ? py_arg(2) : py_None(); DictEntry* entry; if(!Dict__try_get(self, py_arg(1), &entry)) return false; *py_retval() = entry ? entry->val : *default_val; @@ -399,7 +399,7 @@ static bool dict_get(int argc, py_Ref argv) { static bool dict_pop(int argc, py_Ref argv) { Dict* self = py_touserdata(argv); if(argc < 2 || argc > 3) return TypeError("pop() takes 1 or 2 arguments (%d given)", argc - 1); - py_Ref default_val = argc == 3 ? py_arg(2) : py_None; + py_Ref default_val = argc == 3 ? py_arg(2) : py_None(); int res = Dict__pop(self, py_arg(1)); if(res == -1) return false; if(res == 0) { py_assign(py_retval(), default_val); } @@ -482,7 +482,7 @@ py_Type pk_dict__register() { py_bindmethod(type, "keys", dict_keys); py_bindmethod(type, "values", dict_values); - py_setdict(py_tpobject(type), __hash__, py_None); + py_setdict(py_tpobject(type), __hash__, py_None()); return type; } diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 9985bba0..784085d2 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -134,8 +134,8 @@ bool py_matchexc(py_Type type) { void py_clearexc(py_StackRef p0) { VM* vm = pk_current_vm; - vm->last_retval = *py_NIL; - vm->curr_exception = *py_NIL; + vm->last_retval = *py_NIL(); + vm->curr_exception = *py_NIL(); vm->is_curr_exc_handled = false; /* Don't clear this, because StopIteration() may corrupt the class defination */ diff --git a/src/public/py_list.c b/src/public/py_list.c index 2dafd0fb..f8489042 100644 --- a/src/public/py_list.c +++ b/src/public/py_list.c @@ -444,6 +444,6 @@ py_Type pk_list__register() { py_bind(py_tpobject(type), "sort(self, key=None, reverse=False)", list_sort); - py_setdict(py_tpobject(type), __hash__, py_None); + py_setdict(py_tpobject(type), __hash__, py_None()); return type; } diff --git a/src/public/py_property.c b/src/public/py_property.c index 0f93f78c..f0e1a151 100644 --- a/src/public/py_property.c +++ b/src/public/py_property.c @@ -8,7 +8,7 @@ static bool property__new__(int argc, py_Ref argv) { py_newobject(py_retval(), tp_property, 2, 0); if(argc == 1 + 1) { py_setslot(py_retval(), 0, py_arg(1)); - py_setslot(py_retval(), 1, py_None); + py_setslot(py_retval(), 1, py_None()); } else if(argc == 1 + 2) { py_setslot(py_retval(), 0, py_arg(1)); py_setslot(py_retval(), 1, py_arg(2)); diff --git a/src/public/py_slice.c b/src/public/py_slice.c index 66c8110b..de96f24e 100644 --- a/src/public/py_slice.c +++ b/src/public/py_slice.c @@ -100,7 +100,7 @@ py_Type pk_slice__register() { py_bindmagic(type, __eq__, slice__eq__); py_bindmagic(type, __ne__, slice__ne__); - py_setdict(py_tpobject(type), __hash__, py_None); + py_setdict(py_tpobject(type), __hash__, py_None()); py_bindproperty(type, "start", slice_start, NULL); py_bindproperty(type, "stop", slice_stop, NULL); diff --git a/src/public/stack_ops.c b/src/public/stack_ops.c index cd656758..ff84bc48 100644 --- a/src/public/stack_ops.c +++ b/src/public/stack_ops.c @@ -30,7 +30,7 @@ void py_setdict(py_Ref self, py_Name name, py_Ref val) { } py_ItemRef py_emplacedict(py_Ref self, py_Name name){ - py_setdict(self, name, py_NIL); + py_setdict(self, name, py_NIL()); return py_getdict(self, name); } diff --git a/src/public/values.c b/src/public/values.c index 84b00015..20baf8d8 100644 --- a/src/public/values.c +++ b/src/public/values.c @@ -67,7 +67,7 @@ void py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFu if(setter) { py_newnativefunc(py_getslot(&tmp, 1), setter); } else { - py_setslot(&tmp, 1, py_None); + py_setslot(&tmp, 1, py_None()); } py_setdict(py_tpobject(type), py_name(name), &tmp); }