From 88f893ddd70a3f72e983464c5cf0da6d70f0e4b3 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 10 Aug 2024 20:21:40 +0800 Subject: [PATCH] ... --- build.sh | 2 +- include/pocketpy/common/_generated.h | 1 - include/pocketpy/compiler/lexer.h | 2 +- include/pocketpy/interpreter/vm.h | 9 +- include/pocketpy/xmacros/opcodes.h | 1 - python/_long.py | 352 ------------------ python/builtins.py | 7 +- python/collections.py | 8 +- python/pickle.py | 2 +- src/common/_generated.c | 7 +- src/compiler/compiler.c | 11 +- src/compiler/lexer.c | 302 +++++++++------ src/interpreter/ceval.c | 64 ++-- src/interpreter/vm.c | 16 +- src/modules/math.c | 1 - src/modules/random.c | 18 +- src/objects/codeobject.c | 1 + src/public/modules.c | 34 +- src/public/py_array.c | 52 ++- src/public/py_dict.c | 18 +- src/public/py_list.c | 19 +- src/public/py_mappingproxy.c | 37 +- src/public/py_number.c | 28 +- src/public/py_object.c | 14 + src/public/py_str.c | 46 ++- src/public/py_tuple.c | 13 +- tests/00_tmp.py | 7 + tests/04_str.py | 4 + tests/51_yield.py | 2 +- tests/{80_cmath.py => 71_cmath.py} | 0 tests/{80_gc.py => 71_gc.py} | 0 .../{70_collections.py => 79_collections.py} | 2 + tests/{80_datetime.py => 79_datetime.py} | 0 tests/{80_easing.py => 79_easing.py} | 0 tests/{70_file.py => 79_file.py} | 0 tests/80_long.py | 50 --- 36 files changed, 437 insertions(+), 693 deletions(-) delete mode 100644 python/_long.py create mode 100644 tests/00_tmp.py rename tests/{80_cmath.py => 71_cmath.py} (100%) rename tests/{80_gc.py => 71_gc.py} (100%) rename tests/{70_collections.py => 79_collections.py} (99%) rename tests/{80_datetime.py => 79_datetime.py} (100%) rename tests/{80_easing.py => 79_easing.py} (100%) rename tests/{70_file.py => 79_file.py} (100%) delete mode 100644 tests/80_long.py diff --git a/build.sh b/build.sh index 0fb29e1e..a1387c71 100644 --- a/build.sh +++ b/build.sh @@ -33,7 +33,7 @@ else LINK_FLAGS="-Wl,-rpath=." fi -clang $FLAGS -o libpocketpy$LIB_EXTENSION $SRC -fPIC -shared +clang $FLAGS -o libpocketpy$LIB_EXTENSION $SRC -fPIC -shared -lm # compile main.cpp and link to libpocketpy.so echo "> Compiling main.c and linking to libpocketpy$LIB_EXTENSION..." diff --git a/include/pocketpy/common/_generated.h b/include/pocketpy/common/_generated.h index c06608d4..cfff7e87 100644 --- a/include/pocketpy/common/_generated.h +++ b/include/pocketpy/common/_generated.h @@ -4,7 +4,6 @@ const char* load_kPythonLib(const char* name); extern const char kPythonLibs__enum[]; -extern const char kPythonLibs__long[]; extern const char kPythonLibs_bisect[]; extern const char kPythonLibs_builtins[]; extern const char kPythonLibs_cmath[]; diff --git a/include/pocketpy/compiler/lexer.h b/include/pocketpy/compiler/lexer.h index 03201c2c..730155a1 100644 --- a/include/pocketpy/compiler/lexer.h +++ b/include/pocketpy/compiler/lexer.h @@ -10,7 +10,7 @@ extern const char* TokenSymbols[]; typedef enum TokenIndex{ TK_EOF, TK_EOL, TK_SOF, - TK_ID, TK_NUM, TK_STR, TK_FSTR, TK_LONG, TK_BYTES, TK_IMAG, + TK_ID, TK_NUM, TK_STR, TK_FSTR, TK_BYTES, TK_IMAG, TK_INDENT, TK_DEDENT, /***************/ TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM, diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 4ad8cb94..3a7ebba3 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -6,6 +6,11 @@ #include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/modules.h" +// TODO: +// 1. __eq__ and __ne__ fallbacks +// 2. un-cleared exception detection +// 3. super() + typedef struct py_TypeInfo { py_Name name; py_Type base; @@ -89,8 +94,8 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall const char* pk_opname(Opcode op); -py_TValue* pk_arrayview(py_Ref self, int* length); -int pk_arrayequal(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length); +int pk_arrayview(py_Ref self, py_TValue** p); +bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv); bool pk_arrayiter(py_Ref val); bool pk_arraycontains(py_Ref self, py_Ref val); diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index d353ecc9..4c479aac 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -42,7 +42,6 @@ OPCODE(DELETE_GLOBAL) OPCODE(DELETE_ATTR) OPCODE(DELETE_SUBSCR) /**************************/ -OPCODE(BUILD_LONG) OPCODE(BUILD_IMAG) OPCODE(BUILD_BYTES) OPCODE(BUILD_TUPLE) diff --git a/python/_long.py b/python/_long.py deleted file mode 100644 index 0e5aa3dc..00000000 --- a/python/_long.py +++ /dev/null @@ -1,352 +0,0 @@ -# after v1.2.2, int is always 64-bit -PyLong_SHIFT = 60//2 - 1 - -PyLong_BASE = 2 ** PyLong_SHIFT -PyLong_MASK = PyLong_BASE - 1 -PyLong_DECIMAL_SHIFT = 4 -PyLong_DECIMAL_BASE = 10 ** PyLong_DECIMAL_SHIFT - -############################################################## - -def ulong_fromint(x: int): - # return a list of digits and sign - if x == 0: return [0], 1 - sign = 1 if x > 0 else -1 - if sign < 0: x = -x - res = [] - while x: - res.append(x & PyLong_MASK) - x >>= PyLong_SHIFT - return res, sign - -def ulong_cmp(a: list, b: list) -> int: - # return 1 if a>b, -1 if a len(b): return 1 - if len(a) < len(b): return -1 - for i in range(len(a)-1, -1, -1): - if a[i] > b[i]: return 1 - if a[i] < b[i]: return -1 - return 0 - -def ulong_pad_(a: list, size: int): - # pad leading zeros to have `size` digits - delta = size - len(a) - if delta > 0: - a.extend([0] * delta) - -def ulong_unpad_(a: list): - # remove leading zeros - while len(a)>1 and a[-1]==0: - a.pop() - -def ulong_add(a: list, b: list) -> list: - res = [0] * max(len(a), len(b)) - ulong_pad_(a, len(res)) - ulong_pad_(b, len(res)) - carry = 0 - for i in range(len(res)): - carry += a[i] + b[i] - res[i] = carry & PyLong_MASK - carry >>= PyLong_SHIFT - if carry > 0: - res.append(carry) - return res - -def ulong_inc_(a: list): - a[0] += 1 - for i in range(len(a)): - if a[i] < PyLong_BASE: break - a[i] -= PyLong_BASE - if i+1 == len(a): - a.append(1) - else: - a[i+1] += 1 - - -def ulong_sub(a: list, b: list) -> list: - # a >= b - res = [] - borrow = 0 - for i in range(len(b)): - tmp = a[i] - b[i] - borrow - if tmp < 0: - tmp += PyLong_BASE - borrow = 1 - else: - borrow = 0 - res.append(tmp) - for i in range(len(b), len(a)): - tmp = a[i] - borrow - if tmp < 0: - tmp += PyLong_BASE - borrow = 1 - else: - borrow = 0 - res.append(tmp) - ulong_unpad_(res) - return res - -def ulong_divmodi(a: list, b: int): - # b > 0 - res = [] - carry = 0 - for i in range(len(a)-1, -1, -1): - carry <<= PyLong_SHIFT - carry += a[i] - res.append(carry // b) - carry %= b - res.reverse() - ulong_unpad_(res) - return res, carry - - -def ulong_divmod(a: list, b: list): - - if ulong_cmp(a, b) < 0: - return [0], a - - if len(b) == 1: - q, r = ulong_divmodi(a, b[0]) - r, _ = ulong_fromint(r) - return q, r - - max = (len(a) - len(b)) * PyLong_SHIFT + \ - (a[-1].bit_length() - b[-1].bit_length()) - - low = [0] - - high = (max // PyLong_SHIFT) * [0] + \ - [(2**(max % PyLong_SHIFT)) & PyLong_MASK] - - while ulong_cmp(low, high) < 0: - ulong_inc_(high) - mid, r = ulong_divmodi(ulong_add(low, high), 2) - if ulong_cmp(a, ulong_mul(b, mid)) >= 0: - low = mid - else: - high = ulong_sub(mid, [1]) - - q = [0] * (len(a) - len(b) + 1) - while ulong_cmp(a, ulong_mul(b, low)) >= 0: - q = ulong_add(q, low) - a = ulong_sub(a, ulong_mul(b, low)) - ulong_unpad_(q) - return q, a - -def ulong_floordivi(a: list, b: int): - # b > 0 - return ulong_divmodi(a, b)[0] - -def ulong_muli(a: list, b: int): - # b >= 0 - res = [0] * len(a) - carry = 0 - for i in range(len(a)): - carry += a[i] * b - res[i] = carry & PyLong_MASK - carry >>= PyLong_SHIFT - if carry > 0: - res.append(carry) - return res - -def ulong_mul(a: list, b: list): - N = len(a) + len(b) - # use grade-school multiplication - res = [0] * N - for i in range(len(a)): - carry = 0 - for j in range(len(b)): - carry += res[i+j] + a[i] * b[j] - res[i+j] = carry & PyLong_MASK - carry >>= PyLong_SHIFT - res[i+len(b)] = carry - ulong_unpad_(res) - return res - -def ulong_powi(a: list, b: int): - # b >= 0 - if b == 0: return [1] - res = [1] - while b: - if b & 1: - res = ulong_mul(res, a) - a = ulong_mul(a, a) - b >>= 1 - return res - -def ulong_repr(x: list) -> str: - res = [] - while len(x)>1 or x[0]>0: # non-zero - x, r = ulong_divmodi(x, PyLong_DECIMAL_BASE) - res.append(str(r).zfill(PyLong_DECIMAL_SHIFT)) - res.reverse() - s = ''.join(res) - if len(s) == 0: return '0' - if len(s) > 1: s = s.lstrip('0') - return s - -def ulong_fromstr(s: str): - if s[-1] == 'L': - s = s[:-1] - res, base = [0], [1] - if s[0] == '-': - sign = -1 - s = s[1:] - else: - sign = 1 - s = s[::-1] - for c in s: - c = ord(c) - 48 - assert 0 <= c <= 9 - res = ulong_add(res, ulong_muli(base, c)) - base = ulong_muli(base, 10) - return res, sign - -class long: - def __init__(self, x): - if type(x) is tuple: - self.digits, self.sign = x - elif type(x) is int: - self.digits, self.sign = ulong_fromint(x) - elif type(x) is float: - self.digits, self.sign = ulong_fromint(int(x)) - elif type(x) is str: - self.digits, self.sign = ulong_fromstr(x) - elif type(x) is long: - self.digits, self.sign = x.digits.copy(), x.sign - else: - raise TypeError('expected int or str') - - def __len__(self): - return len(self.digits) - - def __add__(self, other): - if type(other) is int: - other = long(other) - elif type(other) is not long: - return NotImplemented - if self.sign == other.sign: - return long((ulong_add(self.digits, other.digits), self.sign)) - else: - cmp = ulong_cmp(self.digits, other.digits) - if cmp == 0: - return long(0) - if cmp > 0: - return long((ulong_sub(self.digits, other.digits), self.sign)) - else: - return long((ulong_sub(other.digits, self.digits), other.sign)) - - def __radd__(self, other): - return self.__add__(other) - - def __sub__(self, other): - if type(other) is int: - other = long(other) - elif type(other) is not long: - return NotImplemented - if self.sign != other.sign: - return long((ulong_add(self.digits, other.digits), self.sign)) - cmp = ulong_cmp(self.digits, other.digits) - if cmp == 0: - return long(0) - if cmp > 0: - return long((ulong_sub(self.digits, other.digits), self.sign)) - else: - return long((ulong_sub(other.digits, self.digits), -other.sign)) - - def __rsub__(self, other): - if type(other) is int: - other = long(other) - elif type(other) is not long: - return NotImplemented - return other.__sub__(self) - - def __mul__(self, other): - if type(other) is int: - return long(( - ulong_muli(self.digits, abs(other)), - self.sign * (1 if other >= 0 else -1) - )) - elif type(other) is long: - return long(( - ulong_mul(self.digits, other.digits), - self.sign * other.sign - )) - return NotImplemented - - def __rmul__(self, other): - return self.__mul__(other) - - ####################################################### - def __divmod__(self, other): - if type(other) is int: - assert self.sign == 1 and other > 0 - q, r = ulong_divmodi(self.digits, other) - return long((q, 1)), r - if type(other) is long: - assert self.sign == 1 and other.sign == 1 - q, r = ulong_divmod(self.digits, other.digits) - assert len(other)>1 or other.digits[0]>0 - return long((q, 1)), long((r, 1)) - raise NotImplementedError - - def __floordiv__(self, other): - return self.__divmod__(other)[0] - - def __mod__(self, other): - return self.__divmod__(other)[1] - - def __pow__(self, other: int): - assert type(other) is int and other >= 0 - if self.sign == -1 and other & 1: - sign = -1 - else: - sign = 1 - return long((ulong_powi(self.digits, other), sign)) - - def __lshift__(self, other: int): - assert type(other) is int and other >= 0 - x = self.digits.copy() - q, r = divmod(other, PyLong_SHIFT) - x = [0]*q + x - for _ in range(r): x = ulong_muli(x, 2) - return long((x, self.sign)) - - def __rshift__(self, other: int): - assert type(other) is int and other >= 0 - x = self.digits.copy() - q, r = divmod(other, PyLong_SHIFT) - x = x[q:] - if not x: return long(0) - for _ in range(r): x = ulong_floordivi(x, 2) - return long((x, self.sign)) - - def __neg__(self): - return long((self.digits, -self.sign)) - - def __cmp__(self, other): - if type(other) is int: - other = long(other) - elif type(other) is not long: - return NotImplemented - if self.sign > other.sign: - return 1 - elif self.sign < other.sign: - return -1 - else: - return ulong_cmp(self.digits, other.digits) - - def __eq__(self, other): - return self.__cmp__(other) == 0 - def __lt__(self, other): - return self.__cmp__(other) < 0 - def __le__(self, other): - return self.__cmp__(other) <= 0 - def __gt__(self, other): - return self.__cmp__(other) > 0 - def __ge__(self, other): - return self.__cmp__(other) >= 0 - - def __repr__(self): - prefix = '-' if self.sign < 0 else '' - return prefix + ulong_repr(self.digits) + 'L' diff --git a/python/builtins.py b/python/builtins.py index c96bc027..a906ddf6 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -174,13 +174,10 @@ def help(obj): if obj.__doc__: print(obj.__doc__) -def complex(*args, **kwargs): +def complex(real, imag=0): import cmath - return cmath.complex(*args, **kwargs) + return cmath.complex(real, imag) -def long(*args, **kwargs): - import _long - return _long.long(*args, **kwargs) class set: def __init__(self, iterable=None): diff --git a/python/collections.py b/python/collections.py index 98632b72..da12c5cd 100644 --- a/python/collections.py +++ b/python/collections.py @@ -15,7 +15,6 @@ def Counter(iterable: Iterable[T]): class defaultdict(dict): def __init__(self, default_factory, *args): super().__init__(*args) - _enable_instance_dict(self) self.default_factory = default_factory def __missing__(self, key): @@ -133,7 +132,7 @@ class deque(Generic[T]): def __eq__(self, other: object) -> bool: if not isinstance(other, deque): - return False + return NotImplemented if len(self) != len(other): return False for x, y in zip(self, other): @@ -141,6 +140,11 @@ class deque(Generic[T]): return False return True + def __ne__(self, other: object) -> bool: + if not isinstance(other, deque): + return NotImplemented + return not self == other + def __repr__(self) -> str: return f"deque({list(self)!r})" diff --git a/python/pickle.py b/python/pickle.py index bfd7ef4d..d069fe76 100644 --- a/python/pickle.py +++ b/python/pickle.py @@ -22,7 +22,7 @@ class _Pickler: name = t.__name__ mod = t.__module__ if mod is not None: - name = mod.__path__ + _MOD_T_SEP + name + name = mod + _MOD_T_SEP + name return name def wrap(self, o): diff --git a/src/common/_generated.c b/src/common/_generated.c index 012b05de..aef582bc 100644 --- a/src/common/_generated.c +++ b/src/common/_generated.c @@ -2,17 +2,16 @@ #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_bisect[] = "\"\"\"Bisection algorithms.\"\"\"\n\ndef insort_right(a, x, lo=0, hi=None):\n \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n If x is already in a, insert it to the right of the rightmost x.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n lo = bisect_right(a, x, lo, hi)\n a.insert(lo, x)\n\ndef bisect_right(a, x, lo=0, hi=None):\n \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n The return value i is such that all e in a[:i] have e <= x, and all e in\n a[i:] have e > x. So if x already appears in the list, a.insert(x) will\n insert just after the rightmost x already there.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n if lo < 0:\n raise ValueError('lo must be non-negative')\n if hi is None:\n hi = len(a)\n while lo < hi:\n mid = (lo+hi)//2\n if x < a[mid]: hi = mid\n else: lo = mid+1\n return lo\n\ndef insort_left(a, x, lo=0, hi=None):\n \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n If x is already in a, insert it to the left of the leftmost x.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n lo = bisect_left(a, x, lo, hi)\n a.insert(lo, x)\n\n\ndef bisect_left(a, x, lo=0, hi=None):\n \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n The return value i is such that all e in a[:i] have e < x, and all e in\n a[i:] have e >= x. So if x already appears in the list, a.insert(x) will\n insert just before the leftmost x already there.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n if lo < 0:\n raise ValueError('lo must be non-negative')\n if hi is None:\n hi = len(a)\n while lo < hi:\n mid = (lo+hi)//2\n if a[mid] < x: lo = mid+1\n else: hi = mid\n return lo\n\n# Create aliases\nbisect = bisect_right\ninsort = insort_right\n"; -const char kPythonLibs_builtins[] = "from pkpy import next as __pkpy_next\n\ndef all(iterable):\n for i in iterable:\n if not i:\n return False\n return True\n\ndef any(iterable):\n for i in iterable:\n if i:\n return True\n return False\n\ndef enumerate(iterable, start=0):\n n = start\n for elem in iterable:\n yield n, elem\n n += 1\n\ndef sum(iterable):\n res = 0\n for i in iterable:\n res += i\n return res\n\ndef map(f, iterable):\n for i in iterable:\n yield f(i)\n\ndef filter(f, iterable):\n for i in iterable:\n if f(i):\n yield i\n\ndef zip(a, b):\n a = iter(a)\n b = iter(b)\n while True:\n ai = __pkpy_next(a)\n bi = __pkpy_next(b)\n if ai is StopIteration or bi is StopIteration:\n break\n yield ai, bi\n\ndef reversed(iterable):\n a = list(iterable)\n a.reverse()\n return a\n\ndef sorted(iterable, key=None, reverse=False):\n a = list(iterable)\n a.sort(key=key, reverse=reverse)\n return a\n\n##### str #####\ndef __format_string(self: str, *args, **kwargs) -> str:\n def tokenizeString(s: str):\n tokens = []\n L, R = 0,0\n \n mode = None\n curArg = 0\n # lookingForKword = False\n \n while(R int:\n n = 0\n for item in self:\n if item == x:\n n += 1\n return n\n \n def extend(self, iterable: Iterable[T]):\n for x in iterable:\n self.append(x)\n\n def extendleft(self, iterable: Iterable[T]):\n for x in iterable:\n self.appendleft(x)\n \n def pop(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n self._tail = (self._tail - 1 + self._capacity) % self._capacity\n return self._data[self._tail]\n \n def popleft(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n x = self._data[self._head]\n self._head = (self._head + 1) % self._capacity\n return x\n \n def clear(self):\n i = self._head\n while i != self._tail:\n self._data[i] = None\n i = (i + 1) % self._capacity\n self._head = 0\n self._tail = 0\n\n def rotate(self, n: int = 1):\n if len(self) == 0:\n return\n if n > 0:\n n = n % len(self)\n for _ in range(n):\n self.appendleft(self.pop())\n elif n < 0:\n n = -n % len(self)\n for _ in range(n):\n self.append(self.popleft())\n\n def __len__(self) -> int:\n return (self._tail - self._head + self._capacity) % self._capacity\n\n def __contains__(self, x: object) -> bool:\n for item in self:\n if item == x:\n return True\n return False\n \n def __iter__(self):\n i = self._head\n while i != self._tail:\n yield self._data[i]\n i = (i + 1) % self._capacity\n\n def __eq__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return False\n if len(self) != len(other):\n return False\n for x, y in zip(self, other):\n if x != y:\n return False\n return True\n \n def __repr__(self) -> str:\n return f\"deque({list(self)!r})\"\n\n"; +const char kPythonLibs_collections[] = "from typing import Generic, TypeVar, Iterable\n\nT = TypeVar('T')\n\ndef Counter(iterable: Iterable[T]):\n a: dict[T, int] = {}\n for x in iterable:\n if x in a:\n a[x] += 1\n else:\n a[x] = 1\n return a\n\n\nclass defaultdict(dict):\n def __init__(self, default_factory, *args):\n super().__init__(*args)\n self.default_factory = default_factory\n\n def __missing__(self, key):\n self[key] = self.default_factory()\n return self[key]\n\n def __repr__(self) -> str:\n return f\"defaultdict({self.default_factory}, {super().__repr__()})\"\n\n def copy(self):\n return defaultdict(self.default_factory, self)\n\n\nclass deque(Generic[T]):\n _data: list[T]\n _head: int\n _tail: int\n _capacity: int\n\n def __init__(self, iterable: Iterable[T] = None):\n self._data = [None] * 8 # initial capacity\n self._head = 0\n self._tail = 0\n self._capacity = len(self._data)\n\n if iterable is not None:\n self.extend(iterable)\n\n def __resize_2x(self):\n backup = list(self)\n self._capacity *= 2\n self._head = 0\n self._tail = len(backup)\n self._data.clear()\n self._data.extend(backup)\n self._data.extend([None] * (self._capacity - len(backup)))\n\n def append(self, x: T):\n self._data[self._tail] = x\n self._tail = (self._tail + 1) % self._capacity\n if (self._tail + 1) % self._capacity == self._head:\n self.__resize_2x()\n\n def appendleft(self, x: T):\n self._head = (self._head - 1 + self._capacity) % self._capacity\n self._data[self._head] = x\n if (self._tail + 1) % self._capacity == self._head:\n self.__resize_2x()\n\n def copy(self):\n return deque(self)\n \n def count(self, x: T) -> int:\n n = 0\n for item in self:\n if item == x:\n n += 1\n return n\n \n def extend(self, iterable: Iterable[T]):\n for x in iterable:\n self.append(x)\n\n def extendleft(self, iterable: Iterable[T]):\n for x in iterable:\n self.appendleft(x)\n \n def pop(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n self._tail = (self._tail - 1 + self._capacity) % self._capacity\n return self._data[self._tail]\n \n def popleft(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n x = self._data[self._head]\n self._head = (self._head + 1) % self._capacity\n return x\n \n def clear(self):\n i = self._head\n while i != self._tail:\n self._data[i] = None\n i = (i + 1) % self._capacity\n self._head = 0\n self._tail = 0\n\n def rotate(self, n: int = 1):\n if len(self) == 0:\n return\n if n > 0:\n n = n % len(self)\n for _ in range(n):\n self.appendleft(self.pop())\n elif n < 0:\n n = -n % len(self)\n for _ in range(n):\n self.append(self.popleft())\n\n def __len__(self) -> int:\n return (self._tail - self._head + self._capacity) % self._capacity\n\n def __contains__(self, x: object) -> bool:\n for item in self:\n if item == x:\n return True\n return False\n \n def __iter__(self):\n i = self._head\n while i != self._tail:\n yield self._data[i]\n i = (i + 1) % self._capacity\n\n def __eq__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return NotImplemented\n if len(self) != len(other):\n return False\n for x, y in zip(self, other):\n if x != y:\n return False\n return True\n \n def __ne__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return NotImplemented\n return not self == other\n \n def __repr__(self) -> str:\n return f\"deque({list(self)!r})\"\n\n"; const char kPythonLibs_datetime[] = "from time import localtime\n\nclass timedelta:\n def __init__(self, days=0, seconds=0):\n self.days = days\n self.seconds = seconds\n\n def __repr__(self):\n return f\"datetime.timedelta(days={self.days}, seconds={self.seconds})\"\n\n def __eq__(self, other: 'timedelta') -> bool:\n if type(other) is not timedelta:\n return NotImplemented\n return (self.days, self.seconds) == (other.days, other.seconds)\n\n def __lt__(self, other: 'timedelta') -> bool:\n if type(other) is not timedelta:\n return NotImplemented\n return (self.days, self.seconds) < (other.days, other.seconds)\n\n def __le__(self, other: 'timedelta') -> bool:\n if type(other) is not timedelta:\n return NotImplemented\n return (self.days, self.seconds) <= (other.days, other.seconds)\n\n def __gt__(self, other: 'timedelta') -> bool:\n if type(other) is not timedelta:\n return NotImplemented\n return (self.days, self.seconds) > (other.days, other.seconds)\n\n def __ge__(self, other: 'timedelta') -> bool:\n if type(other) is not timedelta:\n return NotImplemented\n return (self.days, self.seconds) >= (other.days, other.seconds)\n\n\nclass date:\n def __init__(self, year: int, month: int, day: int):\n self.year = year\n self.month = month\n self.day = day\n\n @staticmethod\n def today():\n t = localtime()\n return date(t.tm_year, t.tm_mon, t.tm_mday)\n\n def __eq__(self, other: 'date') -> bool:\n if type(other) is not date:\n return NotImplemented\n return (self.year, self.month, self.day) == (other.year, other.month, other.day)\n\n def __lt__(self, other: 'date') -> bool:\n if type(other) is not date:\n return NotImplemented\n return (self.year, self.month, self.day) < (other.year, other.month, other.day)\n\n def __le__(self, other: 'date') -> bool:\n if type(other) is not date:\n return NotImplemented\n return (self.year, self.month, self.day) <= (other.year, other.month, other.day)\n\n def __gt__(self, other: 'date') -> bool:\n if type(other) is not date:\n return NotImplemented\n return (self.year, self.month, self.day) > (other.year, other.month, other.day)\n\n def __ge__(self, other: 'date') -> bool:\n if type(other) is not date:\n return NotImplemented\n return (self.year, self.month, self.day) >= (other.year, other.month, other.day)\n\n def __str__(self):\n return f\"{self.year}-{self.month:02}-{self.day:02}\"\n\n def __repr__(self):\n return f\"datetime.date({self.year}, {self.month}, {self.day})\"\n\n\nclass datetime(date):\n def __init__(self, year: int, month: int, day: int, hour: int, minute: int, second: int):\n super().__init__(year, month, day)\n # Validate and set hour, minute, and second\n if not 0 <= hour <= 23:\n raise ValueError(\"Hour must be between 0 and 23\")\n self.hour = hour\n if not 0 <= minute <= 59:\n raise ValueError(\"Minute must be between 0 and 59\")\n self.minute = minute\n if not 0 <= second <= 59:\n raise ValueError(\"Second must be between 0 and 59\")\n self.second = second\n\n def date(self) -> date:\n return date(self.year, self.month, self.day)\n\n @staticmethod\n def now():\n t = localtime()\n tm_sec = t.tm_sec\n if tm_sec == 60:\n tm_sec = 59\n return datetime(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, tm_sec)\n\n def __str__(self):\n return f\"{self.year}-{self.month:02}-{self.day:02} {self.hour:02}:{self.minute:02}:{self.second:02}\"\n\n def __repr__(self):\n return f\"datetime.datetime({self.year}, {self.month}, {self.day}, {self.hour}, {self.minute}, {self.second})\"\n\n def __eq__(self, other) -> bool:\n if type(other) is not datetime:\n return NotImplemented\n return (self.year, self.month, self.day, self.hour, self.minute, self.second) ==\x5c\n (other.year, other.month, other.day,\n other.hour, other.minute, other.second)\n\n def __lt__(self, other) -> bool:\n if type(other) is not datetime:\n return NotImplemented\n return (self.year, self.month, self.day, self.hour, self.minute, self.second) <\x5c\n (other.year, other.month, other.day,\n other.hour, other.minute, other.second)\n\n def __le__(self, other) -> bool:\n if type(other) is not datetime:\n return NotImplemented\n return (self.year, self.month, self.day, self.hour, self.minute, self.second) <=\x5c\n (other.year, other.month, other.day,\n other.hour, other.minute, other.second)\n\n def __gt__(self, other) -> bool:\n if type(other) is not datetime:\n return NotImplemented\n return (self.year, self.month, self.day, self.hour, self.minute, self.second) >\x5c\n (other.year, other.month, other.day,\n other.hour, other.minute, other.second)\n\n def __ge__(self, other) -> bool:\n if type(other) is not datetime:\n return NotImplemented\n return (self.year, self.month, self.day, self.hour, self.minute, self.second) >=\x5c\n (other.year, other.month, other.day,\n other.hour, other.minute, other.second)\n\n def timestamp(self) -> float:\n raise NotImplementedError\n\n"; const char kPythonLibs_functools[] = "class cache:\n def __init__(self, f):\n self.f = f\n self.cache = {}\n\n def __call__(self, *args):\n if args not in self.cache:\n self.cache[args] = self.f(*args)\n return self.cache[args]\n \ndef reduce(function, sequence, initial=...):\n it = iter(sequence)\n if initial is ...:\n try:\n value = next(it)\n except StopIteration:\n raise TypeError(\"reduce() of empty sequence with no initial value\")\n else:\n value = initial\n for element in it:\n value = function(value, element)\n return value\n\nclass partial:\n def __init__(self, f, *args, **kwargs):\n self.f = f\n if not callable(f):\n raise TypeError(\"the first argument must be callable\")\n self.args = args\n self.kwargs = kwargs\n\n def __call__(self, *args, **kwargs):\n kwargs.update(self.kwargs)\n return self.f(*self.args, *args, **kwargs)\n\n"; const char kPythonLibs_heapq[] = "# Heap queue algorithm (a.k.a. priority queue)\ndef heappush(heap, item):\n \"\"\"Push item onto heap, maintaining the heap invariant.\"\"\"\n heap.append(item)\n _siftdown(heap, 0, len(heap)-1)\n\ndef heappop(heap):\n \"\"\"Pop the smallest item off the heap, maintaining the heap invariant.\"\"\"\n lastelt = heap.pop() # raises appropriate IndexError if heap is empty\n if heap:\n returnitem = heap[0]\n heap[0] = lastelt\n _siftup(heap, 0)\n return returnitem\n return lastelt\n\ndef heapreplace(heap, item):\n \"\"\"Pop and return the current smallest value, and add the new item.\n\n This is more efficient than heappop() followed by heappush(), and can be\n more appropriate when using a fixed-size heap. Note that the value\n returned may be larger than item! That constrains reasonable uses of\n this routine unless written as part of a conditional replacement:\n\n if item > heap[0]:\n item = heapreplace(heap, item)\n \"\"\"\n returnitem = heap[0] # raises appropriate IndexError if heap is empty\n heap[0] = item\n _siftup(heap, 0)\n return returnitem\n\ndef heappushpop(heap, item):\n \"\"\"Fast version of a heappush followed by a heappop.\"\"\"\n if heap and heap[0] < item:\n item, heap[0] = heap[0], item\n _siftup(heap, 0)\n return item\n\ndef heapify(x):\n \"\"\"Transform list into a heap, in-place, in O(len(x)) time.\"\"\"\n n = len(x)\n # Transform bottom-up. The largest index there's any point to looking at\n # is the largest with a child index in-range, so must have 2*i + 1 < n,\n # or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so\n # j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is\n # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.\n for i in reversed(range(n//2)):\n _siftup(x, i)\n\n# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos\n# is the index of a leaf with a possibly out-of-order value. Restore the\n# heap invariant.\ndef _siftdown(heap, startpos, pos):\n newitem = heap[pos]\n # Follow the path to the root, moving parents down until finding a place\n # newitem fits.\n while pos > startpos:\n parentpos = (pos - 1) >> 1\n parent = heap[parentpos]\n if newitem < parent:\n heap[pos] = parent\n pos = parentpos\n continue\n break\n heap[pos] = newitem\n\ndef _siftup(heap, pos):\n endpos = len(heap)\n startpos = pos\n newitem = heap[pos]\n # Bubble up the smaller child until hitting a leaf.\n childpos = 2*pos + 1 # leftmost child position\n while childpos < endpos:\n # Set childpos to index of smaller child.\n rightpos = childpos + 1\n if rightpos < endpos and not heap[childpos] < heap[rightpos]:\n childpos = rightpos\n # Move the smaller child up.\n heap[pos] = heap[childpos]\n pos = childpos\n childpos = 2*pos + 1\n # The leaf at pos is empty now. Put newitem there, and bubble it up\n # to its final resting place (by sifting its parents down).\n heap[pos] = newitem\n _siftdown(heap, startpos, pos)"; const char kPythonLibs_itertools[] = "from pkpy import next\n\ndef zip_longest(a, b):\n a = iter(a)\n b = iter(b)\n while True:\n ai = next(a)\n bi = next(b)\n if ai is StopIteration and bi is StopIteration:\n break\n if ai is StopIteration:\n ai = None\n if bi is StopIteration:\n bi = None\n yield ai, bi\n"; const char kPythonLibs_operator[] = "# https://docs.python.org/3/library/operator.html#mapping-operators-to-functions\n\ndef le(a, b): return a <= b\ndef lt(a, b): return a < b\ndef ge(a, b): return a >= b\ndef gt(a, b): return a > b\ndef eq(a, b): return a == b\ndef ne(a, b): return a != b\n\ndef and_(a, b): return a & b\ndef or_(a, b): return a | b\ndef xor(a, b): return a ^ b\ndef invert(a): return ~a\ndef lshift(a, b): return a << b\ndef rshift(a, b): return a >> b\n\ndef is_(a, b): return a is b\ndef is_not(a, b): return a is not b\ndef not_(a): return not a\ndef truth(a): return bool(a)\ndef contains(a, b): return b in a\n\ndef add(a, b): return a + b\ndef sub(a, b): return a - b\ndef mul(a, b): return a * b\ndef truediv(a, b): return a / b\ndef floordiv(a, b): return a // b\ndef mod(a, b): return a % b\ndef pow(a, b): return a ** b\ndef neg(a): return -a\ndef matmul(a, b): return a @ b\n\ndef getitem(a, b): return a[b]\ndef setitem(a, b, c): a[b] = c\ndef delitem(a, b): del a[b]\n\ndef iadd(a, b): a += b; return a\ndef isub(a, b): a -= b; return a\ndef imul(a, b): a *= b; return a\ndef itruediv(a, b): a /= b; return a\ndef ifloordiv(a, b): a //= b; return a\ndef imod(a, b): a %= b; return a\n# def ipow(a, b): a **= b; return a\n# def imatmul(a, b): a @= b; return a\ndef iand(a, b): a &= b; return a\ndef ior(a, b): a |= b; return a\ndef ixor(a, b): a ^= b; return a\ndef ilshift(a, b): a <<= b; return a\ndef irshift(a, b): a >>= b; return a\n"; -const char kPythonLibs_pickle[] = "import json\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 if 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_pickle[] = "import json\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 + _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 if 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"; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index e2f89cbb..9544f212 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -647,8 +647,8 @@ static void _load_expr(Ctx* ctx, c11_sv expr, int line) { } c11_string* source = c11_string__new2(expr.data, expr.size); - bool ok = py_compile(source->data, "", EVAL_MODE, false); - if(!ok){ + bool ok = py_compile(source->data, "", EVAL_MODE, true); + if(!ok) { py_printexc(); c11__abort("f-string: invalid expression"); } @@ -1662,12 +1662,6 @@ static Error* exprLiteral(Compiler* self) { return NULL; } -static Error* exprLong(Compiler* self) { - c11_sv sv = Token__sv(prev()); - Ctx__s_push(ctx(), (Expr*)RawStringExpr__new(prev()->line, sv, OP_BUILD_LONG)); - return NULL; -} - static Error* exprBytes(Compiler* self) { c11_sv sv = c11_string__sv(prev()->value._str); Ctx__s_push(ctx(), (Expr*)RawStringExpr__new(prev()->line, sv, OP_BUILD_BYTES)); @@ -2836,7 +2830,6 @@ const static PrattRule rules[TK__COUNT__] = { [TK_NUM] = { exprLiteral, }, [TK_STR] = { exprLiteral, }, [TK_FSTR] = { exprFString, }, - [TK_LONG] = { exprLong, }, [TK_IMAG] = { exprImag, }, [TK_BYTES] = { exprBytes, }, [TK_LBRACE] = { exprMap }, diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index ea5e776e..aabaabba 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -8,17 +8,17 @@ #include #include -#define is_raw_string_used(t) ((t) == TK_ID || (t) == TK_LONG) +#define is_raw_string_used(t) ((t) == TK_ID) -typedef struct Lexer{ +typedef struct Lexer { SourceData_ src; const char* token_start; const char* curr_char; int current_line; int brackets_level; - c11_vector/*T=Token*/ nexts; - c11_vector/*T=int*/ indents; + c11_vector /*T=Token*/ nexts; + c11_vector /*T=int*/ indents; } Lexer; typedef struct TokenDeserializer { @@ -34,10 +34,9 @@ int TokenDeserializer__read_count(TokenDeserializer* self); int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c); double TokenDeserializer__read_float(TokenDeserializer* self, char c); - const static TokenValue EmptyTokenValue; -static void Lexer__ctor(Lexer* self, SourceData_ src){ +static void Lexer__ctor(Lexer* self, SourceData_ src) { PK_INCREF(src); self->src = src; self->curr_char = self->token_start = src->source->data; @@ -47,20 +46,20 @@ static void Lexer__ctor(Lexer* self, SourceData_ src){ c11_vector__ctor(&self->indents, sizeof(int)); } -static void Lexer__dtor(Lexer* self){ +static void Lexer__dtor(Lexer* self) { PK_DECREF(self->src); c11_vector__dtor(&self->nexts); c11_vector__dtor(&self->indents); } -static char eatchar(Lexer* self){ +static char eatchar(Lexer* self) { char c = *self->curr_char; assert(c != '\n'); // eatchar() cannot consume a newline self->curr_char++; return c; } -static char eatchar_include_newline(Lexer* self){ +static char eatchar_include_newline(Lexer* self) { char c = *self->curr_char; self->curr_char++; if(c == '\n') { @@ -70,7 +69,7 @@ static char eatchar_include_newline(Lexer* self){ return c; } -static int eat_spaces(Lexer* self){ +static int eat_spaces(Lexer* self) { int count = 0; while(true) { switch(*self->curr_char) { @@ -82,13 +81,13 @@ static int eat_spaces(Lexer* self){ } } -static bool matchchar(Lexer* self, char c){ +static bool matchchar(Lexer* self, char c) { if(*self->curr_char != c) return false; eatchar_include_newline(self); return true; } -static bool match_n_chars(Lexer* self, int n, char c0){ +static bool match_n_chars(Lexer* self, int n, char c0) { const char* c = self->curr_char; for(int i = 0; i < n; i++) { if(*c == '\0') return false; @@ -100,14 +99,14 @@ static bool match_n_chars(Lexer* self, int n, char c0){ return true; } -static void skip_line_comment(Lexer* self){ +static void skip_line_comment(Lexer* self) { while(*self->curr_char) { if(*self->curr_char == '\n') return; eatchar(self); } } -static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value){ +static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value) { switch(type) { case TK_LBRACE: case TK_LBRACKET: @@ -118,11 +117,11 @@ static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value) default: break; } Token token = {type, - self->token_start, - (int)(self->curr_char - self->token_start), - self->current_line - ((type == TK_EOL) ? 1 : 0), - self->brackets_level, - value}; + self->token_start, + (int)(self->curr_char - self->token_start), + self->current_line - ((type == TK_EOL) ? 1 : 0), + self->brackets_level, + value}; // handle "not in", "is not", "yield from" if(self->nexts.count > 0) { Token* back = &c11_vector__back(Token, &self->nexts); @@ -142,34 +141,42 @@ static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value) } } -static void add_token(Lexer* self, TokenIndex type){ +static void add_token(Lexer* self, TokenIndex type) { add_token_with_value(self, type, EmptyTokenValue); } -static void add_token_2(Lexer* self, char c, TokenIndex one, TokenIndex two){ +static void add_token_2(Lexer* self, char c, TokenIndex one, TokenIndex two) { if(matchchar(self, c)) add_token(self, two); else add_token(self, one); } -static bool eat_indentation(Lexer* self){ +static bool eat_indentation(Lexer* self) { if(self->brackets_level > 0) return true; int spaces = eat_spaces(self); if(*self->curr_char == '#') skip_line_comment(self); - if(*self->curr_char == '\0' || *self->curr_char == '\n'){ - return true; - } + if(*self->curr_char == '\0' || *self->curr_char == '\n') { return true; } // https://docs.python.org/3/reference/lexical_analysis.html#indentation int indents_back = c11_vector__back(int, &self->indents); if(spaces > indents_back) { c11_vector__push(int, &self->indents, spaces); - Token t = {TK_INDENT, self->token_start, 0, self->current_line, self->brackets_level, EmptyTokenValue}; + Token t = {TK_INDENT, + self->token_start, + 0, + self->current_line, + self->brackets_level, + EmptyTokenValue}; c11_vector__push(Token, &self->nexts, t); } else if(spaces < indents_back) { do { c11_vector__pop(&self->indents); - Token t = {TK_DEDENT, self->token_start, 0, self->current_line, self->brackets_level, EmptyTokenValue}; + Token t = {TK_DEDENT, + self->token_start, + 0, + self->current_line, + self->brackets_level, + EmptyTokenValue}; c11_vector__push(Token, &self->nexts, t); indents_back = c11_vector__back(int, &self->indents); } while(spaces < indents_back); @@ -178,28 +185,26 @@ static bool eat_indentation(Lexer* self){ return true; } -static bool is_possible_number_char(char c){ +static bool is_possible_number_char(char c) { switch(c) { // clang-format off case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case '.': case 'L': case 'x': case 'o': case 'j': + case '.': case 'x': case 'o': case 'j': return true; default: return false; - // clang-format on + // clang-format on } } /******************************/ -static Error* SyntaxError(Lexer* self, const char* fmt, ...){ +static Error* SyntaxError(Lexer* self, const char* fmt, ...) { Error* err = malloc(sizeof(Error)); err->src = self->src; PK_INCREF(self->src); err->lineno = self->current_line; - if(*self->curr_char == '\n') { - err->lineno--; - } + if(*self->curr_char == '\n') { err->lineno--; } va_list args; va_start(args, fmt); vsnprintf(err->msg, sizeof(err->msg), fmt, args); @@ -207,7 +212,7 @@ static Error* SyntaxError(Lexer* self, const char* fmt, ...){ return err; } -static Error* eat_name(Lexer* self){ +static Error* eat_name(Lexer* self) { self->curr_char--; while(true) { unsigned char c = *self->curr_char; @@ -236,9 +241,9 @@ static Error* eat_name(Lexer* self){ value |= (b & 0b00111111) << (6 * (u8bytes - k - 1)); } } - if(c11__is_unicode_Lo_char(value)){ + if(c11__is_unicode_Lo_char(value)) { self->curr_char += u8bytes; - }else{ + } else { break; } } @@ -249,10 +254,10 @@ static Error* eat_name(Lexer* self){ const char** KW_BEGIN = TokenSymbols + TK_FALSE; int KW_COUNT = TK__COUNT__ - TK_FALSE; - #define less(a, b) (c11_sv__cmp2(b, a) > 0) +#define less(a, b) (c11_sv__cmp2(b, a) > 0) int out; c11__lower_bound(const char*, KW_BEGIN, KW_COUNT, name, less, &out); - #undef less +#undef less if(out != KW_COUNT && c11__sveq2(name, KW_BEGIN[out])) { add_token(self, (TokenIndex)(out + TK_FALSE)); @@ -276,9 +281,7 @@ static Error* eat_string_until(Lexer* self, char quote, bool raw, c11_string** o } break; } - if(c == '\0') { - return SyntaxError(self, "EOL while scanning string literal"); - } + if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); } if(c == '\n') { if(!quote3) return SyntaxError(self, "EOL while scanning string literal"); @@ -314,36 +317,33 @@ static Error* eat_string_until(Lexer* self, char quote, bool raw, c11_string** o return NULL; } -enum StringType { - NORMAL_STRING, - RAW_STRING, - F_STRING, - NORMAL_BYTES -}; +enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES }; -static Error* eat_string(Lexer* self, char quote, enum StringType type){ +static Error* eat_string(Lexer* self, char quote, enum StringType type) { c11_string* s; Error* err = eat_string_until(self, quote, type == RAW_STRING, &s); if(err) return err; TokenValue value = {TokenValue_STR, ._str = s}; if(type == F_STRING) { add_token_with_value(self, TK_FSTR, value); - }else if(type == NORMAL_BYTES) { + } else if(type == NORMAL_BYTES) { add_token_with_value(self, TK_BYTES, value); - }else{ + } else { add_token_with_value(self, TK_STR, value); } return NULL; } -static Error* eat_number(Lexer* self){ +static Error* eat_number(Lexer* self) { const char* i = self->token_start; - while(is_possible_number_char(*i)) i++; + while(is_possible_number_char(*i)) + i++; bool is_scientific_notation = false; if(*(i - 1) == 'e' && (*i == '+' || *i == '-')) { i++; - while(isdigit(*i) || *i == 'j') i++; + while(isdigit(*i) || *i == 'j') + i++; is_scientific_notation = true; } @@ -351,21 +351,12 @@ static Error* eat_number(Lexer* self){ self->curr_char = i; if(text.data[0] != '.' && !is_scientific_notation) { - // try long - if(i[-1] == 'L') { - add_token(self, TK_LONG); - return NULL; - } // try integer TokenValue value = {.index = TokenValue_I64}; switch(c11__parse_uint(text, &value._i64, -1)) { - case IntParsing_SUCCESS: - add_token_with_value(self, TK_NUM, value); - return NULL; - case IntParsing_OVERFLOW: - return SyntaxError(self, "int literal is too large"); - case IntParsing_FAILURE: - break; // do nothing + case IntParsing_SUCCESS: add_token_with_value(self, TK_NUM, value); return NULL; + case IntParsing_OVERFLOW: return SyntaxError(self, "int literal is too large"); + case IntParsing_FAILURE: break; // do nothing } } @@ -374,7 +365,7 @@ static Error* eat_number(Lexer* self){ char* p_end; float_out = strtod(text.data, &p_end); - if(p_end == text.data + text.size){ + if(p_end == text.data + text.size) { TokenValue value = {.index = TokenValue_F64, ._f64 = float_out}; add_token_with_value(self, TK_NUM, value); return NULL; @@ -389,7 +380,7 @@ static Error* eat_number(Lexer* self){ return SyntaxError(self, "invalid number literal"); } -static Error* lex_one_token(Lexer* self, bool* eof){ +static Error* lex_one_token(Lexer* self, bool* eof) { *eof = false; while(*self->curr_char) { self->token_start = self->curr_char; @@ -474,9 +465,9 @@ static Error* lex_one_token(Lexer* self, bool* eof){ return NULL; } case '!': - if(matchchar(self, '=')){ + if(matchchar(self, '=')) { add_token(self, TK_NE); - }else{ + } else { Error* err = SyntaxError(self, "expected '=' after '!'"); if(err) return err; } @@ -499,7 +490,7 @@ static Error* lex_one_token(Lexer* self, bool* eof){ case '\t': eat_spaces(self); break; case '\n': { add_token(self, TK_EOL); - if(!eat_indentation(self)){ + if(!eat_indentation(self)) { return SyntaxError(self, "unindent does not match any outer indentation level"); } return NULL; @@ -542,7 +533,7 @@ static Error* from_precompiled(Lexer* self) { if(c11_sv__cmp2(version, PK_VERSION) != 0) { return SyntaxError(self, "precompiled version mismatch"); } - if(TokenDeserializer__read_uint(&deserializer, '\n') != (int64_t)self->src->mode){ + if(TokenDeserializer__read_uint(&deserializer, '\n') != (int64_t)self->src->mode) { return SyntaxError(self, "precompiled mode mismatch"); } @@ -580,7 +571,7 @@ static Error* from_precompiled(Lexer* self) { t.brackets_level = (int)TokenDeserializer__read_uint(&deserializer, ','); } - char type = (*deserializer.curr++); // read_char + char type = (*deserializer.curr++); // read_char switch(type) { case 'I': { int64_t res = TokenDeserializer__read_uint(&deserializer, '\n'); @@ -594,16 +585,14 @@ static Error* from_precompiled(Lexer* self) { c11_string* res = TokenDeserializer__read_string_from_hex(&deserializer, '\n'); t.value = (TokenValue){TokenValue_STR, ._str = res}; } break; - default: - t.value = EmptyTokenValue; - break; + default: t.value = EmptyTokenValue; break; } c11_vector__push(Token, &self->nexts, t); } return NULL; } -Error* Lexer__process(SourceData_ src, TokenArray* out_tokens){ +Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) { Lexer lexer; Lexer__ctor(&lexer, src); @@ -614,14 +603,15 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens){ return err; } // push initial tokens - Token sof = {TK_SOF, lexer.token_start, 0, lexer.current_line, lexer.brackets_level, EmptyTokenValue}; + Token sof = + {TK_SOF, lexer.token_start, 0, lexer.current_line, lexer.brackets_level, EmptyTokenValue}; c11_vector__push(Token, &lexer.nexts, sof); c11_vector__push(int, &lexer.indents, 0); bool eof = false; while(!eof) { void* err = lex_one_token(&lexer, &eof); - if(err){ + if(err) { Lexer__dtor(&lexer); return err; } @@ -635,7 +625,7 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens){ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) { assert(!src->is_precompiled); - TokenArray nexts; // output tokens + TokenArray nexts; // output tokens Error* err = Lexer__process(src, &nexts); if(err) return err; @@ -665,7 +655,7 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) { c11_sbuf__write_char(&ss, '\n'); uint16_t index = 0; - for(int i=0; ikey.data, kv->key.size); @@ -683,27 +673,27 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) { c11_sbuf__write_char(&ss, ','); if(is_raw_string_used(token->type)) { - uint16_t *p = c11_smallmap_s2n__try_get( - &token_indices, (c11_sv){token->start, token->length}); + uint16_t* p = + c11_smallmap_s2n__try_get(&token_indices, (c11_sv){token->start, token->length}); assert(p != NULL); c11_sbuf__write_int(&ss, (int)*p); c11_sbuf__write_char(&ss, ','); } - if(i > 0 && c11__getitem(Token, &nexts, i-1).line == token->line){ + if(i > 0 && c11__getitem(Token, &nexts, i - 1).line == token->line) { c11_sbuf__write_char(&ss, ','); - }else{ + } else { c11_sbuf__write_int(&ss, token->line); c11_sbuf__write_char(&ss, ','); } - - if(i > 0 && c11__getitem(Token, &nexts, i-1).brackets_level == token->brackets_level){ + + if(i > 0 && c11__getitem(Token, &nexts, i - 1).brackets_level == token->brackets_level) { c11_sbuf__write_char(&ss, ','); - }else{ + } else { c11_sbuf__write_int(&ss, token->brackets_level); c11_sbuf__write_char(&ss, ','); } // visit token value - switch(token->value.index){ + switch(token->value.index) { case TokenValue_EMPTY: break; case TokenValue_I64: c11_sbuf__write_char(&ss, 'I'); @@ -716,7 +706,7 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) { case TokenValue_STR: { c11_sbuf__write_char(&ss, 'S'); c11_sv sv = c11_string__sv(token->value._str); - for(int i=0; idata; - for(int i=0; icount; i++){ - if(data[i].value.index == TokenValue_STR){ - c11_string__delete(data[i].value._str); - } + for(int i = 0; i < self->count; i++) { + if(data[i].value.index == TokenValue_STR) { c11_string__delete(data[i].value._str); } } c11_array__dtor(self); } const char* TokenSymbols[] = { - "@eof", "@eol", "@sof", - "@id", "@num", "@str", "@fstr", "@long", "@bytes", "@imag", - "@indent", "@dedent", + "@eof", + "@eol", + "@sof", + "@id", + "@num", + "@str", + "@fstr", + "@bytes", + "@imag", + "@indent", + "@dedent", // These 3 are compound keywords which are generated on the fly - "is not", "not in", "yield from", + "is not", + "not in", + "yield from", /*****************************************/ - "+", "+=", "-", "-=", // (INPLACE_OP - 1) can get '=' removed - "*", "*=", "/", "/=", "//", "//=", "%", "%=", - "&", "&=", "|", "|=", "^", "^=", - "<<", "<<=", ">>", ">>=", + "+", + "+=", + "-", + "-=", // (INPLACE_OP - 1) can get '=' removed + "*", + "*=", + "/", + "/=", + "//", + "//=", + "%", + "%=", + "&", + "&=", + "|", + "|=", + "^", + "^=", + "<<", + "<<=", + ">>", + ">>=", /*****************************************/ - "(", ")", "[", "]", "{", "}", - ".", "..", "...", ",", ":", ";", - "**", "->", "#", "@", - ">", "<", "=", "==", "!=", ">=", "<=", "~", + "(", + ")", + "[", + "]", + "{", + "}", + ".", + "..", + "...", + ",", + ":", + ";", + "**", + "->", + "#", + "@", + ">", + "<", + "=", + "==", + "!=", + ">=", + "<=", + "~", /** KW_BEGIN **/ // NOTE: These keywords should be sorted in ascending order!! - "False", "None", "True", "and", "as", "assert", "break", "class", "continue", - "def", "del", "elif", "else", "except", "finally", "for", "from", "global", - "if", "import", "in", "is", "lambda", "not", "or", "pass", "raise", "return", - "try", "while", "with", "yield", + "False", + "None", + "True", + "and", + "as", + "assert", + "break", + "class", + "continue", + "def", + "del", + "elif", + "else", + "except", + "finally", + "for", + "from", + "global", + "if", + "import", + "in", + "is", + "lambda", + "not", + "or", + "pass", + "raise", + "return", + "try", + "while", + "with", + "yield", }; -void TokenDeserializer__ctor(TokenDeserializer* self, const char* source){ +void TokenDeserializer__ctor(TokenDeserializer* self, const char* source) { self->curr = source; self->source = source; } -bool TokenDeserializer__match_char(TokenDeserializer* self, char c){ +bool TokenDeserializer__match_char(TokenDeserializer* self, char c) { if(*self->curr == c) { self->curr++; return true; @@ -776,16 +840,16 @@ bool TokenDeserializer__match_char(TokenDeserializer* self, char c){ return false; } -c11_sv TokenDeserializer__read_string(TokenDeserializer* self, char c){ +c11_sv TokenDeserializer__read_string(TokenDeserializer* self, char c) { const char* start = self->curr; while(*self->curr != c) self->curr++; - c11_sv retval = {start, (int)(self->curr-start)}; + c11_sv retval = {start, (int)(self->curr - start)}; self->curr++; // skip the delimiter return retval; } -c11_string* TokenDeserializer__read_string_from_hex(TokenDeserializer* self, char c){ +c11_string* TokenDeserializer__read_string_from_hex(TokenDeserializer* self, char c) { c11_sv sv = TokenDeserializer__read_string(self, c); const char* s = sv.data; c11_sbuf ss; @@ -810,13 +874,13 @@ c11_string* TokenDeserializer__read_string_from_hex(TokenDeserializer* self, cha return c11_sbuf__submit(&ss); } -int TokenDeserializer__read_count(TokenDeserializer* self){ +int TokenDeserializer__read_count(TokenDeserializer* self) { assert(*self->curr == '='); self->curr++; return TokenDeserializer__read_uint(self, '\n'); } -int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c){ +int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c) { int64_t out = 0; while(*self->curr != c) { out = out * 10 + (*self->curr - '0'); @@ -826,7 +890,7 @@ int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c){ return out; } -double TokenDeserializer__read_float(TokenDeserializer* self, char c){ +double TokenDeserializer__read_float(TokenDeserializer* self, char c) { c11_sv sv = TokenDeserializer__read_string(self, c); // TODO: optimize this c11_string* nullterm = c11_string__new2(sv.data, sv.size); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 112c9a1a..abc176a4 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -89,6 +89,13 @@ FrameResult VM__run_top_frame(VM* self) { pk_print_stack(self, frame, byte); + // #if PK_DEBUG + // if(py_checkexc()) { + // py_printexc(); + // c11__abort("unhandled exception!"); + // } + // #endif + switch((Opcode)byte.op) { case OP_NO_OP: DISPATCH(); /*****************************************/ @@ -451,17 +458,7 @@ FrameResult VM__run_top_frame(VM* self) { TypeError("'%t' object does not support item deletion", SECOND()->type); goto __ERROR; } - /*****************************************/ - - case OP_BUILD_LONG: { - // [x] - py_Ref f = py_getdict(&self->builtins, py_name("long")); - assert(f != NULL); - if(!py_call(f, 1, TOP())) goto __ERROR; - *TOP() = self->last_retval; - DISPATCH(); - } - + /*****************************************/ case OP_BUILD_IMAG: { // [x] py_Ref f = py_getdict(&self->builtins, py_name("complex")); @@ -694,9 +691,9 @@ FrameResult VM__run_top_frame(VM* self) { buf[n++] = *curr; } else { py_TValue* args = py_getslot(curr, 0); - int length; - py_TValue* p = pk_arrayview(args, &length); - if(p) { + py_TValue* p; + int length = pk_arrayview(args, &p); + if(length != -1) { for(int j = 0; j < length; j++) { buf[n++] = p[j]; } @@ -827,7 +824,7 @@ FrameResult VM__run_top_frame(VM* self) { int res = py_import(path); if(res == -1) goto __ERROR; if(res == 0) { - ImportError("module '%s' not found", path); + ImportError("No module named '%s'", path); goto __ERROR; } PUSH(py_retval()); @@ -838,9 +835,9 @@ FrameResult VM__run_top_frame(VM* self) { NameDict* dict = PyObject__dict(TOP()->_obj); py_Ref all = NameDict__try_get(dict, __all__); if(all) { - int length; - py_TValue* p = pk_arrayview(all, &length); - if(!p) { + py_TValue* p; + int length = pk_arrayview(all, &p); + if(length == -1) { TypeError("'__all__' must be a list or tuple, got '%t'", all->type); goto __ERROR; } @@ -872,10 +869,10 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_UNPACK_EX: { - int length; - py_TValue* p = pk_arrayview(TOP(), &length); - if(!p) { - TypeError("expected list or tuple to unpack, got '%t'", TOP()->type); + py_TValue* p; + int length = pk_arrayview(TOP(), &p); + if(length == -1) { + TypeError("expected list or tuple to unpack, got %t", TOP()->type); goto __ERROR; } int exceed = length - byte.arg; @@ -1027,9 +1024,12 @@ FrameResult VM__run_top_frame(VM* self) { } ////////////////// case OP_FSTRING_EVAL: { - py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg); - assert(py_istype(tmp, tp_code)); - if(!pk_exec(py_touserdata(tmp), frame->module)) goto __ERROR; + py_TValue* code = c11__at(py_TValue, &frame->co->consts, byte.arg); + assert(py_istype(code, tp_code)); + py_newglobals(SP()++); + py_newlocals(SP()++); + PUSH(code); + if(!pk_exec(py_touserdata(code), frame->module)) goto __ERROR; PUSH(py_retval()); DISPATCH(); } @@ -1100,11 +1100,15 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) { } } // eq/ne op never fails - if(op == __eq__ || op == __ne__) { - bool res = py_isidentical(SECOND(), TOP()); + bool res = py_isidentical(SECOND(), TOP()); + if(op == __eq__) { py_newbool(py_retval(), res); return true; } + if(op == __ne__) { + py_newbool(py_retval(), !res); + return true; + } return TypeError("unsupported operand type(s) for '%n'", op); } @@ -1118,9 +1122,9 @@ bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { } static bool stack_unpack_sequence(VM* self, uint16_t arg) { - int length; - py_TValue* p = pk_arrayview(TOP(), &length); - if(!p) return TypeError("expected list or tuple to unpack, got '%t'", TOP()->type); + py_TValue* p; + int length = pk_arrayview(TOP(), &p); + if(length == -1) return TypeError("expected list or tuple to unpack, got %t", TOP()->type); if(length != arg) return ValueError("expected %d values to unpack, got %d", arg, length); POP(); for(int i = 0; i < length; i++) { diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index b2eb72d1..c029af8e 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -571,7 +571,7 @@ static void mark_object(PyObject* obj) { } py_TypeInfo* types = c11__at(py_TypeInfo, &pk_current_vm->types, obj->type); - if(types->gc_mark) { types->gc_mark(PyObject__userdata(obj)); } + if(types->gc_mark) types->gc_mark(PyObject__userdata(obj)); } void CodeObject__gc_mark(const CodeObject* self) { @@ -590,6 +590,17 @@ void ManagedHeap__mark(ManagedHeap* self) { for(py_TValue* p = vm->stack.begin; p != vm->stack.end; p++) { pk__mark_value(p); } + // mark magic slots + py_TypeInfo* types = vm->types.data; + int types_length = vm->types.count; + // 0-th type is placeholder + for(int i = 1; i < types_length; i++) { + for(int j = 0; j <= __missing__; j++) { + py_TValue* slot = types[i].magic + j; + if(py_isnil(slot)) continue; + pk__mark_value(slot); + } + } // mark frame for(Frame* frame = vm->top_frame; frame; frame = frame->f_back) { Frame__gc_mark(frame); @@ -652,7 +663,8 @@ void pk_print_stack(VM* self, Frame* frame, Bytecode byte) { } c11_string* stack_str = c11_sbuf__submit(&buf); - printf("L%-3d: %-25s %-6d [%s]\n", + printf("%s:%-3d: %-25s %-6d [%s]\n", + frame->co->src->filename->data, Frame__lineno(frame), pk_opname(byte.op), byte.arg, diff --git a/src/modules/math.c b/src/modules/math.c index 7236e9a3..cfc4b155 100644 --- a/src/modules/math.c +++ b/src/modules/math.c @@ -83,7 +83,6 @@ static bool math_isclose(int argc, py_Ref argv) { ONE_ARG_FUNC(exp, exp) static bool math_log(int argc, py_Ref argv) { - PY_CHECK_ARG_TYPE(0, tp_float); double x; if(!py_castfloat(py_arg(0), &x)) return false; if(argc == 1) { diff --git a/src/modules/random.c b/src/modules/random.c index a7a9f174..0d7031b0 100644 --- a/src/modules/random.c +++ b/src/modules/random.c @@ -195,9 +195,9 @@ static bool Random_randint(int argc, py_Ref argv) { static bool Random_choice(int argc, py_Ref argv) { PY_CHECK_ARGC(2); mt19937* ud = py_touserdata(py_arg(0)); - int length; - py_TValue* p = pk_arrayview(py_arg(1), &length); - if(!p) return TypeError("choice(): argument must be a list or tuple"); + py_TValue* p; + int length = pk_arrayview(py_arg(1), &p); + if(length == -1) return TypeError("choice(): argument must be a list or tuple"); if(length == 0) return IndexError("cannot choose from an empty sequence"); int index = mt19937__randint(ud, 0, length - 1); py_assign(py_retval(), p + index); @@ -206,9 +206,9 @@ static bool Random_choice(int argc, py_Ref argv) { static bool Random_choices(int argc, py_Ref argv) { mt19937* ud = py_touserdata(py_arg(0)); - int length; - py_TValue* p = pk_arrayview(py_arg(1), &length); - if(!p) return TypeError("choices(): argument must be a list or tuple"); + py_TValue* p; + int length = pk_arrayview(py_arg(1), &p); + if(length == -1) return TypeError("choices(): argument must be a list or tuple"); if(length == 0) return IndexError("cannot choose from an empty sequence"); py_Ref weights = py_arg(2); if(!py_checktype(py_arg(3), tp_int)) return false; @@ -219,9 +219,9 @@ static bool Random_choices(int argc, py_Ref argv) { for(int i = 0; i < length; i++) cum_weights[i] = i + 1; } else { - int wlen; - py_TValue* w = pk_arrayview(weights, &wlen); - if(!w) { + py_TValue* w; + int wlen = pk_arrayview(weights, &w); + if(wlen == -1) { free(cum_weights); return TypeError("choices(): weights must be a list or tuple"); } diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 8e6634c1..7c005b83 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -179,6 +179,7 @@ int CodeObject__add_varname(CodeObject* self, py_Name name) { } void Function__dtor(Function* self) { + // printf("%s() in %s freed!\n", self->decl->code.name->data, self->decl->code.src->filename->data); PK_DECREF(self->decl); if(self->closure) NameDict__delete(self->closure); } \ No newline at end of file diff --git a/src/public/modules.c b/src/public/modules.c index ec71fd82..574e9355 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -144,11 +144,11 @@ int py_import(const char* path_cstr) { return 0; __SUCCESS: - py_push(py_newmodule(path_cstr)); - py_Ref mod = py_peek(-1); + do { + } while(0); + py_GlobalRef mod = py_newmodule(path_cstr); bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod); py_assign(py_retval(), mod); - py_pop(); c11_string__delete(filename); c11_string__delete(slashed_path); @@ -273,9 +273,9 @@ static bool builtins_round(int argc, py_Ref argv) { } static bool builtins_print(int argc, py_Ref argv) { - int length; - py_TValue* args = pk_arrayview(argv, &length); - assert(args != NULL); + py_TValue* args; + int length = pk_arrayview(argv, &args); + assert(length != -1); c11_sv sep = py_tosv(py_arg(1)); c11_sv end = py_tosv(py_arg(2)); c11_sbuf buf; @@ -394,6 +394,16 @@ static bool builtins_ord(int argc, py_Ref argv) { return true; } +static bool builtins_id(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + if(argv->is_ptr) { + py_newint(py_retval(), (py_i64)argv->_obj); + } else { + py_newnone(py_retval()); + } + return true; +} + static bool builtins_globals(int argc, py_Ref argv) { PY_CHECK_ARGC(0); py_newglobals(py_retval()); @@ -512,6 +522,15 @@ static bool builtins_compile(int argc, py_Ref argv) { return py_compile(source, filename, compile_mode, true); } +static bool builtins__import__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_str); + int res = py_import(py_tostr(argv)); + if(res == -1) return false; + if(res) return true; + return ImportError("No module named '%s'", py_tostr(argv)); +} + static bool NoneType__repr__(int argc, py_Ref argv) { py_newstr(py_retval(), "None"); return true; @@ -552,6 +571,7 @@ py_TValue pk_builtins__register() { py_bindfunc(builtins, "chr", builtins_chr); py_bindfunc(builtins, "ord", builtins_ord); + py_bindfunc(builtins, "id", builtins_id); py_bindfunc(builtins, "globals", builtins_globals); py_bindfunc(builtins, "locals", builtins_locals); @@ -559,6 +579,8 @@ py_TValue pk_builtins__register() { py_bindfunc(builtins, "eval", builtins_eval); py_bindfunc(builtins, "compile", builtins_compile); + py_bindfunc(builtins, "__import__", builtins__import__); + // some patches py_bindmagic(tp_NoneType, __repr__, NoneType__repr__); py_bindmagic(tp_ellipsis, __repr__, ellipsis__repr__); diff --git a/src/public/py_array.c b/src/public/py_array.c index cd2d5228..23f7eada 100644 --- a/src/public/py_array.c +++ b/src/public/py_array.c @@ -11,32 +11,48 @@ typedef struct array_iterator { int index; } array_iterator; -py_TValue* pk_arrayview(py_Ref self, int* length) { +int pk_arrayview(py_Ref self, py_TValue** p) { if(self->type == tp_list) { - *length = py_list_len(self); - return py_list_data(self); + *p = py_list_data(self); + return py_list_len(self); } if(self->type == tp_tuple) { - *length = py_tuple_len(self); - return PyObject__slots(self->_obj); + *p = PyObject__slots(self->_obj); + return py_tuple_len(self); } - return NULL; + return -1; } -int pk_arrayequal(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length) { - if(lhs_length != rhs_length) return false; - for(int i = 0; i < lhs_length; i++) { - int res = py_equal(lhs + i, rhs + i); - if(res == -1) return -1; - if(!res) return false; +bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + if(!py_istype(py_arg(1), type)) { + py_newnotimplemented(py_retval()); + return true; } + py_TValue *p0, *p1; + int lhs_length = pk_arrayview(py_arg(0), &p0); + int rhs_length = pk_arrayview(py_arg(1), &p1); + assert(lhs_length != -1 && rhs_length != -1); + if(lhs_length != rhs_length) { + py_newbool(py_retval(), false); + return true; + } + for(int i = 0; i < lhs_length; i++) { + int res = py_equal(p0 + i, p1 + i); + if(res == -1) return false; + if(!res) { + py_newbool(py_retval(), false); + return true; + } + } + py_newbool(py_retval(), true); return true; } bool pk_arrayiter(py_Ref val) { - int length; - py_TValue* p = pk_arrayview(val, &length); - if(!p) return TypeError("expected list or tuple, got %t", val->type); + py_TValue* p; + int length = pk_arrayview(val, &p); + if(length == -1) return TypeError("expected list or tuple, got %t", val->type); array_iterator* ud = py_newobject(py_retval(), tp_array_iterator, 1, sizeof(array_iterator)); ud->p = p; ud->length = length; @@ -46,9 +62,9 @@ bool pk_arrayiter(py_Ref val) { } bool pk_arraycontains(py_Ref self, py_Ref val) { - int length; - py_TValue* p = pk_arrayview(self, &length); - if(!p) return TypeError("expected list or tuple, got %t", self->type); + py_TValue* p; + int length = pk_arrayview(self, &p); + if(length == -1) return TypeError("expected list or tuple, got %t", self->type); for(int i = 0; i < length; i++) { int res = py_equal(p + i, val); if(res == -1) return false; diff --git a/src/public/py_dict.c b/src/public/py_dict.c index db82c27a..e60b6b62 100644 --- a/src/public/py_dict.c +++ b/src/public/py_dict.c @@ -206,10 +206,18 @@ static DictEntry* DictIterator__next(DictIterator* self) { /////////////////////////////// static bool dict__new__(int argc, py_Ref argv) { - py_newdict(py_retval()); + py_Type cls = py_totype(argv); + int slots = cls == tp_dict ? 0 : -1; + Dict* ud = py_newobject(py_retval(), cls, slots, sizeof(Dict)); + Dict__ctor(ud, 8); return true; } +void py_newdict(py_Ref out) { + Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict)); + Dict__ctor(ud, 8); +} + static bool dict__init__(int argc, py_Ref argv) { py_newnone(py_retval()); if(argc > 2) return TypeError("dict.__init__() takes at most 2 arguments (%d given)", argc); @@ -239,6 +247,9 @@ static bool dict__getitem__(int argc, py_Ref argv) { *py_retval() = entry->val; return true; } + // try __missing__ + py_Ref missing = py_tpfindmagic(argv->type, __missing__); + if(missing) return py_call(missing, argc, argv); return KeyError(py_arg(1)); } @@ -495,11 +506,6 @@ py_Type pk_dict_items__register() { ////////////////////////// -void py_newdict(py_Ref out) { - Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict)); - Dict__ctor(ud, 8); -} - py_Ref py_dict_getitem(py_Ref self, py_Ref key) { assert(py_isdict(self)); Dict* ud = py_touserdata(self); diff --git a/src/public/py_list.c b/src/public/py_list.c index 87f8d65b..a25b3705 100644 --- a/src/public/py_list.c +++ b/src/public/py_list.c @@ -75,18 +75,7 @@ static bool list__len__(int argc, py_Ref argv) { } static bool list__eq__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - if(py_istype(py_arg(1), tp_list)) { - int length0, length1; - py_TValue* a0 = pk_arrayview(py_arg(0), &length0); - py_TValue* a1 = pk_arrayview(py_arg(1), &length1); - int res = pk_arrayequal(a0, length0, a1, length1); - if(res == -1) return false; - py_newbool(py_retval(), res); - } else { - py_newnotimplemented(py_retval()); - } - return true; + return pk_wrapper__arrayequal(tp_list, argc, argv); } static bool list__ne__(int argc, py_Ref argv) { @@ -104,9 +93,9 @@ static bool list__new__(int argc, py_Ref argv) { return true; } if(argc == 2) { - int length; - py_TValue* p = pk_arrayview(py_arg(1), &length); - if(p) { + py_TValue* p; + int length = pk_arrayview(py_arg(1), &p); + if(length != -1) { py_newlistn(py_retval(), length); for(int i = 0; i < length; i++) { py_list_setitem(py_retval(), i, p + i); diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index f69b2d7a..1b9ff5df 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -5,14 +5,13 @@ #include "pocketpy/interpreter/vm.h" #include "pocketpy/common/sstream.h" - -void pk_mappingproxy__namedict(py_Ref out, py_Ref object){ +void pk_mappingproxy__namedict(py_Ref out, py_Ref object) { py_newobject(out, tp_namedict, 1, 0); assert(object->is_ptr && object->_obj->slots == -1); py_setslot(out, 0, object); } -static bool namedict__getitem__(int argc, py_Ref argv){ +static bool namedict__getitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_str); py_Name name = py_namev(py_tosv(py_arg(1))); @@ -22,7 +21,7 @@ static bool namedict__getitem__(int argc, py_Ref argv){ return true; } -static bool namedict__setitem__(int argc, py_Ref argv){ +static bool namedict__setitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(3); PY_CHECK_ARG_TYPE(1, tp_str); py_Name name = py_namev(py_tosv(py_arg(1))); @@ -31,7 +30,7 @@ static bool namedict__setitem__(int argc, py_Ref argv){ return true; } -static bool namedict__delitem__(int argc, py_Ref argv){ +static bool namedict__delitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_str); py_Name name = py_namev(py_tosv(py_arg(1))); @@ -40,7 +39,7 @@ static bool namedict__delitem__(int argc, py_Ref argv){ return true; } -static bool namedict__contains__(int argc, py_Ref argv){ +static bool namedict__contains__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_str); py_Name name = py_namev(py_tosv(py_arg(1))); @@ -49,6 +48,21 @@ static bool namedict__contains__(int argc, py_Ref argv){ return true; } +static bool namedict_items(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + py_Ref object = py_getslot(argv, 0); + NameDict* dict = PyObject__dict(object->_obj); + py_newtuple(py_retval(), dict->count); + for(int i = 0; i < dict->count; i++) { + py_Ref slot = py_tuple_getitem(py_retval(), i); + py_newtuple(slot, 2); + NameDict_KV* kv = c11__at(NameDict_KV, dict, i); + py_newstr(py_tuple_getitem(slot, 0), py_name2str(kv->key)); + py_assign(py_tuple_getitem(slot, 1), &kv->value); + } + return true; +} + py_Type pk_namedict__register() { py_Type type = pk_newtype("namedict", tp_object, NULL, NULL, false, true); @@ -56,18 +70,19 @@ py_Type pk_namedict__register() { py_bindmagic(type, __setitem__, namedict__setitem__); py_bindmagic(type, __delitem__, namedict__delitem__); py_bindmagic(type, __contains__, namedict__contains__); + py_bindmethod(type, "items", namedict_items); return type; } ////////////////////// -void pk_mappingproxy__locals(py_Ref out, Frame* frame){ +void pk_mappingproxy__locals(py_Ref out, Frame* frame) { assert(frame->has_function && !frame->is_dynamic); Frame** ud = py_newobject(out, tp_locals, 0, sizeof(Frame*)); *ud = frame; } -static bool locals__getitem__(int argc, py_Ref argv){ +static bool locals__getitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_str); Frame** ud = py_touserdata(argv); @@ -78,7 +93,7 @@ static bool locals__getitem__(int argc, py_Ref argv){ return true; } -static bool locals__setitem__(int argc, py_Ref argv){ +static bool locals__setitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(3); PY_CHECK_ARG_TYPE(1, tp_str); Frame** ud = py_touserdata(argv); @@ -90,7 +105,7 @@ static bool locals__setitem__(int argc, py_Ref argv){ return true; } -static bool locals__delitem__(int argc, py_Ref argv){ +static bool locals__delitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_str); Frame** ud = py_touserdata(argv); @@ -102,7 +117,7 @@ static bool locals__delitem__(int argc, py_Ref argv){ return true; } -static bool locals__contains__(int argc, py_Ref argv){ +static bool locals__contains__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_str); Frame** ud = py_touserdata(argv); diff --git a/src/public/py_number.c b/src/public/py_number.c index 07df271a..a74098b0 100644 --- a/src/public/py_number.c +++ b/src/public/py_number.c @@ -4,8 +4,16 @@ #include +static bool try_castfloat(py_Ref self, double* out) { + switch(self->type) { + case tp_int: *out = (double)self->_i64; return true; + case tp_float: *out = self->_f64; return true; + default: return false; + } +} + #define DEF_NUM_BINARY_OP(name, op, rint, rfloat) \ - static bool int##name(int argc, py_Ref argv) { \ + static bool int##name(int argc, py_Ref argv) { \ PY_CHECK_ARGC(2); \ if(py_isint(&argv[1])) { \ py_i64 lhs = py_toint(&argv[0]); \ @@ -20,11 +28,11 @@ } \ return true; \ } \ - static bool float##name(int argc, py_Ref argv) { \ + static bool float##name(int argc, py_Ref argv) { \ PY_CHECK_ARGC(2); \ py_f64 lhs = py_tofloat(&argv[0]); \ py_f64 rhs; \ - if(py_castfloat(&argv[1], &rhs)) { \ + if(try_castfloat(&argv[1], &rhs)) { \ rfloat(py_retval(), lhs op rhs); \ } else { \ py_newnotimplemented(py_retval()); \ @@ -63,7 +71,7 @@ static bool int__truediv__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); py_i64 lhs = py_toint(&argv[0]); py_f64 rhs; - if(py_castfloat(&argv[1], &rhs)) { + if(try_castfloat(&argv[1], &rhs)) { py_newfloat(py_retval(), lhs / rhs); } else { py_newnotimplemented(py_retval()); @@ -75,7 +83,7 @@ static bool float__truediv__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); py_f64 lhs = py_tofloat(&argv[0]); py_f64 rhs; - if(py_castfloat(&argv[1], &rhs)) { + if(try_castfloat(&argv[1], &rhs)) { py_newfloat(py_retval(), lhs / rhs); } else { py_newnotimplemented(py_retval()); @@ -107,8 +115,8 @@ static bool number__pow__(int argc, py_Ref argv) { } } else { py_f64 lhs, rhs; - py_castfloat(&argv[0], &lhs); - if(py_castfloat(&argv[1], &rhs)) { + if(!py_castfloat(&argv[0], &lhs)) return false; + if(try_castfloat(&argv[1], &rhs)) { py_newfloat(py_retval(), pow(lhs, rhs)); } else { py_newnotimplemented(py_retval()); @@ -177,7 +185,7 @@ static bool int_bit_length(int argc, py_Ref argv) { } #define DEF_INT_BITWISE_OP(name, op) \ - static bool int##name(int argc, py_Ref argv) { \ + static bool int##name(int argc, py_Ref argv) { \ PY_CHECK_ARGC(2); \ py_i64 lhs = py_toint(&argv[0]); \ if(py_isint(&argv[1])) { \ @@ -369,11 +377,11 @@ static bool float__new__(int argc, py_Ref argv) { // tp_bool static bool bool__new__(int argc, py_Ref argv) { assert(argc > 0); - if(argc == 1){ + if(argc == 1) { py_newbool(py_retval(), false); return true; } - if(argc == 2){ + if(argc == 2) { int res = py_bool(py_arg(1)); if(res == -1) return false; py_newbool(py_retval(), res); diff --git a/src/public/py_object.c b/src/public/py_object.c index 97ba38af..baa37a6a 100644 --- a/src/public/py_object.c +++ b/src/public/py_object.c @@ -95,6 +95,19 @@ static bool type__getitem__(int argc, py_Ref argv) { return true; } +static bool type__module__getter(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + py_Type type = py_totype(argv); + py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + if(py_isnil(&ti->module)) { + py_newnone(py_retval()); + } else { + py_Ref path = py_getdict(&ti->module, __path__); + py_assign(py_retval(), path); + } + return true; +} + void pk_object__register() { // TODO: use staticmethod py_bindmagic(tp_object, __new__, object__new__); @@ -108,6 +121,7 @@ void pk_object__register() { py_bindmagic(tp_type, __repr__, type__repr__); py_bindmagic(tp_type, __new__, type__new__); py_bindmagic(tp_type, __getitem__, type__getitem__); + py_bindproperty(tp_type, "__module__", type__module__getter, NULL); py_bindproperty(tp_type, "__base__", type__base__getter, NULL); py_bindproperty(tp_type, "__name__", type__name__getter, NULL); diff --git a/src/public/py_str.c b/src/public/py_str.c index 08adf6ec..f3ade2a0 100644 --- a/src/public/py_str.c +++ b/src/public/py_str.c @@ -262,32 +262,30 @@ static bool str_endswith(int argc, py_Ref argv) { static bool str_join(int argc, py_Ref argv) { PY_CHECK_ARGC(2); - c11_sv self = c11_string__sv(py_touserdata(&argv[0])); - py_Ref _1 = py_arg(1); - // join a list or tuple - py_TValue* p; - int length; - if(py_istype(_1, tp_list)) { - p = py_list_getitem(_1, 0); - length = py_list_len(_1); - } else if(py_istype(_1, tp_tuple)) { - p = py_tuple_getitem(_1, 0); - length = py_tuple_len(_1); - } else { - return TypeError("join() argument must be a list or tuple"); - } + c11_sv self = c11_string__sv(py_touserdata(argv)); + + if(!py_iter(py_arg(1))) return false; + py_push(py_retval()); // iter c11_sbuf buf; c11_sbuf__ctor(&buf); - for(int i = 0; i < length; i++) { - if(i > 0) c11_sbuf__write_sv(&buf, self); - if(!py_checkstr(&p[i])) { + bool first = true; + while(true) { + int res = py_next(py_peek(-1)); + if(res == -1) return false; + if(res == 0) break; + + if(!first) c11_sbuf__write_sv(&buf, self); + if(!py_checkstr(py_retval())) { c11_sbuf__dtor(&buf); return false; } - c11_string* item = py_touserdata(&p[i]); + c11_string* item = py_touserdata(py_retval()); c11_sbuf__write_cstrn(&buf, item->data, item->size); + first = false; } + + py_pop(); // iter c11_sbuf__py_submit(&buf, py_retval()); return true; } @@ -516,17 +514,17 @@ py_Type pk_str_iterator__register() { return type; } -static bool bytes__new__(int argc, py_Ref argv){ - if(argc == 1){ +static bool bytes__new__(int argc, py_Ref argv) { + if(argc == 1) { py_newbytes(py_retval(), 0); return true; } if(argc > 2) return TypeError("bytes() takes at most 1 argument"); - int length; - py_TValue* p = pk_arrayview(&argv[1], &length); - if(!p) return TypeError("bytes() argument must be a list or tuple"); + py_TValue* p; + int length = pk_arrayview(&argv[1], &p); + if(length == -1) return TypeError("bytes() argument must be a list or tuple"); unsigned char* data = py_newbytes(py_retval(), length); - for(int i = 0; i < length; i++){ + for(int i = 0; i < length; i++) { if(!py_checktype(&p[i], tp_int)) return false; py_i64 v = py_toint(&p[i]); if(v < 0 || v > 255) return ValueError("bytes must be in range(0, 256)"); diff --git a/src/public/py_tuple.c b/src/public/py_tuple.c index 19e28500..ba504276 100644 --- a/src/public/py_tuple.c +++ b/src/public/py_tuple.c @@ -98,18 +98,7 @@ static bool tuple__getitem__(int argc, py_Ref argv) { } static bool tuple__eq__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - if(py_istype(py_arg(1), tp_tuple)) { - int length0, length1; - py_TValue* a0 = pk_arrayview(py_arg(0), &length0); - py_TValue* a1 = pk_arrayview(py_arg(1), &length1); - int res = pk_arrayequal(a0, length0, a1, length1); - if(res == -1) return false; - py_newbool(py_retval(), res); - } else { - py_newnotimplemented(py_retval()); - } - return true; + return pk_wrapper__arrayequal(tp_tuple, argc, argv); } static bool tuple__ne__(int argc, py_Ref argv) { diff --git a/tests/00_tmp.py b/tests/00_tmp.py new file mode 100644 index 00000000..05f0b0cb --- /dev/null +++ b/tests/00_tmp.py @@ -0,0 +1,7 @@ +from cmath import isclose, sqrt + +res = sqrt(1+2j) +assert isclose(res, 1.272019649514069+0.7861513777574233j) + +a = 1+2j +{a: 1} \ No newline at end of file diff --git a/tests/04_str.py b/tests/04_str.py index ea9f4a20..c1e462e8 100644 --- a/tests/04_str.py +++ b/tests/04_str.py @@ -177,6 +177,10 @@ assert hex(17) == '0x11' assert '-'.join(['r', 'u', 'n', 'o', 'o', 'b']) == 'r-u-n-o-o-b' +assert (1 != '1') is True +assert (1 == '1') is False +assert 1 == 1.0 + exit() # test format() diff --git a/tests/51_yield.py b/tests/51_yield.py index 03f54215..b570b905 100644 --- a/tests/51_yield.py +++ b/tests/51_yield.py @@ -56,7 +56,7 @@ assert a == [1, 2, 3] def f(): for i in range(5): yield str(i) -assert '|'.join(list(f())) == '0|1|2|3|4' +assert '|'.join(f()) == '0|1|2|3|4' def f(n): diff --git a/tests/80_cmath.py b/tests/71_cmath.py similarity index 100% rename from tests/80_cmath.py rename to tests/71_cmath.py diff --git a/tests/80_gc.py b/tests/71_gc.py similarity index 100% rename from tests/80_gc.py rename to tests/71_gc.py diff --git a/tests/70_collections.py b/tests/79_collections.py similarity index 99% rename from tests/70_collections.py rename to tests/79_collections.py index da6b7bab..bfdb755f 100644 --- a/tests/70_collections.py +++ b/tests/79_collections.py @@ -115,6 +115,8 @@ class ArithmeticError(Exception): pass class BadCompare: def __eq__(self, other): raise ArithmeticError + def __ne__(self, other): + raise ArithmeticError d = deque([1, 2, BadCompare(), 3]) diff --git a/tests/80_datetime.py b/tests/79_datetime.py similarity index 100% rename from tests/80_datetime.py rename to tests/79_datetime.py diff --git a/tests/80_easing.py b/tests/79_easing.py similarity index 100% rename from tests/80_easing.py rename to tests/79_easing.py diff --git a/tests/70_file.py b/tests/79_file.py similarity index 100% rename from tests/70_file.py rename to tests/79_file.py diff --git a/tests/80_long.py b/tests/80_long.py deleted file mode 100644 index 9026bdf9..00000000 --- a/tests/80_long.py +++ /dev/null @@ -1,50 +0,0 @@ -assert long(123) == long('123') == 123 == 123L - -a = long(2) -assert a ** 0 == 1 -assert a ** 60 == 1152921504606846976 - -assert a + 1 == 3 -assert a - 1 == 1 -assert a * 2 == 4 -assert a // 2 == 1 - -assert -a == -2 - -assert 1 + a == 3L -assert 1 - a == -1L -assert 2 * a == 4L - -# __lshift__ and __rshift__ -for i in range(29): - assert 1L << i == 2 ** i - -for i in range(29): - assert 2L ** i >> i == 1L - -assert 12L >> 100 == 0 - -a = 32764L -s = [] -while a != 0: - a, r = divmod(a, 10L) - s.append(r) - -assert s == [4, 6, 7, 2, 3] - -assert 1 < 2L < 3 < 6.6 -assert 1L < 2 < 9.6 >= 7 > 2L -assert 1L < 2 < 3 < 6.6 - -assert 10000000000000000000000L // 3333L == 3000300030003000300L -assert 10000000000000000000000L % 3333L == 100L -assert 2L ** 100 // 3L ** 50 == 1765780L -assert 2L ** 200 // 3L ** 100 == 3117982410207L -assert 2L ** 500 // 3L ** 200 == 12323863745843010927046405923587284941366070573310012484L -assert 2L ** 500 % 3L ** 200 == 242990057207501525999897657892105676264485903550870122812212566096970021710762636168532352280892L - -assert 3L**500 // 3L**400 == 3L**100 -assert 4562645248L // 3L == 1520881749L -assert 3L**10 // 2L**200 == 0 -assert 2L**500 % 3L**200 == 242990057207501525999897657892105676264485903550870122812212566096970021710762636168532352280892L -assert divmod(10L**115, 3L**47) == (376098003656605353510839433041026531338835641081336270795298342081499446459650747504311257564L, 10150482013473427429132L)