From 2bca80ff7f2d326ae7c54464e6431b7946f45af3 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 25 Oct 2024 18:10:59 +0800 Subject: [PATCH] remove `goto` and support `finally` --- docs/features/goto.md | 30 ------------- include/pocketpy/objects/codeobject.h | 1 - include/pocketpy/xmacros/opcodes.h | 3 -- src/compiler/compiler.c | 65 ++++++++++----------------- src/interpreter/ceval.c | 21 ++------- src/modules/dis.c | 4 -- src/objects/codeobject.c | 2 - tests/27_goto.py | 38 ---------------- tests/28_exception.py | 35 ++++++++------- tests/52_context.py | 7 --- 10 files changed, 48 insertions(+), 158 deletions(-) delete mode 100644 docs/features/goto.md delete mode 100644 tests/27_goto.py diff --git a/docs/features/goto.md b/docs/features/goto.md deleted file mode 100644 index e5c77fd2..00000000 --- a/docs/features/goto.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -icon: dot -title: Goto Statement ---- - -pkpy supports goto/label just like C. You are allowed to **change the control flow unconditionally**. - -## Define a label - -``` -== == -``` - -## Goto a label - -``` --> -``` - -## Example - -```python -for i in range(10): - for j in range(10): - for k in range(10): - -> exit - -== exit == -print('exit') -``` diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index b66b1eb7..ec00fdd4 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -75,7 +75,6 @@ typedef struct CodeObject { 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; diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index 5395832d..b07ed7b9 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -63,9 +63,6 @@ OPCODE(JUMP_IF_FALSE_OR_POP) OPCODE(SHORTCUT_IF_FALSE_OR_POP) OPCODE(LOOP_CONTINUE) OPCODE(LOOP_BREAK) -/***/ -OPCODE(JUMP_ABSOLUTE_TOP) -OPCODE(GOTO) /**************************/ OPCODE(CALL) OPCODE(CALL_VARGS) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 2e3b6af0..e0596b0b 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -3,11 +3,9 @@ #include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/sourcedata.h" #include "pocketpy/objects/object.h" -#include "pocketpy/common/strname.h" #include "pocketpy/common/sstream.h" #include "pocketpy/common/memorypool.h" #include -#include #include /* expr.h */ @@ -82,7 +80,6 @@ static int Ctx__emit_virtual(Ctx* self, Opcode opcode, uint16_t arg, int line, b static void Ctx__revert_last_emit_(Ctx* self); static int Ctx__emit_int(Ctx* self, int64_t value, int line); static void Ctx__patch_jump(Ctx* self, int index); -static bool Ctx__add_label(Ctx* self, py_Name name); static int Ctx__add_varname(Ctx* self, py_Name name); static int Ctx__add_const(Ctx* self, py_Ref); static int Ctx__add_const_string(Ctx* self, c11_sv); @@ -1191,13 +1188,6 @@ static void Ctx__patch_jump(Ctx* self, int index) { Bytecode__set_signed_arg(&co_codes[index], target - index); } -static bool Ctx__add_label(Ctx* self, py_Name name) { - bool ok = c11_smallmap_n2i__contains(&self->co->labels, name); - if(ok) return false; - c11_smallmap_n2i__set(&self->co->labels, name, self->co->codes.length); - return true; -} - static int Ctx__add_varname(Ctx* self, py_Name name) { // PK_MAX_CO_VARNAMES will be checked when pop_context(), not here return CodeObject__add_varname(self->co, name); @@ -2230,9 +2220,7 @@ static Error* consume_pep695_py312(Compiler* self) { Error* err; if(match(TK_LBRACKET)) { consume(TK_ID); - if(match(TK_COLON)){ - check(consume_type_hints(self)); - } + if(match(TK_COLON)) { check(consume_type_hints(self)); } consume(TK_RBRACKET); } return NULL; @@ -2445,18 +2433,25 @@ __EAT_DOTS_END: static Error* compile_try_except(Compiler* self) { Error* err; + int patches[8]; + int patches_length = 0; + Ctx__enter_block(ctx(), CodeBlockType_TRY_EXCEPT); Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line); check(compile_block_body(self, compile_stmt)); - int patches[8]; - int patches_length = 0; - - patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); + bool finally_matched = match(TK_FINALLY); + if(!finally_matched) { + patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); + } Ctx__exit_block(ctx()); - if(curr()->type == TK_FINALLY) { - return SyntaxError(self, "finally clause is not supported yet"); + if(finally_matched) { + // finally only, no except block + compile_block_body(self, compile_stmt); + // re-raise if needed + Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); + return NULL; } do { @@ -2466,14 +2461,17 @@ static Error* compile_try_except(Compiler* self) { py_Name as_name = 0; consume(TK_EXCEPT); if(is_expression(self, false)) { - check(EXPR(self)); // push assumed type on to the stack + // except : + check(EXPR(self)); Ctx__s_emit_top(ctx()); Ctx__emit_(ctx(), OP_EXCEPTION_MATCH, BC_NOARG, prev()->line); if(match(TK_AS)) { + // except as : consume(TK_ID); as_name = py_namev(Token__sv(prev())); } } else { + // except: Ctx__emit_(ctx(), OP_LOAD_TRUE, BC_NOARG, BC_KEEPLINE); } int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); @@ -2490,11 +2488,15 @@ static Error* compile_try_except(Compiler* self) { } while(curr()->type == TK_EXCEPT); // no match, re-raise - Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); + // ... - // no exception or no match, jump to the end + // match one & handled, jump to the end for(int i = 0; i < patches_length; i++) Ctx__patch_jump(ctx(), patches[i]); + + if(match(TK_FINALLY)) compile_block_body(self, compile_stmt); + // re-raise if needed + Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); return NULL; } @@ -2623,25 +2625,6 @@ static Error* compile_stmt(Compiler* self) { Ctx__exit_block(ctx()); } break; /*************************************************/ - case TK_EQ: { - consume(TK_ID); - if(mode() != EXEC_MODE) - return SyntaxError(self, "'label' is only available in EXEC_MODE"); - c11_sv name = Token__sv(prev()); - bool ok = Ctx__add_label(ctx(), py_namev(name)); - if(!ok) return SyntaxError(self, "label %q already exists", name); - consume(TK_EQ); - consume_end_stmt(); - } break; - case TK_ARROW: - consume(TK_ID); - if(mode() != EXEC_MODE) - return SyntaxError(self, "'goto' is only available in EXEC_MODE"); - py_Name name = py_namev(Token__sv(prev())); - Ctx__emit_(ctx(), OP_GOTO, name, prev()->line); - consume_end_stmt(); - break; - /*************************************************/ // handle dangling expression or assignment default: { // do revert since we have pre-called advance() at the beginning diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 49e21500..67725c34 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -2,7 +2,6 @@ #include "pocketpy/common/utils.h" #include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/vm.h" -#include "pocketpy/common/memorypool.h" #include "pocketpy/common/sstream.h" #include "pocketpy/objects/codeobject.h" #include "pocketpy/pocketpy.h" @@ -622,20 +621,6 @@ FrameResult VM__run_top_frame(VM* self) { int target = Frame__ip(frame) + byte.arg; Frame__prepare_jump_break(frame, &self->stack, target); DISPATCH_JUMP((int16_t)byte.arg); - } - case OP_JUMP_ABSOLUTE_TOP: { - int target = py_toint(TOP()); - POP(); - DISPATCH_JUMP_ABSOLUTE(target); - } - case OP_GOTO: { - int target = c11_smallmap_n2i__get(&frame->co->labels, byte.arg, -1); - if(target < 0) { - RuntimeError("label '%n' not found", byte.arg); - goto __ERROR; - } - Frame__prepare_jump_break(frame, &self->stack, target); - DISPATCH_JUMP_ABSOLUTE(target); } /*****************************************/ case OP_CALL: { @@ -1002,8 +987,10 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_RE_RAISE: { - assert(self->curr_exception.type); - goto __ERROR_RE_RAISE; + if(self->curr_exception.type && !self->is_curr_exc_handled) { + goto __ERROR_RE_RAISE; + } + DISPATCH(); } case OP_PUSH_EXCEPTION: { assert(self->curr_exception.type); diff --git a/src/modules/dis.c b/src/modules/dis.c index 7d1c936a..1d580c06 100644 --- a/src/modules/dis.c +++ b/src/modules/dis.c @@ -1,7 +1,4 @@ #include "pocketpy/pocketpy.h" - -#include "pocketpy/common/utils.h" -#include "pocketpy/objects/object.h" #include "pocketpy/common/sstream.h" #include "pocketpy/interpreter/vm.h" @@ -75,7 +72,6 @@ static void disassemble(CodeObject* co) { case OP_STORE_ATTR: case OP_DELETE_ATTR: case OP_BEGIN_CLASS: - case OP_GOTO: case OP_DELETE_GLOBAL: case OP_STORE_CLASS_ATTR: { pk_sprintf(&ss, " (%n)", byte.arg); diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 28842f33..d9e52fa8 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -125,7 +125,6 @@ void CodeObject__ctor(CodeObject* self, SourceData_ src, c11_sv name) { self->nlocals = 0; c11_smallmap_n2i__ctor(&self->varnames_inv); - c11_smallmap_n2i__ctor(&self->labels); c11_vector__ctor(&self->blocks, sizeof(CodeBlock)); c11_vector__ctor(&self->func_decls, sizeof(FuncDecl_)); @@ -148,7 +147,6 @@ void CodeObject__dtor(CodeObject* self) { c11_vector__dtor(&self->varnames); c11_smallmap_n2i__dtor(&self->varnames_inv); - c11_smallmap_n2i__dtor(&self->labels); c11_vector__dtor(&self->blocks); diff --git a/tests/27_goto.py b/tests/27_goto.py deleted file mode 100644 index 1ac563c9..00000000 --- a/tests/27_goto.py +++ /dev/null @@ -1,38 +0,0 @@ -a = [] - -for i in range(10): # [0] - for j in range(10): # [0-0] - -> test - print(2) - == test == - a.append(i) - for k in range(5): # [0-1] - for t in range(7): # [0-1-0] - pass - -assert a == list(range(10)) - -b = False - -for i in range(10): # [1] - for j in range(10): # [1-0] - -> out - b = True -== out == -assert not b - -sum = 0 -i = 1 - -== loop == -sum += i -i += 1 -if i <= 100: - -> loop - -assert sum == 5050 - -for i in range(4): - _ = 0 -# if there is no op here, the block check will fail -while i: i-=1 diff --git a/tests/28_exception.py b/tests/28_exception.py index 1a9cee99..f5d909cd 100644 --- a/tests/28_exception.py +++ b/tests/28_exception.py @@ -138,32 +138,38 @@ except Exception: exit(1) assert ok -# ok = False -# try: -# eval('1+') -# except SyntaxError as e: -# assert type(e) is SyntaxError -# ok = True -# assert ok +ok = False +try: + eval('1+') +except SyntaxError as e: + assert type(e) is SyntaxError + ok = True +assert ok + -""" # finally, only def finally_only(): try: raise KeyError finally: - return True - -assert finally_only() is True + x = 1 + +try: + finally_only() + exit(1) +except KeyError: + pass def finally_only_2(): + x = 0 try: pass finally: - return True - -assert finally_only_2() is True + x = 1 + return x +assert finally_only_2() == 1 + # finally, no exception def finally_no_exception(): ok = False @@ -208,4 +214,3 @@ except KeyError: exit(0) exit(1) -""" \ No newline at end of file diff --git a/tests/52_context.py b/tests/52_context.py index b27f6c20..53d22336 100644 --- a/tests/52_context.py +++ b/tests/52_context.py @@ -27,11 +27,4 @@ assert path == ['enter', 'in', 'exit'] path.clear() -with A(123) as a: - assert path == ['enter'] - -> end - path.append('in') - -== end == -assert path == ['enter']