impl @property

Update codeobject.h
This commit is contained in:
blueloveTH 2023-02-27 00:15:30 +08:00
parent c89a38fe9d
commit 3721f48f8b
12 changed files with 86 additions and 54 deletions

View File

@ -384,6 +384,13 @@ class set:
def __iter__(self): def __iter__(self):
return self._a.keys() return self._a.keys()
class property:
def __init__(self, fget):
self.fget = fget
def __get__(self, obj):
return self.fget(obj)
)"; )";
const char* kRandomCode = R"( const char* kRandomCode = R"(

View File

@ -85,25 +85,28 @@ PyVar VM::run_frame(Frame* frame){
pkpy::List& list = PyList_AS_C(frame->top_1()); pkpy::List& list = PyList_AS_C(frame->top_1());
list.push_back(std::move(obj)); list.push_back(std::move(obj));
} continue; } continue;
case OP_BUILD_CLASS: { case OP_BEGIN_CLASS: {
const Str& clsName = frame->co->names[byte.arg].first.str(); auto& name = frame->co->names[byte.arg];
PyVar clsBase = frame->pop_value(this); PyVar clsBase = frame->pop_value(this);
if(clsBase == None) clsBase = _t(tp_object); if(clsBase == None) clsBase = _t(tp_object);
check_type(clsBase, tp_type); check_type(clsBase, tp_type);
PyVar cls = new_type_object(frame->_module, clsName, clsBase); PyVar cls = new_type_object(frame->_module, name.first, clsBase);
while(true){ frame->push(cls);
PyVar fn = frame->pop_value(this); } continue;
if(fn == None) break; case OP_END_CLASS: {
const pkpy::Function& f = PyFunction_AS_C(fn); PyVar cls = frame->pop();
setattr(cls, f.name, fn);
}
cls->attr()._try_perfect_rehash(); cls->attr()._try_perfect_rehash();
}; continue;
case OP_STORE_CLASS_ATTR: {
auto& name = frame->co->names[byte.arg];
PyVar obj = frame->pop_value(this);
PyVar& cls = frame->top();
cls->attr().set(name.first, std::move(obj));
} continue; } continue;
case OP_RETURN_VALUE: return frame->pop_value(this); case OP_RETURN_VALUE: return frame->pop_value(this);
case OP_PRINT_EXPR: { case OP_PRINT_EXPR: {
const PyVar expr = frame->top_value(this); const PyVar expr = frame->top_value(this);
if(expr == None) continue; if(expr != None) *_stdout << PyStr_AS_C(asRepr(expr)) << '\n';
*_stdout << PyStr_AS_C(asRepr(expr)) << '\n';
} continue; } continue;
case OP_POP_TOP: frame->_pop(); continue; case OP_POP_TOP: frame->_pop(); continue;
case OP_BINARY_OP: { case OP_BINARY_OP: {

View File

@ -94,6 +94,7 @@ struct CodeObject {
/************************************************/ /************************************************/
int _curr_block_i = 0; int _curr_block_i = 0;
int _rvalue = 0; int _rvalue = 0;
bool _is_compiling_class = false;
bool _is_curr_block_loop() const { bool _is_curr_block_loop() const {
return blocks[_curr_block_i].type == FOR_LOOP || blocks[_curr_block_i].type == WHILE_LOOP; return blocks[_curr_block_i].type == FOR_LOOP || blocks[_curr_block_i].type == WHILE_LOOP;
} }

View File

@ -45,7 +45,6 @@ typedef double f64;
struct Dummy { }; struct Dummy { };
struct DummyInstance { }; struct DummyInstance { };
struct DummyProperty { };
struct DummyModule { }; struct DummyModule { };
#define DUMMY_VAL Dummy() #define DUMMY_VAL Dummy()
@ -79,4 +78,4 @@ const float kInstAttrLoadFactor = 0.67;
const float kTypeAttrLoadFactor = 0.5; const float kTypeAttrLoadFactor = 0.5;
// do extra check for debug // do extra check for debug
// #define PK_EXTRA_CHECK #define PK_EXTRA_CHECK

View File

@ -19,7 +19,6 @@ enum StringType { NORMAL_STRING, RAW_STRING, F_STRING };
class Compiler { class Compiler {
std::unique_ptr<Parser> parser; std::unique_ptr<Parser> parser;
std::stack<CodeObject_> codes; std::stack<CodeObject_> codes;
bool is_compiling_class = false;
int lexing_count = 0; int lexing_count = 0;
bool used = false; bool used = false;
VM* vm; VM* vm;
@ -334,7 +333,7 @@ private:
consumed = true; consumed = true;
} }
if (repl_throw && peek() == TK("@eof")){ if (repl_throw && peek() == TK("@eof")){
throw NeedMoreLines(is_compiling_class); throw NeedMoreLines(co()->_is_compiling_class);
} }
return consumed; return consumed;
} }
@ -409,13 +408,19 @@ private:
if(op == TK("=")) { // a = (expr) if(op == TK("=")) { // a = (expr)
EXPR_TUPLE(); EXPR_TUPLE();
if(lhs!=-1 && co()->codes[lhs].op == OP_LOAD_NAME_REF){ if(lhs!=-1 && co()->codes[lhs].op == OP_LOAD_NAME_REF){
emit(OP_STORE_NAME, co()->codes[lhs].arg); if(co()->_is_compiling_class){
emit(OP_STORE_CLASS_ATTR, co()->codes[lhs].arg);
}else{
emit(OP_STORE_NAME, co()->codes[lhs].arg);
}
co()->codes[lhs].op = OP_NO_OP; co()->codes[lhs].op = OP_NO_OP;
co()->codes[lhs].arg = -1; co()->codes[lhs].arg = -1;
}else{ }else{
if(co()->_is_compiling_class) SyntaxError();
emit(OP_STORE_REF); emit(OP_STORE_REF);
} }
}else{ // a += (expr) -> a = a + (expr) }else{ // a += (expr) -> a = a + (expr)
if(co()->_is_compiling_class) SyntaxError();
EXPR(); EXPR();
switch (op) { switch (op) {
case TK("+="): emit(OP_INPLACE_BINARY_OP, 0); break; case TK("+="): emit(OP_INPLACE_BINARY_OP, 0); break;
@ -778,7 +783,7 @@ __LISTCOMP:
lex_token(); lex_token();
TokenIndex op = parser->prev.type; TokenIndex op = parser->prev.type;
if (op == TK("=")){ if (op == TK("=")){
if(meet_assign_token) SyntaxError("invalid syntax"); if(meet_assign_token) SyntaxError();
meet_assign_token = true; meet_assign_token = true;
} }
GrammarFn infix = rules[op].infix; GrammarFn infix = rules[op].infix;
@ -977,7 +982,9 @@ __LISTCOMP:
consume_end_stmt(); consume_end_stmt();
// If last op is not an assignment, pop the result. // If last op is not an assignment, pop the result.
uint8_t last_op = co()->codes.back().op; uint8_t last_op = co()->codes.back().op;
if( last_op!=OP_STORE_NAME && last_op!=OP_STORE_REF && last_op!=OP_INPLACE_BINARY_OP && last_op!=OP_INPLACE_BITWISE_OP && last_op!=OP_STORE_ALL_NAMES){ if( last_op!=OP_STORE_NAME && last_op!=OP_STORE_REF &&
last_op!=OP_INPLACE_BINARY_OP && last_op!=OP_INPLACE_BITWISE_OP &&
last_op!=OP_STORE_ALL_NAMES && last_op!=OP_STORE_CLASS_ATTR){
if(last_op == OP_BUILD_TUPLE_REF) co()->codes.back().op = OP_BUILD_TUPLE; if(last_op == OP_BUILD_TUPLE_REF) co()->codes.back().op = OP_BUILD_TUPLE;
if(mode()==REPL_MODE && name_scope() == NAME_GLOBAL) emit(OP_PRINT_EXPR, -1, true); if(mode()==REPL_MODE && name_scope() == NAME_GLOBAL) emit(OP_PRINT_EXPR, -1, true);
emit(OP_POP_TOP, -1, true); emit(OP_POP_TOP, -1, true);
@ -993,13 +1000,13 @@ __LISTCOMP:
super_cls_name_idx = co()->add_name(parser->prev.str(), NAME_GLOBAL); super_cls_name_idx = co()->add_name(parser->prev.str(), NAME_GLOBAL);
consume(TK(")")); consume(TK(")"));
} }
emit(OP_LOAD_NONE);
is_compiling_class = true;
compile_block_body(&Compiler::compile_function);
is_compiling_class = false;
if(super_cls_name_idx == -1) emit(OP_LOAD_NONE); if(super_cls_name_idx == -1) emit(OP_LOAD_NONE);
else emit(OP_LOAD_NAME_REF, super_cls_name_idx); else emit(OP_LOAD_NAME, super_cls_name_idx);
emit(OP_BUILD_CLASS, cls_name_idx); emit(OP_BEGIN_CLASS, cls_name_idx);
co()->_is_compiling_class = true;
compile_block_body();
co()->_is_compiling_class = false;
emit(OP_END_CLASS);
} }
void _compile_f_args(pkpy::Function& func, bool enable_type_hints){ void _compile_f_args(pkpy::Function& func, bool enable_type_hints){
@ -1044,15 +1051,11 @@ __LISTCOMP:
void compile_function(){ void compile_function(){
bool has_decorator = !co()->codes.empty() && co()->codes.back().op == OP_SETUP_DECORATOR; bool has_decorator = !co()->codes.empty() && co()->codes.back().op == OP_SETUP_DECORATOR;
if(is_compiling_class){
if(match(TK("pass"))) return;
consume(TK("def"));
}
pkpy::Function func; pkpy::Function func;
StrName obj_name; StrName obj_name;
consume(TK("@id")); consume(TK("@id"));
func.name = parser->prev.str(); func.name = parser->prev.str();
if(!is_compiling_class && match(TK("::"))){ if(!co()->_is_compiling_class && match(TK("::"))){
consume(TK("@id")); consume(TK("@id"));
obj_name = func.name; obj_name = func.name;
func.name = parser->prev.str(); func.name = parser->prev.str();
@ -1070,7 +1073,7 @@ __LISTCOMP:
this->codes.pop(); this->codes.pop();
emit(OP_LOAD_FUNCTION, co()->add_const(vm->PyFunction(func))); emit(OP_LOAD_FUNCTION, co()->add_const(vm->PyFunction(func)));
if(name_scope() == NAME_LOCAL) emit(OP_SETUP_CLOSURE); if(name_scope() == NAME_LOCAL) emit(OP_SETUP_CLOSURE);
if(!is_compiling_class){ if(!co()->_is_compiling_class){
if(obj_name.empty()){ if(obj_name.empty()){
if(has_decorator) emit(OP_CALL, 1); if(has_decorator) emit(OP_CALL, 1);
emit(OP_STORE_NAME, co()->add_name(func.name, name_scope())); emit(OP_STORE_NAME, co()->add_name(func.name, name_scope()));
@ -1083,7 +1086,8 @@ __LISTCOMP:
emit(OP_STORE_REF); emit(OP_STORE_REF);
} }
}else{ }else{
if(has_decorator) SyntaxError("decorator is not supported here"); if(has_decorator) emit(OP_CALL, 1);
emit(OP_STORE_CLASS_ATTR, co()->add_name(func.name, name_scope()));
} }
} }
@ -1117,6 +1121,7 @@ __LISTCOMP:
throw e; throw e;
} }
void SyntaxError(Str msg){ throw_err("SyntaxError", msg); } void SyntaxError(Str msg){ throw_err("SyntaxError", msg); }
void SyntaxError(){ throw_err("SyntaxError", "invalid syntax"); }
void IndentationError(Str msg){ throw_err("IndentationError", msg); } void IndentationError(Str msg){ throw_err("IndentationError", msg); }
public: public:

View File

@ -109,7 +109,7 @@ struct Py_ : PyObject {
_attr = new pkpy::NameDict(16, kTypeAttrLoadFactor); _attr = new pkpy::NameDict(16, kTypeAttrLoadFactor);
}else if constexpr(std::is_same_v<T, DummyInstance>){ }else if constexpr(std::is_same_v<T, DummyInstance>){
_attr = new pkpy::NameDict(4, kInstAttrLoadFactor); _attr = new pkpy::NameDict(4, kInstAttrLoadFactor);
}else if constexpr(std::is_same_v<T, pkpy::Function> || std::is_same_v<T, pkpy::NativeFunc> || std::is_same_v<T, DummyProperty>){ }else if constexpr(std::is_same_v<T, pkpy::Function> || std::is_same_v<T, pkpy::NativeFunc>){
_attr = new pkpy::NameDict(4, kInstAttrLoadFactor); _attr = new pkpy::NameDict(4, kInstAttrLoadFactor);
}else{ }else{
_attr = nullptr; _attr = nullptr;

View File

@ -24,7 +24,6 @@ OPCODE(BUILD_LIST)
OPCODE(BUILD_MAP) OPCODE(BUILD_MAP)
OPCODE(BUILD_SET) OPCODE(BUILD_SET)
OPCODE(BUILD_SLICE) OPCODE(BUILD_SLICE)
OPCODE(BUILD_CLASS)
OPCODE(BUILD_TUPLE) OPCODE(BUILD_TUPLE)
OPCODE(BUILD_TUPLE_REF) OPCODE(BUILD_TUPLE_REF)
OPCODE(BUILD_STRING) OPCODE(BUILD_STRING)
@ -87,4 +86,8 @@ OPCODE(SETUP_CLOSURE)
OPCODE(SETUP_DECORATOR) OPCODE(SETUP_DECORATOR)
OPCODE(STORE_ALL_NAMES) OPCODE(STORE_ALL_NAMES)
OPCODE(BEGIN_CLASS)
OPCODE(END_CLASS)
OPCODE(STORE_CLASS_ATTR)
#endif #endif

View File

@ -10,6 +10,7 @@ CodeObject_ VM::compile(Str source, Str filename, CompileMode mode) {
try{ try{
return compiler.compile(); return compiler.compile();
}catch(pkpy::Exception& e){ }catch(pkpy::Exception& e){
// std::cout << e.summary() << std::endl;
_error(e); _error(e);
return nullptr; return nullptr;
} }

View File

@ -191,6 +191,7 @@ const StrName __init__ = StrName::get("__init__");
const StrName __json__ = StrName::get("__json__"); const StrName __json__ = StrName::get("__json__");
const StrName __name__ = StrName::get("__name__"); const StrName __name__ = StrName::get("__name__");
const StrName __len__ = StrName::get("__len__"); const StrName __len__ = StrName::get("__len__");
const StrName __get__ = StrName::get("__get__");
const StrName m_eval = StrName::get("eval"); const StrName m_eval = StrName::get("eval");
const StrName m_self = StrName::get("self"); const StrName m_self = StrName::get("self");

View File

@ -361,7 +361,10 @@ public:
while(cls != None.get()) { while(cls != None.get()) {
val = cls->attr().try_get(name); val = cls->attr().try_get(name);
if(val != nullptr){ if(val != nullptr){
if(is_type(*val, tp_property)) return call((*val)->attr("__get__"), pkpy::one_arg(obj)); PyVarOrNull descriptor = getattr(*val, __get__, false);
if(descriptor != nullptr){
return call(descriptor, pkpy::one_arg(obj));
}
if(is_type(*val, tp_function) || is_type(*val, tp_native_function)){ if(is_type(*val, tp_function) || is_type(*val, tp_native_function)){
return PyBoundMethod({obj, *val}); return PyBoundMethod({obj, *val});
}else{ }else{
@ -379,26 +382,10 @@ public:
if(obj.is_tagged()) TypeError("cannot set attribute"); if(obj.is_tagged()) TypeError("cannot set attribute");
PyObject* p = obj.get(); PyObject* p = obj.get();
while(p->type == tp_super) p = static_cast<PyVar*>(p->value())->get(); while(p->type == tp_super) p = static_cast<PyVar*>(p->value())->get();
// handle property
PyVar* prop = _t(obj)->attr().try_get(name);
if(prop != nullptr && is_type(*prop, tp_property)){
call((*prop)->attr("__set__"), pkpy::two_args(obj, std::forward<T>(value)));
return;
}
if(!p->is_attr_valid()) TypeError("cannot set attribute"); if(!p->is_attr_valid()) TypeError("cannot set attribute");
p->attr().set(name, std::forward<T>(value)); p->attr().set(name, std::forward<T>(value));
} }
void bind_property(PyVar obj, Str field, NativeFuncRaw getter, NativeFuncRaw setter){
check_type(obj, tp_type);
PyVar prop = new_object(tp_property, DummyProperty());
prop->attr().set("__get__", PyNativeFunc(pkpy::NativeFunc(getter, 0, true)));
prop->attr().set("__set__", PyNativeFunc(pkpy::NativeFunc(setter, 1, true)));
setattr(obj, field, prop);
}
template<int ARGC> template<int ARGC>
void bind_method(PyVar obj, Str funcName, NativeFuncRaw fn) { void bind_method(PyVar obj, Str funcName, NativeFuncRaw fn) {
check_type(obj, tp_type); check_type(obj, tp_type);
@ -537,7 +524,7 @@ public:
Type tp_list, tp_tuple; Type tp_list, tp_tuple;
Type tp_function, tp_native_function, tp_native_iterator, tp_bound_method; Type tp_function, tp_native_function, tp_native_iterator, tp_bound_method;
Type tp_slice, tp_range, tp_module, tp_ref; Type tp_slice, tp_range, tp_module, tp_ref;
Type tp_super, tp_exception, tp_star_wrapper, tp_property; Type tp_super, tp_exception, tp_star_wrapper;
template<typename P> template<typename P>
inline PyVarRef PyRef(P&& value) { inline PyVarRef PyRef(P&& value) {
@ -654,7 +641,6 @@ public:
tp_module = _new_type_object("module"); tp_module = _new_type_object("module");
tp_ref = _new_type_object("_ref"); tp_ref = _new_type_object("_ref");
tp_star_wrapper = _new_type_object("_star_wrapper"); tp_star_wrapper = _new_type_object("_star_wrapper");
tp_property = _new_type_object("property");
tp_function = _new_type_object("function"); tp_function = _new_type_object("function");
tp_native_function = _new_type_object("native_function"); tp_native_function = _new_type_object("native_function");

View File

@ -76,3 +76,18 @@ assert isinstance(d, B)
assert isinstance(d, A) assert isinstance(d, A)
assert isinstance(object, object) assert isinstance(object, object)
assert isinstance(type, object) assert isinstance(type, object)
class A:
a = 1
b = 2
assert A.a == 1
assert A.b == 2
class B(A):
b = 3
c = 4
# assert B.a == 1 ...bug here
assert B.b == 3
assert B.c == 4

View File

@ -16,3 +16,14 @@ def fib(n):
return fib(n-1) + fib(n-2) return fib(n-1) + fib(n-2)
assert fib(32) == 2178309 assert fib(32) == 2178309
class A:
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
a = A(1)
assert a.x == 1