Forráskód Böngészése

impl starred unpack

blueloveTH 3 éve
szülő
commit
256f9af147
6 módosított fájl, 49 hozzáadás és 33 törlés
  1. 1 1
      README.md
  2. 8 13
      src/ceval.h
  3. 2 2
      src/compiler.h
  4. 2 1
      src/opcodes.h
  5. 21 15
      src/vm.h
  6. 15 1
      tests/_star.py

+ 1 - 1
README.md

@@ -35,7 +35,7 @@ Please see https://pocketpy.dev for details or try [Live Demo](https://bluelovet
 | Dict            | `{'a': 1, 'b': 2}`              | YES       |
 | F-String        | `f'value is {x}'`               | YES       |
 | Unpacking       | `a, b = 1, 2`                   | YES       |
-| Star Unpacking  | `a, *b = [1, 2, 3]`             | NO        |
+| Star Unpacking  | `a, *b = [1, 2, 3]`             | YES       |
 | Exception       | `raise/try..catch`              | YES       |
 | Dynamic Code    | `eval()/exec()`                 | YES       |
 | Reflection      | `hasattr()/getattr()/setattr()` | YES       |

+ 8 - 13
src/ceval.h

@@ -44,8 +44,8 @@ PyVar VM::run_frame(Frame* frame){
         case OP_BUILD_INDEX: {
             PyVar index = frame->pop_value(this);
             auto ref = IndexRef(frame->pop_value(this), index);
-            if(byte.arg == 0) frame->push(PyRef(ref));
-            else frame->push(ref.get(this, frame));
+            if(byte.arg > 0) frame->push(ref.get(this, frame));
+            else frame->push(PyRef(ref));
         } continue;
         case OP_FAST_INDEX: case OP_FAST_INDEX_REF: {
             auto& a = frame->co->names[byte.arg & 0xFFFF];
@@ -65,18 +65,13 @@ PyVar VM::run_frame(Frame* frame){
             PyRef_AS_C(frame->top())->del(this, frame);
             frame->_pop();
             continue;
-        case OP_BUILD_SMART_TUPLE: {
+        case OP_BUILD_TUPLE: {
             pkpy::Args items = frame->pop_n_reversed(byte.arg);
-            bool done = false;
-            for(int i=0; i<items.size(); i++){
-                if(!is_type(items[i], tp_ref)) {
-                    done = true;
-                    for(int j=i; j<items.size(); j++) frame->try_deref(this, items[j]);
-                    frame->push(PyTuple(std::move(items)));
-                    break;
-                }
-            }
-            if(!done) frame->push(PyRef(TupleRef(std::move(items))));
+            frame->push(PyTuple(std::move(items)));
+        } continue;
+        case OP_BUILD_TUPLE_REF: {
+            pkpy::Args items = frame->pop_n_reversed(byte.arg);
+            frame->push(PyRef(TupleRef(std::move(items))));
         } continue;
         case OP_BUILD_STRING: {
             pkpy::Args items = frame->pop_n_values_reversed(this, byte.arg);

+ 2 - 2
src/compiler.h

@@ -436,7 +436,7 @@ private:
             EXPR();         // NOTE: "1," will fail, "1,2" will be ok
             size++;
         } while(match(TK(",")));
-        emit(OP_BUILD_SMART_TUPLE, size);
+        emit(co()->_rvalue ? OP_BUILD_TUPLE : OP_BUILD_TUPLE_REF, size);
     }
 
     void exprOr() {
@@ -813,7 +813,7 @@ __LISTCOMP:
             consume(TK("@id"));
             _exprName(true); size++;
         } while (match(TK(",")));
-        if(size > 1) emit(OP_BUILD_SMART_TUPLE, size);
+        if(size > 1) emit(OP_BUILD_TUPLE_REF, size);
     }
 
     void compile_for_loop() {

+ 2 - 1
src/opcodes.h

@@ -24,7 +24,8 @@ OPCODE(BUILD_MAP)
 OPCODE(BUILD_SET)
 OPCODE(BUILD_SLICE)
 OPCODE(BUILD_CLASS)
-OPCODE(BUILD_SMART_TUPLE)
+OPCODE(BUILD_TUPLE)
+OPCODE(BUILD_TUPLE_REF)
 OPCODE(BUILD_STRING)
 
 OPCODE(LIST_APPEND)

+ 21 - 15
src/vm.h

@@ -859,21 +859,27 @@ PyVar TupleRef::get(VM* vm, Frame* frame) const{
 }
 
 void TupleRef::set(VM* vm, Frame* frame, PyVar val) const{
-#define TUPLE_REF_SET() \
-    if(args.size() > objs.size()) vm->ValueError("too many values to unpack");       \
-    if(args.size() < objs.size()) vm->ValueError("not enough values to unpack");     \
-    for (int i = 0; i < objs.size(); i++) vm->PyRef_AS_C(objs[i])->set(vm, frame, args[i]);
-
-    if(is_type(val, vm->tp_tuple)){
-        const pkpy::Tuple& args = OBJ_GET(pkpy::Tuple, val);
-        TUPLE_REF_SET()
-    }else if(is_type(val, vm->tp_list)){
-        const pkpy::List& args = OBJ_GET(pkpy::List, val);
-        TUPLE_REF_SET()
-    }else{
-        vm->TypeError("only tuple or list can be unpacked");
-    }
-#undef TUPLE_REF_SET
+    val = vm->asIter(val);
+    pkpy::shared_ptr<BaseIter> iter = vm->PyIter_AS_C(val);
+    for(int i=0; i<objs.size(); i++){
+        PyVarOrNull x;
+        if(is_type(objs[i], vm->tp_star_wrapper)){
+            auto& star = vm->PyStarWrapper_AS_C(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);
+            pkpy::List list;
+            while((x = iter->next()) != nullptr) list.push_back(x);
+            ref->set(vm, frame, vm->PyList(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);
+        }
+    }
+    PyVarOrNull x = iter->next();
+    if(x != nullptr) vm->ValueError("too many values to unpack");
 }
 
 void TupleRef::del(VM* vm, Frame* frame) const{

+ 15 - 1
tests/_star.py

@@ -18,4 +18,18 @@ def f(a, b, *args, c=16):
 assert f(1, 2, 3, 4) == 26
 assert f(1, 2, 3, 4, c=32) == 42
 
-assert f(*a, c=-26) == 0
+assert f(*a, c=-26) == 0
+
+
+a, b = 1, 2
+a, b = b, a
+assert a == 2
+assert b == 1
+
+a, *b = 1, 2, 3, 4
+assert a == 1
+assert b == [2, 3, 4]
+
+a, *b = [1]
+assert a == 1
+assert b == []