diff --git a/src/compiler.h b/src/compiler.h index 1d6e7ecf..065e23b0 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -74,6 +74,8 @@ public: rules[TK("@str")] = { METHOD(exprLiteral), NO_INFIX }; rules[TK("@fstr")] = { METHOD(exprFString), NO_INFIX }; rules[TK("?")] = { nullptr, METHOD(exprTernary), PREC_TERNARY }; + // do not include standalone, as it allows naked assignment + //rules[TK(":=")] = { nullptr, METHOD(exprWalrus), PREC_ASSIGNMENT }; rules[TK("=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; rules[TK("+=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; rules[TK("-=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; @@ -176,7 +178,7 @@ private: } }catch(std::exception& _){ SyntaxError("invalid number literal"); - } + } } void lex_token(){ @@ -201,12 +203,12 @@ private: case '{': parser->set_next_token(TK("{")); return; case '}': parser->set_next_token(TK("}")); return; case ',': parser->set_next_token(TK(",")); return; - case ':': parser->set_next_token(TK(":")); return; case ';': parser->set_next_token(TK(";")); return; case '(': parser->set_next_token(TK("(")); return; case ')': parser->set_next_token(TK(")")); return; case '[': parser->set_next_token(TK("[")); return; case ']': parser->set_next_token(TK("]")); return; + case ':': parser->set_next_token_2('=', TK(":"), TK(":=")); return; case '%': parser->set_next_token_2('=', TK("%"), TK("%=")); return; case '&': parser->set_next_token_2('=', TK("&"), TK("&=")); return; case '|': parser->set_next_token_2('=', TK("|"), TK("|=")); return; @@ -283,7 +285,7 @@ private: eat_number(); return; } - + switch (parser->eat_name()) { case 0: break; @@ -400,13 +402,21 @@ private: emit(OP_LOAD_LAMBDA, co()->add_const(vm->PyFunction(func))); } + void exprWalrus() { + // NAME is on stack + EXPR_TUPLE(); + // EXPR is on stack + emit(OP_STORE_REF); // will pop the value and the name off the stack, + // we need to return the value now + } void exprAssign() { co()->_rvalue = true; TokenIndex op = parser->prev.type; if(op == TK("=")) { // a = (expr) EXPR_TUPLE(); emit(OP_STORE_REF); - }else{ // a += (expr) -> a = a + (expr) + } + else{ // a += (expr) -> a = a + (expr) emit(OP_DUP_TOP); EXPR(); switch (op) { @@ -502,8 +512,48 @@ private: } void exprGrouping() { + // either a expr in parens, + // or a tuple. match_newlines(mode()==REPL_MODE); - EXPR_TUPLE(); + // do { + // if (peek() == TK("@id")) { + // consume(TK("@id")); + // exprAssign(); + // } + // else { + // EXPR(); + // } + // } while (match(TK(","))); + // EXPR_TUPLE(); + do { + if (peek() == TK(")")) break; + if(peek() == TK("@id") && peek_next() == TK(":=")) { + consume(TK("@id")); + Token tkname = parser->prev; + int index = co()->add_name( + tkname.str(), + codes.size()>1 ? NAME_LOCAL : NAME_GLOBAL + ); + emit(OP_LOAD_NAME_REF, index); + consume(TK(":=")); + exprWalrus(); + emit(OP_LOAD_NAME, index); + + // const Str& key = parser->prev.str(); + // emit(OP_LOAD_CONST, co()->add_const(vm->PyStr(key))); + // consume(TK("=")); + // co()->_rvalue=true; EXPR(); co()->_rvalue=false; + } + else { + EXPR_TUPLE(); + } + } while (match(TK(","))); + // if (peek() == TK("@id")) { + // consume(TK("@id")); + // exprAssign(); + // } else { + // EXPR_TUPLE(); + // } match_newlines(mode()==REPL_MODE); consume(TK(")")); } @@ -532,7 +582,7 @@ __LISTCOMP: emit(OP_BUILD_LIST, 0); EXPR_FOR_VARS();consume(TK("in"));EXPR_TUPLE(); match_newlines(mode()==REPL_MODE); - + int _skipPatch = emit(OP_JUMP_ABSOLUTE); int _cond_start = co()->codes.size(); int _cond_end_return = -1; diff --git a/src/vm.h b/src/vm.h index 7428f12c..a4b4f6ad 100644 --- a/src/vm.h +++ b/src/vm.h @@ -23,9 +23,9 @@ public: PyVar run_frame(Frame* frame){ while(frame->has_next_bytecode()){ const Bytecode& byte = frame->next_bytecode(); - // if(true || frame->_module != builtins){ - // printf("%d: %s (%d) %s\n", frame->_ip, OP_NAMES[byte.op], byte.arg, frame->stack_info().c_str()); - // } + if(true || frame->_module != builtins){ + printf("%d: %s (%d)\n", frame->_ip, OP_NAMES[byte.op], byte.arg); + } switch (byte.op) { case OP_NO_OP: break; // do nothing @@ -354,7 +354,7 @@ public: bool use_stdio; std::ostream* _stdout; std::ostream* _stderr; - + PyVar builtins; // builtins module PyVar _main; // __main__ module @@ -454,7 +454,7 @@ public: callable = &bm.method; // get unbound method args.extend_self(bm.obj); } - + if((*callable)->is_type(tp_native_function)){ const auto& f = OBJ_GET(pkpy::NativeFunc, *callable); if(kwargs.size() != 0) TypeError("native_function does not accept keyword arguments"); @@ -491,7 +491,7 @@ public: } if(i < args.size()) TypeError("too many arguments"); } - + for(int i=0; ikwArgs.contains(key)){ @@ -666,7 +666,7 @@ public: for(int i=0; iattr(__base__).get(); it = (*root)->attr().find(name); - if(it != (*root)->attr().end()) return it->second; + if(it != (*root)->attr().end()) return it->second; }else{ if(obj->is_attr_valid()){ it = obj->attr().find(name); @@ -712,7 +712,7 @@ public: template void bind_func(Str typeName, Str funcName, NativeFuncRaw fn) { - bind_func(_types[typeName], funcName, fn); + bind_func(_types[typeName], funcName, fn); } template @@ -869,7 +869,7 @@ public: DEF_NATIVE(Range, pkpy::Range, tp_range) DEF_NATIVE(Slice, pkpy::Slice, tp_slice) DEF_NATIVE(Exception, pkpy::Exception, tp_exception) - + // there is only one True/False, so no need to copy them! inline bool PyBool_AS_C(const PyVar& obj){return obj == True;} inline const PyVar& PyBool(bool value){return value ? True : False;} @@ -894,7 +894,7 @@ public: tp_range = _new_type_object("range"); tp_module = _new_type_object("module"); tp_ref = _new_type_object("_ref"); - + tp_function = _new_type_object("function"); tp_native_function = _new_type_object("native_function"); tp_native_iterator = _new_type_object("native_iterator"); @@ -913,7 +913,7 @@ public: setattr(_t(tp_type), __base__, _t(tp_object)); setattr(_t(tp_object), __base__, None); - + for (auto& [name, type] : _types) { setattr(type, __name__, PyStr(name)); } diff --git a/tests/_functions.py b/tests/_functions.py index 651e790d..170c2150 100644 --- a/tests/_functions.py +++ b/tests/_functions.py @@ -1,10 +1,21 @@ ## Function Tests. - - +#a := 1 +#_b = (1, 2) +#(d := 4) + 6 +#(D := 4) +#assert D == 4 +#x = D + 1 +x = (a := 3,4) +y = (a := 3) +print(y) +#assert False # (D := 4)==4 +# b = (d := 4) + (c := 4) +# c = (d := 2) + 1 +# b = (x := a + 1) + 2 def f1(): return 'f1' assert f1() == 'f1' -def f2(a, b, c, d): +def f2(a, b, c, d): return c assert f2('a', 'b', 'c', 'd') == 'c' def f3(a,b): @@ -15,7 +26,7 @@ def fact(n): if n == 1: return 1 return n * fact(n - 1) -assert fact(5)==120 +assert fact(5)==120 def f(a=1, b=-1): return a + b diff --git a/tests/_walrus.py b/tests/_walrus.py new file mode 100644 index 00000000..e42f02bd --- /dev/null +++ b/tests/_walrus.py @@ -0,0 +1,6 @@ +assert ( x := 1) == 1 +assert ( y := 1) + 2 == 3 + +a = (y := 4) + 5 +assert a == 9 +assert y == 4 \ No newline at end of file