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;
@ -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);
@ -256,6 +263,24 @@ static int pkpy_Dict__next_entry_idx(const pkpy_Dict* self, int idx) {
return idx; return idx;
} }
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) { pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict* self) {
return (pkpy_DictIter){ return (pkpy_DictIter){
._dict = self, ._dict = self,
@ -266,7 +291,8 @@ pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict *self) {
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;

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