mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
reimpl dict
This commit is contained in:
parent
42b4c56543
commit
1a4b88829c
@ -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();
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -58,6 +58,7 @@ MAGIC_METHOD(__all__)
|
||||
MAGIC_METHOD(__package__)
|
||||
MAGIC_METHOD(__path__)
|
||||
MAGIC_METHOD(__class__)
|
||||
MAGIC_METHOD(__abs__)
|
||||
MAGIC_METHOD(__missing__)
|
||||
|
||||
#endif
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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__);
|
||||
|
@ -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) { \
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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
6
tests/00_tmp.py
Normal 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))
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
Loading…
x
Reference in New Issue
Block a user