From f3d876c5efb83b438ba2b35e1c548bac237cfb60 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 7 Jul 2024 23:54:11 +0800 Subject: [PATCH] remove *.cpp --- src/compiler/compiler.cpp | 1329 ------------------------------------- src/compiler/expr.cpp | 778 ---------------------- 2 files changed, 2107 deletions(-) delete mode 100644 src/compiler/compiler.cpp delete mode 100644 src/compiler/expr.cpp diff --git a/src/compiler/compiler.cpp b/src/compiler/compiler.cpp deleted file mode 100644 index 61588e2d..00000000 --- a/src/compiler/compiler.cpp +++ /dev/null @@ -1,1329 +0,0 @@ -#include "pocketpy/compiler/compiler.hpp" -#include "pocketpy/common/config.h" -#include "pocketpy/compiler/expr.hpp" -#include "pocketpy/interpreter/vm.hpp" -#include "pocketpy/objects/codeobject.hpp" - -#include - -namespace pkpy { - -#define consume(expected) if(!match(expected)) return SyntaxError("expected '%s', got '%s'", pk_TokenSymbols[expected], pk_TokenSymbols[curr().type]); -#define consume_end_stmt() if(!match_end_stmt()) return SyntaxError("expected statement end") -#define check_newlines_repl() { bool __nml; match_newlines(&__nml); if(__nml) return NeedMoreLines(); } -#define check(B) if((err = B)) return err - -PrattRule Compiler::rules[TK__COUNT__]; - -NameScope Compiler::name_scope() const noexcept{ - auto s = contexts.size() > 1 ? NAME_LOCAL : NAME_GLOBAL; - if(unknown_global_scope && s == NAME_GLOBAL) s = NAME_UNKNOWN; - return s; -} - -CodeObject* Compiler::push_global_context() noexcept{ - CodeObject* co = CodeObject__new(lexer.src, py_Str__sv(&lexer.src->filename)); - co->start_line = __i == 0 ? 1 : prev().line; - contexts.push_back(CodeEmitContext(vm, co, contexts.size())); - return co; -} - -FuncDecl_ Compiler::push_f_context(c11_sv name, int* out_index) noexcept{ - FuncDecl_ decl = FuncDecl__rcnew(lexer.src, name); - decl->code->start_line = __i == 0 ? 1 : prev().line; - decl->nested = name_scope() == NAME_LOCAL; - // add_func_decl - CodeEmitContext* ctx = &contexts.back(); - c11_vector__push(FuncDecl_, &ctx->co->func_decls, decl); - *out_index = ctx->co->func_decls.count - 1; - // push new context - contexts.push_back(CodeEmitContext(vm, decl->code, contexts.size())); - contexts.back().func = decl; - return decl; -} - -Error* Compiler::pop_context() noexcept{ - assert(ctx()->s_size() == 0); - // add a `return None` in the end as a guard - // previously, we only do this if the last opcode is not a return - // however, this is buggy...since there may be a jump to the end (out of bound) even if the last opcode is a return - ctx()->emit_(OP_RETURN_VALUE, 1, BC_KEEPLINE, true); - // find the last valid token - int j = __i - 1; - while(tk(j).type == TK_EOL || tk(j).type == TK_DEDENT || tk(j).type == TK_EOF) - j--; - ctx()->co->end_line = tk(j).line; - - // some check here - auto& codes = ctx()->co->codes; - if(ctx()->co->nlocals > PK_MAX_CO_VARNAMES) { - return SyntaxError("maximum number of local variables exceeded"); - } - if(ctx()->co->consts.count > 65530) { - return SyntaxError("maximum number of constants exceeded"); - } - // pre-compute LOOP_BREAK and LOOP_CONTINUE - for(int i = 0; i < codes.count; i++) { - Bytecode* bc = c11__at(Bytecode, &codes, i); - if(bc->op == OP_LOOP_CONTINUE) { - CodeBlock* block = c11__at(CodeBlock, &ctx()->co->blocks, bc->arg); - Bytecode__set_signed_arg(bc, block->start - i); - } else if(bc->op == OP_LOOP_BREAK) { - CodeBlock* block = c11__at(CodeBlock, &ctx()->co->blocks, bc->arg); - Bytecode__set_signed_arg(bc, (block->end2 != -1 ? block->end2 : block->end) - i); - } - } - // pre-compute func->is_simple - FuncDecl* func = contexts.back().func; - if(func) { - // check generator - c11__foreach(Bytecode, &func->code->codes, bc) { - if(bc->op == OP_YIELD_VALUE || bc->op == OP_FOR_ITER_YIELD_VALUE) { - func->type = FuncType_GENERATOR; - c11__foreach(Bytecode, &func->code->codes, bc) { - if(bc->op == OP_RETURN_VALUE && bc->arg == BC_NOARG) { - return SyntaxError("'return' with argument inside generator function"); - } - } - break; - } - } - if(func->type == FuncType_UNSET) { - bool is_simple = true; - if(func->kwargs.count > 0) is_simple = false; - if(func->starred_arg >= 0) is_simple = false; - if(func->starred_kwarg >= 0) is_simple = false; - - if(is_simple) { - func->type = FuncType_SIMPLE; - - bool is_empty = false; - if(func->code->codes.count == 1) { - Bytecode bc = c11__getitem(Bytecode, &func->code->codes, 0); - if(bc.op == OP_RETURN_VALUE && bc.arg == 1) { - is_empty = true; - } - } - if(is_empty) func->type = FuncType_EMPTY; - } else - func->type = FuncType_NORMAL; - } - - assert(func->type != FuncType_UNSET); - } - contexts.back().s_clean(); - contexts.pop_back(); - return NULL; -} - -void Compiler::init_pratt_rules() noexcept{ - static bool initialized = false; - if(initialized) return; - initialized = true; -} - -bool Compiler::match(TokenIndex expected) noexcept{ - if(curr().type != expected) return false; - advance(); - return true; -} - -bool Compiler::match_newlines(bool* need_more_lines) noexcept{ - bool consumed = false; - if(curr().type == TK_EOL) { - while(curr().type == TK_EOL) advance(); - consumed = true; - } - if(need_more_lines) { - *need_more_lines = (mode() == REPL_MODE && curr().type == TK_EOF); - } - return consumed; -} - -bool Compiler::match_end_stmt() noexcept{ - if(match(TK_SEMICOLON)) { - match_newlines(); - return true; - } - if(match_newlines() || curr().type == TK_EOF) return true; - if(curr().type == TK_DEDENT) return true; - return false; -} - -Error* Compiler::EXPR_TUPLE(bool allow_slice) noexcept{ - Error* err; - check(parse_expression(PREC_LOWEST + 1, allow_slice)); - if(!match(TK_COMMA)) return NULL; - // tuple expression - int count = 1; - do { - if(curr().brackets_level) check_newlines_repl() - if(!is_expression(allow_slice)) break; - check(parse_expression(PREC_LOWEST + 1, allow_slice)); - count += 1; - if(curr().brackets_level) check_newlines_repl(); - } while(match(TK_COMMA)); - TupleExpr* e = make_expr(count); - for(int i=count-1; i>=0; i--) - e->items[i] = ctx()->s_popx(); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::EXPR_VARS() noexcept{ - int count = 0; - do { - consume(TK_ID); - ctx()->s_push(make_expr(prev().str(), name_scope())); - count += 1; - } while(match(TK_COMMA)); - if(count > 1){ - TupleExpr* e = make_expr(count); - for(int i=count-1; i>=0; i--) - e->items[i] = ctx()->s_popx(); - ctx()->s_push(e); - } - return NULL; -} - -Error* Compiler::exprLiteral() noexcept{ - ctx()->s_push(make_expr(prev().value)); - return NULL; -} - -Error* Compiler::exprLong() noexcept{ - ctx()->s_push(make_expr(prev().str())); - return NULL; -} - -Error* Compiler::exprImag() noexcept{ - ctx()->s_push(make_expr(std::get(prev().value))); - return NULL; -} - -Error* Compiler::exprBytes() noexcept{ - ctx()->s_push(make_expr(std::get(prev().value))); - return NULL; -} - -Error* Compiler::exprFString() noexcept{ - ctx()->s_push(make_expr(std::get(prev().value))); - return NULL; -} - -Error* Compiler::exprLambda() noexcept{ - Error* err; - int decl_index; - FuncDecl_ decl = push_f_context({"", 8}, &decl_index); - int line = prev().line; // backup line - if(!match(TK_COLON)) { - check(_compile_f_args(decl, false)); - consume(TK_COLON); - } - // https://github.com/pocketpy/pocketpy/issues/37 - check(parse_expression(PREC_LAMBDA + 1)); - ctx()->s_emit_top(); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); - check(pop_context()); - LambdaExpr* e = make_expr(decl_index); - e->line = line; - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprOr() noexcept{ - int line = prev().line; - Error* err; - check(parse_expression(PREC_LOGICAL_OR + 1)); - auto e = make_expr(); - e->line = line; - e->rhs = ctx()->s_popx(); - e->lhs = ctx()->s_popx(); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprAnd() noexcept{ - int line = prev().line; - Error* err; - check(parse_expression(PREC_LOGICAL_AND + 1)); - auto e = make_expr(); - e->line = line; - e->rhs = ctx()->s_popx(); - e->lhs = ctx()->s_popx(); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprTernary() noexcept{ - // [true_expr] - Error* err; - int line = prev().line; - check(parse_expression(PREC_TERNARY + 1)); // [true_expr, cond] - consume(TK_ELSE); - check(parse_expression(PREC_TERNARY + 1)); // [true_expr, cond, false_expr] - auto e = make_expr(); - e->line = line; - e->false_expr = ctx()->s_popx(); - e->cond = ctx()->s_popx(); - e->true_expr = ctx()->s_popx(); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprBinaryOp() noexcept{ - Error* err; - int line = prev().line; - TokenIndex op = prev().type; - check(parse_expression(rules[op].precedence + 1)); - BinaryExpr* e = make_expr(op); - e->line = line; - e->rhs = ctx()->s_popx(); - e->lhs = ctx()->s_popx(); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprNot() noexcept{ - Error* err; - check(parse_expression(PREC_LOGICAL_NOT + 1)); - NotExpr* e = make_expr(ctx()->s_popx()); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprUnaryOp() noexcept{ - Error* err; - TokenIndex op = prev().type; - check(parse_expression(PREC_UNARY + 1)); - switch(op) { - case TK_SUB: ctx()->s_push(make_expr(ctx()->s_popx())); break; - case TK_INVERT: ctx()->s_push(make_expr(ctx()->s_popx())); break; - case TK_MUL: ctx()->s_push(make_expr(ctx()->s_popx(), 1)); break; - case TK_POW: ctx()->s_push(make_expr(ctx()->s_popx(), 2)); break; - default: assert(false); - } - return NULL; -} - -Error* Compiler::exprGroup() noexcept{ - Error* err; - check_newlines_repl() - check(EXPR_TUPLE()); // () is just for change precedence - check_newlines_repl() - consume(TK_RPAREN); - if(ctx()->s_top()->is_tuple()) return NULL; - Expr* g = make_expr(ctx()->s_popx()); - ctx()->s_push(g); - return NULL; -} - -Error* Compiler::consume_comp(Opcode op0, Opcode op1) noexcept{ - // [expr] - Error* err; - bool has_cond = false; - check(EXPR_VARS()); // [expr, vars] - consume(TK_IN); - check(parse_expression(PREC_TERNARY + 1)); // [expr, vars, iter] - check_newlines_repl() - if(match(TK_IF)) { - check(parse_expression(PREC_TERNARY + 1)); // [expr, vars, iter, cond] - has_cond = true; - } - CompExpr* ce = make_expr(op0, op1); - if(has_cond) ce->cond = ctx()->s_popx(); - ce->iter = ctx()->s_popx(); - ce->vars = ctx()->s_popx(); - ce->expr = ctx()->s_popx(); - ctx()->s_push(ce); - check_newlines_repl() - return NULL; -} - -Error* Compiler::exprList() noexcept{ - Error* err; - int line = prev().line; - int count = 0; - do { - check_newlines_repl() - if(curr().type == TK_RBRACKET) break; - check(EXPR()); count += 1; - check_newlines_repl() - if(count == 1 && match(TK_FOR)) { - check(consume_comp(OP_BUILD_LIST, OP_LIST_APPEND)); - consume(TK_RBRACKET); - return NULL; - } - check_newlines_repl() - } while(match(TK_COMMA)); - consume(TK_RBRACKET); - ListExpr* e = make_expr(count); - e->line = line; // override line - for(int i=count-1; i>=0; i--) - e->items[i] = ctx()->s_popx(); - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprMap() noexcept{ - Error* err; - bool parsing_dict = false; // {...} may be dict or set - int count = 0; - do { - check_newlines_repl() - if(curr().type == TK_RBRACE) break; - check(EXPR()); // [key] - int star_level = ctx()->s_top()->star_level(); - if(star_level == 2 || curr().type == TK_COLON) { parsing_dict = true; } - if(parsing_dict) { - if(star_level == 2) { - DictItemExpr* dict_item = make_expr(); - dict_item->key = NULL; - dict_item->value = ctx()->s_popx(); - ctx()->s_push(dict_item); - } else { - consume(TK_COLON); - check(EXPR()); - DictItemExpr* dict_item = make_expr(); - dict_item->value = ctx()->s_popx(); - dict_item->key = ctx()->s_popx(); - ctx()->s_push(dict_item); - } - } - count += 1; - check_newlines_repl() - if(count == 1 && match(TK_FOR)) { - if(parsing_dict){ - check(consume_comp(OP_BUILD_DICT, OP_DICT_ADD)); - }else{ - check(consume_comp(OP_BUILD_SET, OP_SET_ADD)); - } - consume(TK_RBRACE); - return NULL; - } - check_newlines_repl() - } while(match(TK_COMMA)); - consume(TK_RBRACE); - - SequenceExpr* se; - if(count == 0 || parsing_dict) { - se = make_expr(count); - } else { - se = make_expr(count); - } - for(int i=count-1; i>=0; i--) - se->items[i] = ctx()->s_popx(); - ctx()->s_push(se); - return NULL; -} - -Error* Compiler::exprCall() noexcept{ - Error* err; - CallExpr* e = make_expr(); - e->callable = ctx()->s_popx(); - ctx()->s_push(e); // push onto the stack in advance - do { - check_newlines_repl() - if(curr().type == TK_RPAREN) break; - if(curr().type == TK_ID && next().type == TK_ASSIGN) { - consume(TK_ID); - StrName key(prev().sv()); - consume(TK_ASSIGN); - check(EXPR()); - e->kwargs.push_back({key, ctx()->s_popx()}); - } else { - check(EXPR()); - if(ctx()->s_top()->star_level() == 2) { - // **kwargs - e->kwargs.push_back({"**", ctx()->s_popx()}); - } else { - // positional argument - if(!e->kwargs.empty()) return SyntaxError("positional argument follows keyword argument"); - e->args.push_back(ctx()->s_popx()); - } - } - check_newlines_repl() - } while(match(TK_COMMA)); - consume(TK_RPAREN); - return NULL; -} - -Error* Compiler::exprName() noexcept{ - StrName name(prev().sv()); - NameScope scope = name_scope(); - if(ctx()->global_names.contains(name)) { scope = NAME_GLOBAL; } - ctx()->s_push(make_expr(name, scope)); - return NULL; -} - -Error* Compiler::exprAttrib() noexcept{ - consume(TK_ID); - ctx()->s_push(make_expr(ctx()->s_popx(), StrName::get(prev().sv()))); - return NULL; -} - -Error* Compiler::exprSlice0() noexcept{ - Error* err; - SliceExpr* slice = make_expr(); - ctx()->s_push(slice); // push onto the stack in advance - if(is_expression()) { // : - check(EXPR()); - slice->stop = ctx()->s_popx(); - // try optional step - if(match(TK_COLON)) { // :: - check(EXPR()); - slice->step = ctx()->s_popx(); - } - } else if(match(TK_COLON)) { - if(is_expression()) { // :: - check(EXPR()); - slice->step = ctx()->s_popx(); - } // else :: - } // else : - return NULL; -} - -Error* Compiler::exprSlice1() noexcept{ - Error* err; - SliceExpr* slice = make_expr(); - slice->start = ctx()->s_popx(); - ctx()->s_push(slice); // push onto the stack in advance - if(is_expression()) { // : - check(EXPR()); - slice->stop = ctx()->s_popx(); - // try optional step - if(match(TK_COLON)) { // :: - check(EXPR()); - slice->step = ctx()->s_popx(); - } - } else if(match(TK_COLON)) { // :: - check(EXPR()); - slice->step = ctx()->s_popx(); - } // else : - return NULL; -} - -Error* Compiler::exprSubscr() noexcept{ - Error* err; - int line = prev().line; - check_newlines_repl() - check(EXPR_TUPLE(true)); - check_newlines_repl() - consume(TK_RBRACKET); // [lhs, rhs] - SubscrExpr* e = make_expr(); - e->line = line; - e->rhs = ctx()->s_popx(); // [lhs] - e->lhs = ctx()->s_popx(); // [] - ctx()->s_push(e); - return NULL; -} - -Error* Compiler::exprLiteral0() noexcept{ - ctx()->s_push(make_expr(prev().type)); - return NULL; -} - -Error* Compiler::compile_block_body(PrattCallback callback) noexcept{ - Error* err; - if(!callback) callback = &Compiler::compile_stmt; - consume(TK_COLON); - if(curr().type != TK_EOL && curr().type != TK_EOF) { - while(true) { - check(compile_stmt()); - bool possible = curr().type != TK_EOL && curr().type != TK_EOF; - if(prev().type != TK_SEMICOLON || !possible) break; - } - return NULL; - } - - bool need_more_lines; - bool consumed = match_newlines(&need_more_lines); - if(need_more_lines) return NeedMoreLines(); - if(!consumed) return SyntaxError("expected a new line after ':'"); - - consume(TK_INDENT); - while(curr().type != TK_DEDENT) { - match_newlines(); - check((this->*callback)()); - match_newlines(); - } - consume(TK_DEDENT); - return NULL; -} - -// import a [as b] -// import a [as b], c [as d] -Error* Compiler::compile_normal_import() noexcept{ - do { - consume(TK_ID); - Str name = prev().str(); - ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(name.sv()), prev().line); - if(match(TK_AS)) { - consume(TK_ID); - name = prev().str(); - } - ctx()->emit_store_name(name_scope(), StrName(name), prev().line); - } while(match(TK_COMMA)); - consume_end_stmt(); - return NULL; -} - -// from a import b [as c], d [as e] -// from a.b import c [as d] -// from . import a [as b] -// from .a import b [as c] -// from ..a import b [as c] -// from .a.b import c [as d] -// from xxx import * -Error* Compiler::compile_from_import() noexcept{ - int dots = 0; - - while(true) { - switch(curr().type) { - case TK_DOT: dots += 1; break; - case TK_DOTDOT: dots += 2; break; - case TK_DOTDOTDOT: dots += 3; break; - default: goto __EAT_DOTS_END; - } - advance(); - } -__EAT_DOTS_END: - SStream ss; - for(int i = 0; i < dots; i++) - ss << '.'; - - if(dots > 0) { - // @id is optional if dots > 0 - if(match(TK_ID)) { - ss << prev().sv(); - while(match(TK_DOT)) { - consume(TK_ID); - ss << "." << prev().sv(); - } - } - } else { - // @id is required if dots == 0 - consume(TK_ID); - ss << prev().sv(); - while(match(TK_DOT)) { - consume(TK_ID); - ss << "." << prev().sv(); - } - } - - ctx()->emit_(OP_IMPORT_PATH, ctx()->add_const_string(ss.str().sv()), prev().line); - consume(TK_IMPORT); - - if(match(TK_MUL)) { - if(name_scope() != NAME_GLOBAL) return SyntaxError("from import * can only be used in global scope"); - // pop the module and import __all__ - ctx()->emit_(OP_POP_IMPORT_STAR, BC_NOARG, prev().line); - consume_end_stmt(); - return NULL; - } - - do { - ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); - consume(TK_ID); - Str name = prev().str(); - ctx()->emit_(OP_LOAD_ATTR, StrName(name).index, prev().line); - if(match(TK_AS)) { - consume(TK_ID); - name = prev().str(); - } - ctx()->emit_store_name(name_scope(), StrName(name), prev().line); - } while(match(TK_COMMA)); - ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); - consume_end_stmt(); - return NULL; -} - -bool Compiler::is_expression(bool allow_slice) noexcept{ - PrattCallback prefix = rules[curr().type].prefix; - return prefix != nullptr && (allow_slice || curr().type != TK_COLON); -} - -Error* Compiler::parse_expression(int precedence, bool allow_slice) noexcept{ - PrattCallback prefix = rules[curr().type].prefix; - if(prefix == nullptr || (curr().type == TK_COLON && !allow_slice)) { - return SyntaxError("expected an expression, got %s", pk_TokenSymbols[curr().type]); - } - advance(); - Error* err; - check((this->*prefix)()); - while(rules[curr().type].precedence >= precedence && (allow_slice || curr().type != TK_COLON)) { - TokenIndex op = curr().type; - advance(); - PrattCallback infix = rules[op].infix; - assert(infix != nullptr); - check((this->*infix)()); - } - return NULL; -} - -Error* Compiler::compile_if_stmt() noexcept{ - Error* err; - check(EXPR()); // condition - ctx()->s_emit_top(); - int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); - err = compile_block_body(); - if(err) return err; - if(match(TK_ELIF)) { - int exit_patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, prev().line); - ctx()->patch_jump(patch); - check(compile_if_stmt()); - ctx()->patch_jump(exit_patch); - } else if(match(TK_ELSE)) { - int exit_patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, prev().line); - ctx()->patch_jump(patch); - check(compile_block_body()); - ctx()->patch_jump(exit_patch); - } else { - ctx()->patch_jump(patch); - } - return NULL; -} - -Error* Compiler::compile_while_loop() noexcept{ - Error* err; - CodeBlock* block = ctx()->enter_block(CodeBlockType_WHILE_LOOP); - check(EXPR()); // condition - ctx()->s_emit_top(); - int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); - check(compile_block_body()); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); - ctx()->patch_jump(patch); - ctx()->exit_block(); - // optional else clause - if(match(TK_ELSE)) { - check(compile_block_body()); - block->end2 = ctx()->co->codes.count; - } - return NULL; -} - -Error* Compiler::compile_for_loop() noexcept{ - Error* err; - check(EXPR_VARS()); // [vars] - consume(TK_IN); - check(EXPR_TUPLE()); // [vars, iter] - ctx()->s_emit_top(); // [vars] - ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, BC_KEEPLINE); - CodeBlock* block = ctx()->enter_block(CodeBlockType_FOR_LOOP); - int for_codei = ctx()->emit_(OP_FOR_ITER, ctx()->curr_iblock, BC_KEEPLINE); - Expr* vars = ctx()->s_popx(); - bool ok = vars->emit_store(ctx()); - delete_expr(vars); - if(!ok) return SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind - ctx()->try_merge_for_iter_store(for_codei); - check(compile_block_body()); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); - ctx()->exit_block(); - // optional else clause - if(match(TK_ELSE)) { - check(compile_block_body()); - block->end2 = ctx()->co->codes.count; - } - return NULL; -} - -Error* Compiler::compile_try_except() noexcept{ - Error* err; - ctx()->enter_block(CodeBlockType_TRY_EXCEPT); - ctx()->emit_(OP_TRY_ENTER, BC_NOARG, prev().line); - check(compile_block_body()); - small_vector_2 patches; - patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE)); - ctx()->exit_block(); - - int finally_entry = -1; - if(curr().type != TK_FINALLY) { - do { - StrName as_name; - consume(TK_EXCEPT); - if(is_expression()) { - check(EXPR()); // push assumed type on to the stack - ctx()->s_emit_top(); - ctx()->emit_(OP_EXCEPTION_MATCH, BC_NOARG, prev().line); - if(match(TK_AS)) { - consume(TK_ID); - as_name = StrName(prev().sv()); - } - } else { - ctx()->emit_(OP_LOAD_TRUE, BC_NOARG, BC_KEEPLINE); - } - int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); - // on match - if(!as_name.empty()) { - ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); - ctx()->emit_store_name(name_scope(), as_name, BC_KEEPLINE); - } - // pop the exception - ctx()->emit_(OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE); - check(compile_block_body()); - patches.push_back(ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE)); - ctx()->patch_jump(patch); - } while(curr().type == TK_EXCEPT); - } - - if(match(TK_FINALLY)) { - int patch = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); - finally_entry = ctx()->co->codes.count; - check(compile_block_body()); - ctx()->emit_(OP_JUMP_ABSOLUTE_TOP, BC_NOARG, BC_KEEPLINE); - ctx()->patch_jump(patch); - } - // no match, re-raise - if(finally_entry != -1) { - i64 target = ctx()->co->codes.count + 2; - ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE); - int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); - Bytecode* bc = c11__at(Bytecode, &ctx()->co->codes, i); - Bytecode__set_signed_arg(bc, finally_entry - i); - } - ctx()->emit_(OP_RE_RAISE, BC_NOARG, BC_KEEPLINE); - - // no exception or no match, jump to the end - for(int patch: patches) - ctx()->patch_jump(patch); - if(finally_entry != -1) { - i64 target = ctx()->co->codes.count + 2; - ctx()->emit_(OP_LOAD_CONST, ctx()->add_const(VAR(target)), BC_KEEPLINE); - int i = ctx()->emit_(OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE); - Bytecode* bc = c11__at(Bytecode, &ctx()->co->codes, i); - Bytecode__set_signed_arg(bc, finally_entry - i); - } - return NULL; -} - -Error* Compiler::compile_decorated() noexcept{ - Error* err; - int count = 0; - do { - check(EXPR()); - count += 1; - bool need_more_lines; - bool consumed = match_newlines(&need_more_lines); - if(need_more_lines) return NeedMoreLines(); - if(!consumed) return SyntaxError("expected a newline after '@'"); - } while(match(TK_DECORATOR)); - - if(match(TK_CLASS)) { - check(compile_class(count)); - } else { - consume(TK_DEF); - check(compile_function(count)); - } - return NULL; -} - -Error* Compiler::try_compile_assignment(bool* is_assign) noexcept{ - Error* err; - switch(curr().type) { - case TK_IADD: - case TK_ISUB: - case TK_IMUL: - case TK_IDIV: - case TK_IFLOORDIV: - case TK_IMOD: - case TK_ILSHIFT: - case TK_IRSHIFT: - case TK_IAND: - case TK_IOR: - case TK_IXOR: { - if(ctx()->s_top()->is_starred()) return SyntaxError(); - if(ctx()->is_compiling_class){ - return SyntaxError("can't use inplace operator in class definition"); - } - advance(); - // a[x] += 1; a and x should be evaluated only once - // a.x += 1; a should be evaluated only once - // -1 to remove =; inplace=true - int line = prev().line; - TokenIndex op = (TokenIndex)(prev().type - 1); - // [lhs] - check(EXPR_TUPLE()); // [lhs, rhs] - if(ctx()->s_top()->is_starred()) return SyntaxError(); - BinaryExpr* e = make_expr(op, true); - e->line = line; - e->rhs = ctx()->s_popx(); // [lhs] - e->lhs = ctx()->s_popx(); // [] - e->emit_(ctx()); - bool ok = e->lhs->emit_store_inplace(ctx()); - delete_expr(e); - if(!ok) return SyntaxError(); - *is_assign = true; - return NULL; - } - case TK_ASSIGN: { - int n = 0; - while(match(TK_ASSIGN)) { - check(EXPR_TUPLE()); - n += 1; - } - // stack size is n+1 - ctx()->s_emit_top(); // emit and pop - for(int j = 1; j < n; j++) - ctx()->emit_(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE); - for(int j = 0; j < n; j++) { - if(ctx()->s_top()->is_starred()) return SyntaxError(); - bool ok = ctx()->s_top()->emit_store(ctx()); - ctx()->s_pop(); - if(!ok) return SyntaxError(); - } - *is_assign = true; - return NULL; - } - default: *is_assign = false; - } - return NULL; -} - -Error* Compiler::compile_stmt() noexcept{ - Error* err; - if(match(TK_CLASS)) { - check(compile_class()); - return NULL; - } - advance(); - int kw_line = prev().line; // backup line number - int curr_loop_block = ctx()->get_loop(); - switch(prev().type) { - case TK_BREAK: - if(curr_loop_block < 0) return SyntaxError("'break' outside loop"); - ctx()->emit_(OP_LOOP_BREAK, curr_loop_block, kw_line); - consume_end_stmt(); - break; - case TK_CONTINUE: - if(curr_loop_block < 0) return SyntaxError("'continue' not properly in loop"); - ctx()->emit_(OP_LOOP_CONTINUE, curr_loop_block, kw_line); - consume_end_stmt(); - break; - case TK_YIELD: - if(contexts.size() <= 1) return SyntaxError("'yield' outside function"); - check(EXPR_TUPLE()); - ctx()->s_emit_top(); - ctx()->emit_(OP_YIELD_VALUE, BC_NOARG, kw_line); - consume_end_stmt(); - break; - case TK_YIELD_FROM: - if(contexts.size() <= 1) return SyntaxError("'yield from' outside function"); - check(EXPR_TUPLE()); - ctx()->s_emit_top(); - - ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, kw_line); - ctx()->enter_block(CodeBlockType_FOR_LOOP); - ctx()->emit_(OP_FOR_ITER_YIELD_VALUE, BC_NOARG, kw_line); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), kw_line); - ctx()->exit_block(); - consume_end_stmt(); - break; - case TK_RETURN: - if(contexts.size() <= 1) return SyntaxError("'return' outside function"); - if(match_end_stmt()) { - ctx()->emit_(OP_RETURN_VALUE, 1, kw_line); - } else { - check(EXPR_TUPLE()); - ctx()->s_emit_top(); - consume_end_stmt(); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, kw_line); - } - break; - /*************************************************/ - case TK_IF: check(compile_if_stmt()); break; - case TK_WHILE: check(compile_while_loop()); break; - case TK_FOR: check(compile_for_loop()); break; - case TK_IMPORT: check(compile_normal_import()); break; - case TK_FROM: check(compile_from_import()); break; - case TK_DEF: check(compile_function()); break; - case TK_DECORATOR: check(compile_decorated()); break; - case TK_TRY: check(compile_try_except()); break; - case TK_PASS: consume_end_stmt(); break; - /*************************************************/ - case TK_ASSERT: { - check(EXPR()); // condition - ctx()->s_emit_top(); - int index = ctx()->emit_(OP_POP_JUMP_IF_TRUE, BC_NOARG, kw_line); - int has_msg = 0; - if(match(TK_COMMA)) { - check(EXPR()); // message - ctx()->s_emit_top(); - has_msg = 1; - } - ctx()->emit_(OP_RAISE_ASSERT, has_msg, kw_line); - ctx()->patch_jump(index); - consume_end_stmt(); - break; - } - case TK_GLOBAL: - do { - consume(TK_ID); - ctx()->global_names.push_back(StrName(prev().sv())); - } while(match(TK_COMMA)); - consume_end_stmt(); - break; - case TK_RAISE: { - check(EXPR()); - ctx()->s_emit_top(); - ctx()->emit_(OP_RAISE, BC_NOARG, kw_line); - consume_end_stmt(); - } break; - case TK_DEL: { - check(EXPR_TUPLE()); - if(!ctx()->s_top()->emit_del(ctx())) return SyntaxError(); - ctx()->s_pop(); - consume_end_stmt(); - } break; - case TK_WITH: { - check(EXPR()); // [ ] - ctx()->s_emit_top(); - ctx()->enter_block(CodeBlockType_CONTEXT_MANAGER); - Expr* as_name = nullptr; - if(match(TK_AS)) { - consume(TK_ID); - as_name = make_expr(prev().str(), name_scope()); - } - ctx()->emit_(OP_WITH_ENTER, BC_NOARG, prev().line); - // [ .__enter__() ] - if(as_name) { - bool ok = as_name->emit_store(ctx()); - delete_expr(as_name); - if(!ok) return SyntaxError(); - } else { - ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); - } - check(compile_block_body()); - ctx()->emit_(OP_WITH_EXIT, BC_NOARG, prev().line); - ctx()->exit_block(); - } break; - /*************************************************/ - case TK_EQ: { - consume(TK_ID); - if(mode() != EXEC_MODE) return SyntaxError("'label' is only available in EXEC_MODE"); - if(!ctx()->add_label(prev().str())) { - Str escaped(prev().str().escape()); - return SyntaxError("label %s already exists", escaped.c_str()); - } - consume(TK_EQ); - consume_end_stmt(); - } break; - case TK_ARROW: - consume(TK_ID); - if(mode() != EXEC_MODE) return SyntaxError("'goto' is only available in EXEC_MODE"); - ctx()->emit_(OP_GOTO, StrName(prev().sv()).index, prev().line); - consume_end_stmt(); - break; - /*************************************************/ - // handle dangling expression or assignment - default: { - advance(-1); // do revert since we have pre-called advance() at the beginning - check(EXPR_TUPLE()); - - bool is_typed_name = false; // e.g. x: int - // eat variable's type hint if it is a single name - if(ctx()->s_top()->is_name()) { - if(match(TK_COLON)) { - check(consume_type_hints()); - is_typed_name = true; - - if(ctx()->is_compiling_class) { - NameExpr* ne = static_cast(ctx()->s_top()); - ctx()->emit_(OP_ADD_CLASS_ANNOTATION, ne->name.index, BC_KEEPLINE); - } - } - } - bool is_assign = false; - check(try_compile_assignment(&is_assign)); - if(!is_assign) { - if(ctx()->s_size() > 0 && ctx()->s_top()->is_starred()) { - return SyntaxError(); - } - if(!is_typed_name) { - ctx()->s_emit_top(); - if((mode() == CELL_MODE || mode() == REPL_MODE) && name_scope() == NAME_GLOBAL) { - ctx()->emit_(OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE); - } else { - ctx()->emit_(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); - } - } else { - ctx()->s_pop(); - } - } - consume_end_stmt(); - break; - } - } - return NULL; -} - -Error* Compiler::consume_type_hints() noexcept{ - Error* err; - check(EXPR()); - ctx()->s_pop(); - return NULL; -} - -Error* Compiler::compile_class(int decorators) noexcept{ - Error* err; - consume(TK_ID); - int namei = StrName(prev().sv()).index; - bool has_base = false; - if(match(TK_LPAREN)) { - if(is_expression()) { - check(EXPR()); - has_base = true; // [base] - } - consume(TK_RPAREN); - } - if(!has_base) { - ctx()->emit_(OP_LOAD_NONE, BC_NOARG, prev().line); - } else { - ctx()->s_emit_top(); // [] - } - ctx()->emit_(OP_BEGIN_CLASS, namei, BC_KEEPLINE); - - for(auto& c: this->contexts) { - if(c.is_compiling_class) return SyntaxError("nested class is not allowed"); - } - ctx()->is_compiling_class = true; - check(compile_block_body()); - ctx()->is_compiling_class = false; - - if(decorators > 0) { - ctx()->emit_(OP_BEGIN_CLASS_DECORATION, BC_NOARG, BC_KEEPLINE); - ctx()->s_emit_decorators(decorators); - ctx()->emit_(OP_END_CLASS_DECORATION, BC_NOARG, BC_KEEPLINE); - } - - ctx()->emit_(OP_END_CLASS, namei, BC_KEEPLINE); - return NULL; -} - -Error* Compiler::_compile_f_args(FuncDecl* decl, bool enable_type_hints) noexcept{ - int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs - Error* err; - do { - if(state > 3) return SyntaxError(); - if(state == 3) return SyntaxError("**kwargs should be the last argument"); - match_newlines(); - if(match(TK_MUL)) { - if(state < 1) - state = 1; - else - return SyntaxError("*args should be placed before **kwargs"); - } else if(match(TK_POW)) { - state = 3; - } - consume(TK_ID); - StrName name(prev().sv()); - - // check duplicate argument name - uint16_t tmp_name; - c11__foreach(int, &decl->args, j) { - tmp_name = c11__getitem(uint16_t, &decl->args, *j); - if(tmp_name == name.index) return SyntaxError("duplicate argument name"); - } - c11__foreach(FuncDeclKwArg, &decl->kwargs, kv) { - tmp_name = c11__getitem(uint16_t, &decl->code->varnames, kv->index); - if(tmp_name == name.index) return SyntaxError("duplicate argument name"); - } - - if(decl->starred_arg != -1) { - tmp_name = c11__getitem(uint16_t, &decl->code->varnames, decl->starred_arg); - if(tmp_name == name.index) return SyntaxError("duplicate argument name"); - } - if(decl->starred_kwarg != -1) { - tmp_name = c11__getitem(uint16_t, &decl->code->varnames, decl->starred_kwarg); - if(tmp_name == name.index) return SyntaxError("duplicate argument name"); - } - - // eat type hints - if(enable_type_hints && match(TK_COLON)) check(consume_type_hints()); - if(state == 0 && curr().type == TK_ASSIGN) state = 2; - int index = ctx()->add_varname(name); - switch(state) { - case 0: - c11_vector__push(int, &decl->args, index); - break; - case 1: - decl->starred_arg = index; - state += 1; - break; - case 2: { - consume(TK_ASSIGN); - PyVar value; - check(read_literal(&value)); - if(value == nullptr) return SyntaxError("default argument must be a literal"); - FuncDecl__add_kwarg(decl, index, name.index, (const ::PyVar*)&value); - } break; - case 3: - decl->starred_kwarg = index; - state += 1; - break; - } - } while(match(TK_COMMA)); - return NULL; -} - -Error* Compiler::compile_function(int decorators) noexcept{ - Error* err; - consume(TK_ID); - std::string_view decl_name = prev().sv(); - int decl_index; - FuncDecl_ decl = push_f_context({decl_name.data(), (int)decl_name.size()}, &decl_index); - consume(TK_LPAREN); - if(!match(TK_RPAREN)) { - check(_compile_f_args(decl, true)); - consume(TK_RPAREN); - } - if(match(TK_ARROW)) check(consume_type_hints()); - check(compile_block_body()); - check(pop_context()); - - if(decl->code->codes.count >= 2) { - Bytecode* codes = (Bytecode*)decl->code->codes.data; - if(codes[0].op == OP_LOAD_CONST && codes[1].op == OP_POP_TOP) { - // handle optional docstring - PyVar* c = c11__at(PyVar, &decl->code->consts, codes[0].arg); - if(is_type(*c, vm->tp_str)) { - codes[0].op = OP_NO_OP; - codes[1].op = OP_NO_OP; - decl->docstring = PK_OBJ_GET(Str, *c).c_str(); - } - } - } - ctx()->emit_(OP_LOAD_FUNCTION, decl_index, prev().line); - - ctx()->s_emit_decorators(decorators); - - if(!ctx()->is_compiling_class) { - NameExpr* e = make_expr(StrName(decl_name), name_scope()); - e->emit_store(ctx()); - delete_expr(e); - } else { - int index = StrName(decl_name).index; - ctx()->emit_(OP_STORE_CLASS_ATTR, index, prev().line); - } - return NULL; -} - -PyVar Compiler::to_object(const TokenValue& value) noexcept{ - PyVar obj = nullptr; - if(std::holds_alternative(value)) { obj = VAR(std::get(value)); } - if(std::holds_alternative(value)) { obj = VAR(std::get(value)); } - if(std::holds_alternative(value)) { obj = VAR(std::get(value)); } - assert(obj != nullptr); - return obj; -} - -Error* Compiler::read_literal(PyVar* out) noexcept{ - Error* err; - advance(); - switch(prev().type) { - case TK_SUB: { - consume(TK_NUM); - PyVar val = to_object(prev().value); - *out = vm->py_negate(val); - return NULL; - } - case TK_NUM: *out = to_object(prev().value); return NULL; - case TK_STR: *out = to_object(prev().value); return NULL; - case TK_TRUE: *out = VAR(true); return NULL; - case TK_FALSE: *out = VAR(false); return NULL; - case TK_NONE: *out = vm->None; return NULL; - case TK_DOTDOTDOT: *out = vm->Ellipsis; return NULL; - case TK_LPAREN: { - List cpnts; - while(true) { - PyVar elem; - check(read_literal(&elem)); - cpnts.push_back(elem); - if(curr().type == TK_RPAREN) break; - consume(TK_COMMA); - if(curr().type == TK_RPAREN) break; - } - consume(TK_RPAREN); - *out = VAR(cpnts.to_tuple()); - return NULL; - } - default: *out = nullptr; return NULL; - } -} - -Compiler::Compiler(VM* vm, std::string_view source, const Str& filename, CompileMode mode, bool unknown_global_scope) noexcept: - lexer(vm, source, filename, mode){ - this->vm = vm; - this->unknown_global_scope = unknown_global_scope; - init_pratt_rules(); -} - -Error* Compiler::compile(CodeObject** out) noexcept{ - assert(__i == 0); // make sure it is the first time to compile - - Error* err; - check(lexer.run()); - - // if(lexer.src.filename()[0] != '<'){ - // printf("%s\n", lexer.src.filename().c_str()); - // for(int i=0; is_emit_top(); - consume(TK_EOF); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); - check(pop_context()); - *out = code; - return NULL; - } else if(mode() == JSON_MODE) { - check(EXPR()); - Expr* e = ctx()->s_popx(); - if(!e->is_json_object()) return SyntaxError("expect a JSON object, literal or array"); - consume(TK_EOF); - e->emit_(ctx()); - ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); - check(pop_context()); - *out = code; - return NULL; - } - - while(!match(TK_EOF)) { - check(compile_stmt()); - match_newlines(); - } - check(pop_context()); - *out = code; - return NULL; -} - -Compiler::~Compiler(){ - for(CodeEmitContext& ctx: contexts){ - ctx.s_clean(); - } -} - -Error* Compiler::SyntaxError(const char* msg, ...) noexcept{ - va_list args; - va_start(args, msg); - Error* e = lexer._error(false, "SyntaxError", msg, &args); - e->lineno = err().line; - e->cursor = err().start; - va_end(args); - return e; -} - -#undef consume -#undef consume_end_stmt -#undef check -#undef check_newlines_repl - -} // namespace pkpy diff --git a/src/compiler/expr.cpp b/src/compiler/expr.cpp deleted file mode 100644 index d11381d9..00000000 --- a/src/compiler/expr.cpp +++ /dev/null @@ -1,778 +0,0 @@ -#include "pocketpy/compiler/expr.hpp" -#include "pocketpy/interpreter/vm.hpp" -#include "pocketpy/objects/codeobject.h" -#include "pocketpy/objects/public.h" - -namespace pkpy { - -inline bool is_identifier(std::string_view s) { - if(s.empty()) return false; - if(!isalpha(s[0]) && s[0] != '_') return false; - for(char c: s) - if(!isalnum(c) && c != '_') return false; - return true; -} - -inline bool is_small_int(i64 value) { return value >= INT16_MIN && value <= INT16_MAX; } - -int CodeEmitContext::get_loop() const noexcept{ - int index = curr_iblock; - while(index >= 0) { - CodeBlock* block = c11__at(CodeBlock, &co->blocks, index); - if(block->type == CodeBlockType_FOR_LOOP) break; - if(block->type == CodeBlockType_WHILE_LOOP) break; - index = block->parent; - } - return index; -} - -CodeBlock* CodeEmitContext::enter_block(CodeBlockType type) noexcept{ - CodeBlock block = {type, curr_iblock, co->codes.count, -1, -1}; - c11_vector__push(CodeBlock, &co->blocks, block); - curr_iblock = co->blocks.count - 1; - return c11__at(CodeBlock, &co->blocks, curr_iblock); -} - -void CodeEmitContext::exit_block() noexcept{ - CodeBlock* block = c11__at(CodeBlock, &co->blocks, curr_iblock); - CodeBlockType curr_type = block->type; - block->end = co->codes.count; - curr_iblock = block->parent; - assert(curr_iblock >= 0); - if(curr_type == CodeBlockType_FOR_LOOP) { - // add a no op here to make block check work - emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE, true); - } -} - -void CodeEmitContext::s_emit_decorators(int count) noexcept{ - assert(s_size() >= count); - // [obj] - for(int i=0; iemit_(this); // [obj, f] - emit_(OP_ROT_TWO, BC_NOARG, deco->line); // [f, obj] - emit_(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); // [f, obj, NULL] - emit_(OP_ROT_TWO, BC_NOARG, BC_KEEPLINE); // [obj, NULL, f] - emit_(OP_CALL, 1, deco->line); // [obj] - delete_expr(deco); - } -} - -int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual) noexcept{ - c11_vector__push(Bytecode, &co->codes, (Bytecode{(uint8_t)opcode, arg})); - c11_vector__push(BytecodeEx, &co->codes_ex, (BytecodeEx{line, is_virtual, curr_iblock})); - int i = co->codes.count - 1; - BytecodeEx* codes_ex = (BytecodeEx*)co->codes_ex.data; - if(line == BC_KEEPLINE) { - codes_ex[i].lineno = i>=1 ? codes_ex[i-1].lineno : 1; - } - return i; -} - -void CodeEmitContext::revert_last_emit_() noexcept{ - c11_vector__pop(&co->codes); - c11_vector__pop(&co->codes_ex); -} - -void CodeEmitContext::try_merge_for_iter_store(int i) noexcept{ - // [FOR_ITER, STORE_?, ] - Bytecode* co_codes = (Bytecode*)co->codes.data; - if(co_codes[i].op != OP_FOR_ITER) return; - if(co->codes.count - i != 2) return; - uint16_t arg = co_codes[i + 1].arg; - if(co_codes[i + 1].op == OP_STORE_FAST) { - revert_last_emit_(); - co_codes[i].op = OP_FOR_ITER_STORE_FAST; - co_codes[i].arg = arg; - return; - } - if(co_codes[i + 1].op == OP_STORE_GLOBAL) { - revert_last_emit_(); - co_codes[i].op = OP_FOR_ITER_STORE_GLOBAL; - co_codes[i].arg = arg; - return; - } -} - -int CodeEmitContext::emit_int(i64 value, int line) noexcept{ - if(is_small_int(value)) { - return emit_(OP_LOAD_SMALL_INT, (uint16_t)value, line); - } else { - return emit_(OP_LOAD_CONST, add_const(VAR(value)), line); - } -} - -void CodeEmitContext::patch_jump(int index) noexcept{ - Bytecode* co_codes = (Bytecode*)co->codes.data; - int target = co->codes.count; - Bytecode__set_signed_arg(&co_codes[index], target - index); -} - -bool CodeEmitContext::add_label(StrName name) noexcept{ - bool ok = c11_smallmap_n2i__contains(&co->labels, name.index); - if(ok) return false; - c11_smallmap_n2i__set(&co->labels, name.index, co->codes.count); - return true; -} - -int CodeEmitContext::add_varname(StrName name) noexcept{ - // PK_MAX_CO_VARNAMES will be checked when pop_context(), not here - int index = c11_smallmap_n2i__get(&co->varnames_inv, name.index, -1); - if(index >= 0) return index; - c11_vector__push(uint16_t, &co->varnames, name.index); - co->nlocals++; - index = co->varnames.count - 1; - c11_smallmap_n2i__set(&co->varnames_inv, name.index, index); - return index; -} - -int CodeEmitContext::add_const_string(std::string_view key) noexcept{ - uint16_t* val = c11_smallmap_s2n__try_get(&_co_consts_string_dedup_map, {key.data(), (int)key.size()}); - if(val) { - return *val; - } else { - // co->consts.push_back(VAR(key)); - c11_vector__push(PyVar, &co->consts, VAR(key)); - int index = co->consts.count - 1; - key = c11__getitem(PyVar, &co->consts, index).obj_get().sv(); - c11_smallmap_s2n__set(&_co_consts_string_dedup_map, {key.data(), (int)key.size()}, index); - return index; - } -} - -int CodeEmitContext::add_const(PyVar v) noexcept{ - assert(!is_type(v, VM::tp_str)); - c11_vector__push(PyVar, &co->consts, v); - return co->consts.count - 1; -} - -void CodeEmitContext::emit_store_name(NameScope scope, StrName name, int line) noexcept{ - switch(scope) { - case NAME_LOCAL: emit_(OP_STORE_FAST, add_varname(name), line); break; - case NAME_GLOBAL: emit_(OP_STORE_GLOBAL, StrName(name).index, line); break; - case NAME_GLOBAL_UNKNOWN: emit_(OP_STORE_NAME, StrName(name).index, line); break; - default: assert(false); break; - } -} - -void NameExpr::emit_(CodeEmitContext* ctx) { - int index = c11_smallmap_n2i__get(&ctx->co->varnames_inv, name.index, -1); - if(scope == NAME_LOCAL && index >= 0) { - ctx->emit_(OP_LOAD_FAST, index, line); - } else { - Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL; - if(ctx->is_compiling_class && scope == NAME_GLOBAL) { - // if we are compiling a class, we should use OP_LOAD_ATTR_GLOBAL instead of OP_LOAD_GLOBAL - // this supports @property.setter - op = OP_LOAD_CLASS_GLOBAL; - // exec()/eval() won't work with OP_LOAD_ATTR_GLOBAL in class body - } else { - // we cannot determine the scope when calling exec()/eval() - if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; - } - ctx->emit_(op, StrName(name).index, line); - } -} - -bool NameExpr::emit_del(CodeEmitContext* ctx) { - switch(scope) { - case NAME_LOCAL: ctx->emit_(OP_DELETE_FAST, ctx->add_varname(name), line); break; - case NAME_GLOBAL: ctx->emit_(OP_DELETE_GLOBAL, StrName(name).index, line); break; - case NAME_GLOBAL_UNKNOWN: ctx->emit_(OP_DELETE_NAME, StrName(name).index, line); break; - default: assert(false); break; - } - return true; -} - -bool NameExpr::emit_store(CodeEmitContext* ctx) { - if(ctx->is_compiling_class) { - ctx->emit_(OP_STORE_CLASS_ATTR, name.index, line); - return true; - } - ctx->emit_store_name(scope, name, line); - return true; -} - -void InvertExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_UNARY_INVERT, BC_NOARG, line); -} - -void StarredExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_UNARY_STAR, level, line); -} - -bool StarredExpr::emit_store(CodeEmitContext* ctx) { - if(level != 1) return false; - // simply proxy to child - return child->emit_store(ctx); -} - -void NotExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_UNARY_NOT, BC_NOARG, line); -} - -void AndExpr::emit_(CodeEmitContext* ctx) { - lhs->emit_(ctx); - int patch = ctx->emit_(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line); - rhs->emit_(ctx); - ctx->patch_jump(patch); -} - -void OrExpr::emit_(CodeEmitContext* ctx) { - lhs->emit_(ctx); - int patch = ctx->emit_(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line); - rhs->emit_(ctx); - ctx->patch_jump(patch); -} - -void Literal0Expr::emit_(CodeEmitContext* ctx) { - switch(token) { - case TK_NONE: ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); break; - case TK_TRUE: ctx->emit_(OP_LOAD_TRUE, BC_NOARG, line); break; - case TK_FALSE: ctx->emit_(OP_LOAD_FALSE, BC_NOARG, line); break; - case TK_DOTDOTDOT: ctx->emit_(OP_LOAD_ELLIPSIS, BC_NOARG, line); break; - default: assert(false); - } -} - -void LongExpr::emit_(CodeEmitContext* ctx) { - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(s.sv()), line); - ctx->emit_(OP_BUILD_LONG, BC_NOARG, line); -} - -void ImagExpr::emit_(CodeEmitContext* ctx) { - VM* vm = ctx->vm; - ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(value)), line); - ctx->emit_(OP_BUILD_IMAG, BC_NOARG, line); -} - -void BytesExpr::emit_(CodeEmitContext* ctx) { - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(s.sv()), line); - ctx->emit_(OP_BUILD_BYTES, BC_NOARG, line); -} - -void LiteralExpr::emit_(CodeEmitContext* ctx) { - VM* vm = ctx->vm; - if(std::holds_alternative(value)) { - i64 _val = std::get(value); - ctx->emit_int(_val, line); - return; - } - if(std::holds_alternative(value)) { - f64 _val = std::get(value); - ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line); - return; - } - if(std::holds_alternative(value)) { - std::string_view key = std::get(value).sv(); - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(key), line); - return; - } -} - -void NegatedExpr::emit_(CodeEmitContext* ctx) { - VM* vm = ctx->vm; - // if child is a int of float, do constant folding - if(child->is_literal()) { - LiteralExpr* lit = static_cast(child); - if(std::holds_alternative(lit->value)) { - i64 _val = -std::get(lit->value); - ctx->emit_int(_val, line); - return; - } - if(std::holds_alternative(lit->value)) { - f64 _val = -std::get(lit->value); - ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line); - return; - } - } - child->emit_(ctx); - ctx->emit_(OP_UNARY_NEGATIVE, BC_NOARG, line); -} - -void SliceExpr::emit_(CodeEmitContext* ctx) { - if(start) { - start->emit_(ctx); - } else { - ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); - } - - if(stop) { - stop->emit_(ctx); - } else { - ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); - } - - if(step) { - step->emit_(ctx); - } else { - ctx->emit_(OP_LOAD_NONE, BC_NOARG, line); - } - - ctx->emit_(OP_BUILD_SLICE, BC_NOARG, line); -} - -void DictItemExpr::emit_(CodeEmitContext* ctx) { - if(is_starred()) { - assert(key == nullptr); - value->emit_(ctx); - } else { - key->emit_(ctx); - value->emit_(ctx); - ctx->emit_(OP_BUILD_TUPLE, 2, line); - } -} - -bool TupleExpr::emit_store(CodeEmitContext* ctx) { - // TOS is an iterable - // items may contain StarredExpr, we should check it - int starred_i = -1; - for(int i = 0; i < items.size(); i++) { - if(!items[i]->is_starred()) continue; - if(starred_i == -1) - starred_i = i; - else - return false; // multiple StarredExpr not allowed - } - - if(starred_i == -1) { - Bytecode* prev = c11__at(Bytecode, &ctx->co->codes, ctx->co->codes.count - 1); - if(prev->op == OP_BUILD_TUPLE && prev->arg == items.size()) { - // build tuple and unpack it is meaningless - ctx->revert_last_emit_(); - } else { - if(prev->op == OP_FOR_ITER) { - prev->op = OP_FOR_ITER_UNPACK; - prev->arg = items.size(); - } else { - ctx->emit_(OP_UNPACK_SEQUENCE, items.size(), line); - } - } - } else { - // starred assignment target must be in a tuple - if(items.size() == 1) return false; - // starred assignment target must be the last one (differ from cpython) - if(starred_i != items.size() - 1) return false; - // a,*b = [1,2,3] - // stack is [1,2,3] -> [1,[2,3]] - ctx->emit_(OP_UNPACK_EX, items.size() - 1, line); - } - // do reverse emit - for(int i = items.size() - 1; i >= 0; i--) { - bool ok = items[i]->emit_store(ctx); - if(!ok) return false; - } - return true; -} - -bool TupleExpr::emit_del(CodeEmitContext* ctx) { - for(auto& e: items) { - bool ok = e->emit_del(ctx); - if(!ok) return false; - } - return true; -} - -void CompExpr::emit_(CodeEmitContext* ctx) { - ctx->emit_(op0, 0, line); - iter->emit_(ctx); - ctx->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE); - ctx->enter_block(CodeBlockType_FOR_LOOP); - int curr_iblock = ctx->curr_iblock; - int for_codei = ctx->emit_(OP_FOR_ITER, curr_iblock, BC_KEEPLINE); - bool ok = vars->emit_store(ctx); - // this error occurs in `vars` instead of this line, but...nevermind - assert(ok); // this should raise a SyntaxError, but we just assert it - ctx->try_merge_for_iter_store(for_codei); - if(cond) { - cond->emit_(ctx); - int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); - expr->emit_(ctx); - ctx->emit_(op1, BC_NOARG, BC_KEEPLINE); - ctx->patch_jump(patch); - } else { - expr->emit_(ctx); - ctx->emit_(op1, BC_NOARG, BC_KEEPLINE); - } - ctx->emit_(OP_LOOP_CONTINUE, curr_iblock, BC_KEEPLINE); - ctx->exit_block(); -} - -void FStringExpr::_load_simple_expr(CodeEmitContext* ctx, Str expr) { - bool repr = false; - if(expr.size >= 2 && expr.end()[-2] == '!') { - switch(expr.end()[-1]) { - case 'r': - repr = true; - expr = expr.slice(0, expr.size - 2); - break; - case 's': - repr = false; - expr = expr.slice(0, expr.size - 2); - break; - default: break; // nothing happens - } - } - // name or name.name - bool is_fastpath = false; - if(is_identifier(expr.sv())) { - ctx->emit_(OP_LOAD_NAME, StrName(expr.sv()).index, line); - is_fastpath = true; - } else { - int dot = expr.index("."); - if(dot > 0) { - std::string_view a = expr.sv().substr(0, dot); - std::string_view b = expr.sv().substr(dot + 1); - if(is_identifier(a) && is_identifier(b)) { - ctx->emit_(OP_LOAD_NAME, StrName(a).index, line); - ctx->emit_(OP_LOAD_ATTR, StrName(b).index, line); - is_fastpath = true; - } - } - } - - if(!is_fastpath) { - int index = ctx->add_const_string(expr.sv()); - ctx->emit_(OP_FSTRING_EVAL, index, line); - } - - if(repr) { ctx->emit_(OP_REPR, BC_NOARG, line); } -} - -static bool is_fmt_valid_char(char c) { - switch(c) { - // clang-format off - case '-': case '=': case '*': case '#': case '@': case '!': case '~': - case '<': case '>': case '^': - case '.': case 'f': case 'd': case 's': - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - return true; - default: return false; - // clang-format on - } -} - -void FStringExpr::emit_(CodeEmitContext* ctx) { - int i = 0; // left index - int j = 0; // right index - int count = 0; // how many string parts - bool flag = false; // true if we are in a expression - - while(j < src.size) { - if(flag) { - if(src[j] == '}') { - // add expression - Str expr = src.slice(i, j); - // BUG: ':' is not a format specifier in f"{stack[2:]}" - int conon = expr.index(":"); - if(conon >= 0) { - Str spec = expr.substr(conon + 1); - // filter some invalid spec - bool ok = true; - for(char c: spec) - if(!is_fmt_valid_char(c)) { - ok = false; - break; - } - if(ok) { - _load_simple_expr(ctx, expr.slice(0, conon)); - ctx->emit_(OP_FORMAT_STRING, ctx->add_const_string(spec.sv()), line); - } else { - // ':' is not a spec indicator - _load_simple_expr(ctx, expr); - } - } else { - _load_simple_expr(ctx, expr); - } - flag = false; - count++; - } - } else { - if(src[j] == '{') { - // look at next char - if(j + 1 < src.size && src[j + 1] == '{') { - // {{ -> { - j++; - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string("{"), line); - count++; - } else { - // { -> } - flag = true; - i = j + 1; - } - } else if(src[j] == '}') { - // look at next char - if(j + 1 < src.size && src[j + 1] == '}') { - // }} -> } - j++; - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string("}"), line); - count++; - } else { - // } -> error - // throw std::runtime_error("f-string: unexpected }"); - // just ignore - } - } else { - // literal - i = j; - while(j < src.size && src[j] != '{' && src[j] != '}') - j++; - Str literal = src.slice(i, j); - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line); - count++; - continue; // skip j++ - } - } - j++; - } - - if(flag) { - // literal - Str literal = src.slice(i, src.size); - ctx->emit_(OP_LOAD_CONST, ctx->add_const_string(literal.sv()), line); - count++; - } - - ctx->emit_(OP_BUILD_STRING, count, line); -} - -void SubscrExpr::emit_(CodeEmitContext* ctx) { - lhs->emit_(ctx); - rhs->emit_(ctx); - Bytecode last_bc = c11__getitem(Bytecode, &ctx->co->codes, ctx->co->codes.count-1); - if(rhs->is_name() && last_bc.op == OP_LOAD_FAST) { - ctx->revert_last_emit_(); - ctx->emit_(OP_LOAD_SUBSCR_FAST, last_bc.arg, line); - } else if(rhs->is_literal() && last_bc.op == OP_LOAD_SMALL_INT) { - ctx->revert_last_emit_(); - ctx->emit_(OP_LOAD_SUBSCR_SMALL_INT, last_bc.arg, line); - } else { - ctx->emit_(OP_LOAD_SUBSCR, BC_NOARG, line); - } -} - -bool SubscrExpr::emit_store(CodeEmitContext* ctx) { - lhs->emit_(ctx); - rhs->emit_(ctx); - Bytecode last_bc = c11__getitem(Bytecode, &ctx->co->codes, ctx->co->codes.count-1); - if(rhs->is_name() && last_bc.op == OP_LOAD_FAST) { - ctx->revert_last_emit_(); - ctx->emit_(OP_STORE_SUBSCR_FAST, last_bc.arg, line); - } else { - ctx->emit_(OP_STORE_SUBSCR, BC_NOARG, line); - } - return true; -} - -void SubscrExpr::emit_inplace(CodeEmitContext* ctx) { - lhs->emit_(ctx); - rhs->emit_(ctx); - ctx->emit_(OP_DUP_TOP_TWO, BC_NOARG, line); - ctx->emit_(OP_LOAD_SUBSCR, BC_NOARG, line); -} - -bool SubscrExpr::emit_store_inplace(CodeEmitContext* ctx) { - // [a, b, val] -> [val, a, b] - ctx->emit_(OP_ROT_THREE, BC_NOARG, line); - ctx->emit_(OP_STORE_SUBSCR, BC_NOARG, line); - return true; -} - -bool SubscrExpr::emit_del(CodeEmitContext* ctx) { - lhs->emit_(ctx); - rhs->emit_(ctx); - ctx->emit_(OP_DELETE_SUBSCR, BC_NOARG, line); - return true; -} - -void AttribExpr::emit_(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_LOAD_ATTR, name.index, line); -} - -bool AttribExpr::emit_del(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_DELETE_ATTR, name.index, line); - return true; -} - -bool AttribExpr::emit_store(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_STORE_ATTR, name.index, line); - return true; -} - -void AttribExpr::emit_method(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_LOAD_METHOD, name.index, line); -} - -void AttribExpr::emit_inplace(CodeEmitContext* ctx) { - child->emit_(ctx); - ctx->emit_(OP_DUP_TOP, BC_NOARG, line); - ctx->emit_(OP_LOAD_ATTR, name.index, line); -} - -bool AttribExpr::emit_store_inplace(CodeEmitContext* ctx) { - // [a, val] -> [val, a] - ctx->emit_(OP_ROT_TWO, BC_NOARG, line); - ctx->emit_(OP_STORE_ATTR, name.index, line); - return true; -} - -void CallExpr::emit_(CodeEmitContext* ctx) { - bool vargs = false; - bool vkwargs = false; - for(auto& arg: args) - if(arg->is_starred()) vargs = true; - for(auto& item: kwargs) - if(item.second->is_starred()) vkwargs = true; - - // if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy - if(callable->is_attrib()) { - auto p = static_cast(callable); - p->emit_method(ctx); // OP_LOAD_METHOD - } else { - callable->emit_(ctx); - ctx->emit_(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE); - } - - if(vargs || vkwargs) { - for(auto& item: args) - item->emit_(ctx); - ctx->emit_(OP_BUILD_TUPLE_UNPACK, (uint16_t)args.size(), line); - - if(!kwargs.empty()) { - for(auto& item: kwargs) { - if(item.second->is_starred()) { - assert(item.second->star_level() == 2); - item.second->emit_(ctx); - } else { - // k=v - int index = ctx->add_const_string(item.first.sv()); - ctx->emit_(OP_LOAD_CONST, index, line); - item.second->emit_(ctx); - ctx->emit_(OP_BUILD_TUPLE, 2, line); - } - } - ctx->emit_(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line); - ctx->emit_(OP_CALL_TP, 1, line); - } else { - ctx->emit_(OP_CALL_TP, 0, line); - } - } else { - // vectorcall protocol - for(auto& item: args) - item->emit_(ctx); - for(auto& item: kwargs) { - i64 _val = StrName(item.first.sv()).index; - ctx->emit_int(_val, line); - item.second->emit_(ctx); - } - int KWARGC = kwargs.size(); - int ARGC = args.size(); - ctx->emit_(OP_CALL, (KWARGC << 8) | ARGC, line); - } -} - -bool BinaryExpr::is_compare() const { - switch(op) { - case TK_LT: - case TK_LE: - case TK_EQ: - case TK_NE: - case TK_GT: - case TK_GE: return true; - default: return false; - } -} - -void BinaryExpr::_emit_compare(CodeEmitContext* ctx, small_vector_2& jmps) { - if(lhs->is_compare()) { - static_cast(lhs)->_emit_compare(ctx, jmps); - } else { - lhs->emit_(ctx); // [a] - } - rhs->emit_(ctx); // [a, b] - ctx->emit_(OP_DUP_TOP, BC_NOARG, line); // [a, b, b] - ctx->emit_(OP_ROT_THREE, BC_NOARG, line); // [b, a, b] - switch(op) { - case TK_LT: ctx->emit_(OP_COMPARE_LT, BC_NOARG, line); break; - case TK_LE: ctx->emit_(OP_COMPARE_LE, BC_NOARG, line); break; - case TK_EQ: ctx->emit_(OP_COMPARE_EQ, BC_NOARG, line); break; - case TK_NE: ctx->emit_(OP_COMPARE_NE, BC_NOARG, line); break; - case TK_GT: ctx->emit_(OP_COMPARE_GT, BC_NOARG, line); break; - case TK_GE: ctx->emit_(OP_COMPARE_GE, BC_NOARG, line); break; - default: PK_UNREACHABLE() - } - // [b, RES] - int index = ctx->emit_(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line); - jmps.push_back(index); -} - -void BinaryExpr::emit_(CodeEmitContext* ctx) { - small_vector_2 jmps; - if(is_compare() && lhs->is_compare()) { - // (a < b) < c - static_cast(lhs)->_emit_compare(ctx, jmps); - // [b, RES] - } else { - // (1 + 2) < c - if(inplace) { - lhs->emit_inplace(ctx); - } else { - lhs->emit_(ctx); - } - } - - rhs->emit_(ctx); - switch(op) { - case TK_ADD: ctx->emit_(OP_BINARY_ADD, BC_NOARG, line); break; - case TK_SUB: ctx->emit_(OP_BINARY_SUB, BC_NOARG, line); break; - case TK_MUL: ctx->emit_(OP_BINARY_MUL, BC_NOARG, line); break; - case TK_DIV: ctx->emit_(OP_BINARY_TRUEDIV, BC_NOARG, line); break; - case TK_FLOORDIV: ctx->emit_(OP_BINARY_FLOORDIV, BC_NOARG, line); break; - case TK_MOD: ctx->emit_(OP_BINARY_MOD, BC_NOARG, line); break; - case TK_POW: ctx->emit_(OP_BINARY_POW, BC_NOARG, line); break; - - case TK_LT: ctx->emit_(OP_COMPARE_LT, BC_NOARG, line); break; - case TK_LE: ctx->emit_(OP_COMPARE_LE, BC_NOARG, line); break; - case TK_EQ: ctx->emit_(OP_COMPARE_EQ, BC_NOARG, line); break; - case TK_NE: ctx->emit_(OP_COMPARE_NE, BC_NOARG, line); break; - case TK_GT: ctx->emit_(OP_COMPARE_GT, BC_NOARG, line); break; - case TK_GE: ctx->emit_(OP_COMPARE_GE, BC_NOARG, line); break; - - case TK_IN: ctx->emit_(OP_CONTAINS_OP, 0, line); break; - case TK_NOT_IN: ctx->emit_(OP_CONTAINS_OP, 1, line); break; - case TK_IS: ctx->emit_(OP_IS_OP, BC_NOARG, line); break; - case TK_IS_NOT: ctx->emit_(OP_IS_NOT_OP, BC_NOARG, line); break; - - case TK_LSHIFT: ctx->emit_(OP_BITWISE_LSHIFT, BC_NOARG, line); break; - case TK_RSHIFT: ctx->emit_(OP_BITWISE_RSHIFT, BC_NOARG, line); break; - case TK_AND: ctx->emit_(OP_BITWISE_AND, BC_NOARG, line); break; - case TK_OR: ctx->emit_(OP_BITWISE_OR, BC_NOARG, line); break; - case TK_XOR: ctx->emit_(OP_BITWISE_XOR, BC_NOARG, line); break; - - case TK_DECORATOR: ctx->emit_(OP_BINARY_MATMUL, BC_NOARG, line); break; - default: PK_FATAL_ERROR("unknown binary operator: %s\n", pk_TokenSymbols[op]); - } - - for(int i: jmps) - ctx->patch_jump(i); -} - -void TernaryExpr::emit_(CodeEmitContext* ctx) { - cond->emit_(ctx); - int patch = ctx->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line); - true_expr->emit_(ctx); - int patch_2 = ctx->emit_(OP_JUMP_FORWARD, BC_NOARG, true_expr->line); - ctx->patch_jump(patch); - false_expr->emit_(ctx); - ctx->patch_jump(patch_2); -} - -} // namespace pkpy