remove goto and support finally

This commit is contained in:
blueloveTH 2024-10-25 18:10:59 +08:00
parent 4ebe09fc82
commit 2bca80ff7f
10 changed files with 48 additions and 158 deletions

View File

@ -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
```
== <identifier> ==
```
## Goto a label
```
-> <identifier>
```
## Example
```python
for i in range(10):
for j in range(10):
for k in range(10):
-> exit
== exit ==
print('exit')
```

View File

@ -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;

View File

@ -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)

View File

@ -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 <assert.h>
#include <ctype.h>
#include <stdbool.h>
/* 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 <expr>:
check(EXPR(self));
Ctx__s_emit_top(ctx());
Ctx__emit_(ctx(), OP_EXCEPTION_MATCH, BC_NOARG, prev()->line);
if(match(TK_AS)) {
// except <expr> as <name>:
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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)
"""

View File

@ -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']