blueloveTH 1 year ago
parent
commit
88f893ddd7

+ 1 - 1
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..."

+ 0 - 1
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[];

+ 1 - 1
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,

+ 7 - 2
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);
 

+ 0 - 1
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)

+ 0 - 352
python/_long.py

@@ -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<b, 0 if a==b
-    if len(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'

+ 2 - 5
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):

+ 6 - 2
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})"
 

+ 1 - 1
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):

File diff suppressed because it is too large
+ 0 - 0
src/common/_generated.c


+ 2 - 9
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, "<f-string>", EVAL_MODE, false);
-    if(!ok){
+    bool ok = py_compile(source->data, "<f-string>", 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      },

+ 183 - 119
src/compiler/lexer.c

@@ -8,17 +8,17 @@
 #include <stdarg.h>
 #include <stdbool.h>
 
-#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; i<token_indices.count; i++){
+    for(int i = 0; i < token_indices.count; i++) {
         c11_smallmap_s2n_KV* kv = c11__at(c11_smallmap_s2n_KV, &token_indices, i);
         // L4: raw strings
         c11_sbuf__write_cstrn(&ss, kv->key.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; i<sv.size; i++){
+                for(int i = 0; i < sv.size; i++) {
                     c11_sbuf__write_hex(&ss, sv.data[i], false);
                 }
                 break;
@@ -729,46 +719,120 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
     return NULL;
 }
 
-void TokenArray__dtor(TokenArray *self){
+void TokenArray__dtor(TokenArray* self) {
     Token* data = self->data;
-    for(int i=0; i<self->count; 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);

+ 34 - 30
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++) {

+ 14 - 2
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,

+ 0 - 1
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) {

+ 9 - 9
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");
         }

+ 1 - 0
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);
 }

+ 28 - 6
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__);

+ 33 - 17
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;
+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(lhs + i, rhs + i);
-        if(res == -1) return -1;
-        if(!res) return false;
+        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;

+ 12 - 6
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);

+ 4 - 15
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);

+ 26 - 11
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);

+ 18 - 10
src/public/py_number.c

@@ -4,8 +4,16 @@
 
 #include <math.h>
 
+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);

+ 14 - 0
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);

+ 22 - 24
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)");

+ 1 - 12
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) {

+ 7 - 0
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}

+ 4 - 0
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()

+ 1 - 1
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):

+ 0 - 0
tests/80_cmath.py → tests/71_cmath.py


+ 0 - 0
tests/80_gc.py → tests/71_gc.py


+ 2 - 0
tests/70_collections.py → 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])

+ 0 - 0
tests/80_datetime.py → tests/79_datetime.py


+ 0 - 0
tests/80_easing.py → tests/79_easing.py


+ 0 - 0
tests/70_file.py → tests/79_file.py


+ 0 - 50
tests/80_long.py

@@ -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)

Some files were not shown because too many files changed in this diff