mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
support for..else
and while..else
This commit is contained in:
parent
ef157946aa
commit
449c0c36f9
@ -42,9 +42,15 @@ struct CodeBlock {
|
|||||||
int for_loop_depth; // this is used for exception handling
|
int for_loop_depth; // this is used for exception handling
|
||||||
int start; // start index of this block in codes, inclusive
|
int start; // start index of this block in codes, inclusive
|
||||||
int end; // end index of this block in codes, exclusive
|
int end; // end index of this block in codes, exclusive
|
||||||
|
int end2; // ...
|
||||||
|
|
||||||
CodeBlock(CodeBlockType type, int parent, int for_loop_depth, int start):
|
CodeBlock(CodeBlockType type, int parent, int for_loop_depth, int start):
|
||||||
type(type), parent(parent), for_loop_depth(for_loop_depth), start(start), end(-1) {}
|
type(type), parent(parent), for_loop_depth(for_loop_depth), start(start), end(-1), end2(-1) {}
|
||||||
|
|
||||||
|
int get_break_end() const{
|
||||||
|
if(end2 != -1) return end2;
|
||||||
|
return end;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CodeObject;
|
struct CodeObject;
|
||||||
|
@ -53,8 +53,8 @@ struct CodeEmitContext{
|
|||||||
bool is_compiling_class = false;
|
bool is_compiling_class = false;
|
||||||
int for_loop_depth = 0;
|
int for_loop_depth = 0;
|
||||||
|
|
||||||
bool is_curr_block_loop() const;
|
int get_loop() const;
|
||||||
void enter_block(CodeBlockType type);
|
CodeBlock* enter_block(CodeBlockType type);
|
||||||
void exit_block();
|
void exit_block();
|
||||||
void emit_expr(); // clear the expression stack and generate bytecode
|
void emit_expr(); // clear the expression stack and generate bytecode
|
||||||
std::string _log_s_expr();
|
std::string _log_s_expr();
|
||||||
|
@ -482,10 +482,10 @@ __NEXT_STEP:;
|
|||||||
} else POP(); // [b]
|
} else POP(); // [b]
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(LOOP_CONTINUE)
|
TARGET(LOOP_CONTINUE)
|
||||||
frame->jump_abs(co_blocks[byte.block].start);
|
frame->jump_abs(co_blocks[byte.arg].start);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(LOOP_BREAK)
|
TARGET(LOOP_BREAK)
|
||||||
frame->jump_abs_break(co_blocks[byte.block].end);
|
frame->jump_abs_break(co_blocks[byte.arg].get_break_end());
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(GOTO) {
|
TARGET(GOTO) {
|
||||||
_name = StrName(byte.arg);
|
_name = StrName(byte.arg);
|
||||||
|
@ -559,13 +559,18 @@ __SUBSCR_END:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_while_loop() {
|
void Compiler::compile_while_loop() {
|
||||||
ctx()->enter_block(WHILE_LOOP);
|
CodeBlock* block = ctx()->enter_block(WHILE_LOOP);
|
||||||
EXPR(false); // condition
|
EXPR(false); // condition
|
||||||
int patch = ctx()->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line);
|
int patch = ctx()->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line);
|
||||||
compile_block_body();
|
compile_block_body();
|
||||||
ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE);
|
||||||
ctx()->patch_jump(patch);
|
ctx()->patch_jump(patch);
|
||||||
ctx()->exit_block();
|
ctx()->exit_block();
|
||||||
|
// optional else clause
|
||||||
|
if (match(TK("else"))) {
|
||||||
|
compile_block_body();
|
||||||
|
block->end2 = ctx()->co->codes.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_for_loop() {
|
void Compiler::compile_for_loop() {
|
||||||
@ -573,13 +578,18 @@ __SUBSCR_END:
|
|||||||
consume(TK("in"));
|
consume(TK("in"));
|
||||||
EXPR_TUPLE(false);
|
EXPR_TUPLE(false);
|
||||||
ctx()->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
||||||
ctx()->enter_block(FOR_LOOP);
|
CodeBlock* block = ctx()->enter_block(FOR_LOOP);
|
||||||
ctx()->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
|
||||||
bool ok = vars->emit_store(ctx());
|
bool ok = vars->emit_store(ctx());
|
||||||
if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind
|
if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind
|
||||||
compile_block_body();
|
compile_block_body();
|
||||||
ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE);
|
||||||
ctx()->exit_block();
|
ctx()->exit_block();
|
||||||
|
// optional else clause
|
||||||
|
if (match(TK("else"))) {
|
||||||
|
compile_block_body();
|
||||||
|
block->end2 = ctx()->co->codes.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_try_except() {
|
void Compiler::compile_try_except() {
|
||||||
@ -668,15 +678,16 @@ __SUBSCR_END:
|
|||||||
void Compiler::compile_stmt() {
|
void Compiler::compile_stmt() {
|
||||||
advance();
|
advance();
|
||||||
int kw_line = prev().line; // backup line number
|
int kw_line = prev().line; // backup line number
|
||||||
|
int curr_loop_block = ctx()->get_loop();
|
||||||
switch(prev().type){
|
switch(prev().type){
|
||||||
case TK("break"):
|
case TK("break"):
|
||||||
if (!ctx()->is_curr_block_loop()) SyntaxError("'break' outside loop");
|
if (curr_loop_block < 0) SyntaxError("'break' outside loop");
|
||||||
ctx()->emit(OP_LOOP_BREAK, BC_NOARG, kw_line);
|
ctx()->emit(OP_LOOP_BREAK, curr_loop_block, kw_line);
|
||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
case TK("continue"):
|
case TK("continue"):
|
||||||
if (!ctx()->is_curr_block_loop()) SyntaxError("'continue' not properly in loop");
|
if (curr_loop_block < 0) SyntaxError("'continue' not properly in loop");
|
||||||
ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, kw_line);
|
ctx()->emit(OP_LOOP_CONTINUE, curr_loop_block, kw_line);
|
||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
case TK("yield"):
|
case TK("yield"):
|
||||||
@ -696,7 +707,7 @@ __SUBSCR_END:
|
|||||||
ctx()->enter_block(FOR_LOOP);
|
ctx()->enter_block(FOR_LOOP);
|
||||||
ctx()->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
|
||||||
ctx()->emit(OP_YIELD_VALUE, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_YIELD_VALUE, BC_NOARG, BC_KEEPLINE);
|
||||||
ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
|
ctx()->emit(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE);
|
||||||
ctx()->exit_block();
|
ctx()->exit_block();
|
||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
break;
|
break;
|
||||||
|
15
src/expr.cpp
15
src/expr.cpp
@ -2,16 +2,23 @@
|
|||||||
|
|
||||||
namespace pkpy{
|
namespace pkpy{
|
||||||
|
|
||||||
bool CodeEmitContext::is_curr_block_loop() const {
|
int CodeEmitContext::get_loop() const {
|
||||||
return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
|
int index = curr_block_i;
|
||||||
|
while(index >= 0){
|
||||||
|
if(co->blocks[index].type == FOR_LOOP) break;
|
||||||
|
if(co->blocks[index].type == WHILE_LOOP) break;
|
||||||
|
index = co->blocks[index].parent;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeEmitContext::enter_block(CodeBlockType type){
|
CodeBlock* CodeEmitContext::enter_block(CodeBlockType type){
|
||||||
if(type == FOR_LOOP) for_loop_depth++;
|
if(type == FOR_LOOP) for_loop_depth++;
|
||||||
co->blocks.push_back(CodeBlock(
|
co->blocks.push_back(CodeBlock(
|
||||||
type, curr_block_i, for_loop_depth, (int)co->codes.size()
|
type, curr_block_i, for_loop_depth, (int)co->codes.size()
|
||||||
));
|
));
|
||||||
curr_block_i = co->blocks.size()-1;
|
curr_block_i = co->blocks.size()-1;
|
||||||
|
return &co->blocks[curr_block_i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeEmitContext::exit_block(){
|
void CodeEmitContext::exit_block(){
|
||||||
@ -338,7 +345,7 @@ namespace pkpy{
|
|||||||
expr->emit(ctx);
|
expr->emit(ctx);
|
||||||
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
|
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
ctx->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
|
ctx->emit(OP_LOOP_CONTINUE, ctx->get_loop(), BC_KEEPLINE);
|
||||||
ctx->exit_block();
|
ctx->exit_block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,3 +71,64 @@ assert d == 1
|
|||||||
d = 1 if 2 < 1 else 2
|
d = 1 if 2 < 1 else 2
|
||||||
assert d == 2
|
assert d == 2
|
||||||
|
|
||||||
|
t = 0
|
||||||
|
for i in range(5):
|
||||||
|
try:
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
t = 1
|
||||||
|
assert t == 0
|
||||||
|
|
||||||
|
t = 0
|
||||||
|
for i in range(5):
|
||||||
|
if True and 1:
|
||||||
|
break
|
||||||
|
t = 1
|
||||||
|
assert t == 0
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
if i==3:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
flag = False
|
||||||
|
for i in range(5):
|
||||||
|
if i==6:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
flag = True
|
||||||
|
assert flag is True
|
||||||
|
|
||||||
|
while True:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
flag = False
|
||||||
|
while False:
|
||||||
|
assert False
|
||||||
|
else:
|
||||||
|
flag = True
|
||||||
|
assert flag is True
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
while 0:
|
||||||
|
while True:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
x = 2
|
||||||
|
assert x == 2
|
||||||
|
|
||||||
|
if x == 2:
|
||||||
|
while 0:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
x = 3
|
||||||
|
assert x == 2
|
Loading…
x
Reference in New Issue
Block a user