reimpl dict

This commit is contained in:
blueloveTH 2024-07-13 19:42:51 +08:00
parent 42b4c56543
commit 1a4b88829c
25 changed files with 741 additions and 570 deletions

View File

@ -66,6 +66,9 @@ void pk_VM__pop_frame(pk_VM* self);
bool pk__parse_int_slice(const py_Ref slice, int length, int* start, int* stop, int* step);
bool pk__normalize_index(int* index, int length);
void pk_list__mark(void* ud, void (*marker)(py_TValue*));
void pk_dict__mark(void* ud, void (*marker)(py_TValue*));
typedef enum pk_FrameResult {
RES_RETURN,
RES_CALL,
@ -88,11 +91,13 @@ const char* pk_opname(Opcode op);
py_TValue* pk_arrayview(py_Ref self, int* length);
int pk_arrayeq(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length);
bool pk_arrayiter(py_Ref val);
/// Assumes [a, b] are on the stack, performs a binary op.
/// The result is stored in `self->last_retval`.
/// The stack remains unchanged.
bool pk_stack_binaryop(pk_VM* self, py_Name op, py_Name rop);
void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte);
// type registration
@ -102,6 +107,7 @@ py_Type pk_str__register();
py_Type pk_str_iterator__register();
py_Type pk_bytes__register();
py_Type pk_dict__register();
py_Type pk_dict_items__register();
py_Type pk_list__register();
py_Type pk_tuple__register();
py_Type pk_array_iterator__register();

View File

@ -1,113 +0,0 @@
#pragma once
#include <stdbool.h>
#include "pocketpy/objects/base.h"
#include "pocketpy/common/vector.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @brief `pkpy_Dict` is the Dict type in Python */
typedef struct {
int count; /** number of elements in the dictionary */
c11_vector _entries; /** contains `pkpy_DictEntry` (hidden type) */
int _htcap; /** capacity of the hashtable, always a power of 2 */
void* _hashtable; /** contains indecies, can be `u8`, `u16` or `u32` according to size*/
} pkpy_Dict;
/** @brief `pkpy_DictIter` is used to iterate over a `pkpy_Dict` */
typedef struct {
const pkpy_Dict* _dict;
int _index;
} pkpy_DictIter;
/**
* @brief `pkpy_Dict` constructor
* @param self `pkpy_Dict` instance
*/
void pkpy_Dict__ctor(pkpy_Dict* self);
/**
* @brief `pkpy_Dict` destructor
* @param self `pkpy_Dict` instance
*/
void pkpy_Dict__dtor(pkpy_Dict* self);
/**
* @brief Copy a `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @return a new `pkpy_Dict` instance, must be destructed by the caller
*/
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
*/
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
*/
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
*/
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
*/
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);
/**
* @brief Clear the `pkpy_Dict`
* @param self `pkpy_Dict` instance
*/
void pkpy_Dict__clear(pkpy_Dict* self);
/**
* @brief Iterate over the `pkpy_Dict`
* @param self `pkpy_Dict` instance
* @return an iterator over the `pkpy_Dict`
*/
pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict* self);
/**
* @brief Iterate over 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 iteration is still valid, `false` otherwise
*/
bool pkpy_DictIter__next(pkpy_DictIter* self, py_TValue* key, py_TValue* value);
#ifdef __cplusplus
}
#endif

View File

