blueloveTH 2 лет назад
Родитель
Сommit
718ba988d5
3 измененных файлов с 78 добавлено и 59 удалено
  1. 49 48
      src/compiler.h
  2. 27 11
      src/expr.h
  3. 2 0
      src/opcodes.h

+ 49 - 48
src/compiler.h

@@ -448,8 +448,9 @@ class Compiler {
                 consume(TK("@id"));
                 name = prev().str();
             }
-            int index = ctx()->add_name(name, name_scope());
-            ctx()->emit(OP_STORE_NAME, index, prev().line);
+            int index = ctx()->add_name(name);
+            auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL;
+            ctx()->emit(op, index, prev().line);
         } while (match(TK(",")));
         consume_end_stmt();
     }
@@ -459,8 +460,8 @@ class Compiler {
         _compile_import();
         consume(TK("import"));
         if (match(TK("*"))) {
-            if(name_scope() != NAME_GLOBAL) SyntaxError("import * can only be used in global scope");
-            ctx()->emit(OP_STORE_ALL_NAMES, BC_NOARG, prev().line);
+            if(name_scope() != NAME_GLOBAL) SyntaxError("import * should be used in global scope");
+            ctx()->emit(OP_IMPORT_STAR, BC_NOARG, prev().line);
             consume_end_stmt();
             return;
         }
@@ -475,7 +476,8 @@ class Compiler {
                 name = prev().str();
             }
             index = ctx()->add_name(name);
-            ctx()->emit(OP_STORE_GLOBAL, index, prev().line);
+            auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL;
+            ctx()->emit(op, index, prev().line);
         } while (match(TK(",")));
         ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
         consume_end_stmt();
@@ -527,6 +529,7 @@ class Compiler {
         ctx()->exit_block();
     }
 
