1
0
blueloveTH 2 жил өмнө
parent
commit
d54cd84138
7 өөрчлөгдсөн 131 нэмэгдсэн , 315 устгасан
  1. 1 1
      amalgamate.py
  2. 4 4
      src/ceval.h
  3. 1 6
      src/codeobject.h
  4. 81 116
      src/compiler.h
  5. 43 1
      src/expr.h
  6. 1 8
      src/opcodes.h
  7. 0 179
      src/ref.h

+ 1 - 1
amalgamate.py

@@ -8,7 +8,7 @@ with open("src/opcodes.h", "rt", encoding='utf-8') as f:
 pipeline = [
 	["common.h", "memory.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"],
 	["obj.h", "codeobject.h", "frame.h"],
-	["gc.h", "vm.h", "ref.h", "ceval.h", "compiler.h", "repl.h"],
+	["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"],
 	["iter.h", "cffi.h", "io.h", "_generated.h", "pocketpy.h"]
 ]
 

+ 4 - 4
src/ceval.h

@@ -2,7 +2,6 @@
 
 #include "common.h"
 #include "vm.h"
-#include "ref.h"
 
 namespace pkpy{
 
@@ -135,10 +134,11 @@ inline PyObject* VM::run_frame(Frame* frame){
             PyObject* cls = frame->top();
             cls->attr().set(name.first, std::move(obj));
         } continue;
-        case OP_RETURN_VALUE: return frame->pop_value(this);
+        case OP_RETURN_VALUE: return frame->popx();
         case OP_PRINT_EXPR: {
-            PyObject* expr = frame->top_value(this);
+            PyObject* expr = frame->top();  // use top() here to avoid accidental gc
             if(expr != None) *_stdout << CAST(Str, asRepr(expr)) << '\n';
+            frame->pop();
         } continue;
         case OP_POP_TOP: frame->_pop(); continue;
         case OP_BINARY_OP: {
@@ -239,7 +239,7 @@ inline PyObject* VM::run_frame(Frame* frame){
             PyObject* obj = frame->pop_value(this);
             call(frame->top_1(), "add", Args{obj});
         } continue;
-        case OP_DUP_TOP_VALUE: frame->push(frame->top_value(this)); continue;
+        case OP_DUP_TOP: frame->push(frame->top()); continue;
         case OP_UNARY_STAR: {
             if(byte.arg > 0){   // rvalue
                 frame->top() = VAR(StarWrapper(frame->top_value(this), true));

+ 1 - 6
src/codeobject.h

@@ -5,12 +5,7 @@
 
 namespace pkpy{
 
-enum NameScope {
-    NAME_LOCAL = 0,
-    NAME_GLOBAL,
-    NAME_ATTR,
-    NAME_SPECIAL,
-};
+enum NameScope { NAME_LOCAL, NAME_GLOBAL };
 
 enum Opcode {
     #define OPCODE(name) OP_##name,

+ 81 - 116
src/compiler.h

@@ -24,10 +24,18 @@ class Compiler {
     std::unique_ptr<Lexer> lexer;
     stack<CodeEmitContext> contexts;
     std::map<TokenIndex, PrattRule> rules;
-    bool used = false;
     VM* vm;
+    bool used;
+    // for parsing token stream
+    int i = 0;
+    std::vector<Token> tokens;
+
+    const Token& prev() { return tokens.at(i-1); }
+    const Token& curr() { return tokens.at(i); }
+    const Token& next() { return tokens.at(i+1); }
+    const Token& peek(int offset) { return tokens.at(i+offset); }
+    void advance() { i++; }
 
-    CodeObject* co() const{ return contexts.top().co.get(); }
     CodeEmitContext* ctx() { return &contexts.top(); }
     CompileMode mode() const{ return lexer->src->mode; }
     NameScope name_scope() const { return contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; }
@@ -41,6 +49,7 @@ class Compiler {
 
     void pop_context(){
         if(!ctx()->s_expr.empty()) UNREACHABLE();
+        // if last instruction is not return, add a default return None
         if(ctx()->co->codes.back().op != OP_RETURN_VALUE){
             ctx()->emit(OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE);
             ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
@@ -52,6 +61,7 @@ class Compiler {
 public:
     Compiler(VM* vm, const char* source, Str filename, CompileMode mode){
         this->vm = vm;
+        this->used = false;
         this->lexer = std::make_unique<Lexer>(
             make_sp<SourceData>(source, filename, mode)
         );
@@ -60,25 +70,32 @@ public:
 #define METHOD(name) &Compiler::name
 #define NO_INFIX nullptr, PREC_NONE
         for(TokenIndex i=0; i<kTokenCount; i++) rules[i] = { nullptr, NO_INFIX };
-        rules[TK(".")] =    { nullptr,               METHOD(exprAttrib),         PREC_ATTRIB };
-        rules[TK("(")] =    { METHOD(exprGroup),     METHOD(exprCall),           PREC_CALL };
-        rules[TK("[")] =    { METHOD(exprList),      METHOD(exprSubscr),         PREC_SUBSCRIPT };
-        rules[TK("{")] =    { METHOD(exprMap),       NO_INFIX };
-        rules[TK("%")] =    { nullptr,               METHOD(exprBinaryOp),       PREC_FACTOR };
-        rules[TK("+")] =    { nullptr,               METHOD(exprBinaryOp),       PREC_TERM };
-        rules[TK("-")] =    { METHOD(exprUnaryOp),   METHOD(exprBinaryOp),       PREC_TERM };
-        rules[TK("*")] =    { METHOD(exprUnaryOp),   METHOD(exprBinaryOp),       PREC_FACTOR };
-        rules[TK("/")] =    { nullptr,               METHOD(exprBinaryOp),       PREC_FACTOR };
-        rules[TK("//")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_FACTOR };
-        rules[TK("**")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_EXPONENT };
-        rules[TK(">")] =    { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
-        rules[TK("<")] =    { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
-        rules[TK("==")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_EQUALITY };
-        rules[TK("!=")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_EQUALITY };
-        rules[TK(">=")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
-        rules[TK("<=")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
-        rules[TK("in")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_TEST };
-        rules[TK("is")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_TEST };
+        rules[TK(".")] =        { nullptr,               METHOD(exprAttrib),         PREC_ATTRIB };
+        rules[TK("(")] =        { METHOD(exprGroup),     METHOD(exprCall),           PREC_CALL };
+        rules[TK("[")] =        { METHOD(exprList),      METHOD(exprSubscr),         PREC_SUBSCRIPT };
+        rules[TK("{")] =        { METHOD(exprMap),       NO_INFIX };
+        rules[TK("%")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_FACTOR };
+        rules[TK("+")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_TERM };
+        rules[TK("-")] =        { METHOD(exprUnaryOp),   METHOD(exprBinaryOp),       PREC_TERM };
+        rules[TK("*")] =        { METHOD(exprUnaryOp),   METHOD(exprBinaryOp),       PREC_FACTOR };
+        rules[TK("/")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_FACTOR };
+        rules[TK("//")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_FACTOR };
+        rules[TK("**")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_EXPONENT };
+        rules[TK(">")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
+        rules[TK("<")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
+        rules[TK("==")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_EQUALITY };
+        rules[TK("!=")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_EQUALITY };
+        rules[TK(">=")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
+        rules[TK("<=")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_COMPARISION };
+        rules[TK("in")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_TEST };
+        rules[TK("is")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_TEST };
+        rules[TK("<<")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_SHIFT };
+        rules[TK(">>")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_SHIFT };
+        rules[TK("&")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_AND };
+        rules[TK("|")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_OR };
+        rules[TK("^")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_XOR };
+        rules[TK("?")] =        { nullptr,               METHOD(exprTernary),        PREC_TERNARY };
+        rules[TK(",")] =        { nullptr,               METHOD(exprTuple),          PREC_TUPLE };
         rules[TK("not in")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_TEST };
         rules[TK("is not")] =   { nullptr,               METHOD(exprBinaryOp),       PREC_TEST };
         rules[TK("and") ] =     { nullptr,               METHOD(exprAnd),            PREC_LOGICAL_AND };
@@ -93,13 +110,6 @@ public:
         rules[TK("@num")] =     { METHOD(exprLiteral),   NO_INFIX };
         rules[TK("@str")] =     { METHOD(exprLiteral),   NO_INFIX };
         rules[TK("@fstr")] =    { METHOD(exprFString),   NO_INFIX };
-        rules[TK("?")] =        { nullptr,               METHOD(exprTernary),        PREC_TERNARY };
-        rules[TK(",")] =        { nullptr,               METHOD(exprTuple),          PREC_TUPLE };
-        rules[TK("<<")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_SHIFT };
-        rules[TK(">>")] =       { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_SHIFT };
-        rules[TK("&")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_AND };
-        rules[TK("|")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_OR };
-        rules[TK("^")] =        { nullptr,               METHOD(exprBinaryOp),       PREC_BITWISE_XOR };
 #undef METHOD
 #undef NO_INFIX
 
@@ -118,15 +128,6 @@ public:
     }
 
 private:
-    int i = 0;
-    std::vector<Token> tokens;
-
-    const Token& prev() { return tokens.at(i-1); }
-    const Token& curr() { return tokens.at(i); }
-    const Token& next() { return tokens.at(i+1); }
-    const Token& peek(int offset) { return tokens.at(i+offset); }
-    void advance() { i++; }
-
     bool match(TokenIndex expected) {
         if (curr().type != expected) return false;
         advance();
@@ -164,12 +165,14 @@ private:
         if (!match_end_stmt()) SyntaxError("expected statement end");
     }
 
-    void EXPR(ExprAction action=EXPR_PUSH_STACK) {
-        parse_expression(PREC_TUPLE + 1, action);
+    /*************************************************/
+
+    void EXPR(bool push_stack=true) {
+        parse_expression(PREC_TUPLE+1, push_stack);
     }
 
-    void EXPR_TUPLE(ExprAction action=EXPR_PUSH_STACK) {
-        parse_expression(PREC_TUPLE, action);
+    void EXPR_TUPLE(bool push_stack=true) {
+        parse_expression(PREC_TUPLE, push_stack);
     }
 
     template <typename T, typename... Args>
@@ -179,8 +182,6 @@ private:
         return expr;
     }
 
-    /********************************************/
-
     // PASS
     void exprLiteral(){
         ctx()->s_expr.push(make_expr<LiteralExpr>(prev().value));
@@ -201,63 +202,12 @@ private:
             consume(TK(":"));
         }
         e->func.code = push_context(lexer->src, "<lambda>");
-        // https://github.com/blueloveTH/pocketpy/issues/37
-        EXPR(true);
+        EXPR(true); // https://github.com/blueloveTH/pocketpy/issues/37
         ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
         pop_context();
         ctx()->s_expr.push(std::move(e));
     }
 
-    void exprAssign(){
-        // if(co()->codes.empty()) UNREACHABLE();
-        // bool is_load_name_ref = co()->codes.back().op == OP_LOAD_NAME_REF;
-        // int _name_arg = co()->codes.back().arg;
-        // // if the last op is OP_LOAD_NAME_REF, remove it
-        // // because we will emit OP_STORE_NAME or OP_STORE_CLASS_ATTR
-        // if(is_load_name_ref) co()->codes.pop_back();
-
-        // co()->_rvalue += 1;
-        // TokenIndex op = prev().type;
-        // if(op == TK("=")) {     // a = (expr)
-        //     EXPR_TUPLE();
-        //     if(is_load_name_ref){
-        //         auto op = ctx()->is_compiling_class ? OP_STORE_CLASS_ATTR : OP_STORE_NAME;
-        //         emit(op, _name_arg);
-        //     }else{
-        //         if(ctx()->is_compiling_class) SyntaxError();
-        //         emit(OP_STORE_REF);
-        //     }
-        // }else{                  // a += (expr) -> a = a + (expr)
-        //     if(ctx()->is_compiling_class) SyntaxError();
-        //     if(is_load_name_ref){
-        //         emit(OP_LOAD_NAME, _name_arg);
-        //     }else{
-        //         emit(OP_DUP_TOP_VALUE);
-        //     }
-        //     EXPR();
-        //     switch (op) {
-        //         case TK("+="):      emit(OP_BINARY_OP, 0);  break;
-        //         case TK("-="):      emit(OP_BINARY_OP, 1);  break;
-        //         case TK("*="):      emit(OP_BINARY_OP, 2);  break;
-        //         case TK("/="):      emit(OP_BINARY_OP, 3);  break;
-        //         case TK("//="):     emit(OP_BINARY_OP, 4);  break;
-        //         case TK("%="):      emit(OP_BINARY_OP, 5);  break;
-        //         case TK("<<="):     emit(OP_BITWISE_OP, 0);  break;
-        //         case TK(">>="):     emit(OP_BITWISE_OP, 1);  break;
-        //         case TK("&="):      emit(OP_BITWISE_OP, 2);  break;
-        //         case TK("|="):      emit(OP_BITWISE_OP, 3);  break;
-        //         case TK("^="):      emit(OP_BITWISE_OP, 4);  break;
-        //         default: UNREACHABLE();
-        //     }
-        //     if(is_load_name_ref){
-        //         emit(OP_STORE_NAME, _name_arg);
-        //     }else{
-        //         emit(OP_STORE_REF);
-        //     }
-        // }
-        // co()->_rvalue -= 1;
-    }
-
     // PASS
     void exprTuple(){
         auto e = make_expr<TupleExpr>();
@@ -540,17 +490,17 @@ private:
             return;
         }
         do {
-            ctx()->emit(OP_DUP_TOP_VALUE, BC_NOARG, BC_KEEPLINE);
+            ctx()->emit(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE);
             consume(TK("@id"));
             Str name = prev().str();
-            int index = ctx()->add_name(name, NAME_ATTR);
-            ctx()->emit(OP_BUILD_ATTR, index, prev().line);
+            int index = ctx()->add_name(name);
+            ctx()->emit(OP_LOAD_ATTR, index, prev().line);
             if (match(TK("as"))) {
                 consume(TK("@id"));
                 name = prev().str();
             }
-            index = ctx()->add_name(name, name_scope());
-            ctx()->emit(OP_STORE_NAME, index, prev().line);
+            index = ctx()->add_name(name);
+            ctx()->emit(OP_STORE_GLOBAL, index, prev().line);
         } while (match(TK(",")));
         ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
         consume_end_stmt();
@@ -650,7 +600,20 @@ private:
     }
 
     bool try_compile_assignment(){
-
+        //     switch (op) {
+        //         case TK("+="):      emit(OP_BINARY_OP, 0);  break;
+        //         case TK("-="):      emit(OP_BINARY_OP, 1);  break;
+        //         case TK("*="):      emit(OP_BINARY_OP, 2);  break;
+        //         case TK("/="):      emit(OP_BINARY_OP, 3);  break;
+        //         case TK("//="):     emit(OP_BINARY_OP, 4);  break;
+        //         case TK("%="):      emit(OP_BINARY_OP, 5);  break;
+        //         case TK("<<="):     emit(OP_BITWISE_OP, 0);  break;
+        //         case TK(">>="):     emit(OP_BITWISE_OP, 1);  break;
+        //         case TK("&="):      emit(OP_BITWISE_OP, 2);  break;
+        //         case TK("|="):      emit(OP_BITWISE_OP, 3);  break;
+        //         case TK("^="):      emit(OP_BITWISE_OP, 4);  break;
+        //         default: UNREACHABLE();
+        //     }
     }
 
     void compile_stmt() {
@@ -728,16 +691,18 @@ private:
                 consume_end_stmt();
             } break;
             case TK("with"): {
-                EXPR(true);
-                consume(TK("as"));
-                consume(TK("@id"));
-                int index = ctx()->add_name(prev().str(), name_scope());
-                emit(OP_STORE_NAME, index);
-                emit(OP_LOAD_NAME_REF, index);
-                emit(OP_WITH_ENTER);
-                compile_block_body();
-                emit(OP_LOAD_NAME_REF, index);
-                emit(OP_WITH_EXIT);
+                // TODO: reimpl this
+                UNREACHABLE();
+                // EXPR(true);
+                // consume(TK("as"));
+                // consume(TK("@id"));
+                // int index = ctx()->add_name(prev().str(), name_scope());
+                // emit(OP_STORE_NAME, index);
+                // emit(OP_LOAD_NAME_REF, index);
+                // emit(OP_WITH_ENTER);
+                // compile_block_body();
+                // emit(OP_LOAD_NAME_REF, index);
+                // emit(OP_WITH_EXIT);
             } break;
             /*************************************************/
             // TODO: refactor goto/label use special $ syntax
@@ -749,21 +714,21 @@ private:
                 consume_end_stmt();
                 break;
             case TK("goto"):
-                if(mode() != EXEC_MODE) SyntaxError("'goto' is only available in EXEC_MODE");
+                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));
                 consume_end_stmt();
                 break;
             /*************************************************/
-            // dangling expression or assignment
+            // handle dangling expression or assignment
             default: {
                 EXPR_TUPLE(true);
-                bool assigment = try_compile_assignment();
-                if(!assigment){
+                if(!try_compile_assignment()){
                     if(mode()==REPL_MODE && name_scope()==NAME_GLOBAL){
                         emit(OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE);
+                    }else{
+                        emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
                     }
-                    emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
                 }
                 consume_end_stmt();
             }

+ 43 - 1
src/expr.h

@@ -594,4 +594,46 @@ struct TernaryExpr: Expr{
 };
 
 
-} // namespace pkpy
+} // namespace pkpy
+
+
+// struct TupleRef : BaseRef {
+//     Tuple objs;
+//     TupleRef(Tuple&& objs) : objs(std::move(objs)) {}
+
+//     PyObject* get(VM* vm, Frame* frame) const{
+//         Tuple args(objs.size());
+//         for (int i = 0; i < objs.size(); i++) {
+//             args[i] = vm->PyRef_AS_C(objs[i])->get(vm, frame);
+//         }
+//         return VAR(std::move(args));
+//     }
+
+//     void set(VM* vm, Frame* frame, PyObject* val) const{
+//         val = vm->asIter(val);
+//         BaseIter* iter = vm->PyIter_AS_C(val);
+//         for(int i=0; i<objs.size(); i++){
+//             PyObject* x;
+//             if(is_type(objs[i], vm->tp_star_wrapper)){
+//                 auto& star = _CAST(StarWrapper&, objs[i]);
+//                 if(star.rvalue) vm->ValueError("can't use starred expression here");
+//                 if(i != objs.size()-1) vm->ValueError("* can only be used at the end");
+//                 auto ref = vm->PyRef_AS_C(star.obj);
+//                 List list;
+//                 while((x = iter->next()) != nullptr) list.push_back(x);
+//                 ref->set(vm, frame, VAR(std::move(list)));
+//                 return;
+//             }else{
+//                 x = iter->next();
+//                 if(x == nullptr) vm->ValueError("not enough values to unpack");
+//                 vm->PyRef_AS_C(objs[i])->set(vm, frame, x);
+//             }
+//         }
+//         PyObject* x = iter->next();
+//         if(x != nullptr) vm->ValueError("too many values to unpack");
+//     }
+
+//     void del(VM* vm, Frame* frame) const{
+//         for(int i=0; i<objs.size(); i++) vm->PyRef_AS_C(objs[i])->del(vm, frame);
+//     }
+// };

+ 1 - 8
src/opcodes.h

@@ -2,7 +2,7 @@
 
 OPCODE(NO_OP)
 OPCODE(POP_TOP)
-OPCODE(DUP_TOP_VALUE)
+OPCODE(DUP_TOP)
 OPCODE(CALL)
 OPCODE(CALL_UNPACK)
 OPCODE(CALL_KWARGS)
@@ -57,20 +57,13 @@ OPCODE(LOAD_FALSE)
 OPCODE(LOAD_EVAL_FN)
 OPCODE(LOAD_FUNCTION)
 OPCODE(LOAD_ELLIPSIS)
-OPCODE(LOAD_NAME)
-OPCODE(LOAD_NAME_REF)
 
 OPCODE(ASSERT)
 OPCODE(EXCEPTION_MATCH)
 OPCODE(RAISE)
 OPCODE(RE_RAISE)
 
-OPCODE(BUILD_INDEX)
-OPCODE(BUILD_ATTR)
-OPCODE(BUILD_ATTR_REF)
-OPCODE(STORE_NAME)
 OPCODE(STORE_FUNCTION)
-OPCODE(STORE_REF)
 
 OPCODE(TRY_BLOCK_ENTER)
 OPCODE(TRY_BLOCK_EXIT)

+ 0 - 179
src/ref.h

@@ -1,179 +0,0 @@
-#pragma once
-
-#include "obj.h"
-#include "vm.h"
-
-namespace pkpy {
-
-struct BaseRef {
-    virtual PyObject* get(VM*, Frame*) const = 0;
-    virtual void set(VM*, Frame*, PyObject*) const = 0;
-    virtual void del(VM*, Frame*) const = 0;
-    virtual ~BaseRef() = default;
-};
-
-struct NameRef : BaseRef {
-    const std::pair<StrName, NameScope> pair;
-    StrName name() const { return pair.first; }
-    NameScope scope() const { return pair.second; }
-    NameRef(const std::pair<StrName, NameScope>& pair) : pair(pair) {}
-
-    PyObject* get(VM* vm, Frame* frame) const{
-        PyObject* val;
-        val = frame->f_locals().try_get(name());
-        if(val != nullptr) return val;
-        val = frame->f_closure_try_get(name());
-        if(val != nullptr) return val;
-        val = frame->f_globals().try_get(name());
-        if(val != nullptr) return val;
-        val = vm->builtins->attr().try_get(name());
-        if(val != nullptr) return val;
-        vm->NameError(name());
-        return nullptr;
-    }
-
-    void set(VM* vm, Frame* frame, PyObject* val) const{
-        switch(scope()) {
-            case NAME_LOCAL: frame->f_locals().set(name(), val); break;
-            case NAME_GLOBAL:
-                if(frame->f_locals().try_set(name(), val)) return;
-                frame->f_globals().set(name(), val);
-                break;
-            default: UNREACHABLE();
-        }
-    }
-
-    void del(VM* vm, Frame* frame) const{
-        switch(scope()) {
-            case NAME_LOCAL: {
-                if(frame->f_locals().contains(name())){
-                    frame->f_locals().erase(name());
-                }else{
-                    vm->NameError(name());
-                }
-            } break;
-            case NAME_GLOBAL:
-            {
-                if(frame->f_locals().contains(name())){
-                    frame->f_locals().erase(name());
-                }else{
-                    if(frame->f_globals().contains(name())){
-                        frame->f_globals().erase(name());
-                    }else{
-                        vm->NameError(name());
-                    }
-                }
-            } break;
-            default: UNREACHABLE();
-        }
-    }
-};
-
-
-struct AttrRef : BaseRef {
-    mutable PyObject* obj;
-    NameRef attr;
-    AttrRef(PyObject* obj, NameRef attr) : obj(obj), attr(attr) {}
-
-    PyObject* get(VM* vm, Frame* frame) const{
-        return vm->getattr(obj, attr.name());
-    }
-
-    void set(VM* vm, Frame* frame, PyObject* val) const{
-        vm->setattr(obj, attr.name(), std::move(val));
-    }
-
-};
-
-struct IndexRef : BaseRef {
-    mutable PyObject* obj;
-    PyObject* index;
-    IndexRef(PyObject* obj, PyObject* index) : obj(obj), index(index) {}
-
-    PyObject* get(VM* vm, Frame* frame) const{
-        return vm->fast_call(__getitem__, Args{obj, index});
-    }
-
-    void set(VM* vm, Frame* frame, PyObject* val) const{
-        vm->fast_call(__setitem__, Args{obj, index, val});
-    }
-
-    void del(VM* vm, Frame* frame) const{
-        vm->fast_call(__delitem__, Args{obj, index});
-    }
-};
-
-struct TupleRef : BaseRef {
-    Tuple objs;
-    TupleRef(Tuple&& objs) : objs(std::move(objs)) {}
-
-    PyObject* get(VM* vm, Frame* frame) const{
-        Tuple args(objs.size());
-        for (int i = 0; i < objs.size(); i++) {
-            args[i] = vm->PyRef_AS_C(objs[i])->get(vm, frame);
-        }
-        return VAR(std::move(args));
-    }
-
-    void set(VM* vm, Frame* frame, PyObject* val) const{
-        val = vm->asIter(val);
-        BaseIter* iter = vm->PyIter_AS_C(val);
-        for(int i=0; i<objs.size(); i++){
-            PyObject* x;
-            if(is_type(objs[i], vm->tp_star_wrapper)){
-                auto& star = _CAST(StarWrapper&, objs[i]);
-                if(star.rvalue) vm->ValueError("can't use starred expression here");
-                if(i != objs.size()-1) vm->ValueError("* can only be used at the end");
-                auto ref = vm->PyRef_AS_C(star.obj);
-                List list;
-                while((x = iter->next()) != nullptr) list.push_back(x);
-                ref->set(vm, frame, VAR(std::move(list)));
-                return;
-            }else{
-                x = iter->next();
-                if(x == nullptr) vm->ValueError("not enough values to unpack");
-                vm->PyRef_AS_C(objs[i])->set(vm, frame, x);
-            }
-        }
-        PyObject* x = iter->next();
-        if(x != nullptr) vm->ValueError("too many values to unpack");
-    }
-
-    void del(VM* vm, Frame* frame) const{
-        for(int i=0; i<objs.size(); i++) vm->PyRef_AS_C(objs[i])->del(vm, frame);
-    }
-};
-
-
-template<typename P>
-PyObject* VM::PyRef(P&& value) {
-    static_assert(std::is_base_of_v<BaseRef, std::decay_t<P>>);
-    return heap.gcnew<P>(tp_ref, std::forward<P>(value));
-}
-
-inline const BaseRef* VM::PyRef_AS_C(PyObject* obj)
-{
-    if(!is_type(obj, tp_ref)) TypeError("expected an l-value");
-    return static_cast<const BaseRef*>(obj->value());
-}
-
-/***** Frame's Impl *****/
-inline void Frame::try_deref(VM* vm, PyObject*& v){
-    if(is_type(v, vm->tp_ref)) v = vm->PyRef_AS_C(v)->get(vm, this);
-}
-
-/***** GC's Impl *****/
-template<> inline void _mark<AttrRef>(AttrRef& t){
-    OBJ_MARK(t.obj);
-}
-
-template<> inline void _mark<IndexRef>(IndexRef& t){
-    OBJ_MARK(t.obj);
-    OBJ_MARK(t.index);
-}
-
-template<> inline void _mark<TupleRef>(TupleRef& t){
-    _mark<Tuple>(t.objs);
-}
-
-}   // namespace pkpy