@ -121,11 +121,18 @@ void* py_touserdata(const py_Ref);
#define py_isfloat(self) py_istype(self, tp_float)
#define py_isbool(self) py_istype(self, tp_bool)
#define py_isstr(self) py_istype(self, tp_str)
#define py_islist(self) py_istype(self, tp_list)
#define py_istuple(self) py_istype(self, tp_tuple)
#define py_isdict(self) py_istype(self, tp_dict)
bool py_istype(const py_Ref, py_Type);
bool py_isinstance(const py_Ref obj, py_Type type);
bool py_issubclass(py_Type derived, py_Type base);
extern py_GlobalRef py_True;
extern py_GlobalRef py_False;
extern py_GlobalRef py_None;
/************* References *************/
#define PY_CHECK_ARGC(n) \
if(argc != n) return TypeError("expected %d arguments, got %d", n, argc)
@ -195,7 +202,7 @@ bool py_delattr(py_Ref self, py_Name name);
/// Gets the unbound method of the object.
bool py_getunboundmethod(py_Ref self, py_Name name, py_Ref out, py_Ref out_self);
bool py_getitem(const py_Ref self, const py_Ref key, py_Ref out);
bool py_getitem(const py_Ref self, const py_Ref key);
bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val);
bool py_delitem(py_Ref self, const py_Ref key);
@ -236,8 +243,6 @@ void py_pop();
void py_shrink(int n);
/// Get a temporary variable from the stack and returns the reference to it.
py_StackRef py_pushtmp();
/// Free n temporary variable.
#define py_poptmp(n) py_shrink(n)
#define py_gettop() py_peek(-1)
#define py_getsecond() py_peek(-2)
@ -262,9 +267,9 @@ void py_formatexc(char* out);
/// Check if an error is set.
bool py_checkexc();
#define KeyError(q) py_exception("KeyError", "%q", (q))
#define NameError(n) py_exception("NameError", "name '%n' is not defined", (n))
#define TypeError(...) py_exception("TypeError", __VA_ARGS__)
#define RuntimeError(...) py_exception("RuntimeError", __VA_ARGS__)
#define ValueError(...) py_exception("ValueError", __VA_ARGS__)
#define IndexError(...) py_exception("IndexError", __VA_ARGS__)
#define NotImplementedError() py_exception("NotImplementedError", "")
@ -274,6 +279,7 @@ bool py_checkexc();
py_exception("UnboundLocalError", "local variable '%n' referenced before assignment", (n))
bool StopIteration();
bool KeyError(py_Ref key);
/************* Operators *************/
/// Equivalent to `bool(val)`.
@ -338,8 +344,8 @@ void py_tuple__setitem(py_Ref self, int i, const py_Ref val);
int py_tuple__len(const py_Ref self);
// unchecked functions, if self is not a list, the behavior is undefined
py_ObjectRef py_list__data(const py_Ref self);
py_ObjectRef py_list__getitem(const py_Ref self, int i);
py_TmpRef py_list__data(const py_Ref self);
py_TmpRef py_list__getitem(const py_Ref self, int i);
void py_list__setitem(py_Ref self, int i, const py_Ref val);
void py_list__delitem(py_Ref self, int i);
int py_list__len(const py_Ref self);
@ -348,6 +354,12 @@ void py_list__clear(py_Ref self);
void py_list__insert(py_Ref self, int i, const py_Ref val);
void py_list__reverse(py_Ref self);
// unchecked functions, if self is not a dict, the behavior is undefined
py_TmpRef py_dict__getitem(const py_Ref self, const py_Ref key);
void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val);
bool py_dict__contains(const py_Ref self, const py_Ref key);
int py_dict__len(const py_Ref self);
/// Search the magic method from the given type to the base type.
/// Return the reference or NULL if not found.
py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
@ -419,6 +431,7 @@ enum py_PredefinedTypes {
tp_bytes,
tp_mappingproxy,
tp_dict,
tp_dict_items, // 1 slot
tp_property, // 2 slots (getter + setter)
tp_star_wrapper, // 1 slot + int level
tp_staticmethod, // 1 slot

View File

@ -58,6 +58,7 @@ MAGIC_METHOD(__all__)
MAGIC_METHOD(__package__)
MAGIC_METHOD(__path__)
MAGIC_METHOD(__class__)
MAGIC_METHOD(__abs__)
MAGIC_METHOD(__missing__)
#endif

View File

@ -310,8 +310,7 @@ void LiteralExpr__emit_(Expr* self_, Ctx* ctx) {
}
LiteralExpr* LiteralExpr__new(int line, const TokenValue* value) {
const static ExprVt Vt = {.emit_ = LiteralExpr__emit_,
.is_literal = true};
const static ExprVt Vt = {.emit_ = LiteralExpr__emit_, .is_literal = true};
static_assert_expr_size(LiteralExpr);
LiteralExpr* self = PoolExpr_alloc();
self->vt = &Vt;
@ -392,6 +391,35 @@ SliceExpr* SliceExpr__new(int line) {
return self;
}
typedef struct DictItemExpr {
EXPR_COMMON_HEADER
Expr* key;
Expr* value;
} DictItemExpr;
static void DictItemExpr__dtor(Expr* self_) {
DictItemExpr* self = (DictItemExpr*)self_;
vtdelete(self->key);
vtdelete(self->value);
}
static void DictItemExpr__emit_(Expr* self_, Ctx* ctx) {
DictItemExpr* self = (DictItemExpr*)self_;
vtemit_(self->key, ctx);
vtemit_(self->value, ctx);
}
static DictItemExpr* DictItemExpr__new(int line) {
const static ExprVt Vt = {.dtor = DictItemExpr__dtor, .emit_ = DictItemExpr__emit_};
static_assert_expr_size(DictItemExpr);
DictItemExpr* self = PoolExpr_alloc();
self->vt = &Vt;
self->line = line;
self->key = NULL;
self->value = NULL;
return self;
}
// ListExpr, DictExpr, SetExpr, TupleExpr
typedef struct SequenceExpr {
EXPR_COMMON_HEADER
@ -477,14 +505,12 @@ static SequenceExpr* SequenceExpr__new(int line, const ExprVt* vt, int count, Op
}
SequenceExpr* ListExpr__new(int line, int count) {
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor,
.emit_ = SequenceExpr__emit_};
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_LIST);
}
SequenceExpr* DictExpr__new(int line, int count) {
const static ExprVt DictExprVt = {.dtor = SequenceExpr__dtor,
.emit_ = SequenceExpr__emit_};
const static ExprVt DictExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
return SequenceExpr__new(line, &DictExprVt, count, OP_BUILD_DICT);
}
@ -1865,7 +1891,11 @@ static Error* exprMap(Compiler* self) {
if(curr()->type == TK_COLON) { parsing_dict = true; }
if(parsing_dict) {
consume(TK_COLON);
check(EXPR(self)); // [key, value]
check(EXPR(self)); // [key, value] -> [item]
DictItemExpr* item = DictItemExpr__new(prev()->line);
item->value = Ctx__s_popx(ctx());
item->key = Ctx__s_popx(ctx());
Ctx__s_push(ctx(), (Expr*)item);
}
count += 1; // key-value pair count
match_newlines();
@ -1884,7 +1914,6 @@ static Error* exprMap(Compiler* self) {
SequenceExpr* se;
if(count == 0 || parsing_dict) {
count *= 2; // key + value
se = DictExpr__new(line, count);
} else {
se = SetExpr__new(line, count);

View File

@ -317,15 +317,14 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
}
case OP_STORE_SUBSCR: {
// [val, a, b] -> a[b] = val
PUSH(THIRD()); // [val, a, b, val]
py_Ref magic = py_tpfindmagic(SECOND()->type, __setitem__);
if(magic) {
PUSH(THIRD()); // [val, a, b, val]
if(magic->type == tp_nativefunc) {
py_TValue* next_sp = THIRD();
py_TValue* next_sp = FOURTH();
bool ok = magic->_cfunc(3, THIRD());
if(!ok) goto __ERROR;
SP() = next_sp;
*TOP() = self->last_retval;
} else {
*FOURTH() = *magic; // [__selitem__, a, b, val]
if(!py_vectorcall(2, 0)) goto __ERROR;
@ -423,10 +422,10 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
py_Ref f = py_getdict(&self->builtins, py_name("complex"));
assert(f != NULL);
py_TValue tmp = *TOP();
*TOP() = *f; // [complex]
py_newnil(SP()++); // [complex, NULL]
py_newint(SP()++, 0); // [complex, NULL, 0]
*SP()++ = tmp; // [complex, NULL, 0, x]
*TOP() = *f; // [complex]
py_newnil(SP()++); // [complex, NULL]
py_newint(SP()++, 0); // [complex, NULL, 0]
*SP()++ = tmp; // [complex, NULL, 0, x]
if(!py_vectorcall(2, 0)) goto __ERROR;
DISPATCH();
}
@ -460,11 +459,12 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
DISPATCH();
}
case OP_BUILD_DICT: {
py_TValue* begin = SP() - byte.arg;
py_TValue* begin = SP() - byte.arg * 2;
py_Ref tmp = py_pushtmp();
py_newdict(tmp);
for(int i = 0; i < byte.arg; i += 2) {
if(!py_setitem(tmp, begin + i, begin + i + 1)) goto __ERROR;
for(int i = 0; i < byte.arg * 2; i += 2) {
py_dict__setitem(tmp, begin + i, begin + i + 1);
if(py_checkexc()) goto __ERROR;
}
SP() = begin;
PUSH(tmp);
@ -477,7 +477,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
py_push(typeobject_set);
py_pushnil();
if(!py_vectorcall(0, 0)) goto __ERROR;
py_push(py_retval()); // empty set
py_push(py_retval()); // empty set
py_Name id_add = py_name("add");
for(int i = 0; i < byte.arg; i++) {
if(!py_callmethod(TOP(), id_add, 1, begin + i)) goto __ERROR;
@ -687,7 +687,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
for(int i = size - 1; i >= 0; i--) {
PUSH(buf + i);
}
vectorcall_opcall(new_argc, new_kwargc);
DISPATCH();
}
@ -718,6 +718,10 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
DISPATCH();
}
case OP_DICT_ADD: {
// [dict, iter, key, value]
py_dict__setitem(FOURTH(), SECOND(), TOP());
if(py_checkexc()) goto __ERROR;
STACK_SHRINK(2);
DISPATCH();
}
case OP_SET_ADD: {
@ -839,7 +843,12 @@ bool pk_stack_binaryop(pk_VM* self, py_Name op, py_Name rop) {
if(self->last_retval.type != tp_NotImplementedType) return true;
}
}
// eq/ne op never fails due to object.__eq__
// eq/ne op never fails
if(op == __eq__ || op == __ne__) {
bool res = py_isidentical(SECOND(), TOP());
py_newbool(py_retval(), res);
return true;
}
return py_exception("TypeError", "unsupported operand type(s) for '%n'", op);
}

View File

@ -117,6 +117,8 @@ void pk_VM__ctor(pk_VM* self) {
validate(tp_mappingproxy, pk_newtype("mappingproxy", tp_object, NULL, NULL, false, true));
validate(tp_dict, pk_dict__register());
validate(tp_dict_items, pk_dict_items__register());
validate(tp_property, pk_newtype("property", tp_object, NULL, NULL, false, true));
validate(tp_star_wrapper, pk_newtype("star_wrapper", tp_object, NULL, NULL, false, true));
@ -330,9 +332,12 @@ static bool
co->name->data);
} else {
// add to **kwargs
// Dict& dict = _CAST(Dict&, vkwargs);
// dict.set(this, VAR(key.sv()), kwargs[j + 1]);
assert(false);
py_Ref tmp = py_pushtmp();
c11_sv key_sv = py_name2sv(key);
py_newstrn(tmp, key_sv.data, key_sv.size);
py_dict__setitem(&buffer[decl->starred_kwarg], tmp, &p1[2 * j + 1]);
if(py_checkexc()) return false;
py_pop();
}
}
}
@ -467,36 +472,31 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo
memcpy(self->stack.sp, argv, span * sizeof(py_TValue));
self->stack.sp += span;
// [new_f, cls, args..., kwargs...]
pk_FrameResult res = pk_VM__vectorcall(self, argc, kwargc, false);
if(res == RES_ERROR) return RES_ERROR;
assert(res == RES_RETURN);
if(pk_VM__vectorcall(self, argc, kwargc, false) == RES_ERROR) return RES_ERROR;
// by recursively using vectorcall, args and kwargs are consumed
// [cls, NULL, args..., kwargs...]
// try __init__
// NOTE: previous we use `get_unbound_method` but here we just use `tpfindmagic`
// NOTE: previously we use `get_unbound_method` but here we just use `tpfindmagic`
// >> [cls, NULL, args..., kwargs...]
// >> py_retval() is the new instance
py_Ref init_f = py_tpfindmagic(py_totype(p0), __init__);
if(init_f) {
// do an inplace patch
*p0 = *init_f; // __init__
p0[1] = self->last_retval; // self
// [__init__, self, args..., kwargs...]
pk_FrameResult res = pk_VM__vectorcall(self, argc, kwargc, false);
if(res == RES_ERROR) return RES_ERROR;
assert(res == RES_RETURN);
} else {
// manually reset the stack
self->stack.sp = p0;
if(pk_VM__vectorcall(self, argc, kwargc, false) == RES_ERROR) return RES_ERROR;
*py_retval() = p0[1]; // restore the new instance
}
// reset the stack
self->stack.sp = p0;
return RES_RETURN;
}
// handle `__call__` overload
if(py_getunboundmethod(p0, __call__, p0, p0 + 1)) {
// [__call__, self, args..., kwargs...]
pk_FrameResult res = pk_VM__vectorcall(self, argc, kwargc, false);
if(res == RES_ERROR) return RES_ERROR;
assert(res == RES_RETURN);
return pk_VM__vectorcall(self, argc, kwargc, opcall);
}
TypeError("'%t' object is not callable", p0->type);
@ -525,13 +525,6 @@ static void mark_object(PyObject* obj) {
if(obj->gc_marked) return;
obj->gc_marked = true;
// list is a special case
if(obj->type == tp_list) {
c11_vector* vec = PyObject__userdata(obj);
c11__foreach(py_TValue, vec, p) mark_value(p);
return;
}
if(obj->slots > 0) {
py_TValue* p = PyObject__slots(obj);
for(int i = 0; i < obj->slots; i++)
@ -547,6 +540,16 @@ static void mark_object(PyObject* obj) {
}
return;
}
if(obj->type == tp_list) {
pk_list__mark(PyObject__userdata(obj), mark_value);
return;
}
if(obj->type == tp_dict) {
pk_dict__mark(PyObject__userdata(obj), mark_value);
return;
}
}
void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
@ -573,6 +576,7 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) {
return;
py_TValue* sp = self->stack.sp;
c11_sbuf buf;
c11_sbuf__ctor(&buf);

View File

@ -1,276 +0,0 @@
#include "pocketpy/objects/dict.h"
#include "pocketpy/common/utils.h"
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.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 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)
struct pkpy_DictEntry {
py_TValue key;
py_TValue val;
};
inline extern int pkpy_Dict__idx_size(const pkpy_Dict* self) {
#if PK_DICT_COMPACT_MODE
if(self->_htcap < 255) return 1;
if(self->_htcap < 65535) return 2;
#endif
return 4;
}
inline extern int pkpy_Dict__idx_null(const pkpy_Dict* self) {
#if PK_DICT_COMPACT_MODE
if(self->_htcap < 255) return 255;
if(self->_htcap < 65535) return 65535;
#endif
return -1;
}
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;
c11_vector__ctor(&self->_entries, sizeof(struct pkpy_DictEntry));
self->_htcap = 16;
self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
}
void pkpy_Dict__dtor(pkpy_Dict* self) {
c11_vector__dtor(&self->_entries);
free(self->_hashtable);
}
pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self) {
int ht_size = pkpy_Dict__ht_byte_size(self);
void* ht_clone = malloc(ht_size);
memcpy(ht_clone, self->_hashtable, ht_size);
return (pkpy_Dict){.count = self->count,
._entries = c11_vector__copy(&self->_entries),
._htcap = self->_htcap,
._hashtable = ht_clone};
}
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)));
return (*p >> ((loc & 3) * 8)) & pkpy_Dict__idx_null(self);
#else
return ((const int*)self->_hashtable)[h];
#endif
}
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)));
const int shift = (loc & 3) * 8;
*p = (v << shift) | (*p & ~(pkpy_Dict__idx_null(self) << shift));
#else
((int*)self->_hashtable)[h] = v;
#endif
}
static int pkpy_Dict__probe0(const pkpy_Dict* self, py_TValue key, int hash) {
const int null = pkpy_Dict__idx_null(self);
const int mask = self->_htcap - 1;
for(int h = hash & mask;; h = DICT_HASH_NEXT(h) & mask) {
int idx = pkpy_Dict__htget(self, h);
if(idx == null) return h;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
if(pkpy_Var__is_null(&entry->key)) return h;
}
c11__unreachedable();
}
static int pkpy_Dict__probe1(const pkpy_Dict* self, py_TValue key, int hash) {
const int null = pkpy_Dict__idx_null(self);
const int mask = self->_htcap - 1;
for(int h = hash & mask;; h = DICT_HASH_NEXT(h) & mask) {
int idx = pkpy_Dict__htget(self, h);
if(idx == null) return h;
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
if(pkpy_Var__is_null(&entry->key)) continue;
if(py_eq(&entry->key, &key)) return h;
}
c11__unreachedable();
}
static void pkpy_Dict__extendht(pkpy_Dict* self) {
free(self->_hashtable);
self->_htcap *= 2;
self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
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;
int64_t out;
int err = py_hash(&entry->key, &out);
int rhash = DICT_HASH_TRANS(out);
int h = pkpy_Dict__probe0(self, entry->key, rhash);
pkpy_Dict__htset(self, h, i);
}
}
bool pkpy_Dict__set(pkpy_Dict* self, py_TValue key, py_TValue val) {
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)) {
idx = self->_entries.count;
c11_vector__push(struct pkpy_DictEntry,
&self->_entries,
((struct pkpy_DictEntry){
.key = key,
.val = val,
}));
h = pkpy_Dict__probe0(self, key, hash);
pkpy_Dict__htset(self, h, idx);
self->count += 1;
if(self->count >= self->_htcap * DICT_MAX_LOAD) pkpy_Dict__extendht(self);
return true;
}
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
if(py_eq(&entry->key, &key)) {
entry->val = val;
} else {
self->count += 1;
h = pkpy_Dict__probe0(self, key, hash);
idx = pkpy_Dict__htget(self, h);
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
entry->key = key;
entry->val = val;
}
return false;
}
bool pkpy_Dict__contains(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 false;
return true;
}
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;
// shrink
// free(self->_hashtable);
// while(self->_htcap * DICT_MAX_LOAD / 2 > self->count && self->_htcap >= 32)
// self->_htcap /= 2;
// self->_hashtable = malloc(pkpy_Dict__ht_byte_size(self));
memset(self->_hashtable, 0xff, pkpy_Dict__ht_byte_size(self));
int new_cnt = 0;
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);
new_cnt += 1;
}
self->_entries.count = new_cnt;
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;
int64_t out;
py_hash(&entry->key, &out);
int rhash = DICT_HASH_TRANS(out);
int h = pkpy_Dict__probe0(self, entry->key, rhash);
pkpy_Dict__htset(self, h, i);
}
return true;
}
bool pkpy_Dict__del(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), 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;
pkpy_Dict__refactor(self);
return true;
}
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) {
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;
pkpy_Dict__set(self, entry->key, entry->val);
}
}
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) {
struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_entries, idx);
if(!pkpy_Var__is_null(&entry->key)) break;
idx++;
}
return idx;
}
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) {
if(self->_index >= self->_dict->_entries.count) return false;
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;
self->_index = pkpy_Dict__next_entry_idx(self->_dict, self->_index + 1);
return true;
}

