blueloveTH 1 год назад
Родитель
Сommit
8e1e29ddd6

+ 2 - 0
include/pocketpy/common/str.h

@@ -30,6 +30,7 @@ bool c11__sveq2(c11_sv a, const char* b);
 
 c11_string* c11_string__new(const char* data);
 c11_string* c11_string__new2(const char* data, int size);
+c11_string* c11_string__new3(const char* fmt, ...);
 void c11_string__ctor(c11_string* self, const char* data);
 void c11_string__ctor2(c11_string* self, const char* data, int size);
 c11_string* c11_string__copy(c11_string* self);
@@ -45,6 +46,7 @@ c11_sv c11_sv__slice(c11_sv sv, int start);
 c11_sv c11_sv__slice2(c11_sv sv, int start, int stop);
 c11_sv c11_sv__strip(c11_sv sv, c11_sv chars, bool left, bool right);
 int c11_sv__index(c11_sv self, char c);
+int c11_sv__rindex(c11_sv self, char c);
 int c11_sv__index2(c11_sv self, c11_sv sub, int start);
 int c11_sv__count(c11_sv self, c11_sv sub);
 

+ 44 - 0
include/pocketpy/interpreter/vfs.h

@@ -0,0 +1,44 @@
+#pragma once
+
+#include "pocketpy/common/vector.h"
+#include "pocketpy/common/str.h"
+#include "pocketpy/pocketpy.h"
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct VfsEntry {
+    bool is_file;
+
+    union {
+        struct {
+            int size;
+            unsigned char* data;
+        } _file;
+
+        c11_vector _dir;
+    };
+} VfsEntry;
+
+#define SMALLMAP_T__HEADER
+#define K c11_sv
+#define V VfsEntry
+#define NAME VfsDir
+#define less(a, b) (c11_sv__cmp((a), (b)) < 0)
+#define equal(a, b) (c11_sv__cmp((a), (b)) == 0)
+#include "pocketpy/xmacros/smallmap.h"
+#undef SMALLMAP_T__HEADER
+
+typedef struct Vfs {
+    VfsEntry root;
+} Vfs;
+
+void Vfs__ctor(Vfs* self);
+void Vfs__dtor(Vfs* self);
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 1
include/pocketpy/interpreter/vm.h

@@ -4,6 +4,7 @@
 #include "pocketpy/pocketpy.h"
 #include "pocketpy/interpreter/gc.h"
 #include "pocketpy/interpreter/frame.h"
+#include "pocketpy/interpreter/vfs.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -39,7 +40,7 @@ typedef struct pk_VM {
     py_TValue main;      // __main__ module
 
     void (*ceval_on_step)(Frame*, Bytecode);
-    unsigned char* (*import_file)(const char*);
+    unsigned char* (*import_file)(const char*, int*);
     void (*print)(const char*);
 
     py_TValue last_retval;
@@ -48,6 +49,7 @@ typedef struct pk_VM {
 
     py_TValue reg[8];  // users' registers
 
+    Vfs __vfs;
     py_TValue* __curr_class;
     FuncDecl_ __dynamic_func_decl;
     py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES];
@@ -124,6 +126,8 @@ py_Type pk_Exception__register();
 
 py_TValue pk_builtins__register();
 
+void pk__add_module_pkpy();
+
 #ifdef __cplusplus
 }
 #endif

+ 11 - 8
include/pocketpy/pocketpy.h

@@ -92,11 +92,8 @@ void py_newlistn(py_Ref, int n);
 void py_newdict(py_Ref);
 void py_newslice(py_Ref);
 void py_newnativefunc(py_Ref out, py_CFunction);
-py_Name py_newfunction(py_Ref out,
-                    const char* sig,
-                    py_CFunction f,
-                    const char* docstring,
-                    int slots);
+py_Name
+    py_newfunction(py_Ref out, const char* sig, py_CFunction f, const char* docstring, int slots);
 
 /************* Name Convertions *************/
 py_Name py_name(const char*);
