mirror of
https://github.com/pocketpy/pocketpy
synced 2025-11-05 11:10:17 +00:00
Add some dict bindings
This commit is contained in:
parent
8ae999ecdf
commit
f88b1a1436
@ -35,7 +35,7 @@ endif()
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
file(GLOB_RECURSE POCKETPY_SRC_CPP ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp)
|
||||
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)
|
||||
if(PK_USE_CJSON)
|
||||
@ -95,7 +95,7 @@ elseif(PK_BUILD_STATIC_LIB)
|
||||
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
|
||||
else()
|
||||
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)
|
||||
# static linked main, for sanitizing purpose
|
||||
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
|
||||
|
||||
@ -44,7 +44,6 @@ pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self);
|
||||
/**
|
||||
* @brief Set a key-value pair into the `pkpy_Dict`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to set
|
||||
* @param val value to set
|
||||
* @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`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to check
|
||||
* @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`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to remove
|
||||
* @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`
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param key key to get
|
||||
* @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
|
||||
* @param self `pkpy_Dict` instance
|
||||
* @param vm __eq__ and __hash__ context
|
||||
* @param other `pkpy_Dict` instance to update with
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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`
|
||||
* @param self `pkpy_Dict` instance
|
||||
|
||||
@ -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 TypeError(x) false
|
||||
#define KeyError(...) false
|
||||
#define py_arg(i) py_offset(argv, i)
|
||||
#define py_checkargc(n) \
|
||||
if(argc != n) return TypeError()
|
||||
|
||||
@ -4,13 +4,17 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "dict.h"
|
||||
|
||||
#define DICT_MAX_LOAD 0.75
|
||||
#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 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 {
|
||||
py_TValue key;
|
||||
@ -33,7 +37,9 @@ inline extern int pkpy_Dict__idx_null(const pkpy_Dict* self) {
|
||||
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) {
|
||||
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) {
|
||||
#if PK_DICT_COMPACT_MODE
|
||||
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);
|
||||
#else
|
||||
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) {
|
||||
#if PK_DICT_COMPACT_MODE
|
||||
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;
|
||||
*p = (v << shift) | (*p & ~(pkpy_Dict__idx_null(self) << shift));
|
||||
#else
|
||||
@ -174,7 +180,8 @@ bool pkpy_Dict__contains(const pkpy_Dict* self, py_TValue key) {
|
||||
|
||||
static bool pkpy_Dict__refactor(pkpy_Dict* self) {
|
||||
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
|
||||
// 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));
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -212,7 +219,7 @@ bool pkpy_Dict__del(pkpy_Dict* self, py_TValue key) {
|
||||
int h = pkpy_Dict__probe1(self, key, hash);
|
||||
int idx = pkpy_Dict__htget(self, h), null = pkpy_Dict__idx_null(self);
|
||||
if(idx == null) return false;
|
||||
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
pkpy_Var__set_null(&entry->key);
|
||||
self->count -= 1;
|
||||
@ -220,20 +227,20 @@ bool pkpy_Dict__del(pkpy_Dict* self, py_TValue key) {
|
||||
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;
|
||||
int err = py_hash(&key, &out);
|
||||
int hash = DICT_HASH_TRANS(out);
|
||||
int h = pkpy_Dict__probe1(self, key, hash);
|
||||
|
||||
|
||||
int idx = pkpy_Dict__htget(self, h);
|
||||
if(idx == pkpy_Dict__idx_null(self)) return NULL;
|
||||
|
||||
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
|
||||
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++) {
|
||||
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &other->_entries, i);
|
||||
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->_entries.count = 0;
|
||||
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
|
||||
}
|
||||
|
||||
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);
|
||||
if(!pkpy_Var__is_null(&entry->key)) break;
|
||||
idx++;
|
||||
@ -256,20 +263,39 @@ static int pkpy_Dict__next_entry_idx(const pkpy_Dict* self, int 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){
|
||||
._dict = self,
|
||||
._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;
|
||||
|
||||
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 (key) *key = entry->key;
|
||||
if (val) *val = entry->val;
|
||||
if(key) *key = entry->key;
|
||||
if(val) *val = entry->val;
|
||||
|
||||
self->_index = pkpy_Dict__next_entry_idx(self->_dict, self->_index + 1);
|
||||
return true;
|
||||
|
||||
@ -4,10 +4,224 @@
|
||||
#include "pocketpy/objects/object.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){
|
||||
|
||||
}
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user