Merge branch 'pocketpy:main' into divmod

This commit is contained in:
ykiko 2024-03-29 02:31:37 +08:00 committed by GitHub
commit 7f3bd09f29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 216 additions and 157 deletions

View File

@ -14,5 +14,4 @@ A double-ended queue.
### `collections.defaultdict`
A `dict` wrapper that calls a factory function to supply missing values.
It is not a subclass of `dict`.
A dictionary that returns a default value when a key is not found.

View File

@ -3,7 +3,7 @@ output: .retype
url: https://pocketpy.dev
branding:
title: pocketpy
label: v1.4.3
label: v1.4.4
logo: "./static/logo.png"
favicon: "./static/logo.png"
meta:

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@
#include <typeinfo>
#include <initializer_list>
#define PK_VERSION "1.4.3"
#define PK_VERSION "1.4.4"
#include "config.h"
#include "export.h"

View File

@ -105,6 +105,7 @@ struct CodeEmitContext{
void exit_block();
void emit_expr(); // clear the expression stack and generate bytecode
int emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual=false);
void revert_last_emit_();
int emit_int(i64 value, int line);
void patch_jump(int index);
bool add_label(StrName name);
@ -113,6 +114,7 @@ struct CodeEmitContext{
int add_const_string(std::string_view);
int add_func_decl(FuncDecl_ decl);
void emit_store_name(NameScope scope, StrName name, int line);
void try_merge_for_iter_store(int);
};
struct NameExpr: Expr{

View File

@ -133,6 +133,9 @@ OPCODE(UNARY_INVERT)
/**************************/
OPCODE(GET_ITER)
OPCODE(FOR_ITER)
OPCODE(FOR_ITER_STORE_FAST)
OPCODE(FOR_ITER_STORE_GLOBAL)
OPCODE(FOR_ITER_YIELD_VALUE)
/**************************/
OPCODE(IMPORT_PATH)
OPCODE(POP_IMPORT_STAR)

View File

@ -224,6 +224,7 @@ const StrName __all__ = StrName::get("__all__");
const StrName __package__ = StrName::get("__package__");
const StrName __path__ = StrName::get("__path__");
const StrName __class__ = StrName::get("__class__");
const StrName __missing__ = StrName::get("__missing__");
const StrName pk_id_add = StrName::get("add");
const StrName pk_id_set = StrName::get("set");

View File

@ -82,7 +82,6 @@ struct PyTypeInfo{
void (*m__setattr__)(VM* vm, PyObject*, StrName, PyObject*) = nullptr;
PyObject* (*m__getattr__)(VM* vm, PyObject*, StrName) = nullptr;
bool (*m__delattr__)(VM* vm, PyObject*, StrName) = nullptr;
};
typedef void(*PrintFunc)(const char*, int);

View File

@ -7,63 +7,19 @@ def Counter(iterable):
a[x] = 1
return a
class defaultdict:
def __init__(self, default_factory) -> None:
class defaultdict(dict):
def __init__(self, default_factory, *args):
super().__init__(*args)
self._enable_instance_dict()
self.default_factory = default_factory
self._a = {}
def __getitem__(self, key):
if key not in self._a:
self._a[key] = self.default_factory()
return self._a[key]
def __setitem__(self, key, value):
self._a[key] = value
def __delitem__(self, key):
del self._a[key]
def __missing__(self, key):
self[key] = self.default_factory()
return self[key]
def __repr__(self) -> str:
return f"defaultdict({self.default_factory}, {self._a})"
def __eq__(self, __o: object) -> bool:
if not isinstance(__o, defaultdict):
return False
if self.default_factory != __o.default_factory:
return False
return self._a == __o._a
def __iter__(self):
return iter(self._a)
def __contains__(self, key):
return key in self._a
def __len__(self):
return len(self._a)
def keys(self):
return self._a.keys()
def values(self):
return self._a.values()
def items(self):
return self._a.items()
def pop(self, *args):
return self._a.pop(*args)
def clear(self):
self._a.clear()
return f"defaultdict({self.default_factory}, {super().__repr__()})"
def copy(self):
new_dd = defaultdict(self.default_factory)
new_dd._a = self._a.copy()
return new_dd
def get(self, key, default):
return self._a.get(key, default)
def update(self, other):
self._a.update(other)
return defaultdict(self.default_factory, self)

View File

@ -719,9 +719,34 @@ __NEXT_STEP:;
if(_0 != StopIteration){
PUSH(_0);
}else{
frame->jump_abs_break(&s_data, byte.arg);
frame->jump_abs_break(&s_data, co->_get_block_codei(frame->_ip).end);
}
} DISPATCH();
TARGET(FOR_ITER_STORE_FAST){
PyObject* _0 = py_next(TOP());
if(_0 != StopIteration){
frame->_locals[byte.arg] = _0;
}else{
frame->jump_abs_break(&s_data, co->_get_block_codei(frame->_ip).end);
}
} DISPATCH()
TARGET(FOR_ITER_STORE_GLOBAL){
PyObject* _0 = py_next(TOP());
if(_0 != StopIteration){
frame->f_globals().set(StrName(byte.arg), _0);
}else{
frame->jump_abs_break(&s_data, co->_get_block_codei(frame->_ip).end);
}
} DISPATCH()
TARGET(FOR_ITER_YIELD_VALUE){
PyObject* _0 = py_next(TOP());
if(_0 != StopIteration){
PUSH(_0);
return PY_OP_YIELD;
}else{
frame->jump_abs_break(&s_data, co->_get_block_codei(frame->_ip).end);
}
} DISPATCH()
/*****************************************/
TARGET(IMPORT_PATH){
PyObject* _0 = co->consts[byte.arg];
@ -877,8 +902,16 @@ __NEXT_STEP:;
*p = VAR(CAST(i64, *p) - 1);
} DISPATCH();
/*****************************************/
static_assert(OP_DEC_GLOBAL == 133);
case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: PK_UNREACHABLE() break;
static_assert(OP_DEC_GLOBAL == 136);
case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149:
case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164:
case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179:
case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194:
case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209:
case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224:
case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239:
case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254:
case 255: break;
}
}

