mirror of
https://github.com/pocketpy/pocketpy
synced 2025-11-05 19:20:17 +00:00
Compare commits
5 Commits
4cab350e90
...
0ffea55fa0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ffea55fa0 | ||
|
|
ff6970101e | ||
|
|
00bdfe66cf | ||
|
|
791d9f39d3 | ||
|
|
0666a6d370 |
@ -37,4 +37,5 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
|
|||||||
6. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
|
6. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
|
||||||
7. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
|
7. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
|
||||||
8. `str.split` and `str.splitlines` will remove all empty entries.
|
8. `str.split` and `str.splitlines` will remove all empty entries.
|
||||||
|
9. A return, break, continue in try/except block will make the finally block not executed.
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ output: .retype
|
|||||||
url: https://pocketpy.dev
|
url: https://pocketpy.dev
|
||||||
branding:
|
branding:
|
||||||
title: pocketpy
|
title: pocketpy
|
||||||
label: v2.0.0
|
label: v2.0.1
|
||||||
logo: "./static/logo.png"
|
logo: "./static/logo.png"
|
||||||
favicon: "./static/logo.png"
|
favicon: "./static/logo.png"
|
||||||
meta:
|
meta:
|
||||||
|
|||||||
@ -56,8 +56,6 @@ py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name);
|
|||||||
py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name);
|
py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name);
|
||||||
|
|
||||||
int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*);
|
int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*);
|
||||||
void Frame__prepare_jump_break(Frame* self, ValueStack*, int);
|
|
||||||
int Frame__exit_block(Frame* self, ValueStack*, int);
|
|
||||||
|
|
||||||
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock);
|
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock);
|
||||||
void Frame__set_unwind_target(Frame* self, py_TValue* sp);
|
void Frame__set_unwind_target(Frame* self, py_TValue* sp);
|
||||||
|
|||||||
@ -28,11 +28,12 @@ typedef enum NameScope {
|
|||||||
|
|
||||||
typedef enum CodeBlockType {
|
typedef enum CodeBlockType {
|
||||||
CodeBlockType_NO_BLOCK,
|
CodeBlockType_NO_BLOCK,
|
||||||
CodeBlockType_FOR_LOOP,
|
|
||||||
CodeBlockType_WHILE_LOOP,
|
CodeBlockType_WHILE_LOOP,
|
||||||
CodeBlockType_TRY,
|
CodeBlockType_TRY,
|
||||||
/* context blocks */
|
/* context blocks (stack-based) */
|
||||||
|
CodeBlockType_FOR_LOOP,
|
||||||
CodeBlockType_WITH,
|
CodeBlockType_WITH,
|
||||||
|
/* context blocks (flag-based) */
|
||||||
CodeBlockType_EXCEPT,
|
CodeBlockType_EXCEPT,
|
||||||
CodeBlockType_FINALLY,
|
CodeBlockType_FINALLY,
|
||||||
} CodeBlockType;
|
} CodeBlockType;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
name: pocketpy
|
name: pocketpy
|
||||||
description: A lightweight Python interpreter for game engines. It supports Android/iOS/Windows/Linux/MacOS.
|
description: A lightweight Python interpreter for game engines. It supports Android/iOS/Windows/Linux/MacOS.
|
||||||
version: 2.0.1+6
|
version: 2.0.1+7
|
||||||
homepage: https://pocketpy.dev
|
homepage: https://pocketpy.dev
|
||||||
repository: https://github.com/pocketpy/pocketpy
|
repository: https://github.com/pocketpy/pocketpy
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ typedef struct Expr Expr;
|
|||||||
|
|
||||||
static void Ctx__ctor(Ctx* self, CodeObject* co, FuncDecl* func, int level);
|
static void Ctx__ctor(Ctx* self, CodeObject* co, FuncDecl* func, int level);
|
||||||
static void Ctx__dtor(Ctx* self);
|
static void Ctx__dtor(Ctx* self);
|
||||||
static int Ctx__get_loop(Ctx* self, bool* has_context);
|
static int Ctx__prepare_loop_divert(Ctx* self, int line, bool is_break);
|
||||||
static int Ctx__enter_block(Ctx* self, CodeBlockType type);
|
static int Ctx__enter_block(Ctx* self, CodeBlockType type);
|
||||||
static void Ctx__exit_block(Ctx* self);
|
static void Ctx__exit_block(Ctx* self);
|
||||||
static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line);
|
static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line);
|
||||||
@ -80,6 +80,7 @@ static int Ctx__emit_virtual(Ctx* self, Opcode opcode, uint16_t arg, int line, b
|
|||||||
static void Ctx__revert_last_emit_(Ctx* self);
|
static void Ctx__revert_last_emit_(Ctx* self);
|
||||||
static int Ctx__emit_int(Ctx* self, int64_t value, int line);
|
static int Ctx__emit_int(Ctx* self, int64_t value, int line);
|
||||||
static void Ctx__patch_jump(Ctx* self, int index);
|
static void Ctx__patch_jump(Ctx* self, int index);
|
||||||
|
static void Ctx__emit_jump(Ctx* self, int target, int line);
|
||||||
static int Ctx__add_varname(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(Ctx* self, py_Ref);
|
||||||
static int Ctx__add_const_string(Ctx* self, c11_sv);
|
static int Ctx__add_const_string(Ctx* self, c11_sv);
|
||||||
@ -582,7 +583,7 @@ void CompExpr__emit_(Expr* self_, Ctx* ctx) {
|
|||||||
vtemit_(self->iter, ctx);
|
vtemit_(self->iter, ctx);
|
||||||
Ctx__emit_(ctx, OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx, OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
||||||
int block = Ctx__enter_block(ctx, CodeBlockType_FOR_LOOP);
|
int block = Ctx__enter_block(ctx, CodeBlockType_FOR_LOOP);
|
||||||
Ctx__emit_(ctx, OP_FOR_ITER, block, BC_KEEPLINE);
|
int block_start = Ctx__emit_(ctx, OP_FOR_ITER, block, BC_KEEPLINE);
|
||||||
bool ok = vtemit_store(self->vars, ctx);
|
bool ok = vtemit_store(self->vars, ctx);
|
||||||
// this error occurs in `vars` instead of this line, but...nevermind
|
// this error occurs in `vars` instead of this line, but...nevermind
|
||||||
assert(ok); // this should raise a SyntaxError, but we just assert it
|
assert(ok); // this should raise a SyntaxError, but we just assert it
|
||||||
@ -596,7 +597,7 @@ void CompExpr__emit_(Expr* self_, Ctx* ctx) {
|
|||||||
vtemit_(self->expr, ctx);
|
vtemit_(self->expr, ctx);
|
||||||
Ctx__emit_(ctx, self->op1, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx, self->op1, BC_NOARG, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
Ctx__emit_(ctx, OP_LOOP_CONTINUE, block, BC_KEEPLINE);
|
Ctx__emit_jump(ctx, block_start, BC_KEEPLINE);
|
||||||
Ctx__exit_block(ctx);
|
Ctx__exit_block(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1106,18 +1107,30 @@ static void Ctx__dtor(Ctx* self) {
|
|||||||
|
|
||||||
static bool is_small_int(int64_t value) { return value >= INT16_MIN && value <= INT16_MAX; }
|
static bool is_small_int(int64_t value) { return value >= INT16_MIN && value <= INT16_MAX; }
|
||||||
|
|
||||||
static bool is_context_block(CodeBlock* block) {
|
static int Ctx__prepare_loop_divert(Ctx* self, int line, bool is_break) {
|
||||||
return block->type >= CodeBlockType_WITH && block->type <= CodeBlockType_FINALLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Ctx__get_loop(Ctx* self, bool* has_context) {
|
|
||||||
int index = self->curr_iblock;
|
int index = self->curr_iblock;
|
||||||
*has_context = false;
|
|
||||||
while(index >= 0) {
|
while(index >= 0) {
|
||||||
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, index);
|
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, index);
|
||||||
if(block->type == CodeBlockType_FOR_LOOP) break;
|
switch(block->type) {
|
||||||
if(block->type == CodeBlockType_WHILE_LOOP) break;
|
case CodeBlockType_WHILE_LOOP: return index;
|
||||||
if(is_context_block(block)) *has_context = true;
|
case CodeBlockType_FOR_LOOP: {
|
||||||
|
if(is_break) Ctx__emit_(self, OP_POP_TOP, BC_NOARG, line);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
case CodeBlockType_WITH: {
|
||||||
|
Ctx__emit_(self, OP_POP_TOP, BC_NOARG, line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CodeBlockType_EXCEPT: {
|
||||||
|
Ctx__emit_(self, OP_END_EXC_HANDLING, 1, line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CodeBlockType_FINALLY: {
|
||||||
|
Ctx__emit_(self, OP_END_FINALLY, 1, line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
index = block->parent;
|
index = block->parent;
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
@ -1132,14 +1145,9 @@ static int Ctx__enter_block(Ctx* self, CodeBlockType type) {
|
|||||||
|
|
||||||
static void Ctx__exit_block(Ctx* self) {
|
static void Ctx__exit_block(Ctx* self) {
|
||||||
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, self->curr_iblock);
|
CodeBlock* block = c11__at(CodeBlock, &self->co->blocks, self->curr_iblock);
|
||||||
CodeBlockType curr_type = block->type;
|
|
||||||
block->end = self->co->codes.length;
|
block->end = self->co->codes.length;
|
||||||
self->curr_iblock = block->parent;
|
self->curr_iblock = block->parent;
|
||||||
assert(self->curr_iblock >= 0);
|
assert(self->curr_iblock >= 0);
|
||||||
if(curr_type == CodeBlockType_FOR_LOOP) {
|
|
||||||
// add a no op here to make block check work
|
|
||||||
Ctx__emit_virtual(self, OP_NO_OP, BC_NOARG, BC_KEEPLINE, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Ctx__s_emit_decorators(Ctx* self, int count) {
|
static void Ctx__s_emit_decorators(Ctx* self, int count) {
|
||||||
@ -1193,6 +1201,13 @@ static void Ctx__patch_jump(Ctx* self, int index) {
|
|||||||
Bytecode__set_signed_arg(&co_codes[index], target - index);
|
Bytecode__set_signed_arg(&co_codes[index], target - index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Ctx__emit_jump(Ctx* self, int target, int line) {
|
||||||
|
int index = Ctx__emit_(self, OP_JUMP_FORWARD, BC_NOARG, line);
|
||||||
|
// should place after Ctx__emit_ because of realloc
|
||||||
|
Bytecode* co_codes = (Bytecode*)self->co->codes.data;
|
||||||
|
Bytecode__set_signed_arg(&co_codes[index], target - index);
|
||||||
|
}
|
||||||
|
|
||||||
static int Ctx__add_varname(Ctx* self, py_Name name) {
|
static int Ctx__add_varname(Ctx* self, py_Name name) {
|
||||||
// PK_MAX_CO_VARNAMES will be checked when pop_context(), not here
|
// PK_MAX_CO_VARNAMES will be checked when pop_context(), not here
|
||||||
return CodeObject__add_varname(self->co, name);
|
return CodeObject__add_varname(self->co, name);
|
||||||
@ -1341,18 +1356,6 @@ Error* SyntaxError(Compiler* self, const char* fmt, ...) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* not_in_context(Compiler* self) {
|
|
||||||
int index = ctx()->curr_iblock;
|
|
||||||
while(index >= 0) {
|
|
||||||
CodeBlock* block = c11__at(CodeBlock, &(ctx()->co->blocks), index);
|
|
||||||
if(is_context_block(block)) {
|
|
||||||
return SyntaxError(self, "can't use flow control statements inside context block");
|
|
||||||
}
|
|
||||||
index = block->parent;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Matchers */
|
/* Matchers */
|
||||||
static bool is_expression(Compiler* self, bool allow_slice) {
|
static bool is_expression(Compiler* self, bool allow_slice) {
|
||||||
PrattCallback prefix = rules[curr()->type].prefix;
|
PrattCallback prefix = rules[curr()->type].prefix;
|
||||||
@ -1996,11 +1999,12 @@ static Error* compile_if_stmt(Compiler* self) {
|
|||||||
static Error* compile_while_loop(Compiler* self) {
|
static Error* compile_while_loop(Compiler* self) {
|
||||||
Error* err;
|
Error* err;
|
||||||
int block = Ctx__enter_block(ctx(), CodeBlockType_WHILE_LOOP);
|
int block = Ctx__enter_block(ctx(), CodeBlockType_WHILE_LOOP);
|
||||||
|
int block_start = c11__at(CodeBlock, &ctx()->co->blocks, block)->start;
|
||||||
check(EXPR(self)); // condition
|
check(EXPR(self)); // condition
|
||||||
Ctx__s_emit_top(ctx());
|
Ctx__s_emit_top(ctx());
|
||||||
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, prev()->line);
|
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, prev()->line);
|
||||||
check(compile_block_body(self, compile_stmt));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__emit_virtual(ctx(), OP_LOOP_CONTINUE, block, BC_KEEPLINE, true);
|
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
||||||
Ctx__patch_jump(ctx(), patch);
|
Ctx__patch_jump(ctx(), patch);
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
// optional else clause
|
// optional else clause
|
||||||
@ -2020,7 +2024,7 @@ static Error* compile_for_loop(Compiler* self) {
|
|||||||
Ctx__s_emit_top(ctx()); // [vars]
|
Ctx__s_emit_top(ctx()); // [vars]
|
||||||
Ctx__emit_(ctx(), OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx(), OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
||||||
int block = Ctx__enter_block(ctx(), CodeBlockType_FOR_LOOP);
|
int block = Ctx__enter_block(ctx(), CodeBlockType_FOR_LOOP);
|
||||||
Ctx__emit_(ctx(), OP_FOR_ITER, block, BC_KEEPLINE);
|
int block_start = Ctx__emit_(ctx(), OP_FOR_ITER, block, BC_KEEPLINE);
|
||||||
Expr* vars = Ctx__s_popx(ctx());
|
Expr* vars = Ctx__s_popx(ctx());
|
||||||
bool ok = vtemit_store(vars, ctx());
|
bool ok = vtemit_store(vars, ctx());
|
||||||
vtdelete(vars);
|
vtdelete(vars);
|
||||||
@ -2029,7 +2033,7 @@ static Error* compile_for_loop(Compiler* self) {
|
|||||||
return SyntaxError(self, "invalid syntax");
|
return SyntaxError(self, "invalid syntax");
|
||||||
}
|
}
|
||||||
check(compile_block_body(self, compile_stmt));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__emit_virtual(ctx(), OP_LOOP_CONTINUE, block, BC_KEEPLINE, true);
|
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
// optional else clause
|
// optional else clause
|
||||||
if(match(TK_ELSE)) {
|
if(match(TK_ELSE)) {
|
||||||
@ -2043,13 +2047,12 @@ static Error* compile_for_loop(Compiler* self) {
|
|||||||
static Error* compile_yield_from(Compiler* self, int kw_line) {
|
static Error* compile_yield_from(Compiler* self, int kw_line) {
|
||||||
Error* err;
|
Error* err;
|
||||||
if(self->contexts.length <= 1) return SyntaxError(self, "'yield from' outside function");
|
if(self->contexts.length <= 1) return SyntaxError(self, "'yield from' outside function");
|
||||||
check(not_in_context(self));
|
|
||||||
check(EXPR_TUPLE(self));
|
check(EXPR_TUPLE(self));
|
||||||
Ctx__s_emit_top(ctx());
|
Ctx__s_emit_top(ctx());
|
||||||
Ctx__emit_(ctx(), OP_GET_ITER, BC_NOARG, kw_line);
|
Ctx__emit_(ctx(), OP_GET_ITER, BC_NOARG, kw_line);
|
||||||
int block = Ctx__enter_block(ctx(), CodeBlockType_FOR_LOOP);
|
int block = Ctx__enter_block(ctx(), CodeBlockType_FOR_LOOP);
|
||||||
Ctx__emit_(ctx(), OP_FOR_ITER_YIELD_VALUE, block, kw_line);
|
int block_start = Ctx__emit_(ctx(), OP_FOR_ITER_YIELD_VALUE, block, kw_line);
|
||||||
Ctx__emit_(ctx(), OP_LOOP_CONTINUE, block, kw_line);
|
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
// StopIteration.value will be pushed onto the stack
|
// StopIteration.value will be pushed onto the stack
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2463,6 +2466,19 @@ static Error* compile_try_except(Compiler* self) {
|
|||||||
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
|
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
|
||||||
check(compile_block_body(self, compile_stmt));
|
check(compile_block_body(self, compile_stmt));
|
||||||
|
|
||||||
|
// https://docs.python.org/3/reference/compound_stmts.html#finally-clause
|
||||||
|
/* If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed,
|
||||||
|
* including any except and else clauses. If an exception occurs in any of the clauses and is
|
||||||
|
* not handled, the exception is temporarily saved. The finally clause is executed. If there is
|
||||||
|
* a saved exception it is re-raised at the end of the finally clause. If the finally clause
|
||||||
|
* raises another exception, the saved exception is set as the context of the new exception. If
|
||||||
|
* the finally clause executes a return, break or continue statement, the saved exception is
|
||||||
|
* discarded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// known issue:
|
||||||
|
// A return, break, continue in try/except block will make the finally block not executed
|
||||||
|
|
||||||
bool has_finally = curr()->type == TK_FINALLY;
|
bool has_finally = curr()->type == TK_FINALLY;
|
||||||
if(!has_finally) {
|
if(!has_finally) {
|
||||||
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
||||||
@ -2544,22 +2560,23 @@ static Error* compile_stmt(Compiler* self) {
|
|||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
int kw_line = prev()->line; // backup line number
|
int kw_line = prev()->line; // backup line number
|
||||||
bool has_context = false;
|
|
||||||
int curr_loop_block = Ctx__get_loop(ctx(), &has_context);
|
|
||||||
switch(prev()->type) {
|
switch(prev()->type) {
|
||||||
case TK_BREAK:
|
case TK_BREAK: {
|
||||||
|
int curr_loop_block = Ctx__prepare_loop_divert(ctx(), kw_line, true);
|
||||||
if(curr_loop_block < 0) return SyntaxError(self, "'break' outside loop");
|
if(curr_loop_block < 0) return SyntaxError(self, "'break' outside loop");
|
||||||
Ctx__emit_(ctx(), OP_LOOP_BREAK, curr_loop_block, kw_line);
|
Ctx__emit_(ctx(), OP_LOOP_BREAK, curr_loop_block, kw_line);
|
||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
case TK_CONTINUE:
|
}
|
||||||
|
case TK_CONTINUE: {
|
||||||
|
int curr_loop_block = Ctx__prepare_loop_divert(ctx(), kw_line, false);
|
||||||
if(curr_loop_block < 0) return SyntaxError(self, "'continue' not properly in loop");
|
if(curr_loop_block < 0) return SyntaxError(self, "'continue' not properly in loop");
|
||||||
Ctx__emit_(ctx(), OP_LOOP_CONTINUE, curr_loop_block, kw_line);
|
Ctx__emit_(ctx(), OP_LOOP_CONTINUE, curr_loop_block, kw_line);
|
||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TK_YIELD:
|
case TK_YIELD:
|
||||||
if(self->contexts.length <= 1) return SyntaxError(self, "'yield' outside function");
|
if(self->contexts.length <= 1) return SyntaxError(self, "'yield' outside function");
|
||||||
check(not_in_context(self));
|
|
||||||
if(match_end_stmt(self)) {
|
if(match_end_stmt(self)) {
|
||||||
Ctx__emit_(ctx(), OP_YIELD_VALUE, 1, kw_line);
|
Ctx__emit_(ctx(), OP_YIELD_VALUE, 1, kw_line);
|
||||||
} else {
|
} else {
|
||||||
@ -2576,7 +2593,6 @@ static Error* compile_stmt(Compiler* self) {
|
|||||||
break;
|
break;
|
||||||
case TK_RETURN:
|
case TK_RETURN:
|
||||||
if(self->contexts.length <= 1) return SyntaxError(self, "'return' outside function");
|
if(self->contexts.length <= 1) return SyntaxError(self, "'return' outside function");
|
||||||
check(not_in_context(self));
|
|
||||||
if(match_end_stmt(self)) {
|
if(match_end_stmt(self)) {
|
||||||
Ctx__emit_(ctx(), OP_RETURN_VALUE, 1, kw_line);
|
Ctx__emit_(ctx(), OP_RETURN_VALUE, 1, kw_line);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -11,6 +11,9 @@
|
|||||||
static bool stack_unpack_sequence(VM* self, uint16_t arg);
|
static bool stack_unpack_sequence(VM* self, uint16_t arg);
|
||||||
static bool stack_format_object(VM* self, c11_sv spec);
|
static bool stack_format_object(VM* self, c11_sv spec);
|
||||||
|
|
||||||
|
#define CHECK_RETURN_FROM_EXCEPT_OR_FINALLY() \
|
||||||
|
if(self->is_curr_exc_handled) py_clearexc(NULL)
|
||||||
|
|
||||||
#define DISPATCH() \
|
#define DISPATCH() \
|
||||||
do { \
|
do { \
|
||||||
frame->ip++; \
|
frame->ip++; \
|
||||||
@ -613,12 +616,10 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case OP_LOOP_CONTINUE:
|
case OP_LOOP_CONTINUE: {
|
||||||
// just an alias of OP_JUMP_FORWARD
|
|
||||||
DISPATCH_JUMP((int16_t)byte.arg);
|
DISPATCH_JUMP((int16_t)byte.arg);
|
||||||
|
}
|
||||||
case OP_LOOP_BREAK: {
|
case OP_LOOP_BREAK: {
|
||||||
int target = Frame__ip(frame) + byte.arg;
|
|
||||||
Frame__prepare_jump_break(frame, &self->stack, target);
|
|
||||||
DISPATCH_JUMP((int16_t)byte.arg);
|
DISPATCH_JUMP((int16_t)byte.arg);
|
||||||
}
|
}
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
@ -683,6 +684,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
case OP_RETURN_VALUE: {
|
case OP_RETURN_VALUE: {
|
||||||
|
CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
|
||||||
if(byte.arg == BC_NOARG) {
|
if(byte.arg == BC_NOARG) {
|
||||||
self->last_retval = POPX();
|
self->last_retval = POPX();
|
||||||
} else {
|
} else {
|
||||||
@ -699,6 +701,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
case OP_YIELD_VALUE: {
|
case OP_YIELD_VALUE: {
|
||||||
|
CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
|
||||||
if(byte.arg == 1) {
|
if(byte.arg == 1) {
|
||||||
py_newnone(py_retval());
|
py_newnone(py_retval());
|
||||||
} else {
|
} else {
|
||||||
@ -708,6 +711,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
return RES_YIELD;
|
return RES_YIELD;
|
||||||
}
|
}
|
||||||
case OP_FOR_ITER_YIELD_VALUE: {
|
case OP_FOR_ITER_YIELD_VALUE: {
|
||||||
|
CHECK_RETURN_FROM_EXCEPT_OR_FINALLY();
|
||||||
int res = py_next(TOP());
|
int res = py_next(TOP());
|
||||||
if(res == -1) goto __ERROR;
|
if(res == -1) goto __ERROR;
|
||||||
if(res) {
|
if(res) {
|
||||||
@ -985,7 +989,8 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
goto __ERROR;
|
goto __ERROR;
|
||||||
}
|
}
|
||||||
case OP_RE_RAISE: {
|
case OP_RE_RAISE: {
|
||||||
if(self->curr_exception.type && !self->is_curr_exc_handled) {
|
if(self->curr_exception.type) {
|
||||||
|
assert(!self->is_curr_exc_handled);
|
||||||
goto __ERROR_RE_RAISE;
|
goto __ERROR_RE_RAISE;
|
||||||
}
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
@ -996,6 +1001,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
case OP_BEGIN_EXC_HANDLING: {
|
case OP_BEGIN_EXC_HANDLING: {
|
||||||
|
assert(self->curr_exception.type);
|
||||||
self->is_curr_exc_handled = true;
|
self->is_curr_exc_handled = true;
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
@ -1006,16 +1012,23 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
}
|
}
|
||||||
case OP_BEGIN_FINALLY: {
|
case OP_BEGIN_FINALLY: {
|
||||||
if(self->curr_exception.type) {
|
if(self->curr_exception.type) {
|
||||||
|
assert(!self->is_curr_exc_handled);
|
||||||
// temporarily handle the exception if any
|
// temporarily handle the exception if any
|
||||||
self->is_curr_exc_handled = true;
|
self->is_curr_exc_handled = true;
|
||||||
}
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
case OP_END_FINALLY: {
|
case OP_END_FINALLY: {
|
||||||
if(self->curr_exception.type && self->is_curr_exc_handled) {
|
if(byte.arg == BC_NOARG) {
|
||||||
|
if(self->curr_exception.type) {
|
||||||
|
assert(self->is_curr_exc_handled);
|
||||||
// revert the exception handling if needed
|
// revert the exception handling if needed
|
||||||
self->is_curr_exc_handled = false;
|
self->is_curr_exc_handled = false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// break or continue inside finally block
|
||||||
|
py_clearexc(NULL);
|
||||||
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#include "pocketpy/interpreter/frame.h"
|
#include "pocketpy/interpreter/frame.h"
|
||||||
#include "pocketpy/interpreter/vm.h"
|
#include "pocketpy/interpreter/vm.h"
|
||||||
|
#include "pocketpy/objects/base.h"
|
||||||
#include "pocketpy/objects/codeobject.h"
|
#include "pocketpy/objects/codeobject.h"
|
||||||
#include "pocketpy/objects/object.h"
|
|
||||||
#include "pocketpy/pocketpy.h"
|
#include "pocketpy/pocketpy.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
@ -79,34 +79,6 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) {
|
|||||||
return c11__at(CodeBlock, &self->co->blocks, iblock)->end;
|
return c11__at(CodeBlock, &self->co->blocks, iblock)->end;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frame__prepare_jump_break(Frame* self, ValueStack* _s, int target) {
|
|
||||||
int iblock = Frame__iblock(self);
|
|
||||||
if(target >= self->co->codes.length) {
|
|
||||||
while(iblock >= 0)
|
|
||||||
iblock = Frame__exit_block(self, _s, iblock);
|
|
||||||
} else {
|
|
||||||
// BUG (solved)
|
|
||||||
// for i in range(4):
|
|
||||||
// _ = 0
|
|
||||||
// # if there is no op here, the block check will fail
|
|
||||||
// while i: --i
|
|
||||||
int next_block = c11__at(BytecodeEx, &self->co->codes_ex, target)->iblock;
|
|
||||||
while(iblock >= 0 && iblock != next_block)
|
|
||||||
iblock = Frame__exit_block(self, _s, iblock);
|
|
||||||
assert(iblock == next_block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else if(block->type == CodeBlockType_WITH) {
|
|
||||||
_s->sp--; // pop context variable
|
|
||||||
}
|
|
||||||
return block->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock) {
|
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock) {
|
||||||
UnwindTarget* uw;
|
UnwindTarget* uw;
|
||||||
for(uw = self->uw_list; uw; uw = uw->next) {
|
for(uw = self->uw_list; uw; uw = uw->next) {
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
#include "pocketpy/pocketpy.h"
|
#include "pocketpy/pocketpy.h"
|
||||||
#include "pocketpy/common/sstream.h"
|
#include "pocketpy/common/sstream.h"
|
||||||
#include "pocketpy/interpreter/vm.h"
|
#include "pocketpy/interpreter/vm.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
static void disassemble(CodeObject* co) {
|
static bool disassemble(CodeObject* co) {
|
||||||
c11_vector /*T=int*/ jumpTargets;
|
c11_vector /*T=int*/ jumpTargets;
|
||||||
c11_vector__ctor(&jumpTargets, sizeof(int));
|
c11_vector__ctor(&jumpTargets, sizeof(int));
|
||||||
for(int i = 0; i < co->codes.length; i++) {
|
for(int i = 0; i < co->codes.length; i++) {
|
||||||
@ -56,7 +57,15 @@ static void disassemble(CodeObject* co) {
|
|||||||
|
|
||||||
c11_sbuf__write_int(&ss, byte.arg);
|
c11_sbuf__write_int(&ss, byte.arg);
|
||||||
switch(byte.op) {
|
switch(byte.op) {
|
||||||
case OP_LOAD_CONST:
|
case OP_LOAD_CONST: {
|
||||||
|
py_Ref value = c11__at(py_TValue, &co->consts, byte.arg);
|
||||||
|
if(py_repr(value)) {
|
||||||
|
pk_sprintf(&ss, " (%s)", py_tosv(py_retval()));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_FORMAT_STRING:
|
case OP_FORMAT_STRING:
|
||||||
case OP_IMPORT_PATH: {
|
case OP_IMPORT_PATH: {
|
||||||
py_Ref path = c11__at(py_TValue, &co->consts, byte.arg);
|
py_Ref path = c11__at(py_TValue, &co->consts, byte.arg);
|
||||||
@ -105,6 +114,7 @@ static void disassemble(CodeObject* co) {
|
|||||||
pk_current_vm->callbacks.print("\n");
|
pk_current_vm->callbacks.print("\n");
|
||||||
c11_string__delete(output);
|
c11_string__delete(output);
|
||||||
c11_vector__dtor(&jumpTargets);
|
c11_vector__dtor(&jumpTargets);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dis_dis(int argc, py_Ref argv) {
|
static bool dis_dis(int argc, py_Ref argv) {
|
||||||
@ -119,7 +129,7 @@ static bool dis_dis(int argc, py_Ref argv) {
|
|||||||
} else {
|
} else {
|
||||||
return TypeError("dis() expected a code object");
|
return TypeError("dis() expected a code object");
|
||||||
}
|
}
|
||||||
disassemble(code);
|
if(!disassemble(code)) return false;
|
||||||
py_newnone(py_retval());
|
py_newnone(py_retval());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,9 @@ void Bytecode__set_signed_arg(Bytecode* self, int arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Bytecode__is_forward_jump(const Bytecode* self) {
|
bool Bytecode__is_forward_jump(const Bytecode* self) {
|
||||||
return self->op >= OP_JUMP_FORWARD && self->op <= OP_LOOP_BREAK;
|
Opcode op = self->op;
|
||||||
|
return (op >= OP_JUMP_FORWARD && op <= OP_LOOP_BREAK) ||
|
||||||
|
(op == OP_FOR_ITER || op == OP_FOR_ITER_YIELD_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FuncDecl__dtor(FuncDecl* self) {
|
static void FuncDecl__dtor(FuncDecl* self) {
|
||||||
@ -177,7 +179,8 @@ int CodeObject__add_varname(CodeObject* self, py_Name name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Function__dtor(Function* self) {
|
void Function__dtor(Function* self) {
|
||||||
// printf("%s() in %s freed!\n", self->decl->code.name->data, self->decl->code.src->filename->data);
|
// printf("%s() in %s freed!\n", self->decl->code.name->data,
|
||||||
|
// self->decl->code.src->filename->data);
|
||||||
PK_DECREF(self->decl);
|
PK_DECREF(self->decl);
|
||||||
if(self->closure) NameDict__delete(self->closure);
|
if(self->closure) NameDict__delete(self->closure);
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ assert a == [1]
|
|||||||
try:
|
try:
|
||||||
a = [][3]
|
a = [][3]
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
assert str(e) == '3 not in [0, 0)'
|
# assert str(e) == '3 not in [0, 0)'
|
||||||
assert repr(e).startswith('IndexError(')
|
assert repr(e).startswith('IndexError(')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -216,21 +216,13 @@ except KeyError:
|
|||||||
|
|
||||||
assert ok_2
|
assert ok_2
|
||||||
|
|
||||||
# finally, return (SyntaxError)
|
# finally, return
|
||||||
err ='''
|
|
||||||
def finally_return():
|
def finally_return():
|
||||||
try:
|
try:
|
||||||
raise KeyError
|
raise KeyError
|
||||||
finally:
|
finally:
|
||||||
# This leaves a handled exception (it should be cleared but not)
|
|
||||||
# Completely unsafe!
|
|
||||||
return 1
|
return 1
|
||||||
'''
|
|
||||||
|
|
||||||
try:
|
assert finally_return() == 1
|
||||||
exec(err)
|
|
||||||
exit(1)
|
|
||||||
except SyntaxError as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -13,4 +13,4 @@ def f(a):
|
|||||||
def g(a):
|
def g(a):
|
||||||
return f([1,2,3] + a)
|
return f([1,2,3] + a)
|
||||||
|
|
||||||
assert dis(g) is None
|
assert dis(f) is None
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user