View File

@ -47,4 +47,10 @@ bool py_exception(const char* name, const char* fmt, ...) {
vm->last_exception = *py_retval();
return false;
}
bool KeyError(py_Ref key){
if(!py_repr(key)) return false;
c11_sv message = py_tosv(py_retval());
return py_exception("KeyError", "%q", message);
}

View File

@ -12,7 +12,7 @@ py_Ref py_getmodule(const char* name) {
py_Ref py_newmodule(const char* name, const char* package) {
pk_ManagedHeap* heap = &pk_current_vm->heap;
PyObject* obj = pk_ManagedHeap__gcnew(heap, tp_module, -1, 0);
PyObject* obj = pk_ManagedHeap__new(heap, tp_module, -1, 0);
py_Ref r0 = py_pushtmp();
py_Ref r1 = py_pushtmp();
@ -48,7 +48,7 @@ py_Ref py_newmodule(const char* name, const char* package) {
if(exists) abort();
pk_NameDict__set(&pk_current_vm->modules, py_name(name), *r0);
py_poptmp(2);
py_shrink(2);
return py_getmodule(name);
}
@ -148,6 +148,36 @@ static bool _py_builtins__sorted(int argc, py_Ref argv) {
return true;
}
static bool _py_builtins__hash(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_i64 val;
if(!py_hash(argv, &val)) return false;
py_newint(py_retval(), val);
return true;
}
static bool _py_builtins__abs(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return py_callmagic(__abs__, 1, argv);
}
static bool _py_builtins__sum(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
int length;
py_TValue* p = pk_arrayview(argv, &length);
if(!p) return TypeError("sum() expects a list or tuple");
py_Ref tmp = py_pushtmp();
py_newint(tmp, 0);
for(int i = 0; i < length; i++) {
if(!py_binaryadd(tmp, &p[i])) return false;
*tmp = *py_retval();
}
*py_retval() = *tmp;
return true;
}
py_TValue pk_builtins__register() {
py_Ref builtins = py_newmodule("builtins", NULL);
py_bindnativefunc(builtins, "repr", _py_builtins__repr);
@ -157,6 +187,9 @@ py_TValue pk_builtins__register() {
py_bindnativefunc(builtins, "hex", _py_builtins__hex);
py_bindnativefunc(builtins, "iter", _py_builtins__iter);
py_bindnativefunc(builtins, "next", _py_builtins__next);
py_bindnativefunc(builtins, "hash", _py_builtins__hash);
py_bindnativefunc(builtins, "abs", _py_builtins__abs);
py_bindnativefunc(builtins, "sum", _py_builtins__sum);
py_bind(builtins, "sorted(iterable, key=None, reverse=False)", _py_builtins__sorted);
return *builtins;

View File

@ -33,17 +33,15 @@ int pk_arrayeq(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length) {
return true;
}
static bool _py_array_iterator__new__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
bool pk_arrayiter(py_Ref val) {
int length;
py_TValue* p = pk_arrayview(py_arg(1), &length);
if(!p) return TypeError("expected list or tuple, got %t", py_arg(1)->type);
py_TValue* p = pk_arrayview(val, &length);
if(!p) 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;
ud->index = 0;
// keep a reference to the object
py_setslot(py_retval(), 0, py_arg(1));
py_setslot(py_retval(), 0, val); // keep a reference to the object
return true;
}
@ -65,10 +63,7 @@ static bool _py_array_iterator__next__(int argc, py_Ref argv) {
py_Type pk_array_iterator__register() {
py_Type type = pk_newtype("array_iterator", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __new__, _py_array_iterator__new__);
py_bindmagic(type, __iter__, _py_array_iterator__iter__);
py_bindmagic(type, __next__, _py_array_iterator__next__);
return type;
}
}

View File

@ -1,26 +1,39 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h"
#define PK_DICT_MAX_COLLISION 3
typedef struct {
py_i64 hash;
py_TValue key;
py_TValue val;
} DictEntry;
typedef struct {
int _[PK_DICT_MAX_COLLISION];
} DictIndex;
typedef struct {
int length;
int capacity;
int* indices;
DictIndex* indices;
c11_vector /*T=DictEntry*/ entries;
} Dict;
static void Dict__ctor(Dict* self) {
typedef struct {
DictEntry* curr;
DictEntry* end;
} DictIterator;
static void Dict__ctor(Dict* self, int capacity) {
self->length = 0;
self->capacity = 16;
self->indices = malloc(self->capacity * sizeof(int));
memset(self->indices, -1, self->capacity * sizeof(int));
self->capacity = capacity;
self->indices = malloc(self->capacity * sizeof(DictIndex));
memset(self->indices, -1, self->capacity * sizeof(DictIndex));
c11_vector__ctor(&self->entries, sizeof(DictEntry));
}
@ -31,75 +44,489 @@ static void Dict__dtor(Dict* self) {
c11_vector__dtor(&self->entries);
}
static bool Dict__probe(Dict* self, py_Ref key, DictEntry* out) {
static bool Dict__try_get(Dict* self, py_TValue* key, DictEntry** out) {
py_i64 hash;
if(!py_hash(key, &hash)) return false;
int mask = self->capacity - 1;
for(int idx = hash & mask;; idx = (idx + 1) & mask) {
int idx2 = self->indices[idx];
DictEntry* slot = c11__at(DictEntry, &self->entries, idx2);
if(slot){
int res = py_eq(key, &slot->key);
if(res == -1) return false;
return res;
}else{
int idx = hash & (self->capacity - 1);
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) continue;
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
int res = py_eq(&entry->key, key);
if(res == 1) {
*out = entry;
return true;
}
if(res == -1) return false; // error
}
*out = NULL;
return true;
}
static void Dict__clear(Dict* self) {
memset(self->indices, -1, self->capacity * sizeof(DictIndex));
c11_vector__clear(&self->entries);
self->length = 0;
}
static void Dict__rehash_2x(Dict* self) {
Dict old_dict = *self;
int new_capacity = self->capacity * 2;
assert(new_capacity <= 1073741824);
do {
Dict__ctor(self, new_capacity);
for(int i = 0; i < old_dict.entries.count; i++) {
DictEntry* entry = c11__at(DictEntry, &old_dict.entries, i);
if(py_isnil(&entry->key)) continue;
int idx = entry->hash & (new_capacity - 1);
bool success = false;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) {
// insert new entry (empty slot)
c11_vector__push(DictEntry, &self->entries, *entry);
self->indices[idx]._[i] = self->entries.count - 1;
self->length++;
success = true;
break;
}
}
if(!success) {
Dict__dtor(self);
new_capacity *= 2;
continue;
}
}
// resize complete
Dict__dtor(&old_dict);
return;
} while(1);
}
static void Dict__compact_entries(Dict* self) {
int* mappings = malloc(self->entries.count * sizeof(int));
int n = 0;
for(int i = 0; i < self->entries.count; i++) {
DictEntry* entry = c11__at(DictEntry, &self->entries, i);
if(py_isnil(&entry->key)) continue;
mappings[i] = n;
if(i != n) {
DictEntry* new_entry = c11__at(DictEntry, &self->entries, n);
*new_entry = *entry;
}
n++;
}
self->entries.count = n;
// update indices
for(int i = 0; i < self->capacity; i++) {
for(int j = 0; j < PK_DICT_MAX_COLLISION; j++) {
int idx = self->indices[i]._[j];
if(idx == -1) continue;
self->indices[i]._[j] = mappings[idx];
}
}
free(mappings);
}
static bool Dict__set(Dict* self, py_TValue* key, py_TValue* val) {
py_i64 hash;
if(!py_hash(key, &hash)) return false;
int idx = hash & (self->capacity - 1);
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) {
// insert new entry
DictEntry* new_entry = c11_vector__emplace(&self->entries);
new_entry->hash = hash;
new_entry->key = *key;
new_entry->val = *val;
self->indices[idx]._[i] = self->entries.count - 1;
self->length++;
return true;
}
// update existing entry
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
int res = py_eq(&entry->key, key);
if(res == 1) {
entry->val = *val;
return true;
}
if(res == -1) return false; // error
}
// no empty slot found
Dict__rehash_2x(self);
return Dict__set(self, key, val);
}
/// Delete an entry from the dict.
/// If the key is found, `py_retval()` is set to the value.
/// If the key is not found, `py_retval()` is set to `nil`.
/// Returns false on error.
static bool Dict__pop(Dict* self, py_Ref key) {
py_i64 hash;
if(!py_hash(key, &hash)) return false;
int idx = hash & (self->capacity - 1);
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) continue;
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
int res = py_eq(&entry->key, key);
if(res == 1) {
*py_retval() = entry->val;
py_newnil(&entry->key);
self->indices[idx]._[i] = -1;
self->length--;
if(self->length < self->entries.count / 2) Dict__compact_entries(self);
return true;
}
if(res == -1) return false; // error
}
py_newnil(py_retval());
return true;
}
static void DictIterator__ctor(DictIterator* self, Dict* dict) {
self->curr = dict->entries.data;
self->end = self->curr + dict->entries.count;
}
static DictEntry* DictIterator__next(DictIterator* self) {
DictEntry* retval;
do {
if(self->curr == self->end) return NULL;
retval = self->curr++;
} while(py_isnil(&retval->key));
return retval;
}
///////////////////////////////
static bool _py_dict__new__(int argc, py_Ref argv) {
py_newdict(py_retval());
return true;
}
static bool _py_dict__getitem__(int argc, py_Ref argv){
PY_CHECK_ARGC(2);
py_i64 hash;
if(!py_hash(py_arg(1), &hash)) return false;
static bool _py_dict__init__(int argc, py_Ref argv) {
if(argc > 2) return TypeError("dict.__init__() takes at most 2 arguments (%d given)", argc);
if(argc == 1) return true;
assert(argc == 2);
PY_CHECK_ARG_TYPE(1, tp_list);
Dict* self = py_touserdata(argv);
py_Ref list = py_arg(1);
for(int i = 0; i < py_list__len(list); i++) {
py_Ref tuple = py_list__getitem(list, i);
if(!py_istuple(tuple) || py_tuple__len(tuple) != 2) {
return TypeError("dict.__init__() argument must be a list of tuple-2");
}
py_Ref key = py_tuple__getitem(tuple, 0);
py_Ref val = py_tuple__getitem(tuple, 1);
if(!Dict__set(self, key, val)) return false;
}
return true;
}
static bool _py_dict__setitem__(int argc, py_Ref argv){
static bool _py_dict__getitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
Dict* self = py_touserdata(argv);
DictEntry* entry;
if(!Dict__try_get(self, py_arg(1), &entry)) return false;
if(entry) {
*py_retval() = entry->val;
return true;
}
return KeyError(py_arg(1));
}
static bool _py_dict__setitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
py_i64 hash;
if(!py_hash(py_arg(1), &hash)) return false;
Dict* self = py_touserdata(argv);
return Dict__set(self, py_arg(1), py_arg(2));
}
static bool _py_dict__delitem__(int argc, py_Ref argv){
static bool _py_dict__delitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
py_i64 hash;
if(!py_hash(py_arg(1), &hash)) return false;
Dict* self = py_touserdata(argv);
if(!Dict__pop(self, py_arg(1))) return false;
py_newnone(py_retval());
return true;
}
static bool _py_dict__contains__(int argc, py_Ref argv){
static bool _py_dict__contains__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
py_i64 hash;
if(!py_hash(py_arg(1), &hash)) return false;
Dict* self = py_touserdata(argv);
DictEntry* entry;
if(!Dict__try_get(self, py_arg(1), &entry)) return false;
py_newbool(py_retval(), entry != NULL);
return true;
}
static bool _py_dict__len__(int argc, py_Ref argv){
static bool _py_dict__len__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
py_newint(py_retval(), self->length);
return true;
}
static bool _py_dict__repr__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
c11_sbuf buf;
c11_sbuf__ctor(&buf);
c11_sbuf__write_char(&buf, '{');
bool is_first = true;
for(int i = 0; i < self->entries.count; i++) {
DictEntry* entry = c11__at(DictEntry, &self->entries, i);
if(py_isnil(&entry->key)) continue;
if(!is_first) c11_sbuf__write_cstr(&buf, ", ");
if(!py_repr(&entry->key)) return false;
c11_sbuf__write_sv(&buf, py_tosv(py_retval()));
c11_sbuf__write_cstr(&buf, ": ");
if(!py_repr(&entry->val)) return false;
c11_sbuf__write_sv(&buf, py_tosv(py_retval()));
is_first = false;
}
c11_sbuf__write_char(&buf, '}');
c11_string* res = c11_sbuf__submit(&buf);
py_newstrn(py_retval(), res->data, res->size);
c11_string__delete(res);
return true;
}
static bool _py_dict__eq__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
Dict* self = py_touserdata(py_arg(0));
if(!py_isdict(py_arg(1))) {
py_newnotimplemented(py_retval());
return true;
}
Dict* other = py_touserdata(py_arg(1));
if(self->length != other->length) {
py_newbool(py_retval(), false);
return true;
}
DictIterator iter;
DictIterator__ctor(&iter, self);
// for each self key
while(1) {
DictEntry* entry = DictIterator__next(&iter);
if(!entry) break;
DictEntry* other_entry;
if(!Dict__try_get(other, &entry->key, &other_entry)) return false;
if(!other_entry) {
py_newbool(py_retval(), false);
return true;
}
int res = py_eq(&entry->val, &other_entry->val);
if(res == -1) return false;
if(!res) {
py_newbool(py_retval(), false);
return true;
}
}
return true;
}
static bool _py_dict__ne__(int argc, py_Ref argv) {
if(!_py_dict__eq__(argc, argv)) return false;
if(py_isbool(py_retval())) {
bool res = py_tobool(py_retval());
py_newbool(py_retval(), !res);
}
return true;
}
static bool _py_dict__clear(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
Dict__clear(self);
return true;
}
static bool _py_dict__copy(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
Dict* new_dict = py_newobject(py_retval(), tp_dict, 0, sizeof(Dict));
new_dict->capacity = self->capacity;
new_dict->length = self->length;
new_dict->entries = c11_vector__copy(&self->entries);
// copy indices
new_dict->indices = malloc(new_dict->capacity * sizeof(DictIndex));
memcpy(new_dict->indices, self->indices, new_dict->capacity * sizeof(DictIndex));
return true;
}
static bool _py_dict__update(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_dict);
Dict* self = py_touserdata(argv);
Dict* other = py_touserdata(py_arg(1));
for(int i = 0; i < other->entries.count; i++) {
DictEntry* entry = c11__at(DictEntry, &other->entries, i);
if(py_isnil(&entry->key)) continue;
if(!Dict__set(self, &entry->key, &entry->val)) return false;
}
return true;
}
static bool _py_dict__get(int argc, py_Ref argv) {
Dict* self = py_touserdata(argv);
if(argc > 3) return TypeError("get() takes at most 3 arguments (%d given)", argc);
py_Ref default_val = argc == 3 ? py_arg(2) : py_None;
DictEntry* entry;
if(!Dict__try_get(self, py_arg(1), &entry)) return false;
*py_retval() = entry ? entry->val : *default_val;
return true;
}
static bool _py_dict__pop(int argc, py_Ref argv) {
Dict* self = py_touserdata(argv);
if(argc < 2 || argc > 3) return TypeError("pop() takes 2 or 3 arguments (%d given)", argc);
py_Ref default_val = argc == 3 ? py_arg(2) : py_None;
if(!Dict__pop(self, py_arg(1))) return false;
if(py_isnil(py_retval())) *py_retval() = *default_val;
return true;
}
static bool _py_dict__items(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
DictIterator* ud = py_newobject(py_retval(), tp_dict_items, 1, sizeof(DictIterator));
DictIterator__ctor(ud, self);
py_setslot(py_retval(), 0, argv); // keep a reference to the dict
return true;
}
static bool _py_dict__keys(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
py_newtuple(py_retval(), self->length);
DictIterator iter;
DictIterator__ctor(&iter, self);
int i = 0;
while(1) {
DictEntry* entry = DictIterator__next(&iter);
if(!entry) break;
py_tuple__setitem(py_retval(), i++, &entry->key);
}
assert(i == self->length);
return true;
}
static bool _py_dict__values(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
py_newtuple(py_retval(), self->length);
DictIterator iter;
DictIterator__ctor(&iter, self);
int i = 0;
while(1) {
DictEntry* entry = DictIterator__next(&iter);
if(!entry) break;
py_tuple__setitem(py_retval(), i++, &entry->val);
}
assert(i == self->length);
return true;
}
py_Type pk_dict__register() {
py_Type type = pk_newtype("dict", tp_object, NULL, (void (*)(void*))Dict__dtor, false, false);
py_bindmagic(type, __new__, _py_dict__new__);
// py_bindmagic(type, __init__, _py_dict__init__);
py_bindmagic(type, __init__, _py_dict__init__);
py_bindmagic(type, __getitem__, _py_dict__getitem__);
py_bindmagic(type, __setitem__, _py_dict__setitem__);
py_bindmagic(type, __delitem__, _py_dict__delitem__);
py_bindmagic(type, __contains__, _py_dict__contains__);
py_bindmagic(type, __len__, _py_dict__len__);
py_bindmagic(type, __repr__, _py_dict__repr__);
py_bindmagic(type, __eq__, _py_dict__eq__);
py_bindmagic(type, __ne__, _py_dict__ne__);
py_bindmethod(type, "clear", _py_dict__clear);
py_bindmethod(type, "copy", _py_dict__copy);
py_bindmethod(type, "update", _py_dict__update);
py_bindmethod(type, "get", _py_dict__get);
py_bindmethod(type, "pop", _py_dict__pop);
py_bindmethod(type, "items", _py_dict__items);
py_bindmethod(type, "keys", _py_dict__keys);
py_bindmethod(type, "values", _py_dict__values);
py_setdict(py_tpobject(type), __hash__, py_None);
return type;
}
//////////////////////////
static bool _py_dict_items__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
*py_retval() = *argv;
return true;
}
static bool _py_dict_items__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
DictIterator* iter = py_touserdata(py_arg(0));
DictEntry* entry = (DictIterator__next(iter));
if(entry) {
py_newtuple(py_retval(), 2);
py_tuple__setitem(py_retval(), 0, &entry->key);
py_tuple__setitem(py_retval(), 1, &entry->val);
return true;
}
return StopIteration();
}
py_Type pk_dict_items__register() {
py_Type type = pk_newtype("dict_items", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __iter__, _py_dict_items__iter__);
py_bindmagic(type, __next__, _py_dict_items__next__);
return type;
}
//////////////////////////
void py_newdict(py_Ref out) {
Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict));
Dict__ctor(ud);
Dict__ctor(ud, 8);
}
py_Ref py_dict__getitem(const py_Ref self, const py_Ref key) {
assert(py_isdict(self));
Dict* ud = py_touserdata(self);
DictEntry* entry;
if(!Dict__try_get(ud, key, &entry)) return NULL;
if(entry) return &entry->val;
return NULL;
}
void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val) {
assert(py_isdict(self));
Dict* ud = py_touserdata(self);
Dict__set(ud, key, val);
}
bool py_dict__contains(const py_Ref self, const py_Ref key) {
assert(py_isdict(self));
Dict* ud = py_touserdata(self);
DictEntry* entry;
bool ok = Dict__try_get(ud, key, &entry);
return ok && entry != NULL;
}
int py_dict__len(const py_Ref self) {
assert(py_isdict(self));
Dict* ud = py_touserdata(self);
return ud->length;
}
void pk_dict__mark(void* ud, void (*marker)(py_TValue*)) {
Dict* self = ud;
for(int i = 0; i < self->entries.count; i++) {
DictEntry* entry = c11__at(DictEntry, &self->entries, i);
if(py_isnil(&entry->key)) continue;
marker(&entry->key);
marker(&entry->val);
}
}

