mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
remove goto
and support finally
This commit is contained in:
parent
4ebe09fc82
commit
2bca80ff7f
@ -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')
|
||||
```
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
@ -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)
|
||||
"""
|
@ -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']
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user