diff --git a/backup/vfs.zip b/backup/vfs.zip new file mode 100644 index 00000000..ead66565 Binary files /dev/null and b/backup/vfs.zip differ diff --git a/include/pocketpy/common/_generated.h b/include/pocketpy/common/_generated.h index 23841897..2e2eb1d6 100644 --- a/include/pocketpy/common/_generated.h +++ b/include/pocketpy/common/_generated.h @@ -5,6 +5,8 @@ extern "C" { #endif +const char* load_kPythonLib(const char* name); + extern const char kPythonLibs__enum[]; extern const char kPythonLibs__long[]; extern const char kPythonLibs__set[]; diff --git a/include/pocketpy/interpreter/vfs.h b/include/pocketpy/interpreter/vfs.h deleted file mode 100644 index e80ac031..00000000 --- a/include/pocketpy/interpreter/vfs.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "pocketpy/common/vector.h" -#include "pocketpy/common/str.h" -#include "pocketpy/pocketpy.h" - -#include - -#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 \ No newline at end of file diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 80d6f7b3..e1b178e6 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -4,7 +4,6 @@ #include "pocketpy/pocketpy.h" #include "pocketpy/interpreter/gc.h" #include "pocketpy/interpreter/frame.h" -#include "pocketpy/interpreter/vfs.h" #ifdef __cplusplus extern "C" { @@ -40,7 +39,7 @@ typedef struct pk_VM { py_TValue main; // __main__ module void (*ceval_on_step)(Frame*, Bytecode); - unsigned char* (*import_file)(const char*, int*); + char* (*import_file)(const char*); void (*print)(const char*); py_TValue last_retval; @@ -49,7 +48,6 @@ 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]; diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index c4d62182..602a0397 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -360,11 +360,6 @@ 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); diff --git a/prebuild.py b/prebuild.py index d8790fb8..17901b30 100644 --- a/prebuild.py +++ b/prebuild.py @@ -31,6 +31,8 @@ with open("include/pocketpy/common/_generated.h", "wt", encoding='utf-8', newlin extern "C" { #endif +const char* load_kPythonLib(const char* name); + ''' for key in sorted(sources.keys()): value = sources[key] @@ -45,9 +47,19 @@ extern "C" { with open("src/common/_generated.c", "wt", encoding='utf-8', newline='\n') as f: data = '''// generated by prebuild.py #include "pocketpy/common/_generated.h" - +#include ''' for key in sorted(sources.keys()): value = sources[key] data += f'const char kPythonLibs_{key}[] = {value};\n' f.write(data) + + f.write("\n") + f.write("const char* load_kPythonLib(const char* name) {\n") + for key in sorted(sources.keys()): + if key.startswith('_'): + continue + f.write(f' if (strcmp(name, "{key}") == 0) return kPythonLibs_{key};\n') + f.write(" return NULL;\n") + f.write("}\n") + diff --git a/src/common/_generated.c b/src/common/_generated.c index 147a3469..c471cdd6 100644 --- a/src/common/_generated.c +++ b/src/common/_generated.c @@ -1,6 +1,6 @@ // generated by prebuild.py #include "pocketpy/common/_generated.h" - +#include const char kPythonLibs__enum[] = "class Enum:\n def __init__(self, name, value):\n self.name = name\n self.value = value\n\n def __str__(self):\n return f'{type(self).__name__}.{self.name}'\n \n def __repr__(self):\n return f'<{str(self)}: {self.value!r}>'\n \n"; const char kPythonLibs__long[] = "# after v1.2.2, int is always 64-bit\nPyLong_SHIFT = 60//2 - 1\n\nPyLong_BASE = 2 ** PyLong_SHIFT\nPyLong_MASK = PyLong_BASE - 1\nPyLong_DECIMAL_SHIFT = 4\nPyLong_DECIMAL_BASE = 10 ** PyLong_DECIMAL_SHIFT\n\n##############################################################\n\ndef ulong_fromint(x: int):\n # return a list of digits and sign\n if x == 0: return [0], 1\n sign = 1 if x > 0 else -1\n if sign < 0: x = -x\n res = []\n while x:\n res.append(x & PyLong_MASK)\n x >>= PyLong_SHIFT\n return res, sign\n\ndef ulong_cmp(a: list, b: list) -> int:\n # return 1 if a>b, -1 if a len(b): return 1\n if len(a) < len(b): return -1\n for i in range(len(a)-1, -1, -1):\n if a[i] > b[i]: return 1\n if a[i] < b[i]: return -1\n return 0\n\ndef ulong_pad_(a: list, size: int):\n # pad leading zeros to have `size` digits\n delta = size - len(a)\n if delta > 0:\n a.extend([0] * delta)\n\ndef ulong_unpad_(a: list):\n # remove leading zeros\n while len(a)>1 and a[-1]==0:\n a.pop()\n\ndef ulong_add(a: list, b: list) -> list:\n res = [0] * max(len(a), len(b))\n ulong_pad_(a, len(res))\n ulong_pad_(b, len(res))\n carry = 0\n for i in range(len(res)):\n carry += a[i] + b[i]\n res[i] = carry & PyLong_MASK\n carry >>= PyLong_SHIFT\n if carry > 0:\n res.append(carry)\n return res\n\ndef ulong_inc_(a: list):\n a[0] += 1\n for i in range(len(a)):\n if a[i] < PyLong_BASE: break\n a[i] -= PyLong_BASE\n if i+1 == len(a):\n a.append(1)\n else:\n a[i+1] += 1\n \n\ndef ulong_sub(a: list, b: list) -> list:\n # a >= b\n res = []\n borrow = 0\n for i in range(len(b)):\n tmp = a[i] - b[i] - borrow\n if tmp < 0:\n tmp += PyLong_BASE\n borrow = 1\n else:\n borrow = 0\n res.append(tmp)\n for i in range(len(b), len(a)):\n tmp = a[i] - borrow\n if tmp < 0:\n tmp += PyLong_BASE\n borrow = 1\n else:\n borrow = 0\n res.append(tmp)\n ulong_unpad_(res)\n return res\n\ndef ulong_divmodi(a: list, b: int):\n # b > 0\n res = []\n carry = 0\n for i in range(len(a)-1, -1, -1):\n carry <<= PyLong_SHIFT\n carry += a[i]\n res.append(carry // b)\n carry %= b\n res.reverse()\n ulong_unpad_(res)\n return res, carry\n\n\ndef ulong_divmod(a: list, b: list):\n\n if ulong_cmp(a, b) < 0:\n return [0], a\n\n if len(b) == 1:\n q, r = ulong_divmodi(a, b[0])\n r, _ = ulong_fromint(r)\n return q, r\n\n max = (len(a) - len(b)) * PyLong_SHIFT + \x5c\n (a[-1].bit_length() - b[-1].bit_length())\n\n low = [0]\n\n high = (max // PyLong_SHIFT) * [0] + \x5c\n [(2**(max % PyLong_SHIFT)) & PyLong_MASK]\n\n while ulong_cmp(low, high) < 0:\n ulong_inc_(high)\n mid, r = ulong_divmodi(ulong_add(low, high), 2)\n if ulong_cmp(a, ulong_mul(b, mid)) >= 0:\n low = mid\n else:\n high = ulong_sub(mid, [1])\n\n q = [0] * (len(a) - len(b) + 1)\n while ulong_cmp(a, ulong_mul(b, low)) >= 0:\n q = ulong_add(q, low)\n a = ulong_sub(a, ulong_mul(b, low))\n ulong_unpad_(q)\n return q, a\n\ndef ulong_floordivi(a: list, b: int):\n # b > 0\n return ulong_divmodi(a, b)[0]\n\ndef ulong_muli(a: list, b: int):\n # b >= 0\n res = [0] * len(a)\n carry = 0\n for i in range(len(a)):\n carry += a[i] * b\n res[i] = carry & PyLong_MASK\n carry >>= PyLong_SHIFT\n if carry > 0:\n res.append(carry)\n return res\n\ndef ulong_mul(a: list, b: list):\n N = len(a) + len(b)\n # use grade-school multiplication\n res = [0] * N\n for i in range(len(a)):\n carry = 0\n for j in range(len(b)):\n carry += res[i+j] + a[i] * b[j]\n res[i+j] = carry & PyLong_MASK\n carry >>= PyLong_SHIFT\n res[i+len(b)] = carry\n ulong_unpad_(res)\n return res\n\ndef ulong_powi(a: list, b: int):\n # b >= 0\n if b == 0: return [1]\n res = [1]\n while b:\n if b & 1:\n res = ulong_mul(res, a)\n a = ulong_mul(a, a)\n b >>= 1\n return res\n\ndef ulong_repr(x: list) -> str:\n res = []\n while len(x)>1 or x[0]>0: # non-zero\n x, r = ulong_divmodi(x, PyLong_DECIMAL_BASE)\n res.append(str(r).zfill(PyLong_DECIMAL_SHIFT))\n res.reverse()\n s = ''.join(res)\n if len(s) == 0: return '0'\n if len(s) > 1: s = s.lstrip('0')\n return s\n\ndef ulong_fromstr(s: str):\n if s[-1] == 'L':\n s = s[:-1]\n res, base = [0], [1]\n if s[0] == '-':\n sign = -1\n s = s[1:]\n else:\n sign = 1\n s = s[::-1]\n for c in s:\n c = ord(c) - 48\n assert 0 <= c <= 9\n res = ulong_add(res, ulong_muli(base, c))\n base = ulong_muli(base, 10)\n return res, sign\n\nclass long:\n def __init__(self, x):\n if type(x) is tuple:\n self.digits, self.sign = x\n elif type(x) is int:\n self.digits, self.sign = ulong_fromint(x)\n elif type(x) is float:\n self.digits, self.sign = ulong_fromint(int(x))\n elif type(x) is str:\n self.digits, self.sign = ulong_fromstr(x)\n elif type(x) is long:\n self.digits, self.sign = x.digits.copy(), x.sign\n else:\n raise TypeError('expected int or str')\n \n def __len__(self):\n return len(self.digits)\n\n def __add__(self, other):\n if type(other) is int:\n other = long(other)\n elif type(other) is not long:\n return NotImplemented\n if self.sign == other.sign:\n return long((ulong_add(self.digits, other.digits), self.sign))\n else:\n cmp = ulong_cmp(self.digits, other.digits)\n if cmp == 0:\n return long(0)\n if cmp > 0:\n return long((ulong_sub(self.digits, other.digits), self.sign))\n else:\n return long((ulong_sub(other.digits, self.digits), other.sign))\n \n def __radd__(self, other):\n return self.__add__(other)\n \n def __sub__(self, other):\n if type(other) is int:\n other = long(other)\n elif type(other) is not long:\n return NotImplemented\n if self.sign != other.sign:\n return long((ulong_add(self.digits, other.digits), self.sign))\n cmp = ulong_cmp(self.digits, other.digits)\n if cmp == 0:\n return long(0)\n if cmp > 0:\n return long((ulong_sub(self.digits, other.digits), self.sign))\n else:\n return long((ulong_sub(other.digits, self.digits), -other.sign))\n \n def __rsub__(self, other):\n if type(other) is int:\n other = long(other)\n elif type(other) is not long:\n return NotImplemented\n return other.__sub__(self)\n \n def __mul__(self, other):\n if type(other) is int:\n return long((\n ulong_muli(self.digits, abs(other)),\n self.sign * (1 if other >= 0 else -1)\n ))\n elif type(other) is long:\n return long((\n ulong_mul(self.digits, other.digits),\n self.sign * other.sign\n ))\n return NotImplemented\n \n def __rmul__(self, other):\n return self.__mul__(other)\n \n #######################################################\n def __divmod__(self, other):\n if type(other) is int:\n assert self.sign == 1 and other > 0\n q, r = ulong_divmodi(self.digits, other)\n return long((q, 1)), r\n if type(other) is long:\n assert self.sign == 1 and other.sign == 1\n q, r = ulong_divmod(self.digits, other.digits)\n assert len(other)>1 or other.digits[0]>0\n return long((q, 1)), long((r, 1))\n raise NotImplementedError\n\n def __floordiv__(self, other):\n return self.__divmod__(other)[0]\n\n def __mod__(self, other):\n return self.__divmod__(other)[1]\n\n def __pow__(self, other: int):\n assert type(other) is int and other >= 0\n if self.sign == -1 and other & 1:\n sign = -1\n else:\n sign = 1\n return long((ulong_powi(self.digits, other), sign))\n \n def __lshift__(self, other: int):\n assert type(other) is int and other >= 0\n x = self.digits.copy()\n q, r = divmod(other, PyLong_SHIFT)\n x = [0]*q + x\n for _ in range(r): x = ulong_muli(x, 2)\n return long((x, self.sign))\n \n def __rshift__(self, other: int):\n assert type(other) is int and other >= 0\n x = self.digits.copy()\n q, r = divmod(other, PyLong_SHIFT)\n x = x[q:]\n if not x: return long(0)\n for _ in range(r): x = ulong_floordivi(x, 2)\n return long((x, self.sign))\n \n def __neg__(self):\n return long((self.digits, -self.sign))\n \n def __cmp__(self, other):\n if type(other) is int:\n other = long(other)\n elif type(other) is not long:\n return NotImplemented\n if self.sign > other.sign:\n return 1\n elif self.sign < other.sign:\n return -1\n else:\n return ulong_cmp(self.digits, other.digits)\n \n def __eq__(self, other):\n return self.__cmp__(other) == 0\n def __lt__(self, other):\n return self.__cmp__(other) < 0\n def __le__(self, other):\n return self.__cmp__(other) <= 0\n def __gt__(self, other):\n return self.__cmp__(other) > 0\n def __ge__(self, other):\n return self.__cmp__(other) >= 0\n \n def __repr__(self):\n prefix = '-' if self.sign < 0 else ''\n return prefix + ulong_repr(self.digits) + 'L'\n"; const char kPythonLibs__set[] = "class set:\n def __init__(self, iterable=None):\n iterable = iterable or []\n self._a = {}\n self.update(iterable)\n\n def add(self, elem):\n self._a[elem] = None\n \n def discard(self, elem):\n self._a.pop(elem, None)\n\n def remove(self, elem):\n del self._a[elem]\n \n def clear(self):\n self._a.clear()\n\n def update(self, other):\n for elem in other:\n self.add(elem)\n\n def __len__(self):\n return len(self._a)\n \n def copy(self):\n return set(self._a.keys())\n \n def __and__(self, other):\n return {elem for elem in self if elem in other}\n\n def __sub__(self, other):\n return {elem for elem in self if elem not in other}\n \n def __or__(self, other):\n ret = self.copy()\n ret.update(other)\n return ret\n\n def __xor__(self, other): \n _0 = self - other\n _1 = other - self\n return _0 | _1\n\n def union(self, other):\n return self | other\n\n def intersection(self, other):\n return self & other\n\n def difference(self, other):\n return self - other\n\n def symmetric_difference(self, other): \n return self ^ other\n \n def __eq__(self, other):\n if not isinstance(other, set):\n return NotImplemented\n return len(self ^ other) == 0\n\n def isdisjoint(self, other):\n return len(self & other) == 0\n \n def issubset(self, other):\n return len(self - other) == 0\n \n def issuperset(self, other):\n return len(other - self) == 0\n\n def __contains__(self, elem):\n return elem in self._a\n \n def __repr__(self):\n if len(self) == 0:\n return 'set()'\n return '{'+ ', '.join([repr(i) for i in self._a.keys()]) + '}'\n \n def __iter__(self):\n return iter(self._a.keys())"; @@ -17,3 +17,20 @@ const char kPythonLibs_operator[] = "# https://docs.python.org/3/library/operato const char kPythonLibs_pickle[] = "import json\nfrom c import struct\nimport builtins\n\n_BASIC_TYPES = [int, float, str, bool, type(None)]\n_MOD_T_SEP = \"@\"\n\ndef _find_class(path: str):\n if _MOD_T_SEP not in path:\n return builtins.__dict__[path]\n modpath, name = path.split(_MOD_T_SEP)\n return __import__(modpath).__dict__[name]\n\nclass _Pickler:\n def __init__(self, obj) -> None:\n self.obj = obj\n self.raw_memo = {} # id -> int\n self.memo = [] # int -> object\n\n @staticmethod\n def _type_id(t: type):\n assert type(t) is type\n name = t.__name__\n mod = t.__module__\n if mod is not None:\n name = mod.__path__ + _MOD_T_SEP + name\n return name\n\n def wrap(self, o):\n o_t = type(o)\n if o_t in _BASIC_TYPES:\n return o\n if o_t is type:\n return [\"type\", self._type_id(o)]\n\n index = self.raw_memo.get(id(o), None)\n if index is not None:\n return [index]\n \n ret = []\n index = len(self.memo)\n self.memo.append(ret)\n self.raw_memo[id(o)] = index\n\n if o_t is tuple:\n ret.append(\"tuple\")\n ret.append([self.wrap(i) for i in o])\n return [index]\n if o_t is bytes:\n ret.append(\"bytes\")\n ret.append([o[j] for j in range(len(o))])\n return [index]\n if o_t is list:\n ret.append(\"list\")\n ret.append([self.wrap(i) for i in o])\n return [index]\n if o_t is dict:\n ret.append(\"dict\")\n ret.append([[self.wrap(k), self.wrap(v)] for k,v in o.items()])\n return [index]\n \n _0 = self._type_id(o_t)\n\n if getattr(o_t, '__struct__', False):\n ret.append(_0)\n ret.append(o.tostruct().hex())\n return [index]\n\n if hasattr(o, \"__getnewargs__\"):\n _1 = o.__getnewargs__() # an iterable\n _1 = [self.wrap(i) for i in _1]\n else:\n _1 = None\n\n if o.__dict__ is None:\n _2 = None\n else:\n _2 = {k: self.wrap(v) for k,v in o.__dict__.items()}\n\n ret.append(_0) # type id\n ret.append(_1) # newargs\n ret.append(_2) # state\n return [index]\n \n def run_pipe(self):\n o = self.wrap(self.obj)\n return [o, self.memo]\n\n\n\nclass _Unpickler:\n def __init__(self, obj, memo: list) -> None:\n self.obj = obj\n self.memo = memo\n self._unwrapped = [None] * len(memo)\n\n def tag(self, index, o):\n assert self._unwrapped[index] is None\n self._unwrapped[index] = o\n\n def unwrap(self, o, index=None):\n if type(o) in _BASIC_TYPES:\n return o\n assert type(o) is list\n\n if o[0] == \"type\":\n return _find_class(o[1])\n\n # reference\n if type(o[0]) is int:\n assert index is None # index should be None\n index = o[0]\n if self._unwrapped[index] is None:\n o = self.memo[index]\n assert type(o) is list\n assert type(o[0]) is str\n self.unwrap(o, index)\n assert self._unwrapped[index] is not None\n return self._unwrapped[index]\n \n # concrete reference type\n if o[0] == \"tuple\":\n ret = tuple([self.unwrap(i) for i in o[1]])\n self.tag(index, ret)\n return ret\n if o[0] == \"bytes\":\n ret = bytes(o[1])\n self.tag(index, ret)\n return ret\n if o[0] == \"list\":\n ret = []\n self.tag(index, ret)\n for i in o[1]:\n ret.append(self.unwrap(i))\n return ret\n if o[0] == \"dict\":\n ret = {}\n self.tag(index, ret)\n for k,v in o[1]:\n ret[self.unwrap(k)] = self.unwrap(v)\n return ret\n \n # generic object\n cls = _find_class(o[0])\n if getattr(cls, '__struct__', False):\n inst = cls.fromstruct(struct.fromhex(o[1]))\n self.tag(index, inst)\n return inst\n else:\n _, newargs, state = o\n # create uninitialized instance\n new_f = getattr(cls, \"__new__\")\n if newargs is not None:\n newargs = [self.unwrap(i) for i in newargs]\n inst = new_f(cls, *newargs)\n else:\n inst = new_f(cls)\n self.tag(index, inst)\n # restore state\n if state is not None:\n for k,v in state.items():\n setattr(inst, k, self.unwrap(v))\n return inst\n\n def run_pipe(self):\n return self.unwrap(self.obj)\n\n\ndef _wrap(o):\n return _Pickler(o).run_pipe()\n\ndef _unwrap(packed: list):\n return _Unpickler(*packed).run_pipe()\n\ndef dumps(o) -> bytes:\n o = _wrap(o)\n return json.dumps(o).encode()\n\ndef loads(b) -> object:\n assert type(b) is bytes\n o = json.loads(b.decode())\n return _unwrap(o)"; const char kPythonLibs_this[] = "print(\"\"\"The Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!\"\"\")"; const char kPythonLibs_typing[] = "class _Placeholder:\n def __init__(self, *args, **kwargs):\n pass\n def __getitem__(self, *args):\n return self\n def __call__(self, *args, **kwargs):\n return self\n def __and__(self, other):\n return self\n def __or__(self, other):\n return self\n def __xor__(self, other):\n return self\n\n\n_PLACEHOLDER = _Placeholder()\n\nList = _PLACEHOLDER\nDict = _PLACEHOLDER\nTuple = _PLACEHOLDER\nSet = _PLACEHOLDER\nAny = _PLACEHOLDER\nUnion = _PLACEHOLDER\nOptional = _PLACEHOLDER\nCallable = _PLACEHOLDER\nType = _PLACEHOLDER\nProtocol = _PLACEHOLDER\n\nLiteral = _PLACEHOLDER\nLiteralString = _PLACEHOLDER\n\nIterable = _PLACEHOLDER\nGenerator = _PLACEHOLDER\n\nHashable = _PLACEHOLDER\n\nTypeVar = _PLACEHOLDER\nSelf = _PLACEHOLDER\n\nclass Generic:\n pass\n\nTYPE_CHECKING = False\n\n# decorators\noverload = lambda x: x\nfinal = lambda x: x\n"; + +const char* load_kPythonLib(const char* name) { + if (strcmp(name, "bisect") == 0) return kPythonLibs_bisect; + if (strcmp(name, "builtins") == 0) return kPythonLibs_builtins; + if (strcmp(name, "cmath") == 0) return kPythonLibs_cmath; + if (strcmp(name, "collections") == 0) return kPythonLibs_collections; + if (strcmp(name, "colorsys") == 0) return kPythonLibs_colorsys; + if (strcmp(name, "datetime") == 0) return kPythonLibs_datetime; + if (strcmp(name, "functools") == 0) return kPythonLibs_functools; + if (strcmp(name, "heapq") == 0) return kPythonLibs_heapq; + if (strcmp(name, "itertools") == 0) return kPythonLibs_itertools; + if (strcmp(name, "operator") == 0) return kPythonLibs_operator; + if (strcmp(name, "pickle") == 0) return kPythonLibs_pickle; + if (strcmp(name, "this") == 0) return kPythonLibs_this; + if (strcmp(name, "typing") == 0) return kPythonLibs_typing; + return NULL; +} diff --git a/src/interpreter/vfs.c b/src/interpreter/vfs.c deleted file mode 100644 index c708fe6c..00000000 --- a/src/interpreter/vfs.c +++ /dev/null @@ -1,131 +0,0 @@ -#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; -} \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 5e846e86..4327eaf5 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -6,7 +6,7 @@ #include "pocketpy/common/_generated.h" #include "pocketpy/pocketpy.h" -static unsigned char* pk_default_import_file(const char* path, int* size) { return NULL; } +static char* pk_default_import_file(const char* path) { return NULL; } static void pk_default_print(const char* data) { printf("%s", data); } @@ -36,13 +36,6 @@ 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; @@ -60,7 +53,6 @@ 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; @@ -184,19 +176,6 @@ void pk_VM__ctor(pk_VM* self) { 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) { @@ -209,7 +188,6 @@ 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) { diff --git a/src/public/modules.c b/src/public/modules.c index e9125e76..415e3575 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -4,6 +4,7 @@ #include "pocketpy/objects/object.h" #include "pocketpy/common/sstream.h" #include "pocketpy/interpreter/vm.h" +#include "pocketpy/common/_generated.h" py_Ref py_getmodule(const char* path) { pk_VM* vm = pk_current_vm; @@ -60,7 +61,8 @@ bool py_import(const char* path_cstr) { // 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); + 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); @@ -85,25 +87,23 @@ bool py_import(const char* path_cstr) { // try import c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); + c11_string* filename = c11_string__new3("%s.py", slashed_path->data); - 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; + bool need_free = true; + const char* data = load_kPythonLib(path_cstr); + if(data != NULL) { + need_free = false; + goto __SUCCESS; + } c11_string__delete(filename); filename = c11_string__new3("%s.py", slashed_path->data); - data = vm->import_file(slashed_path->data, &size); + data = vm->import_file(slashed_path->data); 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); + data = vm->import_file(slashed_path->data); if(data != NULL) goto __SUCCESS; c11_string__delete(filename); @@ -121,7 +121,7 @@ __SUCCESS: c11_string__delete(filename); c11_string__delete(slashed_path); - free(data); + if(need_free) free((void*)data); return ok; }