View File

@ -50,15 +50,13 @@ namespace pkpy{
// json mode does not contain jump instructions, so it is safe to ignore this check
SyntaxError("maximum number of opcodes exceeded");
}
// pre-compute LOOP_BREAK and LOOP_CONTINUE and FOR_ITER
// pre-compute LOOP_BREAK and LOOP_CONTINUE
for(int i=0; i<codes.size(); i++){
Bytecode& bc = codes[i];
if(bc.op == OP_LOOP_CONTINUE){
bc.arg = ctx()->co->blocks[bc.arg].start;
}else if(bc.op == OP_LOOP_BREAK){
bc.arg = ctx()->co->blocks[bc.arg].get_break_end();
}else if(bc.op == OP_FOR_ITER){
bc.arg = ctx()->co->_get_block_codei(i).end;
}
}
// pre-compute func->is_simple
@ -658,9 +656,10 @@ __EAT_DOTS_END:
EXPR_TUPLE(); ctx()->emit_expr();
ctx()->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
CodeBlock* block = ctx()->enter_block(CodeBlockType::FOR_LOOP);
ctx()->emit_(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
int for_codei = ctx()->emit_(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
bool ok = vars->emit_store(ctx());
if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind
ctx()->try_merge_for_iter_store(for_codei);
compile_block_body();
ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true);
ctx()->exit_block();
@ -822,8 +821,7 @@ __EAT_DOTS_END:
ctx()->co->is_generator = true;
ctx()->emit_(OP_GET_ITER, BC_NOARG, kw_line);
ctx()->enter_block(CodeBlockType::FOR_LOOP);
ctx()->emit_(OP_FOR_ITER, BC_NOARG, kw_line);
ctx()->emit_(OP_YIELD_VALUE, BC_NOARG, kw_line);
ctx()->emit_(OP_FOR_ITER_YIELD_VALUE, BC_NOARG, kw_line);
ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), kw_line);
ctx()->exit_block();
consume_end_stmt();

View File