@@ -262,12 +259,12 @@ py_StackRef py_pushtmp();
 bool py_pushmethod(py_Name name);
 
 /************* Modules *************/
-py_TmpRef py_newmodule(const char* name, const char* package);
-py_TmpRef py_getmodule(const char* name);
+py_TmpRef py_newmodule(const char* path);
+py_TmpRef py_getmodule(const char* path);
 
 /// Import a module.
 /// The result will be set to `py_retval()`.
-bool py_import(const char* name) PY_RAISE;
+bool py_import(const char* path) PY_RAISE;
 
 /************* Errors *************/
 /// Raise an exception by name and message. Always returns false.
@@ -286,6 +283,7 @@ bool py_checkexc();
 #define RuntimeError(...) py_exception("RuntimeError", __VA_ARGS__)
 #define ValueError(...) py_exception("ValueError", __VA_ARGS__)
 #define IndexError(...) py_exception("IndexError", __VA_ARGS__)
+#define ImportError(...) py_exception("ImportError", __VA_ARGS__)
 #define NotImplementedError() py_exception("NotImplementedError", "")
 #define AttributeError(self, n)                                                                    \
     py_exception("AttributeError", "'%t' object has no attribute '%n'", (self)->type, (n))
@@ -362,6 +360,11 @@ bool py_dict__contains(py_Ref self, py_Ref key);
 int py_dict__len(py_Ref self);
 bool py_dict__apply(py_Ref self, bool (*f)(py_Ref key, py_Ref val, void* ctx), void* ctx);
 
+/************* Virtual File System *************/
+unsigned char* py_vfsread(const char* path, int* size);
+bool py_vfswrite(const char* path, unsigned char* data, int size);
+char** py_vfslist(const char* path, int* length);
+
 /************* Others *************/
 int py_replinput(char* buf);
 

+ 1 - 0
include/pocketpy/xmacros/magics.h

@@ -56,6 +56,7 @@ MAGIC_METHOD(__exit__)
 MAGIC_METHOD(__name__)
 MAGIC_METHOD(__all__)
 MAGIC_METHOD(__package__)
+MAGIC_METHOD(__module_is_pending__)
 MAGIC_METHOD(__path__)
 MAGIC_METHOD(__class__)
 MAGIC_METHOD(__abs__)

+ 0 - 3
include/typings/__builtins.pyi → include/typings/pkpy.pyi

@@ -2,6 +2,3 @@ from typing import Any
 
 def next(iter) -> Any | StopIteration:
     ...
-
-def _enable_instance_dict(obj) -> None:
-    ...

+ 1 - 1
python/builtins.py

@@ -1,4 +1,4 @@
-from __builtins import next as __builtins_next
+from pkpy import next as __builtins_next
 
 def all(iterable):
     for i in iterable:

+ 1 - 1
python/collections.py

@@ -1,4 +1,4 @@
-from __builtins import _enable_instance_dict
+from pkpy import _enable_instance_dict
 from typing import Generic, TypeVar, Iterable
 
 T = TypeVar('T')

+ 1 - 1
python/functools.py

@@ -1,4 +1,4 @@
-from __builtins import next
+from pkpy import next
 
 class cache:
     def __init__(self, f):

+ 1 - 1
python/itertools.py

@@ -1,4 +1,4 @@
-from __builtins import next
+from pkpy import next
 
 def zip_longest(a, b):
     a = iter(a)

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/common/_generated.c


+ 18 - 0
src/common/str.c

@@ -16,6 +16,17 @@ c11_string* c11_string__new2(const char* data, int size) {
     return retval;
 }
 
