Add some dict bindings

This commit is contained in:
方而静 2024-07-03 15:51:42 +08:00
parent 8ae999ecdf
commit f88b1a1436
5 changed files with 276 additions and 31 deletions

View File

@ -35,7 +35,7 @@ endif()
include_directories(${CMAKE_CURRENT_LIST_DIR}/include) include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
file(GLOB_RECURSE POCKETPY_SRC_CPP ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp) file(GLOB_RECURSE POCKETPY_SRC_CPP ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp)
file(GLOB_RECURSE POCKETPY_SRC_C ${CMAKE_CURRENT_LIST_DIR}/src/*.c) file(GLOB_RECURSE POCKETPY_SRC_C ${CMAKE_CURRENT_LIST_DIR}/src/*.c)
set(POCKETPY_SRC ${POCKETPY_SRC_CPP} ${POCKETPY_SRC_C}) set(POCKETPY_SRC ${POCKETPY_SRC_C})
option(PK_USE_CJSON "" OFF) option(PK_USE_CJSON "" OFF)
if(PK_USE_CJSON) if(PK_USE_CJSON)
@ -95,7 +95,7 @@ elseif(PK_BUILD_STATIC_LIB)
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC}) add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
else() else()
set(PROJECT_EXE_NAME main) set(PROJECT_EXE_NAME main)
add_executable(${PROJECT_EXE_NAME} src2/main.cpp) add_executable(${PROJECT_EXE_NAME} src2/main.c)
if (BUILD_TESTING_SANITIZE) if (BUILD_TESTING_SANITIZE)
# static linked main, for sanitizing purpose # static linked main, for sanitizing purpose
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC}) add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})

View File

@ -44,7 +44,6 @@ pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self);
/** /**
* @brief Set a key-value pair into the `pkpy_Dict` * @brief Set a key-value pair into the `pkpy_Dict`
* @param self `pkpy_Dict` instance * @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to set * @param key key to set
* @param val value to set * @param val value to set
* @return `true` if the key is newly added, `false` if the key already exists * @return `true` if the key is newly added, `false` if the key already exists
@ -54,7 +53,6 @@ bool pkpy_Dict__set(pkpy_Dict* self, py_TValue key, py_TValue val);
/** /**
* @brief Check if a key exists in the `pkpy_Dict` * @brief Check if a key exists in the `pkpy_Dict`
* @param self `pkpy_Dict` instance * @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to check * @param key key to check
* @return `true` if the key exists, `false` otherwise * @return `true` if the key exists, `false` otherwise
*/ */
@ -63,7 +61,6 @@ bool pkpy_Dict__contains(const pkpy_Dict* self, py_TValue key);
/** /**
* @brief Remove a key from the `pkpy_Dict` * @brief Remove a key from the `pkpy_Dict`
* @param self `pkpy_Dict` instance * @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to remove * @param key key to remove
* @return `true` if the key was found and removed, `false` if the key doesn't exist * @return `true` if the key was found and removed, `false` if the key doesn't exist
*/ */
@ -72,7 +69,6 @@ bool pkpy_Dict__del(pkpy_Dict* self, py_TValue key);
/** /**
* @brief Try to get a value from the `pkpy_Dict` * @brief Try to get a value from the `pkpy_Dict`
* @param self `pkpy_Dict` instance * @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param key key to get * @param key key to get
* @return the value associated with the key, `NULL` if the key doesn't exist * @return the value associated with the key, `NULL` if the key doesn't exist
*/ */
@ -81,7 +77,6 @@ const py_TValue* pkpy_Dict__try_get(const pkpy_Dict* self, py_TValue key);
/** /**
* @brief Update the `pkpy_Dict` with another one * @brief Update the `pkpy_Dict` with another one
* @param self `pkpy_Dict` instance * @param self `pkpy_Dict` instance
* @param vm __eq__ and __hash__ context
* @param other `pkpy_Dict` instance to update with * @param other `pkpy_Dict` instance to update with
*/ */
void pkpy_Dict__update(pkpy_Dict* self, const pkpy_Dict* other); void pkpy_Dict__update(pkpy_Dict* self, const pkpy_Dict* other);
@ -92,6 +87,15 @@ void pkpy_Dict__update(pkpy_Dict* self, const pkpy_Dict* other);
*/ */
void pkpy_Dict__clear(pkpy_Dict* self); void pkpy_Dict__clear(pkpy_Dict* self);
/**
* @brief Try to pop the latest inserted key-value pair from the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @param key key will be filled with the current key, can be `NULL` if not needed
* @param value value will be filled with the current value, can be `NULL` if not needed
* @return `true` if the operation was successful, `false` if the `pkpy_Dict` is empty
*/
bool pkpy_Dict__try_pop(pkpy_Dict* self, py_TValue *key, py_TValue *val);
/** /**
* @brief Iterate over the `pkpy_Dict` * @brief Iterate over the `pkpy_Dict`
* @param self `pkpy_Dict` instance * @param self `pkpy_Dict` instance

View File

@ -118,6 +118,7 @@ bool py_issubclass(py_Type derived, py_Type base);
#define py_offset(p, i) (py_Ref)((char*)p + ((i) << 4)) #define py_offset(p, i) (py_Ref)((char*)p + ((i) << 4))
#define TypeError(x) false #define TypeError(x) false
#define KeyError(...) false
#define py_arg(i) py_offset(argv, i) #define py_arg(i) py_offset(argv, i)
#define py_checkargc(n) \ #define py_checkargc(n) \
if(argc != n) return TypeError() if(argc != n) return TypeError()

View File

@ -4,13 +4,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include "dict.h"
#define DICT_MAX_LOAD 0.75 #define DICT_MAX_LOAD 0.75
#define DICT_HASH_NEXT(h) ((h) * 5 + 1) #define DICT_HASH_NEXT(h) ((h) * 5 + 1)
#define DICT_HASH_TRANS(h) ((int)((h) & 0xffffffff)) // used for tansform value from __hash__ #define DICT_HASH_TRANS(h) ((int)((h) & 0xffffffff)) // used for tansform value from __hash__
#define PK_DICT_COMPACT_MODE 1 #define PK_DICT_COMPACT_MODE 1
#define pkpy_Var__is_null(self) ((self)->type == 0) #define pkpy_Var__is_null(self) ((self)->type == 0)
#define pkpy_Var__set_null(self) do { (self)->type = 0; } while(0) #define pkpy_Var__set_null(self) \
do { \
(self)->type = 0; \
} while(0)
struct pkpy_DictEntry { struct pkpy_DictEntry {
py_TValue key; py_TValue key;
@ -33,7 +37,9 @@ inline extern int pkpy_Dict__idx_null(const pkpy_Dict* self) {
return -1; return -1;
} }
inline extern int pkpy_Dict__ht_byte_size(const pkpy_Dict* self) { return self->_htcap * pkpy_Dict__idx_size(self); } inline extern int pkpy_Dict__ht_byte_size(const pkpy_Dict* self) {
return self->_htcap * pkpy_Dict__idx_size(self);
}
void pkpy_Dict__ctor(pkpy_Dict* self) { void pkpy_Dict__ctor(pkpy_Dict* self) {
self->count = 0; self->count = 0;
@ -61,7 +67,7 @@ pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self) {
static int pkpy_Dict__htget(const pkpy_Dict* self, int h) { static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
#if PK_DICT_COMPACT_MODE #if PK_DICT_COMPACT_MODE
const int loc = pkpy_Dict__idx_size(self) * h; const int loc = pkpy_Dict__idx_size(self) * h;
const int *p = (const int*)(((const char*)self->_hashtable) + (loc & (~3))); const int* p = (const int*)(((const char*)self->_hashtable) + (loc & (~3)));
return (*p >> ((loc & 3) * 8)) & pkpy_Dict__idx_null(self); return (*p >> ((loc & 3) * 8)) & pkpy_Dict__idx_null(self);
#else #else
return ((const int*)self->_hashtable)[h]; return ((const int*)self->_hashtable)[h];
@ -71,7 +77,7 @@ static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) { static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) {
#if PK_DICT_COMPACT_MODE #if PK_DICT_COMPACT_MODE
const int loc = pkpy_Dict__idx_size(self) * h; const int loc = pkpy_Dict__idx_size(self) * h;
int *p = (int*)(((char*)self->_hashtable) + (loc & (~3))); int* p = (int*)(((char*)self->_hashtable) + (loc & (~3)));
const int shift = (loc & 3) * 8; const int shift = (loc & 3) * 8;
*p = (v << shift) | (*p & ~(pkpy_Dict__idx_null(self) << shift)); *p = (v << shift) | (*p & ~(pkpy_Dict__idx_null(self) << shift));
#else #else
@ -174,7 +180,8 @@ bool pkpy_Dict__contains(const pkpy_Dict* self, py_TValue key) {
static bool pkpy_Dict__refactor(pkpy_Dict* self) { static bool pkpy_Dict__refactor(pkpy_Dict* self) {
int deleted_slots = self->_entries.count - self->count; int deleted_slots = self->_entries.count - self->count;
if(deleted_slots <= 8 || deleted_slots < self->_entries.count * (1 - DICT_MAX_LOAD)) return false; if(deleted_slots <= 8 || deleted_slots < self->_entries.count * (1 - DICT_MAX_LOAD))
return false;
// shrink // shrink
// free(self->_hashtable); // free(self->_hashtable);
@ -184,10 +191,10 @@ static bool pkpy_Dict__refactor(pkpy_Dict* self) {
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self)); memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
int new_cnt = 0; int new_cnt = 0;
for (int i = 0; i < self->_entries.count; ++i) { for(int i = 0; i < self->_entries.count; ++i) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i); struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, i);
if(pkpy_Var__is_null(&entry->key)) continue; if(pkpy_Var__is_null(&entry->key)) continue;
if (i > new_cnt) c11__setitem(struct pkpy_DictEntry, &self->_entries, new_cnt, *entry); if(i > new_cnt) c11__setitem(struct pkpy_DictEntry, &self->_entries, new_cnt, *entry);
new_cnt += 1; new_cnt += 1;
} }
@ -220,7 +227,7 @@ bool pkpy_Dict__del(pkpy_Dict* self, py_TValue key) {
return true; return true;
} }
const py_TValue *pkpy_Dict__try_get(const pkpy_Dict* self, py_TValue key) { const py_TValue* pkpy_Dict__try_get(const pkpy_Dict* self, py_TValue key) {
int64_t out; int64_t out;
int err = py_hash(&key, &out); int err = py_hash(&key, &out);
int hash = DICT_HASH_TRANS(out); int hash = DICT_HASH_TRANS(out);
@ -233,7 +240,7 @@ const py_TValue *pkpy_Dict__try_get(const pkpy_Dict* self, py_TValue key) {
return &entry->val; return &entry->val;
} }
void pkpy_Dict__update(pkpy_Dict *self, const pkpy_Dict *other) { void pkpy_Dict__update(pkpy_Dict* self, const pkpy_Dict* other) {
for(int i = 0; i < other->_entries.count; i++) { for(int i = 0; i < other->_entries.count; i++) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &other->_entries, i); struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &other->_entries, i);
if(pkpy_Var__is_null(&entry->key)) continue; if(pkpy_Var__is_null(&entry->key)) continue;
@ -241,14 +248,14 @@ void pkpy_Dict__update(pkpy_Dict *self, const pkpy_Dict *other) {
} }
} }
void pkpy_Dict__clear(pkpy_Dict *self) { void pkpy_Dict__clear(pkpy_Dict* self) {
self->count = 0; self->count = 0;
self->_entries.count = 0; self->_entries.count = 0;
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self)); memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
} }
static int pkpy_Dict__next_entry_idx(const pkpy_Dict* self, int idx) { static int pkpy_Dict__next_entry_idx(const pkpy_Dict* self, int idx) {
while (idx < self->_entries.count) { while(idx < self->_entries.count) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx); struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
if(!pkpy_Var__is_null(&entry->key)) break; if(!pkpy_Var__is_null(&entry->key)) break;
idx++; idx++;
@ -256,20 +263,39 @@ static int pkpy_Dict__next_entry_idx(const pkpy_Dict* self, int idx) {
return idx; return idx;
} }
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict *self) { bool pkpy_Dict__try_pop(pkpy_Dict* self, py_TValue* key, py_TValue* val) {
int idx = self->count - 1;
struct pkpy_DictEntry* entry;
do {
if (idx < 0)
return false;
entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
idx--;
} while (pkpy_Var__is_null(&entry->key));
if(key) *key = entry->key;
if(val) *val = entry->val;
pkpy_Var__set_null(&entry->key);
self->count -= 1;
pkpy_Dict__refactor(self);
return true;
}
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict* self) {
return (pkpy_DictIter){ return (pkpy_DictIter){
._dict = self, ._dict = self,
._index = pkpy_Dict__next_entry_idx(self, 0), ._index = pkpy_Dict__next_entry_idx(self, 0),
}; };
} }
bool pkpy_DictIter__next(pkpy_DictIter *self, py_TValue *key, py_TValue *val) { bool pkpy_DictIter__next(pkpy_DictIter* self, py_TValue* key, py_TValue* val) {
if(self->_index >= self->_dict->_entries.count) return false; if(self->_index >= self->_dict->_entries.count) return false;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_dict->_entries, self->_index); struct pkpy_DictEntry* entry =
&c11__getitem(struct pkpy_DictEntry, &self->_dict->_entries, self->_index);
if(pkpy_Var__is_null(&entry->key)) return false; if(pkpy_Var__is_null(&entry->key)) return false;
if (key) *key = entry->key; if(key) *key = entry->key;
if (val) *val = entry->val; if(val) *val = entry->val;
self->_index = pkpy_Dict__next_entry_idx(self->_dict, self->_index + 1); self->_index = pkpy_Dict__next_entry_idx(self->_dict, self->_index + 1);
return true; return true;

View File

@ -4,10 +4,224 @@
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
void py_newdict(py_Ref out){ #include "pocketpy/common/vector.h"
#include "pocketpy/objects/dict.h"
py_Type pk_dict__register() {
pk_VM* vm = pk_current_vm;
py_Type type = pk_VM__new_type(vm, "dict", tp_object, NULL, false);
pk_TypeInfo* ti = c11__at(pk_TypeInfo, &vm->types, type);
ti->dtor = (void (*)(void*))pkpy_Dict__dtor;
return type;
}
void py_newdict(py_Ref out) {
pk_VM* vm = pk_current_vm;
PyObject* obj = pk_ManagedHeap__gcnew(&vm->heap, tp_dict, 0, sizeof(pkpy_Dict));
pkpy_Dict* userdata = PyObject__userdata(obj);
pkpy_Dict__ctor(userdata);
out->type = tp_dict;
out->is_ptr = true;
out->_obj = obj;
}
// dict medthods
// https://docs.python.org/3/library/stdtypes.html#dict
// TODO: list(d)
bool _py_dict__len__(int argc, py_Ref argv) {
py_checkargc(1);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_newint(py_retval(), dict->count);
return true;
}
void _py_dict__getitem__(int argc, py_Ref argv) {
py_checkargc(2);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1);
py_TValue* val = pkpy_Dict__try_get(dict, *key);
if (val) {
*py_retval() = *val;
return true;
} else {
return KeyError();
}
}
bool _py_dict__setitem__(int argc, py_Ref argv) {
py_checkargc(3);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1), val = py_arg(2);
pkpy_Dict__set(dict, *key, *val);
py_newnone(py_retval());
return true;
}
bool _py_dict__delitem__(int argc, py_Ref argv) {
py_checkargc(2);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1);
pkpy_Dict__del(dict, *key);
py_newnone(py_retval());
return true;
}
bool _py_dict__contains__(int argc, py_Ref argv) {
py_checkargc(2);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1);
py_newbool(py_retval(), pkpy_Dict__contains(dict, *key));
return true;
}
// TODO: iter(d)
bool _py_dict__clear(int argc, py_Ref argv) {
py_checkargc(1);
pkpy_Dict* dict = py_touserdata(py_arg(0));
pkpy_Dict__clear(dict);
py_newnone(py_retval());
return true;
}
bool _py_dict__copy(int argc, py_Ref argv) {
py_checkargc(1);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref ret = py_retval();
py_newdict(ret);
pkpy_Dict__update(py_touserdata(ret), dict);
return true;
}
// TODO: classmethod fromkeys
bool _py_dict__get(int argc, py_Ref argv) {
if (argc < 2 || argc > 3)
return TypeError();
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1);
py_TValue* val = pkpy_Dict__try_get(dict, *key);
if (val) {
*py_retval() = *val;
return true;
}
if (argc == 3)
*py_retval() = *py_arg(2);
else
py_newnone(py_retval());
return true;
}
// TODO: method items
// TODO: method keys
bool _py_dict__pop(int argc, py_Ref argv) {
if (argc < 2 || argc > 3)
return TypeError();
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1);
py_TValue* val = pkpy_Dict__try_get(dict, *key);
if (val) {
pkpy_Dict__del(dict, *key);
*py_retval() = *val;
return true;
}
if (argc == 3) {
*py_retval() = *py_arg(2);
return true;
}
return KeyError();
}
bool _py_dict__popitem(int argc, py_Ref argv) {
py_checkargc(1);
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_StackRef key = py_pushtmp(), val = py_pushtmp();
py_Ref ret = py_retval();
if (pkpy_Dict__try_pop(dict, key, val)) {
py_newtuple(ret, 2);
py_setslot(ret, 0, key);
py_setslot(ret, 1, val);
return true;
}
return KeyError();
}
// https://docs.python.org/3/library/stdtypes.html#dict-views
// TODO: dict_keys, dict_values, dict_items
// TODO: dict_iterator, dict_keyiterator, dict_valueiterator
// TODO: reversed(d)
bool _py_dict__setdefault(int argc, py_Ref argv) {
if (argc < 2 || argc > 3)
return TypeError();
pkpy_Dict* dict = py_touserdata(py_arg(0));
py_Ref key = py_arg(1);
py_TValue* val = pkpy_Dict__try_get(dict, *key);
if (val) {
*py_retval() = *val;
return true;
}
py_Ref dft;
if (argc == 3) {
dft = py_arg(2);
} else {
dft = py_pushtmp();
py_newnone(dft);
}
pkpy_Dict__set(dict, *key, *dft);
*py_retval() = *dft;
return true;
}
bool _py_dict__update(int argc, py_Ref argv) {
// TODO: accept kwargs
py_checkargc(2);
pkpy_Dict* me = py_touserdata(py_arg(0));
pkpy_Dict* other = py_touserdata(py_arg(1));
pkpy_Dict__update(me, other);
py_newnone(py_retval());
return true;
}
// TODO: method values
bool _py_dict__or__(int argc, py_Ref argv) {
py_checkargc(2);
pkpy_Dict* me = py_touserdata(py_arg(0));
pkpy_Dict* other = py_touserdata(py_arg(1));
py_Ref ret = py_retval();
py_newdict(ret);
pkpy_Dict__update(py_touserdata(ret), me);
pkpy_Dict__update(py_touserdata(ret), other);
return true;
}
// TODO: method operator |=
py_Type pk_set__register() {
// TODO: implement
} }
void py_newset(py_Ref out){ void py_newset(py_Ref out){
// TODO: implement
} }