+    // PASS
     void compile_for_loop() {
         EXPR_TUPLE();
         Expr_ vars = ctx()->s_expr.popx();
@@ -535,42 +538,39 @@ class Compiler {
         ctx()->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
         ctx()->enter_block(FOR_LOOP);
         ctx()->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
-        // set variables and handle implicit unpack
         bool ok = vars->emit_store(ctx());
-        // this error occurs in `vars` instead of this line
-        // but...nevermind
-        if(!ok) SyntaxError();
+        if(!ok) SyntaxError();  // this error occurs in `vars` instead of this line, but...nevermind
         compile_block_body();
         ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
         ctx()->exit_block();
     }
 
     void compile_try_except() {
-        ctx()->enter_block(TRY_EXCEPT);
-        ctx()->emit(OP_TRY_BLOCK_ENTER, BC_NOARG, prev().line);
-        compile_block_body();
-        ctx()->emit(OP_TRY_BLOCK_EXIT, BC_NOARG, BC_KEEPLINE);
-        std::vector<int> patches = {
-            ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)
-        };
-        ctx()->exit_block();
-
-        do {
-            consume(TK("except"));
-            if(match(TK("@id"))){
-                int name_idx = ctx()->add_name(prev().str(), NAME_SPECIAL);
-                emit(OP_EXCEPTION_MATCH, name_idx);
-            }else{
-                emit(OP_LOAD_TRUE);
-            }
-            int patch = emit(OP_POP_JUMP_IF_FALSE);
-            emit(OP_POP_TOP);       // pop the exception on match
-            compile_block_body();
-            patches.push_back(emit(OP_JUMP_ABSOLUTE));
-            patch_jump(patch);
-        }while(curr().type == TK("except"));
-        emit(OP_RE_RAISE);      // no match, re-raise
-        for (int patch : patches) patch_jump(patch);
+        // ctx()->enter_block(TRY_EXCEPT);
+        // ctx()->emit(OP_TRY_BLOCK_ENTER, BC_NOARG, prev().line);
+        // compile_block_body();
+        // ctx()->emit(OP_TRY_BLOCK_EXIT, BC_NOARG, BC_KEEPLINE);
+        // std::vector<int> patches = {
+        //     ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)
+        // };
+        // ctx()->exit_block();
+
+        // do {
+        //     consume(TK("except"));
+        //     if(match(TK("@id"))){
+        //         int name_idx = ctx()->add_name(prev().str(), NAME_SPECIAL);
+        //         emit(OP_EXCEPTION_MATCH, name_idx);
+        //     }else{
+        //         emit(OP_LOAD_TRUE);
+        //     }
+        //     int patch = emit(OP_POP_JUMP_IF_FALSE);
+        //     emit(OP_POP_TOP);       // pop the exception on match
+        //     compile_block_body();
+        //     patches.push_back(emit(OP_JUMP_ABSOLUTE));
+        //     patch_jump(patch);
+        // }while(curr().type == TK("except"));
+        // emit(OP_RE_RAISE);      // no match, re-raise
+        // for (int patch : patches) patch_jump(patch);
     }
 
     void compile_decorated(){
@@ -650,13 +650,13 @@ class Compiler {
             case TK("global"):
                 do {
                     consume(TK("@id"));
-                    co()->global_names.insert(prev().str());
+                    ctx()->co->global_names.insert(prev().str());
                 } while (match(TK(",")));
                 consume_end_stmt();
                 break;
             case TK("raise"): {
                 consume(TK("@id"));
-                int dummy_t = ctx()->add_name(prev().str(), NAME_SPECIAL);
+                int dummy_t = ctx()->add_name(prev().str());
                 if(match(TK("(")) && !match(TK(")"))){
                     EXPR(false); consume(TK(")"));
                 }else{
@@ -688,17 +688,17 @@ class Compiler {
             } break;
             /*************************************************/
             // TODO: refactor goto/label use special $ syntax
-            case TK("label"):
+            case TK("label"): {
                 if(mode()!=EXEC_MODE) SyntaxError("'label' is only available in EXEC_MODE");
                 consume(TK(".")); consume(TK("@id"));
                 bool ok = ctx()->add_label(prev().str());
                 if(!ok) SyntaxError("label " + prev().str().escape(true) + " already exists");
                 consume_end_stmt();
-                break;
+            } break;
             case TK("goto"):
                 if(mode()!=EXEC_MODE) SyntaxError("'goto' is only available in EXEC_MODE");
                 consume(TK(".")); consume(TK("@id"));
-                emit(OP_GOTO, co()->add_name(prev().str(), NAME_SPECIAL));
+                ctx()->emit(OP_GOTO, ctx()->add_name(prev().str()), prev().line);
                 consume_end_stmt();
                 break;
             /*************************************************/
@@ -707,9 +707,9 @@ class Compiler {
                 EXPR_TUPLE(true);
                 if(!try_compile_assignment()){
                     if(mode()==REPL_MODE && name_scope()==NAME_GLOBAL){
-                        emit(OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE);
+                        ctx()->emit(OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE);
                     }else{
-                        emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
+                        ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
                     }
                 }
                 consume_end_stmt();
@@ -717,21 +717,22 @@ class Compiler {
         }
     }
 
+    // PASS
     void compile_class(){
         consume(TK("@id"));
-        int cls_name_idx = co()->add_name(prev().str(), NAME_GLOBAL);
-        int super_cls_name_idx = -1;
+        int namei = ctx()->add_name(prev().str());
+        int super_namei = -1;
         if(match(TK("(")) && match(TK("@id"))){
-            super_cls_name_idx = co()->add_name(prev().str(), NAME_GLOBAL);
+            super_namei = ctx()->add_name(prev().str());
             consume(TK(")"));
         }
-        if(super_cls_name_idx == -1) emit(OP_LOAD_NONE);
-        else emit(OP_LOAD_NAME, super_cls_name_idx);
-        emit(OP_BEGIN_CLASS, cls_name_idx);
+        if(super_namei == -1) ctx()->emit(OP_LOAD_NONE, BC_NOARG, prev().line);
+        else ctx()->emit(OP_LOAD_NAME, super_namei, prev().line);
+        ctx()->emit(OP_BEGIN_CLASS, namei, BC_KEEPLINE);
         ctx()->is_compiling_class = true;
         compile_block_body();
         ctx()->is_compiling_class = false;
-        emit(OP_END_CLASS);
+        ctx()->emit(OP_END_CLASS, BC_NOARG, BC_KEEPLINE);
     }
 
     void _compile_f_args(Function& func, bool enable_type_hints){

+ 27 - 11
src/expr.h

@@ -23,7 +23,7 @@ struct Expr{
     virtual bool emit_del(CodeEmitContext* ctx) { return false; }
 
     // for OP_STORE_XXX
-    virtual bool emit_store(CodeEmitContext* ctx) { return false; }
+    [[nodiscard]] virtual bool emit_store(CodeEmitContext* ctx) { return false; }
 };
 
 struct CodeEmitContext{
@@ -153,15 +153,12 @@ struct StarredExpr: Expr{
 
     void emit(CodeEmitContext* ctx) override {
         child->emit(ctx);
-        // as a rvalue, we should do unpack here
-        //ctx->emit(OP_UNARY_STAR, (int)false, line);
+        ctx->emit(OP_UNARY_STAR, BC_NOARG, line);
     }
 
     bool emit_store(CodeEmitContext* ctx) override {
-        child->emit(ctx);
-        // as a lvalue, we should do pack here
-        //ctx->emit(OP_UNARY_STAR, (int)true, line);
-        return true;
+        // simply proxy to child
+        return child->emit_store(ctx);
     }
 };
 
@@ -368,10 +365,29 @@ struct TupleExpr: SequenceExpr{
     Opcode opcode() const override { return OP_BUILD_TUPLE; }
 
     bool emit_store(CodeEmitContext* ctx) override {
-        // assume TOS is an iterable
-        // unpack it and emit several OP_STORE
-        // https://docs.python.org/3/library/dis.html#opcode-UNPACK_SEQUENCE
-        // https://docs.python.org/3/library/dis.html#opcode-UNPACK_EX
+        // 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){
+            // Unpacks TOS into count individual values, which are put onto the stack right-to-left.
+            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;
+            ctx->emit(OP_UNPACK_EX, items.size()-1, line);
+        }
+        for(auto& e: items){
+            bool ok = e->emit_store(ctx);
+            if(!ok) return false;
+        }
         return true;
     }
 

+ 2 - 0
src/opcodes.h

@@ -94,5 +94,7 @@ OPCODE(FOR_ITER)
 OPCODE(IMPORT_NAME)
 OPCODE(IMPORT_STAR)
 /**************************/
+OPCODE(UNPACK_SEQUENCE)
+OPCODE(UNPACK_EX)
 /**************************/
 #endif