blueloveTH 3 lat temu
rodzic
commit
c0180efe84

+ 162 - 93
plugins/flutter/src/pocketpy.h

@@ -273,7 +273,7 @@ private:
     pkpy::shared_ptr<_StrMemory> _s;
     bool interned = false;
 public:
-    _Str(const _StrLiteral& s){
+    _Str(_StrLiteral s){
         construct(s);
         intern();
     }
@@ -296,7 +296,7 @@ public:
         this->_s = pkpy::make_shared<_StrMemory>(std::move(s));
     }
 
-    void construct(const std::string_view& sv){
+    void construct(std::string_view sv){
         auto it = _strIntern.find(sv);
         if(it != _strIntern.end()){
             this->_s = it->second;
@@ -2326,7 +2326,7 @@ public:
     }
 #endif
 
-    PyVarDict() : emhash8::HashMap<_Str, PyVar>(5) {}
+    using emhash8::HashMap<_Str, PyVar>::HashMap;
 };
 
 
@@ -2396,13 +2396,6 @@ namespace pkpy {
             other.clear();
         }
 
-        // deprecated, this is very slow, do not use it!!!
-        ArgList(std::initializer_list<PyVar> args){
-            __tryAlloc(args.size());
-            uint8_t i = 0;
-            for(auto& arg: args) this->_args[i++] = arg;
-        }
-
         PyVar& operator[](uint8_t i){
             __checkIndex(i);
             return _args[i];
@@ -2429,7 +2422,7 @@ namespace pkpy {
             return *this;
         }
 
-        uint8_t size() const {
+        inline uint8_t size() const {
             return _size;
         }
 
@@ -2454,6 +2447,37 @@ namespace pkpy {
             __tryRelease();
         }
     };
+
+    const ArgList& noArg(){
+        static ArgList ret(0);
+        return ret;
+    }
+
+    ArgList oneArg(PyVar&& a) {
+        ArgList ret(1);
+        ret[0] = std::move(a);
+        return ret;
+    }
+
+    ArgList oneArg(const PyVar& a) {
+        ArgList ret(1);
+        ret[0] = a;
+        return ret;
+    }
+
+    ArgList twoArgs(PyVar&& a, PyVar&& b) {
+        ArgList ret(2);
+        ret[0] = std::move(a);
+        ret[1] = std::move(b);
+        return ret;
+    }
+
+    ArgList twoArgs(const PyVar& a, const PyVar& b) {
+        ArgList ret(2);
+        ret[0] = a;
+        ret[1] = b;
+        return ret;
+    }
 }
 
 
@@ -2461,6 +2485,11 @@ const char* __BUILTINS_CODE = R"(
 def len(x):
     return x.__len__()
 
+def print(*args, sep=' ', end='\n'):
+    s = sep.join([str(i) for i in args])
+    __sys_stdout_write(s + end)
+
+
 str.__mul__ = lambda self, n: ''.join([self for _ in range(n)])
 
 def __str4split(self, sep):
@@ -2866,7 +2895,9 @@ struct SourceMetadata {
 
     _Str getLine(int lineno) const {
         if(lineno == -1) return "<?>";
-        const char* _start = lineStarts.at(lineno-1);
+        lineno -= 1;
+        if(lineno < 0) lineno = 0;
+        const char* _start = lineStarts.at(lineno);
         const char* i = _start;
         while(*i != '\n' && *i != '\0') i++;
         return _Str(_start, i-_start);
@@ -3726,11 +3757,15 @@ public:
 
     uint64_t id;
 
+    inline PyVarDict copy_f_locals(){
+        return f_locals;
+    }
+
     inline PyVarDict& f_globals(){
         return _module->attribs;
     }
 
-    Frame(const CodeObject* code, PyVar _module, const PyVarDict& locals)
+    Frame(const CodeObject* code, PyVar _module, PyVarDict&& locals)
         : code(code), _module(_module), f_locals(locals) {
         
         static uint64_t frame_id = 1;
@@ -3906,7 +3941,7 @@ protected:
             } break;
             case OP_BUILD_INDEX_PTR: {
                 PyVar index = frame->popValue(this);
-                PyVar obj = frame->popValue(this);
+                VarRef obj = frame->popValue(this);
                 frame->push(PyPointer(IndexPointer(obj, index)));
             } break;
             case OP_STORE_PTR: {
@@ -4011,8 +4046,7 @@ protected:
             case OP_CONTAINS_OP:
                 {
                     PyVar rhs = frame->popValue(this);
-                    PyVar lhs = frame->popValue(this);
-                    bool ret_c = PyBool_AS_C(call(rhs, __contains__, {std::move(lhs)}));
+                    bool ret_c = PyBool_AS_C(call(rhs, __contains__, pkpy::oneArg(frame->popValue(this))));
                     if(byte.arg == 1) ret_c = !ret_c;
                     frame->push(PyBool(ret_c));
                 } break;
@@ -4070,18 +4104,22 @@ protected:
             case OP_BUILD_MAP:
                 {
                     pkpy::ArgList items = frame->popNValuesReversed(this, byte.arg*2);
-                    PyVar obj = call(builtins->attribs["dict"], {});
+                    PyVar obj = call(builtins->attribs["dict"]);
                     for(int i=0; i<items.size(); i+=2){
-                        call(obj, __setitem__, {items[i], items[i+1]});
+                        call(obj, __setitem__, pkpy::twoArgs(items[i], items[i+1]));
                     }
                     frame->push(obj);
                 } break;
             case OP_DUP_TOP: frame->push(frame->topValue(this)); break;
             case OP_CALL:
                 {
-                    pkpy::ArgList args = frame->popNValuesReversed(this, byte.arg);
+                    int ARGC = byte.arg & 0xFFFF;
+                    int KWARGC = (byte.arg >> 16) & 0xFFFF;
+                    pkpy::ArgList kwargs(0);
+                    if(KWARGC > 0) kwargs = frame->popNValuesReversed(this, KWARGC*2);
+                    pkpy::ArgList args = frame->popNValuesReversed(this, ARGC);
                     PyVar callable = frame->popValue(this);
-                    PyVar ret = call(std::move(callable), std::move(args), true);
+                    PyVar ret = call(callable, std::move(args), kwargs, true);
                     if(ret == __py2py_call_signal) return ret;
                     frame->push(std::move(ret));
                 } break;
@@ -4101,7 +4139,7 @@ protected:
                     PyVar obj = frame->popValue(this);
                     PyVarOrNull iter_fn = getAttr(obj, __iter__, false);
                     if(iter_fn != nullptr){
-                        PyVar tmp = call(iter_fn, {obj});
+                        PyVar tmp = call(iter_fn, pkpy::oneArg(obj));
                         VarRef var = frame->__pop();
                         __checkType(var, _tp_pointer);
                         PyIter_AS_C(tmp)->var = var;
@@ -4162,18 +4200,8 @@ protected:
                         frame->push(it->second);
                     }
                 } break;
-            case OP_WITH_ENTER:
-            {
-                PyVar obj = frame->popValue(this);
-                PyVar enter_fn = getAttr(obj, "__enter__"_c);
-                call(enter_fn, {});
-            } break;
-            case OP_WITH_EXIT:
-            {
-                PyVar obj = frame->popValue(this);
-                PyVar exit_fn = getAttr(obj, "__exit__"_c);
-                call(exit_fn, {});
-            } break;
+            case OP_WITH_ENTER: call(frame->popValue(this), "__enter__"_c); break;
+            case OP_WITH_EXIT: call(frame->popValue(this), "__exit__"_c); break;
             default:
                 systemError(_Str("opcode ") + OP_NAMES[byte.op] + " is not implemented");
                 break;
@@ -4233,7 +4261,7 @@ public:
 
     PyVar asStr(const PyVar& obj){
         PyVarOrNull str_fn = getAttr(obj, __str__, false);
-        if(str_fn != nullptr) return call(str_fn, {});
+        if(str_fn != nullptr) return call(str_fn);
         return asRepr(obj);
     }
 
@@ -4253,11 +4281,11 @@ public:
 
     PyVar asRepr(const PyVar& obj){
         if(obj->isType(_tp_type)) return PyStr("<class '" + UNION_GET(_Str, obj->attribs[__name__]) + "'>");
-        return call(obj, __repr__, {});
+        return call(obj, __repr__);
     }
 
     PyVar asJson(const PyVar& obj){
-        return call(obj, __json__, {});
+        return call(obj, __json__);
     }
 
     const PyVar& asBool(const PyVar& obj){
@@ -4267,7 +4295,7 @@ public:
         if(obj->_type == _tp_float) return PyBool(PyFloat_AS_C(obj) != 0.0);
         PyVarOrNull len_fn = getAttr(obj, __len__, false);
         if(len_fn != nullptr){
-            PyVar ret = call(std::move(len_fn), {});
+            PyVar ret = call(len_fn);
             return PyBool(PyInt_AS_C(ret) > 0);
         }
         return True;
@@ -4278,25 +4306,39 @@ public:
         PyObject* cls = obj->_type.get();
         while(cls != None.get()) {
             auto it = cls->attribs.find(name);
-            if(it != cls->attribs.end()){
-                return call(it->second, args);
-            }
+            if(it != cls->attribs.end()) return call(it->second, std::move(args));
             cls = cls->attribs[__base__].get();
         }
         attributeError(obj, name);
         return nullptr;
     }
 
-    PyVar call(const PyVar& _callable, pkpy::ArgList args, bool opCall=false){
+    inline PyVar call(const PyVar& _callable){
+        return call(_callable, pkpy::noArg(), pkpy::noArg(), false);
+    }
+
+    inline PyVar call(const PyVar& _callable, pkpy::ArgList args){
+        return call(_callable, args, pkpy::noArg(), false);
+    }
+
+    inline PyVar call(const PyVar& obj, const _Str& func, pkpy::ArgList args){
+        return call(getAttr(obj, func), args, pkpy::noArg(), false);
+    }
+
+    inline PyVar call(const PyVar& obj, const _Str& func){
+        return call(getAttr(obj, func), pkpy::noArg(), pkpy::noArg(), false);
+    }
+
+    PyVar call(const PyVar& _callable, pkpy::ArgList args, const pkpy::ArgList& kwargs, bool opCall){
         if(_callable->isType(_tp_type)){
             auto it = _callable->attribs.find(__new__);
             PyVar obj;
             if(it != _callable->attribs.end()){
-                obj = call(it->second, args);
+                obj = call(it->second, args, kwargs, false);
             }else{
                 obj = newObject(_callable, (_Int)-1);
                 PyVarOrNull init_fn = getAttr(obj, __init__, false);
-                if (init_fn != nullptr) call(init_fn, args);
+                if (init_fn != nullptr) call(init_fn, args, kwargs, false);
             }
             return obj;
         }
@@ -4314,54 +4356,67 @@ public:
         
         if((*callable)->isType(_tp_native_function)){
             const auto& f = UNION_GET(_CppFunc, *callable);
+            // _CppFunc do not support kwargs
             return f(this, args);
         } else if((*callable)->isType(_tp_function)){
             const _Func& fn = PyFunction_AS_C((*callable));
             PyVarDict locals;
             int i = 0;
             for(const auto& name : fn->args){
-                if(i < args.size()) {
-                    locals[name] = args[i++];
-                }else{
-                    typeError("missing positional argument '" + name + "'");
+                if(i < args.size()){
+                    locals.emplace(name, args[i++]);
+                    continue;
                 }
+                typeError("missing positional argument '" + name + "'");
             }
-            // handle *args
+
+            locals.insert(fn->kwArgs.begin(), fn->kwArgs.end());
+
+            std::vector<_Str> positional_overrided_keys;
             if(!fn->starredArg.empty()){
+                // handle *args
                 PyVarList vargs;
                 while(i < args.size()) vargs.push_back(args[i++]);
-                locals[fn->starredArg] = PyTuple(vargs);
+                locals.emplace(fn->starredArg, PyTuple(std::move(vargs)));
+            }else{
+                for(const auto& key : fn->kwArgsOrder){
+                    if(i < args.size()){
+                        locals[key] = args[i++];
+                        positional_overrided_keys.push_back(key);
+                    }else{
+                        break;
+                    }
+                }
+                if(i < args.size()) typeError("too many arguments");
             }
-            // handle keyword arguments
-            for(const _Str& name : fn->kwArgsOrder){
-                if(i < args.size()) {
-                    locals[name] = args[i++];
-                }else{
-                    locals[name] = fn->kwArgs[name];
+            
+            for(int i=0; i<kwargs.size(); i+=2){
+                const _Str& key = PyStr_AS_C(kwargs[i]);
+                if(fn->kwArgs.find(key) == fn->kwArgs.end()){
+                    typeError(key.__escape(true) + " is an invalid keyword argument for " + fn->name + "()");
                 }
+                const PyVar& val = kwargs[i+1];
+                if(!positional_overrided_keys.empty()){
+                    auto it = std::find(positional_overrided_keys.begin(), positional_overrided_keys.end(), key);
+                    if(it != positional_overrided_keys.end()){
+                        typeError("multiple values for argument '" + key + "'");
+                    }
+                }
+                locals[key] = val;
             }
 
-            if(i < args.size()) typeError("too many arguments");
-
             auto it_m = (*callable)->attribs.find(__module__);
             PyVar _module = it_m != (*callable)->attribs.end() ? it_m->second : topFrame()->_module;
             if(opCall){
-                __pushNewFrame(fn->code, _module, locals);
+                __pushNewFrame(fn->code, _module, std::move(locals));
                 return __py2py_call_signal;
             }
-            return _exec(fn->code, _module, locals);
+            return _exec(fn->code, _module, std::move(locals));
         }
         typeError("'" + UNION_TP_NAME(*callable) + "' object is not callable");
         return None;
     }
 
-    inline PyVar call(const PyVar& obj, const _Str& func, const pkpy::ArgList& args){
-        return call(getAttr(obj, func), args);
-    }
-
-    inline PyVar call(const PyVar& obj, const _Str& func, pkpy::ArgList&& args){
-        return call(getAttr(obj, func), args);
-    }
 
     // repl mode is only for setting `frame->id` to 0
     virtual PyVarOrNull exec(const _Code& code, PyVar _module=nullptr){
@@ -4381,18 +4436,18 @@ public:
         exec(code);
     }
 
-    Frame* __pushNewFrame(const _Code& code, PyVar _module, const PyVarDict& locals){
+    Frame* __pushNewFrame(const _Code& code, PyVar _module, PyVarDict&& locals){
         if(code == nullptr) UNREACHABLE();
         if(callstack.size() > maxRecursionDepth){
             throw RuntimeError("RecursionError", "maximum recursion depth exceeded", _cleanErrorAndGetSnapshots());
         }
-        Frame* frame = new Frame(code.get(), _module, locals);
+        Frame* frame = new Frame(code.get(), _module, std::move(locals));
         callstack.emplace_back(pkpy::unique_ptr<Frame>(frame));
         return frame;
     }
 
-    PyVar _exec(const _Code& code, PyVar _module, const PyVarDict& locals){
-        Frame* frame = __pushNewFrame(code, _module, locals);
+    PyVar _exec(const _Code& code, PyVar _module, PyVarDict&& locals){
+        Frame* frame = __pushNewFrame(code, _module, std::move(locals));
         if(code->mode() == SINGLE_MODE) frame->id = 0;
         Frame* frameBase = frame;
         PyVar ret = nullptr;
@@ -4539,10 +4594,10 @@ public:
 
     bool isInstance(PyVar obj, PyVar type){
         __checkType(type, _tp_type);
-        PyVar t = obj->_type;
-        while (t != None){
-            if (t == type) return true;
-            t = t->attribs[__base__];
+        PyObject* t = obj->_type.get();
+        while (t != None.get()){
+            if (t == type.get()) return true;
+            t = t->attribs[__base__].get();
         }
         return false;
     }
@@ -4837,15 +4892,15 @@ void AttrPointer::del(VM* vm, Frame* frame) const{
 }
 
 PyVar IndexPointer::get(VM* vm, Frame* frame) const{
-    return vm->call(obj, __getitem__, {index});
+    return vm->call(obj, __getitem__, pkpy::oneArg(index));
 }
 
 void IndexPointer::set(VM* vm, Frame* frame, PyVar val) const{
-    vm->call(obj, __setitem__, {index, val});
+    vm->call(obj, __setitem__, pkpy::twoArgs(index, val));
 }
 
 void IndexPointer::del(VM* vm, Frame* frame) const{
-    vm->call(obj, __delitem__, {index});
+    vm->call(obj, __delitem__, pkpy::oneArg(index));
 }
 
 PyVar CompoundPointer::get(VM* vm, Frame* frame) const{
@@ -5271,10 +5326,16 @@ public:
         parser->setNextToken(TK("@eof"));
     }
 
-    _TokenType peek() {
+    inline _TokenType peek() {
         return parser->current.type;
     }
 
+    // not sure this will work
+    _TokenType peekNext() {
+        if(parser->nexts.empty()) return TK("@eof");
+        return parser->nexts.front().type;
+    }
+
     bool match(_TokenType expected) {
         if (peek() != expected) return false;
         lexToken();
@@ -5540,15 +5601,26 @@ __LISTCOMP:
 
     void exprCall() {
         int ARGC = 0;
+        int KWARGC = 0;
         do {
             matchNewLines(mode()==SINGLE_MODE);
             if (peek() == TK(")")) break;
-            EXPR();
-            ARGC++;
+            if(peek() == TK("@id") && peekNext() == TK("=")) {
+                consume(TK("@id"));
+                const _Str& key = parser->previous.str();
+                emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(key)));
+                consume(TK("="));
+                EXPR();
+                KWARGC++;
+            } else{
+                if(KWARGC > 0) syntaxError("positional argument follows keyword argument");
+                EXPR();
+                ARGC++;
+            }
             matchNewLines(mode()==SINGLE_MODE);
         } while (match(TK(",")));
         consume(TK(")"));
-        emitCode(OP_CALL, ARGC);
+        emitCode(OP_CALL, (KWARGC << 16) | ARGC);
     }
 
     void exprName() {
@@ -6132,12 +6204,9 @@ void __initializeBuiltinFunctions(VM* _vm) {
 #undef BIND_NUM_ARITH_OPT
 #undef BIND_NUM_LOGICAL_OPT
 
-    _vm->bindBuiltinFunc("print", [](VM* vm, const pkpy::ArgList& args) {
-        _StrStream ss;
-        for(int i=0; i<args.size(); i++){
-            ss << vm->PyStr_AS_C(vm->asStr(args[i])) << " ";
-        }
-        (*vm->_stdout) << ss.str() << '\n';
+    _vm->bindBuiltinFunc("__sys_stdout_write", [](VM* vm, const pkpy::ArgList& args) {
+        vm->__checkArgSize(args, 1);
+        (*vm->_stdout) << vm->PyStr_AS_C(args[0]);
         return vm->None;
     });
 
@@ -6152,7 +6221,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
         vm->__checkArgSize(args, 1);
         const _Str& expr = vm->PyStr_AS_C(args[0]);
         _Code code = compile(vm, expr.c_str(), "<eval>", EVAL_MODE, false);
-        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals);
+        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->copy_f_locals());
     });
 
     _vm->bindBuiltinFunc("isinstance", [](VM* vm, const pkpy::ArgList& args) {
@@ -6187,9 +6256,9 @@ void __initializeBuiltinFunctions(VM* _vm) {
     _vm->bindBuiltinFunc("globals", [](VM* vm, const pkpy::ArgList& args) {
         vm->__checkArgSize(args, 0);
         const auto& d = vm->topFrame()->f_globals();
-        PyVar obj = vm->call(vm->builtins->attribs["dict"], {});
+        PyVar obj = vm->call(vm->builtins->attribs["dict"]);
         for (const auto& [k, v] : d) {
-            vm->call(obj, __setitem__, {vm->PyStr(k), v});
+            vm->call(obj, __setitem__, pkpy::twoArgs(vm->PyStr(k), v));
         }
         return obj;
     });
@@ -6197,9 +6266,9 @@ void __initializeBuiltinFunctions(VM* _vm) {
     _vm->bindBuiltinFunc("locals", [](VM* vm, const pkpy::ArgList& args) {
         vm->__checkArgSize(args, 0);
         const auto& d = vm->topFrame()->f_locals;
-        PyVar obj = vm->call(vm->builtins->attribs["dict"], {});
+        PyVar obj = vm->call(vm->builtins->attribs["dict"]);
         for (const auto& [k, v] : d) {
-            vm->call(obj, __setitem__, {vm->PyStr(k), v});
+            vm->call(obj, __setitem__, pkpy::twoArgs(vm->PyStr(k), v));
         }
         return obj;
     });
@@ -6736,7 +6805,7 @@ void __addModuleJson(VM* vm){
         vm->__checkArgSize(args, 1);
         const _Str& expr = vm->PyStr_AS_C(args[0]);
         _Code code = compile(vm, expr.c_str(), "<json>", JSON_MODE, false);
-        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals);
+        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->copy_f_locals());
     });
 
     vm->bindFunc(mod, "dumps", [](VM* vm, const pkpy::ArgList& args) {

+ 1 - 1
plugins/godot/godot-cpp

@@ -1 +1 @@
-Subproject commit 711b948542a1fa76abb1d5786ab5dbd28e5c1b88
+Subproject commit 6a2bba868bc7d65869e8c754972b6d183516e94b

+ 162 - 93
plugins/unity/com.bl.pocketpy/Plugins/iOS/pocketpy.h

@@ -273,7 +273,7 @@ private:
     pkpy::shared_ptr<_StrMemory> _s;
     bool interned = false;
 public:
-    _Str(const _StrLiteral& s){
+    _Str(_StrLiteral s){
         construct(s);
         intern();
     }
@@ -296,7 +296,7 @@ public:
         this->_s = pkpy::make_shared<_StrMemory>(std::move(s));
     }
 
-    void construct(const std::string_view& sv){
+    void construct(std::string_view sv){
         auto it = _strIntern.find(sv);
         if(it != _strIntern.end()){
             this->_s = it->second;
@@ -2326,7 +2326,7 @@ public:
     }
 #endif
 
-    PyVarDict() : emhash8::HashMap<_Str, PyVar>(5) {}
+    using emhash8::HashMap<_Str, PyVar>::HashMap;
 };
 
 
@@ -2396,13 +2396,6 @@ namespace pkpy {
             other.clear();
         }
 
-        // deprecated, this is very slow, do not use it!!!
-        ArgList(std::initializer_list<PyVar> args){
-            __tryAlloc(args.size());
-            uint8_t i = 0;
-            for(auto& arg: args) this->_args[i++] = arg;
-        }
-
         PyVar& operator[](uint8_t i){
             __checkIndex(i);
             return _args[i];
@@ -2429,7 +2422,7 @@ namespace pkpy {
             return *this;
         }
 
-        uint8_t size() const {
+        inline uint8_t size() const {
             return _size;
         }
 
@@ -2454,6 +2447,37 @@ namespace pkpy {
             __tryRelease();
         }
     };
+
+    const ArgList& noArg(){
+        static ArgList ret(0);
+        return ret;
+    }
+
+    ArgList oneArg(PyVar&& a) {
+        ArgList ret(1);
+        ret[0] = std::move(a);
+        return ret;
+    }
+
+    ArgList oneArg(const PyVar& a) {
+        ArgList ret(1);
+        ret[0] = a;
+        return ret;
+    }
+
+    ArgList twoArgs(PyVar&& a, PyVar&& b) {
+        ArgList ret(2);
+        ret[0] = std::move(a);
+        ret[1] = std::move(b);
+        return ret;
+    }
+
+    ArgList twoArgs(const PyVar& a, const PyVar& b) {
+        ArgList ret(2);
+        ret[0] = a;
+        ret[1] = b;
+        return ret;
+    }
 }
 
 
@@ -2461,6 +2485,11 @@ const char* __BUILTINS_CODE = R"(
 def len(x):
     return x.__len__()
 
+def print(*args, sep=' ', end='\n'):
+    s = sep.join([str(i) for i in args])
+    __sys_stdout_write(s + end)
+
+
 str.__mul__ = lambda self, n: ''.join([self for _ in range(n)])
 
 def __str4split(self, sep):
@@ -2866,7 +2895,9 @@ struct SourceMetadata {
 
     _Str getLine(int lineno) const {
         if(lineno == -1) return "<?>";
-        const char* _start = lineStarts.at(lineno-1);
+        lineno -= 1;
+        if(lineno < 0) lineno = 0;
+        const char* _start = lineStarts.at(lineno);
         const char* i = _start;
         while(*i != '\n' && *i != '\0') i++;
         return _Str(_start, i-_start);
@@ -3726,11 +3757,15 @@ public:
 
     uint64_t id;
 
+    inline PyVarDict copy_f_locals(){
+        return f_locals;
+    }
+
     inline PyVarDict& f_globals(){
         return _module->attribs;
     }
 
-    Frame(const CodeObject* code, PyVar _module, const PyVarDict& locals)
+    Frame(const CodeObject* code, PyVar _module, PyVarDict&& locals)
         : code(code), _module(_module), f_locals(locals) {
         
         static uint64_t frame_id = 1;
@@ -3906,7 +3941,7 @@ protected:
             } break;
             case OP_BUILD_INDEX_PTR: {
                 PyVar index = frame->popValue(this);
-                PyVar obj = frame->popValue(this);
+                VarRef obj = frame->popValue(this);
                 frame->push(PyPointer(IndexPointer(obj, index)));
             } break;
             case OP_STORE_PTR: {
@@ -4011,8 +4046,7 @@ protected:
             case OP_CONTAINS_OP:
                 {
                     PyVar rhs = frame->popValue(this);
-                    PyVar lhs = frame->popValue(this);
-                    bool ret_c = PyBool_AS_C(call(rhs, __contains__, {std::move(lhs)}));
+                    bool ret_c = PyBool_AS_C(call(rhs, __contains__, pkpy::oneArg(frame->popValue(this))));
                     if(byte.arg == 1) ret_c = !ret_c;
                     frame->push(PyBool(ret_c));
                 } break;
@@ -4070,18 +4104,22 @@ protected:
             case OP_BUILD_MAP:
                 {
                     pkpy::ArgList items = frame->popNValuesReversed(this, byte.arg*2);
-                    PyVar obj = call(builtins->attribs["dict"], {});
+                    PyVar obj = call(builtins->attribs["dict"]);
                     for(int i=0; i<items.size(); i+=2){
-                        call(obj, __setitem__, {items[i], items[i+1]});
+                        call(obj, __setitem__, pkpy::twoArgs(items[i], items[i+1]));
                     }
                     frame->push(obj);
                 } break;
             case OP_DUP_TOP: frame->push(frame->topValue(this)); break;
             case OP_CALL:
                 {
-                    pkpy::ArgList args = frame->popNValuesReversed(this, byte.arg);
+                    int ARGC = byte.arg & 0xFFFF;
+                    int KWARGC = (byte.arg >> 16) & 0xFFFF;
+                    pkpy::ArgList kwargs(0);
+                    if(KWARGC > 0) kwargs = frame->popNValuesReversed(this, KWARGC*2);
+                    pkpy::ArgList args = frame->popNValuesReversed(this, ARGC);
                     PyVar callable = frame->popValue(this);
-                    PyVar ret = call(std::move(callable), std::move(args), true);
+                    PyVar ret = call(callable, std::move(args), kwargs, true);
                     if(ret == __py2py_call_signal) return ret;
                     frame->push(std::move(ret));
                 } break;
@@ -4101,7 +4139,7 @@ protected:
                     PyVar obj = frame->popValue(this);
                     PyVarOrNull iter_fn = getAttr(obj, __iter__, false);
                     if(iter_fn != nullptr){
-                        PyVar tmp = call(iter_fn, {obj});
+                        PyVar tmp = call(iter_fn, pkpy::oneArg(obj));
                         VarRef var = frame->__pop();
                         __checkType(var, _tp_pointer);
                         PyIter_AS_C(tmp)->var = var;
@@ -4162,18 +4200,8 @@ protected:
                         frame->push(it->second);
                     }
                 } break;
-            case OP_WITH_ENTER:
-            {
-                PyVar obj = frame->popValue(this);
-                PyVar enter_fn = getAttr(obj, "__enter__"_c);
-                call(enter_fn, {});
-            } break;
-            case OP_WITH_EXIT:
-            {
-                PyVar obj = frame->popValue(this);
-                PyVar exit_fn = getAttr(obj, "__exit__"_c);
-                call(exit_fn, {});
-            } break;
+            case OP_WITH_ENTER: call(frame->popValue(this), "__enter__"_c); break;
+            case OP_WITH_EXIT: call(frame->popValue(this), "__exit__"_c); break;
             default:
                 systemError(_Str("opcode ") + OP_NAMES[byte.op] + " is not implemented");
                 break;
@@ -4233,7 +4261,7 @@ public:
 
     PyVar asStr(const PyVar& obj){
         PyVarOrNull str_fn = getAttr(obj, __str__, false);
-        if(str_fn != nullptr) return call(str_fn, {});
+        if(str_fn != nullptr) return call(str_fn);
         return asRepr(obj);
     }
 
@@ -4253,11 +4281,11 @@ public:
 
     PyVar asRepr(const PyVar& obj){
         if(obj->isType(_tp_type)) return PyStr("<class '" + UNION_GET(_Str, obj->attribs[__name__]) + "'>");
-        return call(obj, __repr__, {});
+        return call(obj, __repr__);
     }
 
     PyVar asJson(const PyVar& obj){
-        return call(obj, __json__, {});
+        return call(obj, __json__);
     }
 
     const PyVar& asBool(const PyVar& obj){
@@ -4267,7 +4295,7 @@ public:
         if(obj->_type == _tp_float) return PyBool(PyFloat_AS_C(obj) != 0.0);
         PyVarOrNull len_fn = getAttr(obj, __len__, false);
         if(len_fn != nullptr){
-            PyVar ret = call(std::move(len_fn), {});
+            PyVar ret = call(len_fn);
             return PyBool(PyInt_AS_C(ret) > 0);
         }
         return True;
@@ -4278,25 +4306,39 @@ public:
         PyObject* cls = obj->_type.get();
         while(cls != None.get()) {
             auto it = cls->attribs.find(name);
-            if(it != cls->attribs.end()){
-                return call(it->second, args);
-            }
+            if(it != cls->attribs.end()) return call(it->second, std::move(args));
             cls = cls->attribs[__base__].get();
         }
         attributeError(obj, name);
         return nullptr;
     }
 
-    PyVar call(const PyVar& _callable, pkpy::ArgList args, bool opCall=false){
+    inline PyVar call(const PyVar& _callable){
+        return call(_callable, pkpy::noArg(), pkpy::noArg(), false);
+    }
+
+    inline PyVar call(const PyVar& _callable, pkpy::ArgList args){
+        return call(_callable, args, pkpy::noArg(), false);
+    }
+
+    inline PyVar call(const PyVar& obj, const _Str& func, pkpy::ArgList args){
+        return call(getAttr(obj, func), args, pkpy::noArg(), false);
+    }
+
+    inline PyVar call(const PyVar& obj, const _Str& func){
+        return call(getAttr(obj, func), pkpy::noArg(), pkpy::noArg(), false);
+    }
+
+    PyVar call(const PyVar& _callable, pkpy::ArgList args, const pkpy::ArgList& kwargs, bool opCall){
         if(_callable->isType(_tp_type)){
             auto it = _callable->attribs.find(__new__);
             PyVar obj;
             if(it != _callable->attribs.end()){
-                obj = call(it->second, args);
+                obj = call(it->second, args, kwargs, false);
             }else{
                 obj = newObject(_callable, (_Int)-1);
                 PyVarOrNull init_fn = getAttr(obj, __init__, false);
-                if (init_fn != nullptr) call(init_fn, args);
+                if (init_fn != nullptr) call(init_fn, args, kwargs, false);
             }
             return obj;
         }
@@ -4314,54 +4356,67 @@ public:
         
         if((*callable)->isType(_tp_native_function)){
             const auto& f = UNION_GET(_CppFunc, *callable);
+            // _CppFunc do not support kwargs
             return f(this, args);
         } else if((*callable)->isType(_tp_function)){
             const _Func& fn = PyFunction_AS_C((*callable));
             PyVarDict locals;
             int i = 0;
             for(const auto& name : fn->args){
-                if(i < args.size()) {
-                    locals[name] = args[i++];
-                }else{
-                    typeError("missing positional argument '" + name + "'");
+                if(i < args.size()){
+                    locals.emplace(name, args[i++]);
+                    continue;
                 }
+                typeError("missing positional argument '" + name + "'");
             }
-            // handle *args
+
+            locals.insert(fn->kwArgs.begin(), fn->kwArgs.end());
+
+            std::vector<_Str> positional_overrided_keys;
             if(!fn->starredArg.empty()){
+                // handle *args
                 PyVarList vargs;
                 while(i < args.size()) vargs.push_back(args[i++]);
-                locals[fn->starredArg] = PyTuple(vargs);
+                locals.emplace(fn->starredArg, PyTuple(std::move(vargs)));
+            }else{
+                for(const auto& key : fn->kwArgsOrder){
+                    if(i < args.size()){
+                        locals[key] = args[i++];
+                        positional_overrided_keys.push_back(key);
+                    }else{
+                        break;
+                    }
+                }
+                if(i < args.size()) typeError("too many arguments");
             }
-            // handle keyword arguments
-            for(const _Str& name : fn->kwArgsOrder){
-                if(i < args.size()) {
-                    locals[name] = args[i++];
-                }else{
-                    locals[name] = fn->kwArgs[name];
+            
+            for(int i=0; i<kwargs.size(); i+=2){
+                const _Str& key = PyStr_AS_C(kwargs[i]);
+                if(fn->kwArgs.find(key) == fn->kwArgs.end()){
+                    typeError(key.__escape(true) + " is an invalid keyword argument for " + fn->name + "()");
                 }
+                const PyVar& val = kwargs[i+1];
+                if(!positional_overrided_keys.empty()){
+                    auto it = std::find(positional_overrided_keys.begin(), positional_overrided_keys.end(), key);
+                    if(it != positional_overrided_keys.end()){
+                        typeError("multiple values for argument '" + key + "'");
+                    }
+                }
+                locals[key] = val;
             }
 
-            if(i < args.size()) typeError("too many arguments");
-
             auto it_m = (*callable)->attribs.find(__module__);
             PyVar _module = it_m != (*callable)->attribs.end() ? it_m->second : topFrame()->_module;
             if(opCall){
-                __pushNewFrame(fn->code, _module, locals);
+                __pushNewFrame(fn->code, _module, std::move(locals));
                 return __py2py_call_signal;
             }
-            return _exec(fn->code, _module, locals);
+            return _exec(fn->code, _module, std::move(locals));
         }
         typeError("'" + UNION_TP_NAME(*callable) + "' object is not callable");
         return None;
     }
 
-    inline PyVar call(const PyVar& obj, const _Str& func, const pkpy::ArgList& args){
-        return call(getAttr(obj, func), args);
-    }
-
-    inline PyVar call(const PyVar& obj, const _Str& func, pkpy::ArgList&& args){
-        return call(getAttr(obj, func), args);
-    }
 
     // repl mode is only for setting `frame->id` to 0
     virtual PyVarOrNull exec(const _Code& code, PyVar _module=nullptr){
@@ -4381,18 +4436,18 @@ public:
         exec(code);
     }
 
-    Frame* __pushNewFrame(const _Code& code, PyVar _module, const PyVarDict& locals){
+    Frame* __pushNewFrame(const _Code& code, PyVar _module, PyVarDict&& locals){
         if(code == nullptr) UNREACHABLE();
         if(callstack.size() > maxRecursionDepth){
             throw RuntimeError("RecursionError", "maximum recursion depth exceeded", _cleanErrorAndGetSnapshots());
         }
-        Frame* frame = new Frame(code.get(), _module, locals);
+        Frame* frame = new Frame(code.get(), _module, std::move(locals));
         callstack.emplace_back(pkpy::unique_ptr<Frame>(frame));
         return frame;
     }
 
-    PyVar _exec(const _Code& code, PyVar _module, const PyVarDict& locals){
-        Frame* frame = __pushNewFrame(code, _module, locals);
+    PyVar _exec(const _Code& code, PyVar _module, PyVarDict&& locals){
+        Frame* frame = __pushNewFrame(code, _module, std::move(locals));
         if(code->mode() == SINGLE_MODE) frame->id = 0;
         Frame* frameBase = frame;
         PyVar ret = nullptr;
@@ -4539,10 +4594,10 @@ public:
 
     bool isInstance(PyVar obj, PyVar type){
         __checkType(type, _tp_type);
-        PyVar t = obj->_type;
-        while (t != None){
-            if (t == type) return true;
-            t = t->attribs[__base__];
+        PyObject* t = obj->_type.get();
+        while (t != None.get()){
+            if (t == type.get()) return true;
+            t = t->attribs[__base__].get();
         }
         return false;
     }
@@ -4837,15 +4892,15 @@ void AttrPointer::del(VM* vm, Frame* frame) const{
 }
 
 PyVar IndexPointer::get(VM* vm, Frame* frame) const{
-    return vm->call(obj, __getitem__, {index});
+    return vm->call(obj, __getitem__, pkpy::oneArg(index));
 }
 
 void IndexPointer::set(VM* vm, Frame* frame, PyVar val) const{
-    vm->call(obj, __setitem__, {index, val});
+    vm->call(obj, __setitem__, pkpy::twoArgs(index, val));
 }
 
 void IndexPointer::del(VM* vm, Frame* frame) const{
-    vm->call(obj, __delitem__, {index});
+    vm->call(obj, __delitem__, pkpy::oneArg(index));
 }
 
 PyVar CompoundPointer::get(VM* vm, Frame* frame) const{
@@ -5271,10 +5326,16 @@ public:
         parser->setNextToken(TK("@eof"));
     }
 
-    _TokenType peek() {
+    inline _TokenType peek() {
         return parser->current.type;
     }
 
+    // not sure this will work
+    _TokenType peekNext() {
+        if(parser->nexts.empty()) return TK("@eof");
+        return parser->nexts.front().type;
+    }
+
     bool match(_TokenType expected) {
         if (peek() != expected) return false;
         lexToken();
@@ -5540,15 +5601,26 @@ __LISTCOMP:
 
     void exprCall() {
         int ARGC = 0;
+        int KWARGC = 0;
         do {
             matchNewLines(mode()==SINGLE_MODE);
             if (peek() == TK(")")) break;
-            EXPR();
-            ARGC++;
+            if(peek() == TK("@id") && peekNext() == TK("=")) {
+                consume(TK("@id"));
+                const _Str& key = parser->previous.str();
+                emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(key)));
+                consume(TK("="));
+                EXPR();
+                KWARGC++;
+            } else{
+                if(KWARGC > 0) syntaxError("positional argument follows keyword argument");
+                EXPR();
+                ARGC++;
+            }
             matchNewLines(mode()==SINGLE_MODE);
         } while (match(TK(",")));
         consume(TK(")"));
-        emitCode(OP_CALL, ARGC);
+        emitCode(OP_CALL, (KWARGC << 16) | ARGC);
     }
 
     void exprName() {
@@ -6132,12 +6204,9 @@ void __initializeBuiltinFunctions(VM* _vm) {
 #undef BIND_NUM_ARITH_OPT
 #undef BIND_NUM_LOGICAL_OPT
 
-    _vm->bindBuiltinFunc("print", [](VM* vm, const pkpy::ArgList& args) {
-        _StrStream ss;
-        for(int i=0; i<args.size(); i++){
-            ss << vm->PyStr_AS_C(vm->asStr(args[i])) << " ";
-        }
-        (*vm->_stdout) << ss.str() << '\n';
+    _vm->bindBuiltinFunc("__sys_stdout_write", [](VM* vm, const pkpy::ArgList& args) {
+        vm->__checkArgSize(args, 1);
+        (*vm->_stdout) << vm->PyStr_AS_C(args[0]);
         return vm->None;
     });
 
@@ -6152,7 +6221,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
         vm->__checkArgSize(args, 1);
         const _Str& expr = vm->PyStr_AS_C(args[0]);
         _Code code = compile(vm, expr.c_str(), "<eval>", EVAL_MODE, false);
-        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals);
+        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->copy_f_locals());
     });
 
     _vm->bindBuiltinFunc("isinstance", [](VM* vm, const pkpy::ArgList& args) {
@@ -6187,9 +6256,9 @@ void __initializeBuiltinFunctions(VM* _vm) {
     _vm->bindBuiltinFunc("globals", [](VM* vm, const pkpy::ArgList& args) {
         vm->__checkArgSize(args, 0);
         const auto& d = vm->topFrame()->f_globals();
-        PyVar obj = vm->call(vm->builtins->attribs["dict"], {});
+        PyVar obj = vm->call(vm->builtins->attribs["dict"]);
         for (const auto& [k, v] : d) {
-            vm->call(obj, __setitem__, {vm->PyStr(k), v});
+            vm->call(obj, __setitem__, pkpy::twoArgs(vm->PyStr(k), v));
         }
         return obj;
     });
@@ -6197,9 +6266,9 @@ void __initializeBuiltinFunctions(VM* _vm) {
     _vm->bindBuiltinFunc("locals", [](VM* vm, const pkpy::ArgList& args) {
         vm->__checkArgSize(args, 0);
         const auto& d = vm->topFrame()->f_locals;
-        PyVar obj = vm->call(vm->builtins->attribs["dict"], {});
+        PyVar obj = vm->call(vm->builtins->attribs["dict"]);
         for (const auto& [k, v] : d) {
-            vm->call(obj, __setitem__, {vm->PyStr(k), v});
+            vm->call(obj, __setitem__, pkpy::twoArgs(vm->PyStr(k), v));
         }
         return obj;
     });
@@ -6736,7 +6805,7 @@ void __addModuleJson(VM* vm){
         vm->__checkArgSize(args, 1);
         const _Str& expr = vm->PyStr_AS_C(args[0]);
         _Code code = compile(vm, expr.c_str(), "<json>", JSON_MODE, false);
-        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->f_locals);
+        return vm->_exec(code, vm->topFrame()->_module, vm->topFrame()->copy_f_locals());
     });
 
     vm->bindFunc(mod, "dumps", [](VM* vm, const pkpy::ArgList& args) {