diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 169d81e3..d6df9c5e 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -38,7 +38,7 @@ typedef struct Frame { struct Frame* f_back; const Bytecode* ip; const CodeObject* co; - PyObject* module; + py_TValue module; // weak ref PyObject* function; // a function object or NULL (global scope) py_TValue* p0; // unwinding base py_TValue* locals; // locals base @@ -47,7 +47,7 @@ typedef struct Frame { } Frame; Frame* Frame__new(const CodeObject* co, - PyObject* module, + py_TValue* module, const py_TValue* function, py_TValue* p0, py_TValue* locals, @@ -66,12 +66,6 @@ PK_INLINE int Frame__iblock(const Frame* self) { return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).iblock; } -PK_INLINE pk_NameDict* Frame__f_globals(Frame* self) { return PyObject__dict(self->module); } - -PK_INLINE py_TValue* Frame__f_globals_try_get(Frame* self, py_Name name) { - return pk_NameDict__try_get(Frame__f_globals(self), name); -} - PK_INLINE py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name) { return FastLocals__try_get_by_name(self->locals, self->locals_co, name); } diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 1140ec7d..1a628994 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -23,7 +23,7 @@ typedef struct pk_TypeInfo { c11_vector /*T=py_Name*/ annotated_fields; - py_CFunction on_end_subclass; // backdoor for enum module + void (*on_end_subclass)(struct pk_TypeInfo*); // backdoor for enum module /* Magic Slots */ py_TValue magic[64]; @@ -49,7 +49,7 @@ typedef struct pk_VM { py_TValue reg[8]; // users' registers - py_TValue __curr_class; + py_TValue* __curr_class; FuncDecl_ __dynamic_func_decl; py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES]; diff --git a/include/pocketpy/objects/base.h b/include/pocketpy/objects/base.h index 82bd9aac..9686d60d 100644 --- a/include/pocketpy/objects/base.h +++ b/include/pocketpy/objects/base.h @@ -36,8 +36,6 @@ typedef struct py_TValue { static_assert(sizeof(py_CFunction) <= 8, "sizeof(py_CFunction) > 8"); static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16"); -extern py_TValue PY_NIL; - #ifdef __cplusplus } #endif diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index ff7dbe1d..146711a9 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -137,13 +137,13 @@ FuncDecl_ FuncDecl__build(c11_sv name, // runtime function typedef struct Function { FuncDecl_ decl; - PyObject* module; // weak ref + py_TValue module; // weak ref PyObject* clazz; // weak ref pk_NameDict* closure; // strong ref py_CFunction cfunc; // wrapped C function } Function; -void Function__ctor(Function* self, FuncDecl_ decl, PyObject* module); +void Function__ctor(Function* self, FuncDecl_ decl, py_TValue* module); void Function__dtor(Function* self); #ifdef __cplusplus diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index f49aebae..d2b16d8e 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -132,6 +132,7 @@ bool py_issubclass(py_Type derived, py_Type base); extern py_GlobalRef py_True; extern py_GlobalRef py_False; extern py_GlobalRef py_None; +extern py_GlobalRef py_NIL; /************* References *************/ #define PY_CHECK_ARGC(n) \ @@ -183,6 +184,7 @@ py_GlobalRef py_reg(int i); /// Returns a reference to the value or NULL if not found. py_ObjectRef py_getdict(const py_Ref self, py_Name name); void py_setdict(py_Ref self, py_Name name, const py_Ref val); +bool py_deldict(py_Ref self, py_Name name); /// Get the reference of the i-th slot of the object. /// The object must have slots and `i` must be in range. diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index 74bff974..d5d36f6b 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -96,8 +96,6 @@ OPCODE(UNPACK_EX) OPCODE(BEGIN_CLASS) OPCODE(END_CLASS) OPCODE(STORE_CLASS_ATTR) -OPCODE(BEGIN_CLASS_DECORATION) -OPCODE(END_CLASS_DECORATION) OPCODE(ADD_CLASS_ANNOTATION) /**************************/ OPCODE(WITH_ENTER) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 7c3183ce..5c07c924 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1291,6 +1291,7 @@ static void Ctx__exit_block(Ctx* self) { } static void Ctx__s_emit_decorators(Ctx* self, int count) { + if(count == 0) return; assert(Ctx__s_size(self) >= count); // [obj] for(int i = 0; i < count; i++) { @@ -2252,7 +2253,7 @@ static Error* read_literal(Compiler* self, py_Ref out) { } return NULL; } - default: *out = PY_NIL; return NULL; + default: py_newnil(out); return NULL; } } @@ -2346,6 +2347,37 @@ static Error* compile_function(Compiler* self, int decorators) { return NULL; } +static Error* compile_class(Compiler* self, int decorators) { + Error* err; + consume(TK_ID); + py_Name name = py_namev(Token__sv(prev())); + bool has_base = false; + if(match(TK_LPAREN)) { + if(is_expression(self, false)) { + check(EXPR(self)); + has_base = true; // [base] + } + consume(TK_RPAREN); + } + if(!has_base) { + Ctx__emit_(ctx(), OP_LOAD_NONE, BC_NOARG, prev()->line); + } else { + Ctx__s_emit_top(ctx()); // [] + } + Ctx__emit_(ctx(), OP_BEGIN_CLASS, name, BC_KEEPLINE); + + c11__foreach(Ctx, &self->contexts, it) { + if(it->is_compiling_class) return SyntaxError("nested class is not allowed"); + } + ctx()->is_compiling_class = true; + check(compile_block_body(self, compile_stmt)); + ctx()->is_compiling_class = false; + + Ctx__s_emit_decorators(ctx(), decorators); + Ctx__emit_(ctx(), OP_END_CLASS, name, BC_KEEPLINE); + return NULL; +} + static Error* compile_decorated(Compiler* self) { Error* err; int count = 0; @@ -2356,7 +2388,7 @@ static Error* compile_decorated(Compiler* self) { } while(match(TK_DECORATOR)); if(match(TK_CLASS)) { - // check(compile_class(count)); + check(compile_class(self, count)); } else { consume(TK_DEF); check(compile_function(self, count)); @@ -2467,8 +2499,7 @@ static Error* compile_try_except(Compiler* self) { static Error* compile_stmt(Compiler* self) { Error* err; if(match(TK_CLASS)) { - // check(compile_class()); - assert(false); + check(compile_class(self, 0)); return NULL; } advance(); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index e07d357b..cfb0b8b1 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -143,7 +143,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { case OP_LOAD_FUNCTION: { FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg); Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function)); - Function__ctor(ud, decl, frame->module); + Function__ctor(ud, decl, &frame->module); if(decl->nested) { ud->closure = FastLocals__to_namedict(frame->locals, frame->locals_co); py_Name name = py_namev(c11_string__sv(decl->code.name)); @@ -182,7 +182,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { PUSH(tmp); DISPATCH(); } - tmp = Frame__f_globals_try_get(frame, name); + tmp = py_getdict(&frame->module, name); if(tmp != NULL) { PUSH(tmp); DISPATCH(); @@ -202,7 +202,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { PUSH(tmp); DISPATCH(); } - tmp = Frame__f_globals_try_get(frame, name); + tmp = py_getdict(&frame->module, name); if(tmp != NULL) { PUSH(tmp); DISPATCH(); @@ -217,7 +217,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { } case OP_LOAD_GLOBAL: { py_Name name = byte.arg; - py_Ref tmp = Frame__f_globals_try_get(frame, name); + py_Ref tmp = py_getdict(&frame->module, name); if(tmp != NULL) { PUSH(tmp); DISPATCH(); @@ -238,14 +238,13 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { DISPATCH(); } case OP_LOAD_CLASS_GLOBAL: { - assert(self->__curr_class.type); py_Name name = byte.arg; - if(py_getattr(&self->__curr_class, name, SP())) { + if(py_getattr(self->__curr_class, name, SP())) { SP()++; DISPATCH(); } // load global if attribute not found - py_Ref tmp = Frame__f_globals_try_get(frame, name); + py_Ref tmp = py_getdict(&frame->module, name); if(tmp) { PUSH(tmp); DISPATCH(); @@ -294,12 +293,11 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { } case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH(); case OP_STORE_NAME: { - py_Name _name = byte.arg; - py_TValue _0 = POPX(); + py_Name name = byte.arg; if(frame->function) { - py_Ref slot = Frame__f_locals_try_get(frame, _name); + py_Ref slot = Frame__f_locals_try_get(frame, name); if(slot != NULL) { - *slot = _0; // store in locals if possible + *slot = *TOP(); // store in locals if possible } else { // Function& func = frame->_callable->as(); // if(func.decl == __dynamic_func_decl) { @@ -311,14 +309,16 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { // } } } else { - pk_NameDict__set(Frame__f_globals(frame), _name, _0); + py_setdict(&frame->module, name, TOP()); } + POP(); DISPATCH(); } - case OP_STORE_GLOBAL: - pk_NameDict__set(Frame__f_globals(frame), byte.arg, POPX()); + case OP_STORE_GLOBAL: { + py_setdict(&frame->module, byte.arg, TOP()); + POP(); DISPATCH(); - + } case OP_STORE_ATTR: { int err = py_setattr(TOP(), byte.arg, SECOND()); if(err) goto __ERROR; @@ -370,8 +370,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { // } } } else { - // if(!frame->f_globals().del(_name)) vm->NameError(_name); - bool ok = pk_NameDict__del(Frame__f_globals(frame), name); + bool ok = py_deldict(&frame->module, name); if(!ok) { NameError(name); goto __ERROR; @@ -381,7 +380,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { } case OP_DELETE_GLOBAL: { py_Name name = byte.arg; - bool ok = pk_NameDict__del(Frame__f_globals(frame), name); + bool ok = py_deldict(&frame->module, name); if(!ok) { NameError(name); goto __ERROR; @@ -794,7 +793,58 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { } DISPATCH(); } + /////////// + case OP_BEGIN_CLASS: { + // [base] + py_Name name = byte.arg; + py_Type base; + if(py_isnone(TOP())) { + base = tp_object; + } else { + if(!py_checktype(TOP(), tp_type)) goto __ERROR; + base = py_totype(TOP()); + } + POP(); + py_Type type = py_newtype(py_name2str(name), base, &frame->module, NULL); + PUSH(py_tpobject(type)); + self->__curr_class = TOP(); + DISPATCH(); + } + case OP_END_CLASS: { + // [cls or decorated] + py_Name name = byte.arg; + // set into f_globals + py_setdict(&frame->module, name, TOP()); + if(py_istype(TOP(), tp_type)) { + // call on_end_subclass + pk_TypeInfo* ti = c11__at(pk_TypeInfo, &self->types, py_totype(TOP())); + if(ti->base != tp_object) { + // PyTypeInfo* base_ti = &_all_types[ti->base]; + pk_TypeInfo* base_ti = c11__at(pk_TypeInfo, &self->types, ti->base); + if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti); + } + } + POP(); + self->__curr_class = NULL; + DISPATCH(); + } + case OP_STORE_CLASS_ATTR: { + py_Name name = byte.arg; + if(py_istype(TOP(), tp_function)) { + Function* ud = py_touserdata(TOP()); + ud->clazz = self->__curr_class->_obj; + } + py_setdict(self->__curr_class, name, TOP()); + POP(); + DISPATCH(); + } + case OP_ADD_CLASS_ANNOTATION: { + py_Type type = py_totype(self->__curr_class); + pk_TypeInfo* ti = c11__at(pk_TypeInfo, &self->types, type); + c11_vector__push(py_Name, &ti->annotated_fields, byte.arg); + DISPATCH(); + } /////////// case OP_RAISE_ASSERT: { if(byte.arg) { diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 3d27d58c..80e7099f 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -34,7 +34,7 @@ UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset) { void UnwindTarget__delete(UnwindTarget* self) { free(self); } Frame* Frame__new(const CodeObject* co, - PyObject* module, + py_TValue* module, const py_TValue* function, py_TValue* p0, py_TValue* locals, @@ -44,7 +44,7 @@ Frame* Frame__new(const CodeObject* co, self->f_back = NULL; self->ip = (Bytecode*)co->codes.data - 1; self->co = co; - self->module = module; + self->module = *module; self->function = function ? function->_obj : NULL; self->p0 = p0; self->locals = locals; diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index c1bfdce5..8f2abab7 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -47,7 +47,7 @@ static void pk_TypeInfo__ctor(pk_TypeInfo* self, ._obj = typeobj, }; - self->module = module ? *module : PY_NIL; + self->module = module ? *module : *py_NIL; c11_vector__ctor(&self->annotated_fields, sizeof(py_Name)); } @@ -59,19 +59,19 @@ void pk_VM__ctor(pk_VM* self) { pk_NameDict__ctor(&self->modules); c11_vector__ctor(&self->types, sizeof(pk_TypeInfo)); - self->builtins = PY_NIL; - self->main = PY_NIL; + self->builtins = *py_NIL; + self->main = *py_NIL; self->_ceval_on_step = NULL; self->_import_file = pk_default_import_file; self->_stdout = pk_default_stdout; self->_stderr = pk_default_stderr; - self->last_retval = PY_NIL; - self->last_exception = PY_NIL; + self->last_retval = *py_NIL; + self->last_exception = *py_NIL; self->is_stopiteration = false; - self->__curr_class = PY_NIL; + self->__curr_class = NULL; self->__dynamic_func_decl = NULL; pk_ManagedHeap__ctor(&self->heap, self); @@ -388,7 +388,7 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); // submit the call if(!fn->cfunc) { - pk_VM__push_frame(self, Frame__new(co, fn->module, p0, p0, argv, co)); + pk_VM__push_frame(self, Frame__new(co, &fn->module, p0, p0, argv, co)); return opcall ? RES_CALL : pk_VM__run_top_frame(self); } else { bool ok = py_callcfunc(p0, fn->cfunc, co->nlocals, argv); @@ -408,10 +408,10 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo // [callable, , args..., local_vars...] // ^p0 ^p1 ^_sp self->stack.sp = argv + co->nlocals; - // initialize local variables to PY_NIL + // initialize local variables to py_NIL memset(p1, 0, (char*)self->stack.sp - (char*)p1); // submit the call - pk_VM__push_frame(self, Frame__new(co, fn->module, p0, p0, argv, co)); + pk_VM__push_frame(self, Frame__new(co, &fn->module, p0, p0, argv, co)); return opcall ? RES_CALL : pk_VM__run_top_frame(self); case FuncType_GENERATOR: assert(false); @@ -537,15 +537,17 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) { for(py_TValue* p = vm->stack.begin; p != vm->stack.end; p++) { mark_value(p); } - + // mark frame + for(Frame* frame = vm->top_frame; frame; frame = frame->f_back) { + mark_value(&frame->module); + if(frame->function) mark_object(frame->function); + } // mark vm's registers mark_value(&vm->last_retval); mark_value(&vm->last_exception); for(int i = 0; i < c11__count_array(vm->reg); i++) { mark_value(&vm->reg[i]); } - - mark_value(&vm->__curr_class); } void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) { diff --git a/src/objects/base.c b/src/objects/base.c deleted file mode 100644 index 89807191..00000000 --- a/src/objects/base.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "pocketpy/objects/base.h" - -py_TValue PY_NIL = {.type=0, .is_ptr=false, .extra=0, ._i64=0}; - diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 650c8995..d96142c9 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -158,10 +158,10 @@ void CodeObject__dtor(CodeObject* self) { c11_vector__dtor(&self->func_decls); } -void Function__ctor(Function* self, FuncDecl_ decl, PyObject* module) { +void Function__ctor(Function* self, FuncDecl_ decl, py_TValue* module) { PK_INCREF(decl); self->decl = decl; - self->module = module; + self->module = module ? *module : *py_NIL; self->clazz = NULL; self->closure = NULL; self->cfunc = NULL; diff --git a/src/public/stack_ops.c b/src/public/stack_ops.c index e72f67e4..2615fa34 100644 --- a/src/public/stack_ops.c +++ b/src/public/stack_ops.c @@ -25,6 +25,15 @@ void py_setdict(py_Ref self, py_Name name, const py_Ref val) { pk_NameDict__set(PyObject__dict(self->_obj), name, *val); } +bool py_deldict(py_Ref self, py_Name name) { + assert(self && self->is_ptr); + if(self->type == tp_type && py_ismagicname(name)) { + py_Type* ud = py_touserdata(self); + py_newnil(py_tpmagic(*ud, name)); + } + return pk_NameDict__del(PyObject__dict(self->_obj), name); +} + py_Ref py_getslot(const py_Ref self, int i) { assert(self && self->is_ptr); assert(i >= 0 && i < self->_obj->slots); diff --git a/src/public/vm.c b/src/public/vm.c index 1526db2c..032a6bc2 100644 --- a/src/public/vm.c +++ b/src/public/vm.c @@ -14,6 +14,7 @@ pk_VM* pk_current_vm; py_GlobalRef py_True; py_GlobalRef py_False; py_GlobalRef py_None; +py_GlobalRef py_NIL; static pk_VM pk_default_vm; @@ -23,14 +24,15 @@ void py_initialize() { pk_current_vm = &pk_default_vm; // initialize some convenient references - static py_TValue _True, _False, _None; + static py_TValue _True, _False, _None, _NIL; py_newbool(&_True, true); py_newbool(&_False, false); py_newnone(&_None); + py_newnil(&_NIL); py_True = &_True; py_False = &_False; py_None = &_None; - + py_NIL = &_NIL; pk_VM__ctor(&pk_default_vm); } @@ -183,7 +185,7 @@ static bool // disassemble(&co); - Frame* frame = Frame__new(&co, vm->main._obj, NULL, vm->stack.sp, vm->stack.sp, &co); + Frame* frame = Frame__new(&co, &vm->main, NULL, vm->stack.sp, vm->stack.sp, &co); pk_VM__push_frame(vm, frame); pk_FrameResult res = pk_VM__run_top_frame(vm); CodeObject__dtor(&co); @@ -289,6 +291,7 @@ py_Ref py_tpmagic(py_Type type, py_Name name) { } py_Ref py_tpobject(py_Type type) { + assert(type); pk_VM* vm = pk_current_vm; return &c11__at(pk_TypeInfo, &vm->types, type)->self; } diff --git a/tests/24_inline_blocks.py b/tests/24_inline_blocks.py index 4407f129..a5bbb8f1 100644 --- a/tests/24_inline_blocks.py +++ b/tests/24_inline_blocks.py @@ -1,8 +1,3 @@ -class A: pass -class B: pass -a = A() -assert type(a) is A - x = 0 if x==0: x=1 assert x==1 @@ -17,4 +12,9 @@ else: x=3 assert x==2 def f1(x): return x+1 -assert f1(1)==2 \ No newline at end of file +assert f1(1)==2 + +# class A: pass +# class B: pass +# a = A() +# assert type(a) is A \ No newline at end of file diff --git a/tests/22_bytes.py b/tests/68_bytes.py similarity index 100% rename from tests/22_bytes.py rename to tests/68_bytes.py