@ -60,6 +60,31 @@ namespace pkpy{
return i;
}
void CodeEmitContext::revert_last_emit_(){
co->codes.pop_back();
co->iblocks.pop_back();
co->lines.pop_back();
}
void CodeEmitContext::try_merge_for_iter_store(int i){
// [FOR_ITER, STORE_?, ]
if(co->codes[i].op != OP_FOR_ITER) return;
if(co->codes.size() - i != 2) return;
uint16_t arg = co->codes[i+1].arg;
if(co->codes[i+1].op == OP_STORE_FAST){
revert_last_emit_();
co->codes[i].op = OP_FOR_ITER_STORE_FAST;
co->codes[i].arg = arg;
return;
}
if(co->codes[i+1].op == OP_STORE_GLOBAL){
revert_last_emit_();
co->codes[i].op = OP_FOR_ITER_STORE_GLOBAL;
co->codes[i].arg = arg;
return;
}
}
int CodeEmitContext::emit_int(i64 value, int line){
bool allow_neg_int = is_negative_shift_well_defined() || value >= 0;
if(allow_neg_int && value >= -5 && value <= 16){
@ -370,10 +395,11 @@ namespace pkpy{
iter->emit_(ctx);
ctx->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
ctx->enter_block(CodeBlockType::FOR_LOOP);
ctx->emit_(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
int for_codei = ctx->emit_(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
bool ok = vars->emit_store(ctx);
// this error occurs in `vars` instead of this line, but...nevermind
PK_ASSERT(ok); // TODO: raise a SyntaxError instead
ctx->try_merge_for_iter_store(for_codei);
if(cond){
cond->emit_(ctx);
int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);

View File

@ -77,7 +77,7 @@ void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
buffer_size = size;
}
unsigned char* buffer = new unsigned char[buffer_size];
size_t actual_size = io_fread(buffer, 1, buffer_size, io.fp);
i64 actual_size = io_fread(buffer, 1, buffer_size, io.fp);
PK_ASSERT(actual_size <= buffer_size);
// in text mode, CR may be dropped, which may cause `actual_size < buffer_size`
Bytes b(buffer, actual_size);

View File

@ -399,31 +399,37 @@ void init_builtins(VM* _vm) {
if(args.size() == 1+0) return VAR(0);
// 1 arg
if(args.size() == 1+1){
if (is_type(args[1], vm->tp_float)) return VAR((i64)CAST(f64, args[1]));
if (is_type(args[1], vm->tp_int)) return args[1];
if (is_type(args[1], vm->tp_bool)) return VAR(_CAST(bool, args[1]) ? 1 : 0);
switch(vm->_tp(args[1]).index){
case VM::tp_float.index:
return VAR((i64)_CAST(f64, args[1]));
case VM::tp_int.index:
return args[1];
case VM::tp_bool.index:
return VAR(args[1]==vm->True ? 1 : 0);
case VM::tp_str.index:
break;
default:
vm->TypeError("invalid arguments for int()");
}
}
// 2+ args -> error
if(args.size() > 1+2) vm->TypeError("int() takes at most 2 arguments");
// 2 args
if (is_type(args[1], vm->tp_str)) {
int base = 10;
if(args.size() == 1+2) base = CAST(i64, args[2]);
const Str& s = CAST(Str&, args[1]);
std::string_view sv = s.sv();
bool negative = false;
if(!sv.empty() && (sv[0] == '+' || sv[0] == '-')){
negative = sv[0] == '-';
sv.remove_prefix(1);
}
i64 val;
if(parse_int(sv, &val, base) != IntParsingResult::Success){
vm->ValueError(_S("invalid literal for int() with base ", base, ": ", s.escape()));
}
if(negative) val = -val;
return VAR(val);
// 1 or 2 args with str
int base = 10;
if(args.size() == 1+2) base = CAST(i64, args[2]);
const Str& s = CAST(Str&, args[1]);
std::string_view sv = s.sv();
bool negative = false;
if(!sv.empty() && (sv[0] == '+' || sv[0] == '-')){
negative = sv[0] == '-';
sv.remove_prefix(1);
}
vm->TypeError("invalid arguments for int()");
return vm->None;
i64 val;
if(parse_int(sv, &val, base) != IntParsingResult::Success){
vm->ValueError(_S("invalid literal for int() with base ", base, ": ", s.escape()));
}
if(negative) val = -val;
return VAR(val);
});
_vm->bind__floordiv__(VM::tp_int, [](VM* vm, PyObject* _0, PyObject* _1) {
@ -438,6 +444,14 @@ void init_builtins(VM* _vm) {
return VAR(_CAST(i64, _0) % rhs);
});
_vm->bind_method<0>(VM::tp_int, "bit_length", [](VM* vm, ArgsView args) {
i64 x = _CAST(i64, args[0]);
if(x < 0) x = -x;
int bits = 0;
while(x){ x >>= 1; bits++; }
return VAR(bits);
});
_vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(std::to_string(_CAST(i64, obj))); });
_vm->bind__neg__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(-_CAST(i64, obj)); });
_vm->bind__hash__(VM::tp_int, [](VM* vm, PyObject* obj) { return _CAST(i64, obj); });
@ -460,26 +474,32 @@ void init_builtins(VM* _vm) {
if(args.size() == 1+0) return VAR(0.0);
if(args.size() > 1+1) vm->TypeError("float() takes at most 1 argument");
// 1 arg
if (is_type(args[1], vm->tp_int)) return VAR((f64)CAST(i64, args[1]));
if (is_type(args[1], vm->tp_float)) return args[1];
if (is_type(args[1], vm->tp_bool)) return VAR(_CAST(bool, args[1]) ? 1.0 : 0.0);
if (is_type(args[1], vm->tp_str)) {
const Str& s = CAST(Str&, args[1]);
if(s == "inf") return VAR(INFINITY);
if(s == "-inf") return VAR(-INFINITY);
double float_out;
char* p_end;
try{
float_out = std::strtod(s.data, &p_end);
PK_ASSERT(p_end == s.end());
}catch(...){
vm->ValueError("invalid literal for float(): " + s.escape());
}
return VAR(float_out);
switch(vm->_tp(args[1]).index){
case VM::tp_int.index:
return VAR((f64)CAST(i64, args[1]));
case VM::tp_float.index:
return args[1];
case VM::tp_bool.index:
return VAR(args[1]==vm->True ? 1.0 : 0.0);
case VM::tp_str.index:
break;
default:
vm->TypeError("invalid arguments for float()");
}
vm->TypeError("invalid arguments for float()");
return vm->None;
// str to float
const Str& s = PK_OBJ_GET(Str, args[1]);
if(s == "inf") return VAR(INFINITY);
if(s == "-inf") return VAR(-INFINITY);
double float_out;
char* p_end;
try{
float_out = std::strtod(s.data, &p_end);
PK_ASSERT(p_end == s.end());
}catch(...){
vm->ValueError("invalid literal for float(): " + s.escape());
}
return VAR(float_out);
});
_vm->bind__hash__(VM::tp_float, [](VM* vm, PyObject* _0) {
@ -1216,37 +1236,53 @@ void init_builtins(VM* _vm) {
// tp_dict
_vm->bind_constructor<-1>(_vm->_t(VM::tp_dict), [](VM* vm, ArgsView args){
return VAR(Dict(vm));
Type cls_t = PK_OBJ_GET(Type, args[0]);
return vm->heap.gcnew<Dict>(cls_t, vm);
});
_vm->bind_method<-1>(VM::tp_dict, "__init__", [](VM* vm, ArgsView args){
if(args.size() == 1+0) return vm->None;
if(args.size() == 1+1){
auto _lock = vm->heap.gc_scope_lock();
Dict& self = _CAST(Dict&, args[0]);
List& list = CAST(List&, args[1]);
for(PyObject* item : list){
Tuple& t = CAST(Tuple&, item);
if(t.size() != 2){
vm->ValueError("dict() takes an iterable of tuples (key, value)");
return vm->None;
Dict& self = PK_OBJ_GET(Dict, args[0]);
if(is_non_tagged_type(args[1], vm->tp_dict)){
Dict& other = CAST(Dict&, args[1]);
self.update(other);
return vm->None;
}
if(is_non_tagged_type(args[1], vm->tp_list)){
List& list = PK_OBJ_GET(List, args[1]);
for(PyObject* item : list){
Tuple& t = CAST(Tuple&, item);
if(t.size() != 2){
vm->ValueError("dict() takes an iterable of tuples (key, value)");
return vm->None;
}
self.set(t[0], t[1]);
}
self.set(t[0], t[1]);
}
return vm->None;
}
vm->TypeError("dict() takes at most 1 argument");
return vm->None;
PK_UNREACHABLE()
});
_vm->bind__len__(VM::tp_dict, [](VM* vm, PyObject* _0) {
return (i64)_CAST(Dict&, _0).size();
return (i64)PK_OBJ_GET(Dict, _0).size();
});
_vm->bind__getitem__(VM::tp_dict, [](VM* vm, PyObject* _0, PyObject* _1) {
Dict& self = _CAST(Dict&, _0);
Dict& self = PK_OBJ_GET(Dict, _0);
PyObject* ret = self.try_get(_1);
if(ret == nullptr) vm->KeyError(_1);
if(ret == nullptr){
// try __missing__
PyObject* self;
PyObject* f_missing = vm->get_unbound_method(_0, __missing__, &self, false);
if(f_missing != nullptr){
return vm->call_method(self, f_missing, _1);
}
vm->KeyError(_1);
}
return ret;
});
@ -1360,7 +1396,7 @@ void init_builtins(VM* _vm) {
_vm->bind__eq__(VM::tp_dict, [](VM* vm, PyObject* _0, PyObject* _1) {
Dict& self = _CAST(Dict&, _0);
if(!is_non_tagged_type(_1, vm->tp_dict)) return vm->NotImplemented;
if(!vm->isinstance(_1, vm->tp_dict)) return vm->NotImplemented;
Dict& other = _CAST(Dict&, _1);
if(self.size() != other.size()) return vm->False;
for(int i=0; i<self._capacity; i++){
@ -1597,4 +1633,4 @@ CodeObject_ VM::compile(std::string_view source, const Str& filename, CompileMod
}
}
} // namespace pkpy
} // namespace pkpy

View File

@ -573,10 +573,10 @@ static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){
case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL:
case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR:
case OP_BEGIN_CLASS: case OP_GOTO:
case OP_DELETE_GLOBAL: case OP_INC_GLOBAL: case OP_DEC_GLOBAL: case OP_STORE_CLASS_ATTR:
case OP_DELETE_GLOBAL: case OP_INC_GLOBAL: case OP_DEC_GLOBAL: case OP_STORE_CLASS_ATTR: case OP_FOR_ITER_STORE_GLOBAL:
argStr += _S(" (", StrName(byte.arg).sv(), ")").sv();
break;
case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST: case OP_INC_FAST: case OP_DEC_FAST:
case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST: case OP_INC_FAST: case OP_DEC_FAST: case OP_FOR_ITER_STORE_FAST:
argStr += _S(" (", co->varnames[byte.arg].sv(), ")").sv();
break;
case OP_LOAD_FUNCTION:
@ -594,7 +594,7 @@ Str VM::disassemble(CodeObject_ co){
pod_vector<int> jumpTargets;
for(auto byte : co->codes){
if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE || byte.op == OP_SHORTCUT_IF_FALSE_OR_POP || byte.op == OP_FOR_ITER){
if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE || byte.op == OP_SHORTCUT_IF_FALSE_OR_POP){
jumpTargets.push_back(byte.arg);
}
if(byte.op == OP_GOTO){
@ -714,7 +714,7 @@ void VM::init_builtin_types(){
if(tp_exception != _new_type_object("Exception", 0, true)) exit(-3);
if(tp_bytes != _new_type_object("bytes")) exit(-3);
if(tp_mappingproxy != _new_type_object("mappingproxy")) exit(-3);
if(tp_dict != _new_type_object("dict")) exit(-3);
if(tp_dict != _new_type_object("dict", 0, true)) exit(-3); // dict can be subclassed
if(tp_property != _new_type_object("property")) exit(-3);
if(tp_star_wrapper != _new_type_object("_star_wrapper")) exit(-3);
@ -1301,7 +1301,7 @@ void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){
PyObject* obj = _t(type);
_all_types[type].m__hash__ = f;
PyObject* nf = bind_method<0>(obj, "__hash__", [](VM* vm, ArgsView args){
i64 ret = lambda_get_userdata<i64(*)(VM*, PyObject*)>(args.begin())(vm, args[0]);
i64 ret = lambda_get_userdata<decltype(f)>(args.begin())(vm, args[0]);
return VAR(ret);
});
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);
@ -1311,7 +1311,7 @@ void VM::bind__len__(Type type, i64 (*f)(VM*, PyObject*)){
PyObject* obj = _t(type);
_all_types[type].m__len__ = f;
PyObject* nf = bind_method<0>(obj, "__len__", [](VM* vm, ArgsView args){
i64 ret = lambda_get_userdata<i64(*)(VM*, PyObject*)>(args.begin())(vm, args[0]);
i64 ret = lambda_get_userdata<decltype(f)>(args.begin())(vm, args[0]);
return VAR(ret);
});
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);

View File

@ -52,7 +52,23 @@ assert x == 3
assert str(1) == '1'
assert repr(1) == '1'
# test bit_length
assert (1).bit_length() == 1
assert (2).bit_length() == 2
assert (3).bit_length() == 2
assert (-1).bit_length() == 1
assert (-2).bit_length() == 2
assert (-3).bit_length() == 2
assert (123123123123123).bit_length() == 47
assert (-3123123123).bit_length() == 32
# test int()
assert int() == 0
assert int(True) == 1
assert int(False) == 0
assert int(1) == 1
assert int(1.0) == 1
assert int(1.1) == 1

View File

@ -36,6 +36,12 @@ assert str(1.0) == '1.0'
assert repr(1.0) == '1.0'
# test float()
assert float() == 0.0
assert float(True) == 1.0
assert float(False) == 0.0
assert float(1) == 1.0
assert float(-2) == -2.0
assert eq(float(1), 1.0)
assert eq(float(1.0), 1.0)
assert eq(float(1.1), 1.1)

View File

@ -2,15 +2,15 @@ from collections import Counter, deque, defaultdict
import random
import pickle
import gc
import builtins
dd_dict_keys = sorted(defaultdict.__dict__.keys())
d_dict_keys = sorted(dict.__dict__.keys())
d_dict_keys.remove('__new__')
if dd_dict_keys != d_dict_keys:
print("dd_dict_keys:", dd_dict_keys)
print("d_dict_keys:", d_dict_keys)
raise Exception("dd_dict_keys != d_dict_keys")
# test defaultdict
assert issubclass(defaultdict, dict)
a = defaultdict(int)
a['1'] += 1
assert a == {'1': 1}
a = defaultdict(list)
a['1'].append(1)
assert a == {'1': [1]}
q = deque()
q.append(1)

View File

@ -160,22 +160,6 @@ class A():
repr(A())
# 未完全测试准确性-----------------------------------------------
# 33600: 318: _vm->bind_constructor<-1>("range", [](VM* vm, ArgsView args) {
# 16742: 319: args._begin += 1; // skip cls
# 16742: 320: Range r;
# 16742: 321: switch (args.size()) {
# 8735: 322: case 1: r.stop = CAST(i64, args[0]); break;
# 3867: 323: case 2: r.start = CAST(i64, args[0]); r.stop = CAST(i64, args[1]); break;
# 4140: 324: case 3: r.start = CAST(i64, args[0]); r.stop = CAST(i64, args[1]); r.step = CAST(i64, args[2]); break;
# #####: 325: default: vm->TypeError("expected 1-3 arguments, got " + std::to_string(args.size()));
# #####: 326: }
# 33484: 327: return VAR(r);
# 16742: 328: });
# -: 329:
# test range:
try:
range(1,2,3,4)
print('未能拦截错误, 在测试 range')