+c11_string* c11_string__new3(const char* fmt, ...) {
+    c11_sbuf buf;
+    c11_sbuf__ctor(&buf);
+    va_list args;
+    va_start(args, fmt);
+    // c11_sbuf__write_vfmt(&buf, fmt, args);
+    pk_vsprintf(&buf, fmt, args);
+    va_end(args);
+    return c11_sbuf__submit(&buf);
+}
+
 void c11_string__ctor(c11_string* self, const char* data) {
     c11_string__ctor2(self, data, strlen(data));
 }
@@ -124,6 +135,13 @@ int c11_sv__index(c11_sv self, char c) {
     return -1;
 }
 
+int c11_sv__rindex(c11_sv self, char c) {
+    for(int i = self.size - 1; i >= 0; i--) {
+        if(self.data[i] == c) return i;
+    }
+    return -1;
+}
+
 int c11_sv__index2(c11_sv self, c11_sv sub, int start) {
     if(sub.size == 0) return start;
     int max_end = self.size - sub.size;

+ 44 - 2
src/interpreter/ceval.c

@@ -755,6 +755,47 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 }
             }
             ////////
+            case OP_IMPORT_PATH: {
+                py_Ref path_object = c11__at(py_TValue, &frame->co->consts, byte.arg);
+                bool ok = py_import(py_tostr(path_object));
+                if(!ok) goto __ERROR;
+                PUSH(py_retval());
+                DISPATCH();
+            }
+            case OP_POP_IMPORT_STAR: {
+                // [module]
+                pk_NameDict* dict = PyObject__dict(TOP()->_obj);
+                py_Ref all = pk_NameDict__try_get(dict, __all__);
+                if(all) {
+                    int length;
+                    py_TValue* p = pk_arrayview(all, &length);
+                    if(!p) {
+                        TypeError("'__all__' must be a list or tuple, got '%t'", all->type);
+                        goto __ERROR;
+                    }
+                    for(int i = 0; i < length; i++) {
+                        py_Name name = py_namev(py_tosv(p + i));
+                        py_Ref value = pk_NameDict__try_get(dict, name);
+                        if(value == NULL) {
+                            ImportError("cannot import name '%n'", name);
+                            goto __ERROR;
+                        } else {
+                            py_setdict(&frame->module, name, value);
+                        }
+                    }
+                } else {
+                    for(int i = 0; i < dict->count; i++) {
+                        pk_NameDict_KV* kv = c11__at(pk_NameDict_KV, dict, i);
+                        if(!kv->key) continue;
+                        c11_sv name = py_name2sv(kv->key);
+                        if(name.size == 0 || name.data[0] == '_') continue;
+                        py_setdict(&frame->module, kv->key, &kv->value);
+                    }
+                }
+                POP();
+                DISPATCH();
+            }
+            ////////
             case OP_UNPACK_SEQUENCE: {
                 if(!stack_unpack_sequence(self, byte.arg)) goto __ERROR;
                 DISPATCH();
@@ -907,8 +948,9 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
         pk_print_stack(self, frame, (Bytecode){0});
         py_BaseException__set_lineno(&self->curr_exception, Frame__lineno(frame), frame->co);
     __ERROR_RE_RAISE:
-        do {} while(0);
-        //printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame));
+        do {
+        } while(0);
+        // printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame));
         int lineno = py_BaseException__get_lineno(&self->curr_exception, frame->co);
         py_BaseException__stpush(&self->curr_exception,
                                  frame->co->src,

+ 131 - 0
src/interpreter/vfs.c

@@ -0,0 +1,131 @@
+#include "pocketpy/interpreter/vfs.h"
+#include "pocketpy/interpreter/vm.h"
+
+#define SMALLMAP_T__SOURCE
+#define K c11_sv
+#define V VfsEntry
+#define NAME VfsDir
+#define less(a, b) (c11_sv__cmp((a), (b)) < 0)
+#define equal(a, b) (c11_sv__cmp((a), (b)) == 0)
+#include "pocketpy/xmacros/smallmap.h"
+#undef SMALLMAP_T__SOURCE
+
+static VfsEntry* Vfs__get(const char* path) {
+    c11_vector /*T=c11_sv*/ cpnts = c11_sv__split((c11_sv){path, strlen(path)}, '/');
+
+    VfsEntry* root = &pk_current_vm->__vfs.root;
+    for(int i = 0; i < cpnts.count; i++) {
+        c11_sv cpnt = c11__getitem(c11_sv, &cpnts, i);
+        VfsEntry* entry = VfsDir__try_get(&root->_dir, cpnt);
+        if(entry == NULL) {
+            c11_vector__dtor(&cpnts);
+            return NULL;
+        }
+        if(entry->is_file) {
+            VfsEntry* retval = i == cpnts.count - 1 ? entry : NULL;
+            c11_vector__dtor(&cpnts);
+            return retval;
+        } else {
+            root = entry;
+        }
+    }
+    c11_vector__dtor(&cpnts);
+    return root;
+}
+
+static void VfsDir__delete_recursively(VfsDir* self) {
+    for(int i = 0; i < self->count; i++) {
+        VfsDir_KV* kv = c11__at(VfsDir_KV, self, i);
+        free((char*)kv->key.data);
+        if(kv->value.is_file) {
+            free(kv->value._file.data);
+        } else {
+            VfsDir__delete_recursively(&kv->value._dir);
+        }
+    }
+    VfsDir__dtor(self);
+}
+
+void Vfs__ctor(Vfs* self) {
+    self->root.is_file = false;
+    VfsDir__ctor(&self->root._dir);
+}
+
+void Vfs__dtor(Vfs* self) { VfsDir__delete_recursively(&self->root._dir); }
+
+unsigned char* py_vfsread(const char* path, int* size) {
+    VfsEntry* entry = Vfs__get(path);
+    if(entry == NULL || !entry->is_file) return NULL;
+    *size = entry->_file.size;
+    unsigned char* retval = malloc(*size);
+    memcpy(retval, entry->_file.data, *size);
+    return retval;
+}
+
+static void VfsDir__dupset(VfsDir* self, c11_sv key, VfsEntry value) {
+    char* p = malloc(key.size);
+    memcpy(p, key.data, key.size);
+    VfsDir__set(self, (c11_sv){p, key.size}, value);
+}
+
+bool py_vfswrite(const char* path, unsigned char* data, int size) {
+    c11_vector /*T=c11_sv*/ cpnts = c11_sv__split((c11_sv){path, strlen(path)}, '/');
+    VfsEntry* root = &pk_current_vm->__vfs.root;
+    for(int i = 0; i < cpnts.count; i++) {
+        c11_sv cpnt = c11__getitem(c11_sv, &cpnts, i);
+        VfsEntry* entry = VfsDir__try_get(&root->_dir, cpnt);
+        if(entry == NULL) {
+            if(i == cpnts.count - 1) {
+                // create file
+                VfsEntry entry = {
+                    .is_file = true,
+                    ._file.size = size,
+                    ._file.data = data,
+                };
+                VfsDir__dupset(&root->_dir, cpnt, entry);
+                c11_vector__dtor(&cpnts);
+                return true;
+            } else {
+                // create missing directory
+                VfsEntry entry = {
+                    .is_file = false,
+                };
+                VfsDir__ctor(&entry._dir);
+                VfsDir__dupset(&root->_dir, cpnt, entry);
+            }
+        } else {
+            if(i == cpnts.count - 1) {
+                if(!entry->is_file) break;
+                // update file
+                free(entry->_file.data);
+                entry->_file.size = size;
+                entry->_file.data = data;
+                c11_vector__dtor(&cpnts);
+                return true;
+            } else {
+                if(entry->is_file) break;
+                root = entry;
+            }
+        }
+    }
+    c11_vector__dtor(&cpnts);
+    return false;
+}
+
+char** py_vfslist(const char* path, int* length) {
+    VfsEntry* entry = Vfs__get(path);
+    if(entry == NULL || entry->is_file) return NULL;
+    *length = 0;
+    char** ret = malloc(sizeof(char*) * entry->_dir.count);
+    for(int i = 0; i < entry->_dir.count; i++) {
+        VfsDir_KV* child = c11__at(VfsDir_KV, &entry->_dir, i);
+        if(child->value.is_file) {
+            int size = child->key.size;
+            ret[i] = malloc(size + 1);
+            memcpy(ret[i], child->key.data, size);
+            ret[i][size] = '\0';
+            (*length)++;
+        }
+    }
+    return ret;
+}

+ 29 - 7
src/interpreter/vm.c

@@ -3,13 +3,10 @@
 #include "pocketpy/common/sstream.h"
 #include "pocketpy/common/utils.h"
 #include "pocketpy/objects/base.h"
+#include "pocketpy/common/_generated.h"
 #include "pocketpy/pocketpy.h"
 
-#include <assert.h>
-#include <stdarg.h>
-#include <stdbool.h>
-
-static unsigned char* pk_default_import_file(const char* path) { return NULL; }
+static unsigned char* pk_default_import_file(const char* path, int* size) { return NULL; }
 
 static void pk_default_print(const char* data) { printf("%s", data); }
 
@@ -39,6 +36,13 @@ static void pk_TypeInfo__ctor(pk_TypeInfo* self,
 
 static void pk_TypeInfo__dtor(pk_TypeInfo* self) { c11_vector__dtor(&self->annotated_fields); }
 
+static void save_site_package_to_vfs(const char* name, const char* source) {
+    char buf[512];
+    snprintf(buf, sizeof(buf), "site-packages/%s", name);
+    bool ok = py_vfswrite(buf, (unsigned char*)source, strlen(source) + 1);
+    if(!ok) c11__abort("failed to save '%s' to vfs", name);
+}
+
 void pk_VM__ctor(pk_VM* self) {
     self->top_frame = NULL;
 
@@ -56,6 +60,7 @@ void pk_VM__ctor(pk_VM* self) {
     self->curr_exception = *py_NIL;
     self->is_stopiteration = false;
 
+    Vfs__ctor(&self->__vfs);
     self->__curr_class = NULL;
     self->__dynamic_func_decl = NULL;
 
@@ -175,7 +180,23 @@ void pk_VM__ctor(pk_VM* self) {
     py_newnotimplemented(&tmp);
     py_setdict(&self->builtins, py_name("NotImplemented"), &tmp);
 
-    self->main = *py_newmodule("__main__", NULL);
+    // add modules
+    pk__add_module_pkpy();
+
+    self->main = *py_newmodule("__main__");
+
+    save_site_package_to_vfs("bisect.py", kPythonLibs_bisect);
+    save_site_package_to_vfs("cmath.py", kPythonLibs_cmath);
+    save_site_package_to_vfs("collections.py", kPythonLibs_collections);
+    save_site_package_to_vfs("colorsys.py", kPythonLibs_colorsys);
+    save_site_package_to_vfs("datetime.py", kPythonLibs_datetime);
+    save_site_package_to_vfs("functools.py", kPythonLibs_functools);
+    save_site_package_to_vfs("heapq.py", kPythonLibs_heapq);
+    save_site_package_to_vfs("itertools.py", kPythonLibs_itertools);
+    save_site_package_to_vfs("operator.py", kPythonLibs_operator);
+    save_site_package_to_vfs("pickle.py", kPythonLibs_pickle);
+    save_site_package_to_vfs("this.py", kPythonLibs_this);
+    save_site_package_to_vfs("typing.py", kPythonLibs_typing);
 }
 
 void pk_VM__dtor(pk_VM* self) {
@@ -188,6 +209,7 @@ void pk_VM__dtor(pk_VM* self) {
     c11__foreach(pk_TypeInfo, &self->types, ti) pk_TypeInfo__dtor(ti);
     c11_vector__dtor(&self->types);
     ValueStack__clear(&self->stack);
+    Vfs__dtor(&self->__vfs);
 }
 
 void pk_VM__push_frame(pk_VM* self, Frame* frame) {
@@ -559,7 +581,7 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
 }
 
 void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) {
-    return;
+    // return;
     if(frame == NULL) return;
 
     py_TValue* sp = self->stack.sp;

+ 9 - 25
src/public/internal.c

@@ -52,9 +52,9 @@ void py_finalize() {
     pk_MemoryPools__finalize();
 }
 
-void py_switchvm(int index){
+void py_switchvm(int index) {
     if(index < 0 || index >= 16) c11__abort("invalid vm index");
-    if(!pk_all_vm[index]){
+    if(!pk_all_vm[index]) {
         pk_all_vm[index] = malloc(sizeof(pk_VM));
         pk_VM__ctor(pk_all_vm[index]);
     }
@@ -123,13 +123,9 @@ static void disassemble(CodeObject* co) {
         for(int j = 0; j < padding; j++)
             c11_sbuf__write_char(&ss, ' ');
 
-        // _opcode_argstr(this, i, byte, co);
         do {
             if(Bytecode__is_forward_jump(&byte)) {
-                c11_sbuf__write_int(&ss, (int16_t)byte.arg);
-                c11_sbuf__write_cstr(&ss, " (to ");
-                c11_sbuf__write_int(&ss, (int16_t)byte.arg + i);
-                c11_sbuf__write_char(&ss, ')');
+                pk_sprintf(&ss, "%d (to %d)", (int16_t)byte.arg, (int16_t)byte.arg + i);
                 break;
             }
 
@@ -138,12 +134,8 @@ static void disassemble(CodeObject* co) {
                 case OP_LOAD_CONST:
                 case OP_FORMAT_STRING:
                 case OP_IMPORT_PATH: {
-                    py_Ref tmp = c11__at(py_TValue, &co->consts, byte.arg);
-                    c11_sbuf__write_cstr(&ss, " (");
-                    // here we need to use py_repr, however this function is not ready yet
-                    c11_sbuf__write_cstr(&ss, "<class '");
-                    c11_sbuf__write_cstr(&ss, py_tpname(tmp->type));
-                    c11_sbuf__write_cstr(&ss, "'>)");
+                    py_Ref path = c11__at(py_TValue, &co->consts, byte.arg);
+                    pk_sprintf(&ss, " (%q)", py_tosv(path));
                     break;
                 }
                 case OP_LOAD_NAME:
@@ -158,32 +150,24 @@ static void disassemble(CodeObject* co) {
                 case OP_GOTO:
                 case OP_DELETE_GLOBAL:
                 case OP_STORE_CLASS_ATTR: {
-                    c11_sbuf__write_cstr(&ss, " (");
-                    c11_sbuf__write_cstr(&ss, py_name2str(byte.arg));
-                    c11_sbuf__write_char(&ss, ')');
+                    pk_sprintf(&ss, " (%n)", byte.arg);
                     break;
                 }
                 case OP_LOAD_FAST:
                 case OP_STORE_FAST:
                 case OP_DELETE_FAST: {
                     py_Name name = c11__getitem(py_Name, &co->varnames, byte.arg);
-                    c11_sbuf__write_cstr(&ss, " (");
-                    c11_sbuf__write_cstr(&ss, py_name2str(name));
-                    c11_sbuf__write_char(&ss, ')');
+                    pk_sprintf(&ss, " (%n)", name);
                     break;
                 }
                 case OP_LOAD_FUNCTION: {
                     const FuncDecl* decl = c11__getitem(FuncDecl*, &co->func_decls, byte.arg);
-                    c11_sbuf__write_cstr(&ss, " (");
-                    c11_sbuf__write_cstr(&ss, decl->code.name->data);
-                    c11_sbuf__write_char(&ss, ')');
+                    pk_sprintf(&ss, " (%s)", decl->code.name->data);
                     break;
                 }
                 case OP_BINARY_OP: {
                     py_Name name = byte.arg & 0xFF;
-                    c11_sbuf__write_cstr(&ss, " (");
-                    c11_sbuf__write_cstr(&ss, py_name2str(name));
-                    c11_sbuf__write_char(&ss, ')');
+                    pk_sprintf(&ss, " (%n)", name);
                     break;
                 }
             }

+ 96 - 23
src/public/modules.c

@@ -5,12 +5,12 @@
 #include "pocketpy/common/sstream.h"
 #include "pocketpy/interpreter/vm.h"
 
-py_Ref py_getmodule(const char* name) {
+py_Ref py_getmodule(const char* path) {
     pk_VM* vm = pk_current_vm;
-    return pk_NameDict__try_get(&vm->modules, py_name(name));
+    return pk_NameDict__try_get(&vm->modules, py_name(path));
 }
 
-py_Ref py_newmodule(const char* name, const char* package) {
+py_Ref py_newmodule(const char* path) {
     pk_ManagedHeap* heap = &pk_current_vm->heap;
     PyObject* obj = pk_ManagedHeap__new(heap, tp_module, -1, 0);
 
@@ -23,33 +23,106 @@ py_Ref py_newmodule(const char* name, const char* package) {
         ._obj = obj,
     };
 
-    py_newstr(r1, name);
-    py_setdict(r0, __name__, r1);
-
-    package = package ? package : "";
-
-    py_newstr(r1, package);
-    py_setdict(r0, __package__, r1);
-
-    // convert to fullname
-    if(package[0] != '\0') {
-        // package.name
-        char buf[256];
-        snprintf(buf, sizeof(buf), "%s.%s", package, name);
-        name = buf;
+    int last_dot = c11_sv__rindex((c11_sv){path, strlen(path)}, '.');
+    if(last_dot == -1) {
+        py_newstr(r1, path);
+        py_setdict(r0, __name__, r1);
+        py_newstr(r1, "");
+        py_setdict(r0, __package__, r1);
+    } else {
+        const char* start = path + last_dot + 1;
+        py_newstr(r1, start);
+        py_setdict(r0, __name__, r1);
+        py_newstrn(r1, path, last_dot);
+        py_setdict(r0, __package__, r1);
     }
 
-    py_newstr(r1, name);
+    py_newstr(r1, path);
     py_setdict(r0, __path__, r1);
 
     // we do not allow override in order to avoid memory leak
     // it is because Module objects are not garbage collected
-    bool exists = pk_NameDict__contains(&pk_current_vm->modules, py_name(name));
-    if(exists) c11__abort("module '%s' already exists", name);
-    pk_NameDict__set(&pk_current_vm->modules, py_name(name), *r0);
+    py_Name path_name = py_name(path);
+    bool exists = pk_NameDict__contains(&pk_current_vm->modules, path_name);
+    if(exists) c11__abort("module '%s' already exists", path);
+    pk_NameDict__set(&pk_current_vm->modules, path_name, *r0);
 
     py_shrink(2);
-    return py_getmodule(name);
+    return py_getmodule(path);
+}
+
+bool py_import(const char* path_cstr) {
+    pk_VM* vm = pk_current_vm;
+    c11_sv path = {path_cstr, strlen(path_cstr)};
+    if(path.size == 0) return ValueError("empty module name");
+
+    if(path.data[0] == '.') {
+        // try relative import
+        py_Ref package = py_getdict(&vm->top_frame->module, __package__);
+        c11_sv package_sv = py_tosv(package);
+        if(package_sv.size == 0) return ImportError("relative import %q with no known parent package", path);
+        c11_string* new_path = c11_string__new3("%v.%v", package_sv, path);
+        bool ok = py_import(new_path->data);
+        c11_string__delete(new_path);
+        return ok;
+    }
+
+    assert(path.data[0] != '.' && path.data[path.size - 1] != '.');
+
+    // check existing module
+    py_TmpRef ext_mod = py_getmodule(path.data);
+    if(ext_mod) {
+        py_Ref is_pending = py_getdict(ext_mod, __module_is_pending__);
+        if(is_pending) return ImportError("circular import detected");
+        py_assign(py_retval(), ext_mod);
+        return true;
+    }
+
+    // vector<std::string_view> path_cpnts = path.split('.');
+    // // check circular import
+    // if(__import_context.pending.size() > 128) { ImportError("maximum recursion depth exceeded
+    // while importing"); }
+
+    // try import
+    c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
+
+    c11_string* filename = c11_string__new3("site-packages/%s.py", slashed_path->data);
+    int size;
+    unsigned char* data = py_vfsread(filename->data, &size);
+    if(data != NULL) goto __SUCCESS;
+
+    c11_string__delete(filename);
+    filename = c11_string__new3("site-packages/%s/__init__.py", slashed_path->data);
+    data = py_vfsread(filename->data, &size);
+    if(data != NULL) goto __SUCCESS;
+
+    c11_string__delete(filename);
+    filename = c11_string__new3("%s.py", slashed_path->data);
+    data = vm->import_file(slashed_path->data, &size);
+    if(data != NULL) goto __SUCCESS;
+
+    c11_string__delete(filename);
+    filename = c11_string__new3("%s/__init__.py", slashed_path->data);
+    data = vm->import_file(slashed_path->data, &size);
+    if(data != NULL) goto __SUCCESS;
+
+    c11_string__delete(filename);
+    c11_string__delete(slashed_path);
+    return ImportError("module %q not found", path);
+
+__SUCCESS:
+    py_push(py_newmodule(path_cstr));
+    py_Ref mod = py_peek(-1);
+    py_setdict(mod, __module_is_pending__, py_True);
+    bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod);
+    py_deldict(mod, __module_is_pending__);
+    py_assign(py_retval(), mod);
+    py_pop();
+
+    c11_string__delete(filename);
+    c11_string__delete(slashed_path);
+    free(data);
+    return ok;
 }
 
 //////////////////////////
@@ -224,7 +297,7 @@ static bool _py_builtins__eval(int argc, py_Ref argv) {
 }
 
 py_TValue pk_builtins__register() {
-    py_Ref builtins = py_newmodule("builtins", NULL);
+    py_Ref builtins = py_newmodule("builtins");
     py_bindfunc(builtins, "repr", _py_builtins__repr);
     py_bindfunc(builtins, "exit", _py_builtins__exit);
     py_bindfunc(builtins, "len", _py_builtins__len);

+ 21 - 0
src/public/pkpy.c

@@ -0,0 +1,21 @@
+#include "pocketpy/pocketpy.h"
+
+#include "pocketpy/common/utils.h"
+#include "pocketpy/objects/object.h"
+#include "pocketpy/common/sstream.h"
+#include "pocketpy/interpreter/vm.h"
+
+static bool _py_pkpy__next(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    int res = py_next(argv);
+    if(res == -1) return false;
+    if(res) return true;
+    py_assign(py_retval(), py_tpobject(tp_StopIteration));
+    return true;
+}
+
+void pk__add_module_pkpy() {
+    py_Ref mod = py_newmodule("pkpy");
+
+    py_bindfunc(mod, "next", _py_pkpy__next);
+}

+ 1 - 1
tests/29_iter.py

@@ -1,4 +1,4 @@
-from __builtins import next
+from pkpy import next
 
 a = [1, 2, 3]
 a = iter(a)

Некоторые файлы не были показаны из-за большого количества измененных файлов