View File

@ -88,10 +88,11 @@ static bool _py_list__eq__(int argc, py_Ref argv) {
}
static bool _py_list__ne__(int argc, py_Ref argv) {
bool ok = _py_list__eq__(argc, argv);
if(!ok) return false;
py_Ref retval = py_retval();
py_newbool(retval, !py_tobool(retval));
if(!_py_list__eq__(argc, argv)) return false;
if(py_isbool(py_retval())) {
bool res = py_tobool(py_retval());
py_newbool(py_retval(), !res);
}
return true;
}
@ -396,7 +397,7 @@ static bool _py_list__sort(int argc, py_Ref argv) {
static bool _py_list__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return py_tpcall(tp_array_iterator, 1, argv);
return pk_arrayiter(argv);
}
py_Type pk_list__register() {
@ -429,5 +430,14 @@ py_Type pk_list__register() {
py_bindmethod(type, "sort", _py_list__sort);
py_bind(py_tpobject(type), "sort(self, key=None, reverse=False)", _py_list__sort);
py_setdict(py_tpobject(type), __hash__, py_None);
return type;
}
void pk_list__mark(void* ud, void (*marker)(py_TValue*)){
List* self = ud;
for(int i = 0; i < self->count; i++) {
marker(c11__at(py_TValue, self, i));
}
}

View File

@ -241,6 +241,20 @@ static bool _py_float__hash__(int argc, py_Ref argv) {
return true;
}
static bool _py_int__abs__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_i64 val = py_toint(&argv[0]);
py_newint(py_retval(), val < 0 ? -val : val);
return true;
}
static bool _py_float__abs__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_f64 val = py_tofloat(&argv[0]);
py_newfloat(py_retval(), val < 0 ? -val : val);
return true;
}
static bool _py_int__new__(int argc, py_Ref argv) {
if(argc == 1 + 0) {
// int() == 0
@ -431,6 +445,10 @@ void pk_number__register() {
py_bindmagic(tp_int, __hash__, _py_int__hash__);
py_bindmagic(tp_float, __hash__, _py_float__hash__);
// __abs__
py_bindmagic(tp_int, __abs__, _py_int__abs__);
py_bindmagic(tp_float, __abs__, _py_float__abs__);
// __new__
py_bindmagic(tp_int, __new__, _py_int__new__);
py_bindmagic(tp_float, __new__, _py_float__new__);

View File

@ -48,6 +48,7 @@ bool py_hash(const py_Ref val, int64_t* out) {
pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data;
do {
py_Ref _hash = &types[t].magic[__hash__];
if(py_isnone(_hash)) break;
py_Ref _eq = &types[t].magic[__eq__];
if(!py_isnil(_hash) && !py_isnil(_eq)) {
bool ok = py_call(_hash, 1, val);
@ -82,11 +83,30 @@ bool py_setattr(py_Ref self, py_Name name, const py_Ref val) { return false; }
bool py_delattr(py_Ref self, py_Name name) { return false; }
bool py_getitem(const py_Ref self, const py_Ref key, py_Ref out) { return -1; }
bool py_getitem(const py_Ref self, const py_Ref key) {
py_push(self);
py_push(key);
bool ok = py_callmagic(__getitem__, 2, py_peek(-2));
py_shrink(2);
return ok;
}
bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val) { return -1; }
bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val) {
py_push(self);
py_push(key);
py_push(val);
bool ok = py_callmagic(__setitem__, 3, py_peek(-3));
py_shrink(3);
return ok;
}
bool py_delitem(py_Ref self, const py_Ref key) { return -1; }
bool py_delitem(py_Ref self, const py_Ref key) {
py_push(self);
py_push(key);
bool ok = py_callmagic(__delitem__, 2, py_peek(-2));
py_shrink(2);
return ok;
}
#define COMPARE_OP_IMPL(name, op, rop) \
int py_##name(const py_Ref lhs, const py_Ref rhs) { \

View File

@ -162,7 +162,10 @@ static bool _py_str__repr__(int argc, py_Ref argv) {
static bool _py_str__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return py_tpcall(tp_str_iterator, 1, argv);
int* ud = py_newobject(py_retval(), tp_str_iterator, 1, sizeof(int));
*ud = 0;
py_setslot(py_retval(), 0, argv); // keep a reference to the string
return true;
}
static bool _py_str__getitem__(int argc, py_Ref argv) {
@ -504,15 +507,6 @@ py_Type pk_str__register() {
return type;
}
static bool _py_str_iterator__new__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_str);
int* ud = py_newobject(py_retval(), tp_str_iterator, 1, sizeof(int));
*ud = 0;
py_setslot(py_retval(), 0, &argv[1]);
return true;
}
static bool _py_str_iterator__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
*py_retval() = argv[0];
@ -535,7 +529,6 @@ static bool _py_str_iterator__next__(int argc, py_Ref argv) {
py_Type pk_str_iterator__register() {
py_Type type = pk_newtype("str_iterator", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __new__, _py_str_iterator__new__);
py_bindmagic(type, __iter__, _py_str_iterator__iter__);
py_bindmagic(type, __next__, _py_str_iterator__next__);
return type;

View File

@ -113,16 +113,17 @@ static bool _py_tuple__eq__(int argc, py_Ref argv) {
}
static bool _py_tuple__ne__(int argc, py_Ref argv) {
bool ok = _py_tuple__eq__(argc, argv);
if(!ok) return false;
py_Ref retval = py_retval();
py_newbool(retval, !py_tobool(retval));
if(!_py_tuple__eq__(argc, argv)) return false;
if(py_isbool(py_retval())) {
bool res = py_tobool(py_retval());
py_newbool(py_retval(), !res);
}
return true;
}
static bool _py_tuple__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return py_tpcall(tp_array_iterator, 1, argv);
return pk_arrayiter(argv);
}
py_Type pk_tuple__register() {

View File

@ -10,12 +10,27 @@
#include <stdint.h>
pk_VM* pk_current_vm;
py_GlobalRef py_True;
py_GlobalRef py_False;
py_GlobalRef py_None;
static pk_VM pk_default_vm;
void py_initialize() {
pk_MemoryPools__initialize();
py_Name__initialize();
pk_current_vm = &pk_default_vm;
// initialize some convenient references
static py_TValue _True, _False, _None;
py_newbool(&_True, true);
py_newbool(&_False, false);
py_newnone(&_None);
py_True = &_True;
py_False = &_False;
py_None = &_None;
pk_VM__ctor(&pk_default_vm);
}
@ -190,14 +205,11 @@ bool py_call(py_Ref f, int argc, py_Ref argv) {
if(f->type == tp_nativefunc) {
return f->_cfunc(argc, argv);
} else {
pk_VM* vm = pk_current_vm;
py_push(f);
py_pushnil();
for(int i = 0; i < argc; i++)
py_push(py_offset(argv, i));
pk_FrameResult res = pk_VM__vectorcall(vm, argc, 0, false);
assert(res == RES_ERROR || res == RES_RETURN);
return res == RES_RETURN;
return py_vectorcall(argc, 0);
}
}

6
tests/00_tmp.py Normal file
View File

@ -0,0 +1,6 @@
# dict delete test
data = []
j = 6
for i in range(65535):
j = ((j*5+1) % 65535)
data.append(str(j))

View File

@ -107,6 +107,10 @@ assert 1 < 2 < 3
assert 4 > 3 >= 3
assert not 1 < 2 > 3
assert abs(0) == 0
assert abs(1) == 1
assert abs(-1) == 1
# try:
# 1 // 0
# exit(1)

View File

@ -83,6 +83,10 @@ assert -2e-3 == -0.002
assert 3.4e-3 == 0.0034
assert 3.4e+3 == 3400.0
assert abs(1.0) == 1.0
assert abs(-1.0) == 1.0
assert abs(0.0) == 0.0
# import math
# assert math.isnan(0/0)

View File

@ -5,12 +5,15 @@ assert tinydict['Name'] == 'Tom';assert tinydict['Age'] == 7
tinydict['Age'] = 8;tinydict['School'] = "aaa"
assert tinydict['Age'] == 8;assert tinydict['School'] == "aaa"
del tinydict['Name']
assert repr(tinydict) == "{'Age': 8, 'Class': 'First', 'School': 'aaa'}"
assert len(tinydict) == 3
tinydict.clear()
assert len(tinydict) == 0
dict1 = {'user':'circle','num':[1,2,3]}
dict2 = dict1.copy()
for k,v in dict1.items():
assert dict2[k] == v
for t in dict1.items():
@ -62,20 +65,6 @@ assert a == {3: 4}
assert a.pop(3) == 4
assert a == {}
# unpacking builder
a = {1:2, 3:4}
b = {**a, 5:6, **a}
assert b == {1: 2, 3: 4, 5: 6}
a = {}
b = {**a, 1:2, 3:4}
assert b == {1: 2, 3: 4}
a = {1:2, 3:4, 7:8}
b = {**a, 1:5, 3:6}
c = {**a, **b}
assert c == {1: 5, 3: 6, 7: 8}
a = {}
for i in range(1000):
a[i] = i
@ -84,15 +73,6 @@ for i in range(1000):
del a[i]
assert len(a) == 0
a = {
str(i): i
for i in range(10)
}
for i, s in enumerate(a):
assert s == str(i)
a = {'g': 0}
a['ball_3'] = 0
@ -130,35 +110,11 @@ for i in range(len(data)):
y = b.pop()
del a[y]
# namedict delete test
class A: pass
a = A()
b = ['0', '1']
for i in range(len(data)):
z = data[i]
setattr(a, str(z), i)
b.append(z)
if i % 3 == 0:
y = b.pop()
delattr(a, y)
a = {1: 2, 3: 4}
assert a.pop(1) == 2
try:
a.pop(1)
exit(1)
except KeyError:
pass
assert a.pop(1, None) is None
try:
a.pop(1, 2, 3)
exit(1)
except TypeError:
pass
n = 2 ** 17
a = {}
for i in range(n):
@ -170,13 +126,15 @@ for i in range(n):
for i in range(n):
del a[str(i)]
a = {1: 2, 3: 4}
a['a'] = a
assert repr(a) == "{1: 2, 3: 4, 'a': {...}}"
# namedict delete test
# class A: pass
# a = A()
# b = ['0', '1']
# test gc
import gc
gc.collect()
for k, v in a.items():
pass
assert gc.collect() == 1
# for i in range(len(data)):
# z = data[i]
# setattr(a, str(z), i)
# b.append(z)
# if i % 3 == 0:
# y = b.pop()
# delattr(a, y)

View File

@ -1,3 +1,6 @@
a = {i: j for i, j in [(1, 2), (3, 4)]}
assert a == {1: 2, 3: 4}
a = {i: i**2 for i in range(10)}
assert a == {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
@ -5,4 +8,12 @@ a = {i: i**2 for i in range(10) if i % 2 == 0}
assert a == {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
b = {k:v for k,v in a.items()}
assert b == a
assert b == a
# a = {
# str(i): i
# for i in range(10)
# }
# for i, s in enumerate(a):
# assert s == str(i)