diff --git a/include/pocketpy/common/vector.h b/include/pocketpy/common/vector.h index 398fe7a7..ae6260fa 100644 --- a/include/pocketpy/common/vector.h +++ b/include/pocketpy/common/vector.h @@ -34,6 +34,7 @@ c11_vector c11_vector__copy(const c11_vector* self); void c11_vector__reserve(c11_vector* self, int capacity); void c11_vector__clear(c11_vector* self); void* c11_vector__emplace(c11_vector* self); +bool c11_vector__contains(const c11_vector* self, void* elem); c11_array c11_vector__submit(c11_vector* self); #define c11__getitem(T, self, index) (((T*)(self)->data)[index]) diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index 90c02a4c..cf08dc57 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -7,14 +7,15 @@ #include "pocketpy/common/smallmap.h" #include "pocketpy/objects/base.h" #include "pocketpy/objects/sourcedata.h" +#include "pocketpy/objects/namedict.h" #include "pocketpy/pocketpy.h" #ifdef __cplusplus extern "C" { #endif -#define BC_NOARG 0 -#define BC_KEEPLINE -1 +#define BC_NOARG 0 +#define BC_KEEPLINE -1 typedef enum FuncType { FuncType_UNSET, @@ -39,9 +40,10 @@ typedef enum CodeBlockType { } CodeBlockType; typedef enum Opcode { - #define OPCODE(name) OP_##name, - #include "pocketpy/xmacros/opcodes.h" - #undef OPCODE + +#define OPCODE(name) OP_##name, +#include "pocketpy/xmacros/opcodes.h" +#undef OPCODE } Opcode; typedef struct Bytecode { @@ -70,18 +72,18 @@ typedef struct CodeObject { pk_SourceData_ src; c11_string* name; - c11_vector/*T=Bytecode*/ codes; - c11_vector/*T=CodeObjectByteCodeEx*/ codes_ex; + c11_vector /*T=Bytecode*/ codes; + c11_vector /*T=CodeObjectByteCodeEx*/ codes_ex; - c11_vector/*T=py_TValue*/ consts; // constants - c11_vector/*T=py_Name*/ varnames; // local variables + c11_vector /*T=py_TValue*/ consts; // constants + c11_vector /*T=py_Name*/ varnames; // local variables int nlocals; // cached varnames.size() c11_smallmap_n2i varnames_inv; c11_smallmap_n2i labels; - c11_vector/*T=CodeBlock*/ blocks; - c11_vector/*T=FuncDecl_*/ func_decls; + c11_vector /*T=CodeBlock*/ blocks; + c11_vector /*T=FuncDecl_*/ func_decls; int start_line; int end_line; @@ -91,18 +93,18 @@ void CodeObject__ctor(CodeObject* self, pk_SourceData_ src, c11_sv name); void CodeObject__dtor(CodeObject* self); void CodeObject__gc_mark(const CodeObject* self); -typedef struct FuncDeclKwArg{ - int index; // index in co->varnames - uint16_t key; // name of this argument +typedef struct FuncDeclKwArg { + int index; // index in co->varnames + uint16_t key; // name of this argument py_TValue value; // default value } FuncDeclKwArg; typedef struct FuncDecl { RefCounted rc; - CodeObject code; // strong ref + CodeObject code; // strong ref - c11_vector/*T=int*/ args; // indices in co->varnames - c11_vector/*T=KwArg*/ kwargs; // indices in co->varnames + c11_vector /*T=int*/ args; // indices in co->varnames + c11_vector /*T=KwArg*/ kwargs; // indices in co->varnames int starred_arg; // index in co->varnames, -1 if no *arg int starred_kwarg; // index in co->varnames, -1 if no **kwarg @@ -121,6 +123,17 @@ void FuncDecl__dtor(FuncDecl* self); void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const py_TValue* value); void FuncDecl__gc_mark(const FuncDecl* self); +// runtime function +typedef struct Function { + FuncDecl_ decl; + PyObject* module; // weak ref + PyObject* clazz; // weak ref + pk_NameDict* closure; // strong ref +} Function; + +void Function__ctor(Function* self, FuncDecl_ decl, PyObject* module); +void Function__dtor(Function* self); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/src/common/vector.c b/src/common/vector.c index 241f7bd9..97649f96 100644 --- a/src/common/vector.c +++ b/src/common/vector.c @@ -63,6 +63,14 @@ void* c11_vector__emplace(c11_vector* self){ return p; } +bool c11_vector__contains(const c11_vector *self, void *elem){ + for(int i = 0; i < self->count; i++){ + void* p = (char*)self->data + self->elem_size * i; + if(memcmp(p, elem, self->elem_size) == 0) return true; + } + return false; +} + c11_array c11_vector__submit(c11_vector* self){ c11_array retval = { .data = self->data, diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 74b5c1b4..2738087c 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -635,7 +635,6 @@ static void _load_simple_expr(Ctx* ctx, c11_sv expr, int line) { // name or name.name bool is_fastpath = false; if(is_identifier(expr)) { - // ctx->emit_(OP_LOAD_NAME, py_Name(expr.sv()).index, line); Ctx__emit_(ctx, OP_LOAD_NAME, py_name2(expr), line); is_fastpath = true; } else { @@ -2188,6 +2187,174 @@ Error* try_compile_assignment(Compiler* self, bool* is_assign) { return NULL; } +static FuncDecl_ push_f_context(Compiler* self, c11_sv name, int* out_index) { + FuncDecl_ decl = FuncDecl__rcnew(self->src, name); + decl->code.start_line = self->i == 0 ? 1 : prev()->line; + decl->nested = name_scope(self) == NAME_LOCAL; + // add_func_decl + Ctx* top_ctx = ctx(); + c11_vector__push(FuncDecl_, &top_ctx->co->func_decls, decl); + *out_index = top_ctx->co->func_decls.count - 1; + // push new context + top_ctx = c11_vector__emplace(&self->contexts); + // contexts.push_back(CodeEmitContext(vm, decl->code, contexts.size())); + Ctx__ctor(top_ctx, &decl->code, decl, self->contexts.count); + return decl; +} + +static Error* read_literal(Compiler* self, py_Ref out) { + Error* err; + advance(); + const TokenValue* value = &prev()->value; + bool negated = false; + switch(prev()->type) { + case TK_SUB: + consume(TK_NUM); + value = &prev()->value; + negated = true; + case TK_NUM: { + if(value->index == TokenValue_I64) { + py_newint(out, negated ? -value->_i64 : value->_i64); + } else if(value->index == TokenValue_F64) { + py_newfloat(out, negated ? -value->_f64 : value->_f64); + } else { + return SyntaxError(); + } + return NULL; + } + case TK_STR: py_newstr(out, value->_str->data); return NULL; + case TK_TRUE: py_newbool(out, true); return NULL; + case TK_FALSE: py_newbool(out, false); return NULL; + case TK_NONE: py_newnone(out); return NULL; + case TK_DOTDOTDOT: py_newellipsis(out); return NULL; + case TK_LPAREN: { + py_TValue cpnts[4]; + int count = 0; + while(true) { + if(count == 4) return SyntaxError("default argument tuple exceeds 4 elements"); + check(read_literal(self, &cpnts[count])); + count += 1; + if(curr()->type == TK_RPAREN) break; + consume(TK_COMMA); + if(curr()->type == TK_RPAREN) break; + } + consume(TK_RPAREN); + py_newtuple(out, count); + for(int i = 0; i < count; i++) { + py_tuple__setitem(out, i, &cpnts[i]); + } + return NULL; + } + default: *out = PY_NIL; return NULL; + } +} + +static Error* _compile_f_args(Compiler* self, FuncDecl* decl, bool enable_type_hints) { + int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs + Error* err; + do { + if(state > 3) return SyntaxError(); + if(state == 3) return SyntaxError("**kwargs should be the last argument"); + match_newlines(); + if(match(TK_MUL)) { + if(state < 1) + state = 1; + else + return SyntaxError("*args should be placed before **kwargs"); + } else if(match(TK_POW)) { + state = 3; + } + consume(TK_ID); + py_Name name = py_name2(Token__sv(prev())); + + // check duplicate argument name + py_Name tmp_name; + c11__foreach(int, &decl->args, j) { + tmp_name = c11__getitem(py_Name, &decl->args, *j); + if(tmp_name == name) return SyntaxError("duplicate argument name"); + } + c11__foreach(FuncDeclKwArg, &decl->kwargs, kv) { + tmp_name = c11__getitem(py_Name, &decl->code.varnames, kv->index); + if(tmp_name == name) return SyntaxError("duplicate argument name"); + } + if(decl->starred_arg != -1) { + tmp_name = c11__getitem(py_Name, &decl->code.varnames, decl->starred_arg); + if(tmp_name == name) return SyntaxError("duplicate argument name"); + } + if(decl->starred_kwarg != -1) { + tmp_name = c11__getitem(py_Name, &decl->code.varnames, decl->starred_kwarg); + if(tmp_name == name) return SyntaxError("duplicate argument name"); + } + + // eat type hints + if(enable_type_hints && match(TK_COLON)) check(consume_type_hints(self)); + if(state == 0 && curr()->type == TK_ASSIGN) state = 2; + int index = Ctx__add_varname(ctx(), name); + switch(state) { + case 0: c11_vector__push(int, &decl->args, index); break; + case 1: + decl->starred_arg = index; + state += 1; + break; + case 2: { + consume(TK_ASSIGN); + py_TValue value; + check(read_literal(self, &value)); + if(py_isnil(&value)) return SyntaxError("default argument must be a literal"); + FuncDecl__add_kwarg(decl, index, name, &value); + } break; + case 3: + decl->starred_kwarg = index; + state += 1; + break; + } + } while(match(TK_COMMA)); + return NULL; +} + +static Error* compile_function(Compiler* self, int decorators) { + Error* err; + consume(TK_ID); + c11_sv decl_name = Token__sv(prev()); + int decl_index; + FuncDecl_ decl = push_f_context(self, decl_name, &decl_index); + consume(TK_LPAREN); + if(!match(TK_RPAREN)) { + check(_compile_f_args(self, decl, true)); + consume(TK_RPAREN); + } + if(match(TK_ARROW)) check(consume_type_hints(self)); + check(compile_block_body(self, compile_stmt)); + check(pop_context(self)); + + if(decl->code.codes.count >= 2) { + Bytecode* codes = (Bytecode*)decl->code.codes.data; + + if(codes[0].op == OP_LOAD_CONST && codes[1].op == OP_POP_TOP) { + // handle optional docstring + py_TValue* consts = decl->code.consts.data; + py_TValue* c = &consts[codes[0].arg]; + if(py_isstr(c)) { + decl->docstring = py_tostr(c); + codes[0].op = OP_NO_OP; + codes[1].op = OP_NO_OP; + } + } + } + + Ctx__emit_(ctx(), OP_LOAD_FUNCTION, decl_index, prev()->line); + Ctx__s_emit_decorators(ctx(), decorators); + + if(ctx()->is_compiling_class) { + Ctx__emit_(ctx(), OP_STORE_CLASS_ATTR, py_name2(decl_name), prev()->line); + } else { + NameExpr* e = NameExpr__new(prev()->line, py_name2(decl_name), name_scope(self)); + vtemit_store((Expr*)e, ctx()); + vtdelete((Expr*)e); + } + return NULL; +} + static Error* compile_stmt(Compiler* self) { Error* err; if(match(TK_CLASS)) { @@ -2244,7 +2411,7 @@ static Error* compile_stmt(Compiler* self) { case TK_FOR: check(compile_for_loop(self)); break; // case TK_IMPORT: check(compile_normal_import()); break; // case TK_FROM: check(compile_from_import()); break; - // case TK_DEF: check(compile_function()); break; + case TK_DEF: check(compile_function(self, 0)); break; // case TK_DECORATOR: check(compile_decorated()); break; // case TK_TRY: check(compile_try_except()); break; case TK_PASS: consume_end_stmt(); break; diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 77aecc82..0f6641be 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -164,20 +164,17 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { /*****************************************/ case OP_LOAD_ELLIPSIS: py_newellipsis(SP()++); DISPATCH(); case OP_LOAD_FUNCTION: { - // FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg); - // py_TValue obj; - // if(decl->nested) { - // NameDict* captured = frame->_locals.to_namedict(); - // obj = - // new_object(tp_function, decl, frame->_module, nullptr, - // captured); - // uint16_t name = py_Name__map2(py_Str__sv(&decl->code->name)); - // captured->set(name, obj); - // } else { - // obj = new_object(tp_function, decl, frame->_module, nullptr, - // nullptr); - // } - // PUSH(obj);DISPATCH(); + 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); + if(decl->nested) { + ud->closure = FastLocals__to_namedict(frame->locals, frame->locals_co); + py_Name name = py_name2(c11_string__sv(decl->code.name)); + // capture itself to allow recursion + pk_NameDict__set(ud->closure, name, *SP()); + } + SP()++; + DISPATCH(); } case OP_LOAD_NULL: py_newnil(SP()++); diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index cfa89a03..8b84e3f7 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -6,11 +6,9 @@ void ValueStack__ctor(ValueStack* self) { self->end = self->begin + PK_VM_STACK_SIZE; } -void ValueStack__clear(ValueStack* self) { - self->sp = self->begin; -} +void ValueStack__clear(ValueStack* self) { self->sp = self->begin; } -py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name){ +py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name) { int index = c11_smallmap_n2i__get(&co->varnames_inv, name, -1); if(index == -1) return NULL; return &locals[index]; @@ -20,14 +18,12 @@ pk_NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) { pk_NameDict* dict = pk_NameDict__new(); c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) { py_TValue value = locals[entry->value]; - if(!py_isnil(&value)){ - pk_NameDict__set(dict, entry->key, value); - } + if(!py_isnil(&value)) { pk_NameDict__set(dict, entry->key, value); } } return dict; } -UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset){ +UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset) { UnwindTarget* self = malloc(sizeof(UnwindTarget)); self->next = next; self->iblock = iblock; @@ -35,11 +31,14 @@ UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset){ return self; } -void UnwindTarget__delete(UnwindTarget* self){ - free(self); -} +void UnwindTarget__delete(UnwindTarget* self) { free(self); } -Frame* Frame__new(const CodeObject* co, const py_TValue* module, const py_TValue* function, py_TValue* p0, py_TValue* locals, const CodeObject* locals_co){ +Frame* Frame__new(const CodeObject* co, + const py_TValue* module, + const py_TValue* function, + py_TValue* p0, + py_TValue* locals, + const CodeObject* locals_co) { static_assert(sizeof(Frame) <= kPoolFrameBlockSize, "!(sizeof(Frame) <= kPoolFrameBlockSize)"); Frame* self = PoolFrame_alloc(); self->f_back = NULL; @@ -54,7 +53,7 @@ Frame* Frame__new(const CodeObject* co, const py_TValue* module, const py_TValue return self; } -void Frame__delete(Frame* self){ +void Frame__delete(Frame* self) { while(self->uw_list) { UnwindTarget* p = self->uw_list; self->uw_list = p->next; @@ -63,7 +62,7 @@ void Frame__delete(Frame* self){ PoolFrame_dealloc(self); } -int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s){ +int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) { // try to find a parent try block int iblock = Frame__iblock(self); while(iblock >= 0) { @@ -74,15 +73,16 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s){ if(iblock < 0) return -1; py_TValue obj = *--_s->sp; // pop exception object UnwindTarget* uw = Frame__find_unwind_target(self, iblock); - _s->sp = (self->locals + uw->offset); // unwind the stack - *(_s->sp++) = obj; // push it back + _s->sp = (self->locals + uw->offset); // unwind the stack + *(_s->sp++) = obj; // push it back return c11__at(CodeBlock, &self->co->blocks, iblock)->end; } -void Frame__prepare_jump_break(Frame* self, ValueStack* _s, int target){ +void Frame__prepare_jump_break(Frame* self, ValueStack* _s, int target) { int iblock = Frame__iblock(self); if(target >= self->co->codes.count) { - while(iblock >= 0) iblock = Frame__exit_block(self, _s, iblock); + while(iblock >= 0) + iblock = Frame__exit_block(self, _s, iblock); } else { // BUG (solved) // for i in range(4): @@ -96,14 +96,14 @@ void Frame__prepare_jump_break(Frame* self, ValueStack* _s, int target){ } } -int Frame__prepare_loop_break(Frame* self, ValueStack* _s){ +int Frame__prepare_loop_break(Frame* self, ValueStack* _s) { int iblock = Frame__iblock(self); int target = c11__getitem(CodeBlock, &self->co->blocks, iblock).end; Frame__prepare_jump_break(self, _s, target); return target; } -int Frame__exit_block(Frame* self, ValueStack* _s, int iblock){ +int Frame__exit_block(Frame* self, ValueStack* _s, int iblock) { CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, iblock); if(block->type == CodeBlockType_FOR_LOOP) { _s->sp--; // pop iterator @@ -113,7 +113,7 @@ int Frame__exit_block(Frame* self, ValueStack* _s, int iblock){ return block->parent; } -UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock){ +UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock) { UnwindTarget* uw; for(uw = self->uw_list; uw; uw = uw->next) { if(uw->iblock == iblock) return uw; @@ -132,7 +132,7 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) { } } -py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name){ +py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name) { // if(self->function == NULL) return NULL; // pkpy::Function* fn = PyObject__as(pkpy::Function, self->function); // if(fn->_closure == nullptr) return nullptr; diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 9ab4567d..4d2ae169 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -91,4 +91,17 @@ void CodeObject__dtor(CodeObject* self) { PK_DECREF(decl); } c11_vector__dtor(&self->func_decls); +} + +void Function__ctor(Function* self, FuncDecl_ decl, PyObject* module) { + PK_INCREF(decl); + self->decl = decl; + self->module = module; + self->clazz = NULL; + self->closure = NULL; +} + +void Function__dtor(Function* self) { + PK_DECREF(self->decl); + if(self->closure) pk_NameDict__delete(self->closure); } \ No newline at end of file diff --git a/src/public/py_object.c b/src/public/py_object.c index b7a4fd13..fceb86b6 100644 --- a/src/public/py_object.c +++ b/src/public/py_object.c @@ -1,4 +1,5 @@ #include "pocketpy/interpreter/vm.h" +#include "pocketpy/common/sstream.h" #include "pocketpy/pocketpy.h" static bool _py_object__new__(int argc, py_Ref argv) { @@ -29,9 +30,22 @@ static bool _py_object__ne__(int argc, py_Ref argv) { return true; } +static bool _py_object__repr__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + assert(argv->is_ptr); + c11_sbuf buf; + c11_sbuf__ctor(&buf); + pk_sprintf(&buf, "<%t object at %p>", argv->type, argv->_obj); + c11_string* res = c11_sbuf__submit(&buf); + py_newstrn(py_retval(), res->data, res->size); + c11_string__delete(res); + return true; +} + void pk_object__register() { py_bindmagic(tp_object, __new__, _py_object__new__); py_bindmagic(tp_object, __hash__, _py_object__hash__); py_bindmagic(tp_object, __eq__, _py_object__eq__); py_bindmagic(tp_object, __ne__, _py_object__ne__); + py_bindmagic(tp_object, __repr__, _py_object__repr__); } \ No newline at end of file diff --git a/src2/main.c b/src2/main.c index 7d7b3c68..fe194aba 100644 --- a/src2/main.c +++ b/src2/main.c @@ -27,32 +27,42 @@ int main(int argc, char** argv) { SetConsoleOutputCP(CP_UTF8); #endif - if(argc > 2){ + if(argc > 2) { printf("Usage: pocketpy [filename]\n"); return 0; } py_initialize(); - if(argc == 1){ + if(argc == 1) { printf("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") "); - printf("[%d bit] on %s" "\n", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING); - printf("https://github.com/pocketpy/pocketpy" "\n"); - printf("Type \"exit()\" to exit." "\n"); + printf( + "[%d bit] on %s" + "\n", + (int)(sizeof(void*) * 8), + PY_SYS_PLATFORM_STRING); + printf( + "https://github.com/pocketpy/pocketpy" + "\n"); + printf( + "Type \"exit()\" to exit." + "\n"); - while(true){ + while(true) { int size = py_replinput(buf); assert(size < sizeof(buf)); - if(size >= 0){ + if(size >= 0) { if(!py_exec2(buf, "", REPL_MODE)) py_printexc(); } } - }else{ + } else { char* source = read_file(argv[1]); - if(!py_exec(source)) py_printexc(); - free(source); + if(source) { + if(!py_exec(source)) py_printexc(); + free(source); + } } - + py_finalize(); return 0; } diff --git a/tests/02_float.py b/tests/02_float.py index 4ca8c9b9..52910b0a 100644 --- a/tests/02_float.py +++ b/tests/02_float.py @@ -1,7 +1,3 @@ -def eq(a, b): - dt = a - b - return dt > -0.001 and dt < 0.001 - # test == != >= <= < > assert 1.0 == 1.0 assert 1.0 != 1.1 @@ -10,6 +6,10 @@ assert 1.0 <= 1.0 assert 1.0 < 1.1 assert 1.1 > 1.0 +def eq(a, b): + dt = a - b + return dt > -0.001 and dt < 0.001 + # test + - * ** / assert eq(1.5 + 3, 4.5) assert eq(1.5 + 3.9, 5.4)