mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
This commit is contained in:
parent
56d66ad980
commit
664fc07dcd
@ -7,7 +7,6 @@ These are the undefined behaviours of pkpy. The behaviour of pkpy is undefined i
|
|||||||
|
|
||||||
1. Delete a builtin object. For example, `del int.__add__`.
|
1. Delete a builtin object. For example, `del int.__add__`.
|
||||||
2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`.
|
2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`.
|
||||||
3. Use goto statement to jump out of a context block.
|
3. Type `T`'s `__new__` returns an object that is not an instance of `T`.
|
||||||
4. Type `T`'s `__new__` returns an object that is not an instance of `T`.
|
4. Call `__new__` with a type that is not a subclass of `type`.
|
||||||
5. Call `__new__` with a type that is not a subclass of `type`.
|
5. `__eq__`, `__lt__` or `__contains__`, etc.. returns a value that is not a boolean.
|
||||||
6. `__eq__`, `__lt__` or `__contains__`, etc.. returns a value that is not a boolean.
|
|
||||||
|
@ -24,7 +24,7 @@ struct Bytecode{
|
|||||||
uint16_t arg;
|
uint16_t arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CodeBlockType {
|
enum class CodeBlockType {
|
||||||
NO_BLOCK,
|
NO_BLOCK,
|
||||||
FOR_LOOP,
|
FOR_LOOP,
|
||||||
WHILE_LOOP,
|
WHILE_LOOP,
|
||||||
@ -38,13 +38,13 @@ inline const int BC_KEEPLINE = -1;
|
|||||||
struct CodeBlock {
|
struct CodeBlock {
|
||||||
CodeBlockType type;
|
CodeBlockType type;
|
||||||
int parent; // parent index in blocks
|
int parent; // parent index in blocks
|
||||||
int for_loop_depth; // this is used for exception handling
|
int base_stack_size; // 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; // ...
|
int end2; // ...
|
||||||
|
|
||||||
CodeBlock(CodeBlockType type, int parent, int for_loop_depth, int start):
|
CodeBlock(CodeBlockType type, int parent, int base_stack_size, int start):
|
||||||
type(type), parent(parent), for_loop_depth(for_loop_depth), start(start), end(-1), end2(-1) {}
|
type(type), parent(parent), base_stack_size(base_stack_size), start(start), end(-1), end2(-1) {}
|
||||||
|
|
||||||
int get_break_end() const{
|
int get_break_end() const{
|
||||||
if(end2 != -1) return end2;
|
if(end2 != -1) return end2;
|
||||||
@ -68,7 +68,7 @@ struct CodeObject {
|
|||||||
List consts;
|
List consts;
|
||||||
std::vector<StrName> varnames; // local variables
|
std::vector<StrName> varnames; // local variables
|
||||||
NameDictInt varnames_inv;
|
NameDictInt varnames_inv;
|
||||||
std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
|
std::vector<CodeBlock> blocks = { CodeBlock(CodeBlockType::NO_BLOCK, -1, 0, 0) };
|
||||||
NameDictInt labels;
|
NameDictInt labels;
|
||||||
std::vector<FuncDecl_> func_decls;
|
std::vector<FuncDecl_> func_decls;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ class Compiler {
|
|||||||
void exprSubscr();
|
void exprSubscr();
|
||||||
void exprLiteral0();
|
void exprLiteral0();
|
||||||
|
|
||||||
void compile_block_body();
|
void compile_block_body(void (Compiler::*callback)()=nullptr);
|
||||||
void compile_normal_import();
|
void compile_normal_import();
|
||||||
void compile_from_import();
|
void compile_from_import();
|
||||||
bool is_expression();
|
bool is_expression();
|
||||||
|
@ -25,8 +25,6 @@ struct Expr{
|
|||||||
virtual bool is_name() const { return false; }
|
virtual bool is_name() const { return false; }
|
||||||
bool is_starred() const { return star_level() > 0; }
|
bool is_starred() const { return star_level() > 0; }
|
||||||
|
|
||||||
std::string str() const { PK_ASSERT(false); }
|
|
||||||
|
|
||||||
// for OP_DELETE_XXX
|
// for OP_DELETE_XXX
|
||||||
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) {
|
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) {
|
||||||
PK_UNUSED(ctx);
|
PK_UNUSED(ctx);
|
||||||
@ -53,7 +51,7 @@ struct CodeEmitContext{
|
|||||||
|
|
||||||
int curr_block_i = 0;
|
int curr_block_i = 0;
|
||||||
bool is_compiling_class = false;
|
bool is_compiling_class = false;
|
||||||
int for_loop_depth = 0;
|
int base_stack_size = 0;
|
||||||
|
|
||||||
std::map<void*, int> _co_consts_nonstring_dedup_map;
|
std::map<void*, int> _co_consts_nonstring_dedup_map;
|
||||||
std::map<std::string, int, std::less<>> _co_consts_string_dedup_map;
|
std::map<std::string, int, std::less<>> _co_consts_string_dedup_map;
|
||||||
@ -62,7 +60,6 @@ struct CodeEmitContext{
|
|||||||
CodeBlock* 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();
|
|
||||||
int emit_(Opcode opcode, uint16_t arg, int line);
|
int emit_(Opcode opcode, uint16_t arg, int line);
|
||||||
void patch_jump(int index);
|
void patch_jump(int index);
|
||||||
bool add_label(StrName name);
|
bool add_label(StrName name);
|
||||||
|
@ -782,10 +782,11 @@ __NEXT_STEP:;
|
|||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(WITH_ENTER)
|
TARGET(WITH_ENTER)
|
||||||
call_method(POPX(), __enter__);
|
PUSH(call_method(TOP(), __enter__));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(WITH_EXIT)
|
TARGET(WITH_EXIT)
|
||||||
call_method(POPX(), __exit__);
|
call_method(TOP(), __exit__);
|
||||||
|
POP();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(EXCEPTION_MATCH) {
|
TARGET(EXCEPTION_MATCH) {
|
||||||
|
@ -25,7 +25,7 @@ namespace pkpy{
|
|||||||
|
|
||||||
void Compiler::pop_context(){
|
void Compiler::pop_context(){
|
||||||
if(!ctx()->s_expr.empty()){
|
if(!ctx()->s_expr.empty()){
|
||||||
throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr());
|
throw std::runtime_error("!ctx()->s_expr.empty()");
|
||||||
}
|
}
|
||||||
// add a `return None` in the end as a guard
|
// add a `return None` in the end as a guard
|
||||||
// previously, we only do this if the last opcode is not a return
|
// previously, we only do this if the last opcode is not a return
|
||||||
@ -491,7 +491,8 @@ __SUBSCR_END:
|
|||||||
ctx()->s_expr.push(make_expr<Literal0Expr>(prev().type));
|
ctx()->s_expr.push(make_expr<Literal0Expr>(prev().type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_block_body() {
|
void Compiler::compile_block_body(void (Compiler::*callback)()) {
|
||||||
|
if(callback == nullptr) callback = &Compiler::compile_stmt;
|
||||||
consume(TK(":"));
|
consume(TK(":"));
|
||||||
if(curr().type!=TK("@eol") && curr().type!=TK("@eof")){
|
if(curr().type!=TK("@eol") && curr().type!=TK("@eof")){
|
||||||
compile_stmt(); // inline block
|
compile_stmt(); // inline block
|
||||||
@ -503,7 +504,7 @@ __SUBSCR_END:
|
|||||||
consume(TK("@indent"));
|
consume(TK("@indent"));
|
||||||
while (curr().type != TK("@dedent")) {
|
while (curr().type != TK("@dedent")) {
|
||||||
match_newlines();
|
match_newlines();
|
||||||
compile_stmt();
|
(this->*callback)();
|
||||||
match_newlines();
|
match_newlines();
|
||||||
}
|
}
|
||||||
consume(TK("@dedent"));
|
consume(TK("@dedent"));
|
||||||
@ -633,7 +634,7 @@ __EAT_DOTS_END:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_while_loop() {
|
void Compiler::compile_while_loop() {
|
||||||
CodeBlock* block = ctx()->enter_block(WHILE_LOOP);
|
CodeBlock* block = ctx()->enter_block(CodeBlockType::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();
|
||||||
@ -652,7 +653,7 @@ __EAT_DOTS_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);
|
||||||
CodeBlock* block = ctx()->enter_block(FOR_LOOP);
|
CodeBlock* block = ctx()->enter_block(CodeBlockType::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
|
||||||
@ -667,7 +668,7 @@ __EAT_DOTS_END:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_try_except() {
|
void Compiler::compile_try_except() {
|
||||||
ctx()->enter_block(TRY_EXCEPT);
|
ctx()->enter_block(CodeBlockType::TRY_EXCEPT);
|
||||||
compile_block_body();
|
compile_block_body();
|
||||||
std::vector<int> patches = {
|
std::vector<int> patches = {
|
||||||
ctx()->emit_(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)
|
ctx()->emit_(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)
|
||||||
@ -813,7 +814,7 @@ __EAT_DOTS_END:
|
|||||||
// if yield from present, mark the function as generator
|
// if yield from present, mark the function as generator
|
||||||
ctx()->co->is_generator = true;
|
ctx()->co->is_generator = true;
|
||||||
ctx()->emit_(OP_GET_ITER, BC_NOARG, kw_line);
|
ctx()->emit_(OP_GET_ITER, BC_NOARG, kw_line);
|
||||||
ctx()->enter_block(FOR_LOOP);
|
ctx()->enter_block(CodeBlockType::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, ctx()->get_loop(), BC_KEEPLINE);
|
ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE);
|
||||||
@ -909,17 +910,24 @@ __EAT_DOTS_END:
|
|||||||
consume_end_stmt();
|
consume_end_stmt();
|
||||||
} break;
|
} break;
|
||||||
case TK("with"): {
|
case TK("with"): {
|
||||||
EXPR(false);
|
EXPR(false); // [ <expr> ]
|
||||||
consume(TK("as"));
|
ctx()->enter_block(CodeBlockType::CONTEXT_MANAGER);
|
||||||
consume(TK("@id"));
|
Expr_ as_name;
|
||||||
Expr_ e = make_expr<NameExpr>(prev().str(), name_scope());
|
if(match(TK("as"))){
|
||||||
bool ok = e->emit_store(ctx());
|
consume(TK("@id"));
|
||||||
if(!ok) SyntaxError();
|
as_name = make_expr<NameExpr>(prev().str(), name_scope());
|
||||||
e->emit_(ctx());
|
}
|
||||||
ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line);
|
ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line);
|
||||||
|
// [ <expr> <expr>.__enter__() ]
|
||||||
|
if(as_name){
|
||||||
|
bool ok = as_name->emit_store(ctx());
|
||||||
|
if(!ok) SyntaxError();
|
||||||
|
}else{
|
||||||
|
ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
|
||||||
|
}
|
||||||
compile_block_body();
|
compile_block_body();
|
||||||
e->emit_(ctx());
|
|
||||||
ctx()->emit_(OP_WITH_EXIT, BC_NOARG, prev().line);
|
ctx()->emit_(OP_WITH_EXIT, BC_NOARG, prev().line);
|
||||||
|
ctx()->exit_block();
|
||||||
} break;
|
} break;
|
||||||
/*************************************************/
|
/*************************************************/
|
||||||
case TK("=="): {
|
case TK("=="): {
|
||||||
|
24
src/expr.cpp
24
src/expr.cpp
@ -16,17 +16,17 @@ namespace pkpy{
|
|||||||
int CodeEmitContext::get_loop() const {
|
int CodeEmitContext::get_loop() const {
|
||||||
int index = curr_block_i;
|
int index = curr_block_i;
|
||||||
while(index >= 0){
|
while(index >= 0){
|
||||||
if(co->blocks[index].type == FOR_LOOP) break;
|
if(co->blocks[index].type == CodeBlockType::FOR_LOOP) break;
|
||||||
if(co->blocks[index].type == WHILE_LOOP) break;
|
if(co->blocks[index].type == CodeBlockType::WHILE_LOOP) break;
|
||||||
index = co->blocks[index].parent;
|
index = co->blocks[index].parent;
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeBlock* CodeEmitContext::enter_block(CodeBlockType type){
|
CodeBlock* CodeEmitContext::enter_block(CodeBlockType type){
|
||||||
if(type == FOR_LOOP) for_loop_depth++;
|
if(type==CodeBlockType::FOR_LOOP || type==CodeBlockType::CONTEXT_MANAGER) base_stack_size++;
|
||||||
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, base_stack_size, (int)co->codes.size()
|
||||||
));
|
));
|
||||||
curr_block_i = co->blocks.size()-1;
|
curr_block_i = co->blocks.size()-1;
|
||||||
return &co->blocks[curr_block_i];
|
return &co->blocks[curr_block_i];
|
||||||
@ -34,12 +34,12 @@ namespace pkpy{
|
|||||||
|
|
||||||
void CodeEmitContext::exit_block(){
|
void CodeEmitContext::exit_block(){
|
||||||
auto curr_type = co->blocks[curr_block_i].type;
|
auto curr_type = co->blocks[curr_block_i].type;
|
||||||
if(curr_type == FOR_LOOP) for_loop_depth--;
|
if(curr_type == CodeBlockType::FOR_LOOP || curr_type==CodeBlockType::CONTEXT_MANAGER) base_stack_size--;
|
||||||
co->blocks[curr_block_i].end = co->codes.size();
|
co->blocks[curr_block_i].end = co->codes.size();
|
||||||
curr_block_i = co->blocks[curr_block_i].parent;
|
curr_block_i = co->blocks[curr_block_i].parent;
|
||||||
if(curr_block_i < 0) PK_FATAL_ERROR();
|
if(curr_block_i < 0) PK_FATAL_ERROR();
|
||||||
|
|
||||||
if(curr_type == FOR_LOOP){
|
if(curr_type == CodeBlockType::FOR_LOOP){
|
||||||
// add a no op here to make block check work
|
// add a no op here to make block check work
|
||||||
emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE);
|
emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
@ -47,19 +47,11 @@ namespace pkpy{
|
|||||||
|
|
||||||
// clear the expression stack and generate bytecode
|
// clear the expression stack and generate bytecode
|
||||||
void CodeEmitContext::emit_expr(){
|
void CodeEmitContext::emit_expr(){
|
||||||
if(s_expr.size() != 1){
|
if(s_expr.size() != 1) throw std::runtime_error("s_expr.size() != 1");
|
||||||
throw std::runtime_error("s_expr.size() != 1\n" + _log_s_expr());
|
|
||||||
}
|
|
||||||
Expr_ expr = s_expr.popx();
|
Expr_ expr = s_expr.popx();
|
||||||
expr->emit_(this);
|
expr->emit_(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CodeEmitContext::_log_s_expr(){
|
|
||||||
std::stringstream ss; // debug
|
|
||||||
for(auto& e: s_expr.data()) ss << e->str() << " ";
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line) {
|
int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line) {
|
||||||
co->codes.push_back(Bytecode{(uint8_t)opcode, arg});
|
co->codes.push_back(Bytecode{(uint8_t)opcode, arg});
|
||||||
co->iblocks.push_back(curr_block_i);
|
co->iblocks.push_back(curr_block_i);
|
||||||
@ -379,7 +371,7 @@ namespace pkpy{
|
|||||||
ctx->emit_(op0(), 0, line);
|
ctx->emit_(op0(), 0, line);
|
||||||
iter->emit_(ctx);
|
iter->emit_(ctx);
|
||||||
ctx->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
ctx->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
||||||
ctx->enter_block(FOR_LOOP);
|
ctx->enter_block(CodeBlockType::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);
|
||||||
// this error occurs in `vars` instead of this line, but...nevermind
|
// this error occurs in `vars` instead of this line, but...nevermind
|
||||||
|
@ -27,14 +27,14 @@ namespace pkpy{
|
|||||||
// try to find a parent try block
|
// try to find a parent try block
|
||||||
int block = co->iblocks[_ip];
|
int block = co->iblocks[_ip];
|
||||||
while(block >= 0){
|
while(block >= 0){
|
||||||
if(co->blocks[block].type == TRY_EXCEPT) break;
|
if(co->blocks[block].type == CodeBlockType::TRY_EXCEPT) break;
|
||||||
block = co->blocks[block].parent;
|
block = co->blocks[block].parent;
|
||||||
}
|
}
|
||||||
if(block < 0) return false;
|
if(block < 0) return false;
|
||||||
PyObject* obj = _s->popx(); // pop exception object
|
PyObject* obj = _s->popx(); // pop exception object
|
||||||
// get the stack size of the try block (depth of for loops)
|
// get the stack size of the try block
|
||||||
int _stack_size = co->blocks[block].for_loop_depth;
|
int _stack_size = co->blocks[block].base_stack_size;
|
||||||
if(stack_size() < _stack_size) throw std::runtime_error("invalid stack size");
|
if(stack_size() < _stack_size) throw std::runtime_error(fmt("invalid state: ", stack_size(), '<', _stack_size).str());
|
||||||
_s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack
|
_s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack
|
||||||
_s->push(obj); // push exception object
|
_s->push(obj); // push exception object
|
||||||
_next_ip = co->blocks[block].end;
|
_next_ip = co->blocks[block].end;
|
||||||
@ -42,7 +42,8 @@ namespace pkpy{
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Frame::_exit_block(int i){
|
int Frame::_exit_block(int i){
|
||||||
if(co->blocks[i].type == FOR_LOOP) _s->pop();
|
auto type = co->blocks[i].type;
|
||||||
|
if(type==CodeBlockType::FOR_LOOP || type==CodeBlockType::CONTEXT_MANAGER) _s->pop();
|
||||||
return co->blocks[i].parent;
|
return co->blocks[i].parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ unsigned char* _default_import_handler(const char* name_p, int name_size, int* o
|
|||||||
return vm->None;
|
return vm->None;
|
||||||
});
|
});
|
||||||
|
|
||||||
vm->bind_method<0>(type, "__enter__", PK_LAMBDA(vm->None));
|
vm->bind_method<0>(type, "__enter__", PK_LAMBDA(args[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileIO::FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
|
FileIO::FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
|
||||||
|
@ -31,3 +31,8 @@ if i <= 100:
|
|||||||
-> loop
|
-> loop
|
||||||
|
|
||||||
assert sum == 5050
|
assert sum == 5050
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
_ = 0
|
||||||
|
# if there is no op here, the block check will fail
|
||||||
|
while i: --i
|
||||||
|
44
tests/33_match_case.py
Normal file
44
tests/33_match_case.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# match = 2
|
||||||
|
# assert match == 2
|
||||||
|
# case = 3
|
||||||
|
# assert case == 3
|
||||||
|
|
||||||
|
# def f(match):
|
||||||
|
# match match:
|
||||||
|
# case 1: return 1
|
||||||
|
# case 2: return 2
|
||||||
|
# case _:
|
||||||
|
# return 999
|
||||||
|
# return 0
|
||||||
|
|
||||||
|
# assert f(1) == 1
|
||||||
|
# assert f(2) == 2
|
||||||
|
# assert f(3) == 999
|
||||||
|
# assert f(4) == 999
|
||||||
|
|
||||||
|
# def f():
|
||||||
|
# a = []
|
||||||
|
# try:
|
||||||
|
# match case:
|
||||||
|
# case a[1]: return 1
|
||||||
|
# except IndexError:
|
||||||
|
# return 'IndexError'
|
||||||
|
# return 0
|
||||||
|
|
||||||
|
# assert f() == 'IndexError'
|
||||||
|
|
||||||
|
|
||||||
|
# def f(pos):
|
||||||
|
# match pos:
|
||||||
|
# case 'str': return 'str'
|
||||||
|
# case 0: return 0
|
||||||
|
# case (1, 2): return '1, 2'
|
||||||
|
# case (3, 4): return '3, 4'
|
||||||
|
# case _: return 'other'
|
||||||
|
|
||||||
|
# assert f('str') == 'str'
|
||||||
|
# assert f(0) == 0
|
||||||
|
# assert f((1, 2)) == '1, 2'
|
||||||
|
# assert f((3, 4)) == '3, 4'
|
||||||
|
# assert f((1, 3)) == 'other'
|
||||||
|
# assert f((1, 2, 3)) == 'other'
|
37
tests/34_context.py
Normal file
37
tests/34_context.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
path = []
|
||||||
|
|
||||||
|
class A:
|
||||||
|
def __init__(self, x):
|
||||||
|
self.x = x
|
||||||
|
self.path = []
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
path.append('enter')
|
||||||
|
return self.x
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
path.append('exit')
|
||||||
|
|
||||||
|
|
||||||
|
with A(123):
|
||||||
|
assert path == ['enter']
|
||||||
|
assert path == ['enter', 'exit']
|
||||||
|
|
||||||
|
path.clear()
|
||||||
|
|
||||||
|
with A(123) as a:
|
||||||
|
assert path == ['enter']
|
||||||
|
assert a == 123
|
||||||
|
path.append('in')
|
||||||
|
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