blueloveTH 1 yıl önce
ebeveyn
işleme
ac8f4a1c2d

+ 1 - 9
CMakeLists.txt

@@ -12,12 +12,9 @@ check_ipo_supported(RESULT result)
 if(result AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
     set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
 else()
-    message(WARNING ">> IPO disabled. You will get very poor performance!!")
-    message(WARNING ">> IPO disabled. You will get very poor performance!!")
-    message(WARNING ">> IPO disabled. You will get very poor performance!!")
+    message(WARNING ">> IPO disabled. You will not get the best performance.")
 endif()
 
-
 if(MSVC)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /jumptablerdata /GS-")
     add_compile_options(/wd4267 /wd4244)
@@ -47,11 +44,6 @@ if(PK_ENABLE_OS)
     add_definitions(-DPK_ENABLE_OS=1)
 endif()
 
-option(PK_ENABLE_PROFILER "" OFF)
-if(PK_ENABLE_PROFILER)
-    add_definitions(-DPK_ENABLE_PROFILER=1)
-endif()
-
 # PK_IS_MAIN determines whether the project is being used from root
 # or if it is added as a dependency (through add_subdirectory for example).
 if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")

+ 1 - 4
README.md

@@ -79,6 +79,7 @@ To compile it with your project, these flags must be set:
 
 + `--std=c11` flag must be set
 + For MSVC, `/utf-8` flag must be set
++ `NDEBUG` macro should be defined for release build, or you will get poor performance
 
 For amalgamated build, run `python amalgamate.py` to generate `pocketpy.c` and `pocketpy.h` in `amalgamated/` directory.
 
@@ -146,9 +147,6 @@ __ERROR:
 
 ## Features
 
-Check this [Cheatsheet](https://reference.pocketpy.dev/python.html)
-for a quick overview of the supported features.
-
 | Name            | Example                         | Supported |
 | --------------- | ------------------------------- | --------- |
 | If Else         | `if..else..elif`                | ✅       |
@@ -237,7 +235,6 @@ Your sponsorship will help us develop pkpy continuously.
 [![Star History Chart](https://api.star-history.com/svg?repos=blueloveth/pocketpy&type=Date)](https://star-history.com/#blueloveth/pocketpy&Date)
 
 
-
 ## License
 
 [MIT License](http://opensource.org/licenses/MIT)

+ 1 - 1
amalgamate.py

@@ -8,7 +8,7 @@ from typing import List, Dict
 assert os.system("python prebuild.py") == 0
 
 ROOT = 'include/pocketpy'
-PUBLIC_HEADERS = ['config.h', 'export.h', 'pocketpy.h']
+PUBLIC_HEADERS = ['config.h', 'export.h', 'linalg.h', 'pocketpy.h']
 
 COPYRIGHT = '''/*
  *  Copyright (c) 2024 blueloveTH

+ 291 - 0
docs/bindings-cpp.md

@@ -0,0 +1,291 @@
+---
+icon: cpu
+title: Write C++ Bindings
+order: 17
+---
+
+## Quick Start
+
+pkpy provides a [pybind11](https://pybind11.readthedocs.io/en/stable/) compatible layer which allows users to do convenient bindings.
+
+To begin with, use `py::scoped_interpreter guard{}` to start the interpreter before using any Python objects.
+Or explicitly call `py::interpreter::initialize()` and `py::interpreter::finalize()`.
+
+### module
+
+```cpp
+#include <pybind11/pybind11.h>
+namespace py = pybind11;
+
+PYBIND11_EMBEDDED_MODULE(example, m) {
+    m.def("add", [](int a, int b) {
+        return a + b;
+    });
+
+    auto math = m.def_submodule("math");
+}
+```
+
+### function
+
+```cpp
+int add(int a, int b) { return a + b; }
+
+int add(int a, int b, int c) { return a + b + c; }
+
+void register_function(py::module_& m)
+{
+    m.def("add", py::overload_cast<int, int>(&add));
+
+    // support function overload
+    m.def("add", py::overload_cast<int, int, int>(&add));
+
+    // bind with default arguments
+    m.def("sub", [](int a, int b) { 
+        return a - b; 
+    }, py::arg("a") = 1, py::arg("b") = 2);
+
+    // bind *args
+    m.def("add", [](py::args args) {
+        int sum = 0;
+        for (auto& arg : args) {
+            sum += arg.cast<int>();
+        }
+        return sum;
+    });
+
+    // bind **kwargs
+    m.def("add", [](py::kwargs kwargs) {
+        int sum = 0;
+        for (auto item : kwargs) {
+            sum += item.second.cast<int>();
+        }
+        return sum;
+    });
+}
+```
+
+### class
+
+```cpp
+struct Point
+{
+    const int x;
+    int y;
+
+public:
+    Point() : x(0), y(0) {}
+
+    Point(int x, int y) : x(x), y(y) {}
+
+    Point(const Point& p) : x(p.x), y(p.y) {}
+
+    std::string stringfy() const { 
+        return "(" + std::to_string(x) + ", " + std::to_string(y) + ")"; 
+    }
+};
+
+struct Point3D : Point
+{
+private:
+    int z;
+
+public:
+    Point3D(int x, int y, int z) : Point(x, y), z(z) {}
+
+    int get_z() const { return z; }
+
+    void set_z(int z) { this->z = z; }
+};
+
+void bind_class(py::module_& m)
+{
+    py::class_<Point>(m, "Point")
+        .def(py::init<>())
+        .def(py::init<int, int>())
+        .def(py::init<const Point&>())
+        .def_readonly("x", &Point::x)
+        .def_readwrite("y", &Point::y)
+        .def("__str__", &Point::stringfy);
+
+    // only support single inheritance
+    py::class_<Point3D, Point>(m, "Point3D", py::dynamic_attr())
+        .def(py::init<int, int, int>())
+        .def_property("z", &Point3D::get_z, &Point3D::set_z);
+
+    // dynamic_attr will enable the dict of bound class
+}
+```
+
+### operators
+
+```cpp
+#include <pybind11/operators.h>
+namespace py = pybind11;
+
+struct Int {
+    int value;
+
+    Int(int value) : value(value) {}
+
+    Int operator+(const Int& other) const {
+        return Int(value + other.value);
+    }
+
+    Int operator-(const Int& other) const {
+        return Int(value - other.value);
+    }
+
+    bool operator==(const Int& other) const {
+        return value == other.value;
+    }
+
+    bool operator!=(const Int& other) const {
+        return value != other.value;
+    }
+};
+
+void bind_operators(py::module_& m)
+{
+    py::class_<Int>(m, "Int")
+        .def(py::init<int>())
+        .def(py::self + py::self)
+        .def(py::self - py::self)
+        .def(py::self == py::self)
+        .def(py::self != py::self);
+        // other operators are similar
+}
+```
+
+### py::object
+
+`py::object` is just simple wrapper around `PyVar`. It supports some convenient methods to interact with Python objects.
+
+
+here are some common methods:
+
+```cpp
+obj.attr("x"); // access attribute
+obj[1]; // access item
+
+obj.is_none(); // same as obj is None in Python
+obj.is(obj2); // same as obj is obj2 in Python
+
+// operators
+obj + obj2; // same as obj + obj2 in Python
+// ...
+obj == obj2; // same as obj == obj2 in Python
+// ...
+
+obj(...); // same as obj.__call__(...)
+
+py::cast(obj); // cast to Python object
+obj.cast<T>; // cast to C++ type
+
+py::type::of(obj); // get type of obj
+py::type::of<T>(); // get type of T, if T is registered
+```
+
+you can also create some builtin objects with their according wrappers:
+
+```cpp
+py::bool_ b = {true};
+py::int_ i = {1};
+py::float_ f = {1.0};
+py::str s = {"hello"};
+py::list l = {1, 2, 3};
+py::tuple t = {1, 2, 3};
+// ...
+```
+
+
+
+## More Examples
+
+More examples please see the test [folder](https://github.com/pocketpy/gsoc-2024-dev/tree/main/pybind11/tests) in the GSoC repository. All tested features are supported.
+
+## Limits and Comparison
+
+This is a feature list of pybind11 for pocketpy. It lists all completed and pending features. It also lists the features that cannot be implemented in the current version of pocketpy.
+
+### [Function](https://pybind11.readthedocs.io/en/stable/advanced/functions.html)
+
+- [x] Function overloading
+- [x] Return value policy
+- [x] is_prepend
+- [x] `*args` and `**kwargs`
+- [ ] Keep-alive
+- [ ] Call Guard
+- [x] Default arguments
+- [ ] Keyword-Only arguments
+- [ ] Positional-Only arguments
+- [ ] Allow/Prohibiting None arguments
+
+### [Class](https://pybind11.readthedocs.io/en/stable/classes.html)
+
+- [x] Creating bindings for a custom type
+- [x] Binding lambda functions
+- [x] Dynamic attributes
+- [x] Inheritance and automatic downcasting
+- [x] Enumerations and internal types
+- [ ] Instance and static fields
+
+> Binding static fields may never be implemented in pocketpy because it requires a metaclass, which is a heavy and infrequently used feature.
+
+### [Exceptions](https://pybind11.readthedocs.io/en/stable/advanced/exceptions.html)
+
+Need further discussion.
+
+### [Smart pointers](https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html)
+
+- [ ] std::shared_ptr
+- [ ] std::unique_ptr
+- [ ] Custom smart pointers
+
+### [Type conversions](https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html)
+
+- [x] Python built-in types
+- [x] STL Containers
+- [ ] Functional
+- [ ] Chrono
+
+### [Python C++ interface](https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html)
+
+Need further discussion.
+
+- [x] `object`
+- [x] `none`
+- [x] `type`
+- [x] `bool_`
+- [x] `int_`
+- [x] `float_`
+- [x] `str`
+- [ ] `bytes`
+- [ ] `bytearray`
+- [x] `tuple`
+- [x] `list`
+- [ ] `set`
+- [x] `dict`
+- [ ] `slice`
+- [x] `iterable`
+- [x] `iterator`
+- [ ] `function`
+- [ ] `buffer`
+- [ ] `memoryview`
+- [x] `capsule`
+
+### [Miscellaneous](https://pybind11.readthedocs.io/en/stable/advanced/misc.html)
+
+- [ ] Global Interpreter Lock (GIL)
+- [ ] Binding sequence data types, iterators, the slicing protocol, etc.
+- [x] Convenient operators binding
+
+### Differences between CPython and pocketpy
+
+- only `add`, `sub` and `mul` have corresponding right versions in pocketpy. So if you bind `int() >> py::self`, it will has no effect in pocketpy.
+
+- `__new__` and `__del__` are not supported in pocketpy.
+
+- in-place operators, such as `+=`, `-=`, `*=`, etc., are not supported in pocketpy.
+
+- thre return value of `globals` is immutable in pocketpy.

+ 9 - 1
docs/bindings.md

@@ -1,6 +1,6 @@
 ---
 icon: cpu
-title: Write Bindings
+title: Write C Bindings
 order: 18
 ---
 
@@ -16,3 +16,11 @@ typedef bool (*py_CFunction)(int argc, py_Ref argv);
 
 If successful, the function should return `true` and set the return value in `py_retval()`. In case there is no return value, you should use `py_newnone(py_retval())`.
 If an error occurs, the function should raise an exception and return `false`.
+
+See also:
++ [`py_bind`](/c-api/functions/#py_bind)
++ [`py_bindmethod`](/c-api/functions/#py_bindmethod)
++ [`py_bindfunc`](/c-api/functions/#py_bindfunc)
++ [`py_bindproperty`](/c-api/functions/#py_bindproperty)
++ [`py_newmodule`](/c-api/functions/#py_newmodule)
++ [`py_newtype`](/c-api/functions/#py_newtype)

+ 0 - 3
docs/features/basic.md

@@ -4,9 +4,6 @@ title: Basic Features
 order: 100
 ---
 
-Check this [Cheatsheet](https://reference.pocketpy.dev/python.html)
-for a quick overview of the supported features.
-
 The following table shows the basic features of pkpy with respect to [cpython](https://github.com/python/cpython).
 The features marked with `YES` are supported, and the features marked with `NO` are not supported.
 

+ 1 - 1
docs/features/debugging.md

@@ -4,7 +4,7 @@ title: Debugging
 ---
 
 !!!
-This feature is available in `v1.4.5` or higher. Set `PK_ENABLE_PROFILER` to `1` to enable this feature.
+This feature is not available in `v2.0` yet.
 !!!
 
 You can invoke `breakpoint()` in your python code to start a PDB-like session.

+ 9 - 11
docs/features/differences.md

@@ -23,20 +23,18 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
 1. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
 2. `__slots__` in class definition.
 3. `else` clause in try..except.
-4.  Inplace methods like `__iadd__` and `__imul__`.
+4. Inplace methods like `__iadd__` and `__imul__`.
 5. `__del__` in class definition.
 6. Multiple inheritance.
 
 ## Different behaviors
 
 1. positional and keyword arguments are strictly evaluated.
-2. `++i` and `--j` is an increment/decrement statement, not an expression.
-3. `int` does not derive from `bool`.
-4. `int` is 64-bit. You can use `long` type explicitly for arbitrary sized integers.
-5. `__ne__` is not required. Define `__eq__` is enough.
-6. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
-7. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
-8. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
-9. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
-10. `str.split` and `str.splitlines` will remove all empty entries.
-11. `__getattr__`, `__setattr__` and `__delattr__` can only be set in cpp.
+2. `int` does not derive from `bool`.
+3. `int` is 64-bit.
+4. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
+5. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
+6. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
+7. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
+8. `str.split` and `str.splitlines` will remove all empty entries.
+

+ 0 - 145
docs/features/precompile.md

@@ -1,145 +0,0 @@
----
-icon: dot
-title: Precompiling
----
-
-pkpy allows you to precompile python code into two special forms, which can be executed later.
-
-### In-memory precompilation
-
-You can use `vm->compile` to compile your source code into a `CodeObject_` object.
-This object can be executed later by `vm->_exec`.
-
-```cpp
-CodeObject_ code = vm->compile("print('Hello, world!')", "<string>", EXEC_MODE);
-vm->_exec(code);        // Hello, world!
-```
-
-This `CodeObject_` object is a very non-generic form of the compiled code,
-which is an in-memory form. Very efficient, but not portable.
-You are not able to save it to a file or load it from a file.
-
-
-### String precompilation
-
-In order to save the compiled code to a file, you need to use `vm->precompile`.
-It does some basic preprocessing and outputs the result as a human-readable string.
-
-```cpp
-// precompile the source code into a string
-Str source = vm->precompile("print('Hello, world!')", "<string>", EXEC_MODE);
-
-CodeObject code = vm->compile(source, "<string>", EXEC_MODE);
-vm->_exec(code);        // Hello, world!
-```
-
-You can also use python's `compile` function to achieve the same effect.
-
-```python
-code = compile("print('Hello, world!')", "<string>", "exec")
-exec(code)        # Hello, world!
-```
-
-Let's take a look at the precompiled string.
-```python
-print(code)
-```
-
-```txt
-pkpy:1.4.5
-0
-=1
-print
-=6
-5,1,0,
-6,0,,,
-42,,1,
-8,,,S48656c6c6f2c20776f726c6421
-43,,0,
-3,,,
-
-```
-
-Comparing with **In-memory precompilation**,
-**String precompilation** drops most of the information of the original source code.
-It has an encryption effect, which can protect your source code from being stolen.
-This also means there is no source line information when an error occurs.
-
-```python
-src = """
-def f(a, b):
-    return g(a, b)
-
-def g(a, b):
-    c = f(a, b)
-    d = g(a, b)
-    return c + d
-"""
-
-code = compile(src, "<exec>", "exec")
-exec(code)
-f(1, 2)
-```
-
-You will get this (without source line information):
-```txt
-Traceback (most recent call last):
-  File "<exec>", line 3, in f
-  File "<exec>", line 6, in g
-  File "<exec>", line 3, in f
-  File "<exec>", line 6, in g
-  File "<exec>", line 3, in f
-  File "<exec>", line 6, in g
-  File "<exec>", line 3, in f
-StackOverflowError
-```
-
-instead of this (with source line information):
-
-```txt
-Traceback (most recent call last):
-  File "<stdin>", line 2, in f
-    return g(a, b)
-  File "<stdin>", line 2, in g
-    c = f(a, b)
-  File "<stdin>", line 2, in f
-    return g(a, b)
-  File "<stdin>", line 2, in g
-    c = f(a, b)
-  File "<stdin>", line 2, in f
-    return g(a, b)
-  File "<stdin>", line 2, in g
-    c = f(a, b)
-  File "<stdin>", line 2, in f
-    return g(a, b)
-StackOverflowError
-```
-
-!!!
-String compilation has no guarantee of compatibility between different versions of pkpy.
-!!!
-
-You can use this snippet to convert every python file in a directory into precompiled strings.
-
-```python
-# precompile.py
-import sys, os
-
-def precompile(filepath: str):
-    """Precompile a python file inplace"""
-    print(filepath)
-    with open(filepath, 'r') as f:
-        source = f.read()
-    source = compile(source, filepath, 'exec')
-    with open(filepath, 'w') as f:
-        f.write(source)
-
-def traverse(root: str):
-    """Traverse a directory and precompile every python file"""
-    for entry in os.listdir(root):
-        entrypath = os.path.join(root, entry)
-        if os.path.isdir(entrypath):
-            traverse(entrypath)
-        elif entrypath.endswith(".py"):
-            precompile(entrypath)
-```

+ 1 - 1
docs/features/ub.md

@@ -9,4 +9,4 @@ These are the undefined behaviours of pkpy. The behaviour of pkpy is undefined i
 2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`.
 3. Type `T`'s `__new__` returns an object that is not an instance of `T`.
 4. Call `__new__` with a type that is not a subclass of `type`.
-5. `__eq__`, `__lt__` or `__contains__`, etc.. returns a value that is not a boolean.
+

+ 1 - 6
docs/quick-start.md

@@ -31,6 +31,7 @@ To compile it with your project, these flags must be set:
 
 + `--std=c11` flag must be set
 + For MSVC, `/utf-8` flag must be set
++ `NDEBUG` macro should be defined for release build, or you will get poor performance
 
 ### Get prebuilt binaries
 
@@ -53,12 +54,6 @@ You can download an artifact there which contains the following files.
 │   └── x86_64
 │       ├── libpocketpy.so
 │       └── main
-├── macos
-│   └── pocketpy.bundle
-│       └── Contents
-│           ├── Info.plist
-│           └── MacOS
-│               └── pocketpy
 └── windows
     └── x86_64
         ├── main.exe

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

@@ -4,6 +4,8 @@
 #include "pocketpy/common/utils.h"
 #include "pocketpy/pocketpy.h"
 
+#include <stdarg.h>
+
 /* string */
 typedef struct c11_string{
     // int size | char[] | '\0'

+ 8 - 10
include/pocketpy/linalg.h

@@ -22,15 +22,13 @@ typedef struct c11_vec3 {
     float z;
 } c11_vec3;
 
-typedef struct c11_mat3x3 {
-    union {
-        struct {
-            float _11, _12, _13;
-            float _21, _22, _23;
-            float _31, _32, _33;
-        };
-
-        float m[3][3];
-        float data[9];
+typedef union c11_mat3x3 {
+    struct {
+        float _11, _12, _13;
+        float _21, _22, _23;
+        float _31, _32, _33;
     };
+
+    float m[3][3];
+    float data[9];
 } c11_mat3x3;

+ 0 - 1
include/pocketpy/pocketpy.h

@@ -2,7 +2,6 @@
 
 #include <stdint.h>
 #include <stdbool.h>
-#include <stdarg.h>
 #include <stddef.h>
 
 #include "pocketpy/config.h"

+ 0 - 2
include/typings/cjson.pyi

@@ -1,2 +0,0 @@
-def loads(data: str | bytes) -> object: ...
-def dumps(obj: object) -> str: ...

+ 10 - 0
include/typings/linalg.pyi

@@ -14,6 +14,8 @@ class vec2:
 
     def __init__(self, x: float, y: float) -> None: ...
     def __repr__(self) -> str: ...
+    def __eq__(self, other: vec2) -> bool: ...
+    def __ne__(self, other: vec2) -> bool: ...
 
     def __add__(self, other: vec2) -> vec2: ...
     def __sub__(self, other: vec2) -> vec2: ...
@@ -51,6 +53,8 @@ class vec2:
 class mat3x3:
     def __init__(self, _11, _12, _13, _21, _22, _23, _31, _32, _33) -> None: ...
     def __repr__(self) -> str: ...
+    def __eq__(self, other: mat3x3) -> bool: ...
+    def __ne__(self, other: mat3x3) -> bool: ...
 
     def __getitem__(self, index: tuple[int, int]) -> float: ...
     def __setitem__(self, index: tuple[int, int], value: float) -> None: ...
@@ -101,6 +105,8 @@ class vec2i:
 
     def __init__(self, x: int, y: int) -> None: ...
     def __repr__(self) -> str: ...
+    def __eq__(self, other: vec2i) -> bool: ...
+    def __ne__(self, other: vec2i) -> bool: ...
 
 
 class vec3i:
@@ -117,6 +123,8 @@ class vec3i:
 
     def __init__(self, x: int, y: int, z: int) -> None: ...
     def __repr__(self) -> str: ...
+    def __eq__(self, other: vec3i) -> bool: ...
+    def __ne__(self, other: vec3i) -> bool: ...
 
 
 class vec3:
@@ -133,3 +141,5 @@ class vec3:
 
     def __init__(self, x: float, y: float, z: float) -> None: ...
     def __repr__(self) -> str: ...
+    def __eq__(self, other: vec3) -> bool: ...
+    def __ne__(self, other: vec3) -> bool: ...

+ 2 - 3
src/interpreter/ceval.c

@@ -283,12 +283,13 @@ FrameResult VM__run_top_frame(VM* self) {
                     if(magic->type == tp_nativefunc) {
                         if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
                         POP();
+                        py_assign(TOP(), py_retval());
                     } else {
                         INSERT_THIRD();     // [?, a, b]
                         *THIRD() = *magic;  // [__getitem__, a, b]
                         if(!py_vectorcall(1, 0)) goto __ERROR;
+                        PUSH(py_retval());
                     }
-                    *TOP() = self->last_retval;
                     DISPATCH();
                 }
                 TypeError("'%t' object is not subscriptable", SECOND()->type);
@@ -349,7 +350,6 @@ FrameResult VM__run_top_frame(VM* self) {
                     } else {
                         *FOURTH() = *magic;  // [__selitem__, a, b, val]
                         if(!py_vectorcall(2, 0)) goto __ERROR;
-                        POP();  // discard retval
                     }
                     DISPATCH();
                 }
@@ -423,7 +423,6 @@ FrameResult VM__run_top_frame(VM* self) {
                         INSERT_THIRD();     // [?, a, b]
                         *THIRD() = *magic;  // [__delitem__, a, b]
                         if(!py_vectorcall(1, 0)) goto __ERROR;
-                        POP();  // discard retval
                     }
                     DISPATCH();
                 }

+ 33 - 0
src/public/py_slice.c

@@ -63,11 +63,44 @@ static bool slice_step(int argc, py_Ref argv) {
     return true;
 }
 
+static bool slice__eq__(int argc, py_Ref argv) {
+    py_Ref self = py_arg(0);
+    py_Ref other = py_arg(1);
+    if(!py_istype(other, tp_slice)) {
+        py_newnotimplemented(py_retval());
+        return true;
+    }
+    for(int i = 0; i < 3; i++) {
+        py_Ref lhs = py_getslot(self, i);
+        py_Ref rhs = py_getslot(other, i);
+        int res = py_equal(lhs, rhs);
+        if(res == -1) return false;
+        if(res == 0) {
+            py_newbool(py_retval(), false);
+            return true;
+        }
+    }
+    py_newbool(py_retval(), true);
+    return true;
+}
+
+static bool slice__ne__(int argc, py_Ref argv) {
+    bool ok = slice__eq__(argc, argv);
+    if(!ok) return false;
+    py_Ref res = py_retval();
+    if(py_isbool(res)) py_newbool(py_retval(), !py_tobool(res));
+    return true;
+}
+
 py_Type pk_slice__register() {
     py_Type type = pk_newtype("slice", tp_object, NULL, NULL, false, true);
 
     py_bindmagic(type, __new__, slice__new__);
     py_bindmagic(type, __repr__, slice__repr__);
+    py_bindmagic(type, __eq__, slice__eq__);
+    py_bindmagic(type, __ne__, slice__ne__);
+
+    py_setdict(py_tpobject(type), __hash__, py_None);
 
     py_bindproperty(type, "start", slice_start, NULL);
     py_bindproperty(type, "stop", slice_stop, NULL);

+ 0 - 20
tests/05_list.py

@@ -167,26 +167,6 @@ assert b == [(5, 1), (1, 2), (3,3)]
 
 # assert repr(a) == "[0, [1, 2, [...]]]"
 
-# try:
-#     a.index(1, 1)
-#     exit(1)
-# except ValueError:
-#     pass
-
-# slice extras
-# class A:
-#     def __getitem__(self, index):
-#         return index
-    
-# assert A()[1:2, 3] == (slice(1, 2, None), 3)
-# assert A()[1:2, 3:4] == (slice(1, 2, None), slice(3, 4, None))
-# assert A()[1:2, 3:4, 5] == (slice(1, 2, None), slice(3, 4, None), 5)
-# assert A()[:, :] == (slice(None, None, None), slice(None, None, None))
-# assert A()[::, :] == (slice(None, None, None), slice(None, None, None))
-# assert A()[::, :2] == (slice(None, None, None), slice(None, 2, None))
-# assert A()['b':'c':1, :] == (slice('b', 'c', 1), slice(None, None, None))
-# assert A()[1:2, :A()[3:4, ::-1]] == (slice(1, 2, None), slice(None, (slice(3, 4, None), slice(None, None, -1)), None))
-
 # unpacking builder (not supported)
 # a = [1, 2, 3]
 # b = [*a, 4, 5]

+ 1 - 1
tests/28_exception.py

@@ -47,7 +47,7 @@ try:
     a = A()
     b = a[1]
     exit(1)
-except:
+except KeyError:
     pass
 
 try:

+ 0 - 0
tests/99_bugs.py → tests/95_bugs.py


+ 0 - 0
tests/99_dis.py → tests/95_dis.py


+ 49 - 0
tests/99_extras.py

@@ -0,0 +1,49 @@
+try:
+    a = [1, 2, 3]
+    a.index(999)
+    exit(1)
+except ValueError:
+    pass
+
+# test some python magics
+class A:
+    def __init__(self):
+        self.d = {}
+
+    def __getitem__(self, index):
+        return self.d[index]
+    
+    def __setitem__(self, index, value):
+        self.d[index] = value
+
+    def __contains__(self, index):
+        return index in self.d
+    
+    def __delitem__(self, index):
+        del self.d[index]
+
+a = A()
+a['1'] = 3
+assert '1' in a
+assert '2' not in a
+assert a['1'] == 3
+del a['1']
+assert '1' not in a
+
+# slice extras
+class A:
+    def __getitem__(self, index):
+        return index
+
+assert slice(1, 2, None) == slice(1, 2, None)
+assert slice(1, 3, None) != slice(1, 2, None)
+
+assert A()[1] == 1
+assert A()[1:2, 3] == (slice(1, 2, None), 3)
+assert A()[1:2, 3:4] == (slice(1, 2, None), slice(3, 4, None))
+assert A()[1:2, 3:4, 5] == (slice(1, 2, None), slice(3, 4, None), 5)
+assert A()[:, :] == (slice(None, None, None), slice(None, None, None))
+assert A()[::, :] == (slice(None, None, None), slice(None, None, None))
+assert A()[::, :2] == (slice(None, None, None), slice(None, 2, None))
+assert A()['b':'c':1, :] == (slice('b', 'c', 1), slice(None, None, None))
+assert A()[1:2, :A()[3:4, ::-1]] == (slice(1, 2, None), slice(None, (slice(3, 4, None), slice(None, None, -1)), None))