From b5aba376fe39e33bd661aca9bea3cb578e66f01c Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 7 Nov 2022 18:42:27 +0800 Subject: [PATCH] impl unpack --- src/codeobject.h | 8 ++- src/compiler.h | 125 +++++++++++++++++++++++------------------------ src/opcodes.h | 4 +- src/parser.h | 3 +- src/pointer.h | 10 ++++ src/vm.h | 66 ++++++++++++++++++------- 6 files changed, 130 insertions(+), 86 deletions(-) diff --git a/src/codeobject.h b/src/codeobject.h index a36b5c32..02bf9629 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -139,9 +139,15 @@ public: this->ip = i; } - inline PyVarList popNReversed(VM* vm, int n){ + PyVarList popNValuesReversed(VM* vm, int n){ PyVarList v(n); for(int i=n-1; i>=0; i--) v[i] = popValue(vm); return v; } + + PyVarList __popNReversed(int n){ + PyVarList v(n); + for(int i=n-1; i>=0; i--) v[i] = __pop(); + return v; + } }; \ No newline at end of file diff --git a/src/compiler.h b/src/compiler.h index d2abd155..475f5665 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -26,19 +26,18 @@ struct Loop { Loop(bool forLoop, int start) : forLoop(forLoop), start(start) {} }; -#define ExprCommaSplitArgs(end) \ - int ARGC = 0; \ - do { \ - matchNewLines(); \ - if (peek() == TK(end)) break; \ - compileExpression(); \ - ARGC++; \ - matchNewLines(); \ - } while (match(TK(","))); \ - matchNewLines(); \ +#define ExprCommaSplitArgs(end) \ + int ARGC = 0; \ + do { \ + matchNewLines(); \ + if (peek() == TK(end)) break; \ + EXPR(); \ + ARGC++; \ + matchNewLines(); \ + } while (match(TK(","))); \ + matchNewLines(); \ consume(TK(end)); - class Compiler { public: std::unique_ptr parser; @@ -104,14 +103,19 @@ public: rules[TK("@id")] = { METHOD(exprName), NO_INFIX }; rules[TK("@num")] = { METHOD(exprLiteral), NO_INFIX }; rules[TK("@str")] = { METHOD(exprLiteral), NO_INFIX }; - rules[TK("=")] = { nullptr, METHOD(exprAssign), PREC_LOWEST }; - rules[TK("+=")] = { nullptr, METHOD(exprAssign), PREC_LOWEST }; - rules[TK("-=")] = { nullptr, METHOD(exprAssign), PREC_LOWEST }; - rules[TK("*=")] = { nullptr, METHOD(exprAssign), PREC_LOWEST }; - rules[TK("/=")] = { nullptr, METHOD(exprAssign), PREC_LOWEST }; - rules[TK("//=")] = { nullptr, METHOD(exprAssign), PREC_LOWEST }; + rules[TK("=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("+=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("-=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("*=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("/=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK("//=")] = { nullptr, METHOD(exprAssign), PREC_ASSIGNMENT }; + rules[TK(",")] = { nullptr, METHOD(exprComma), PREC_COMMA }; #undef METHOD #undef NO_INFIX + +#define EXPR() parsePrecedence(PREC_COMMA) // no '=' and ',' just a simple expression +#define EXPR_TUPLE() parsePrecedence(PREC_ASSIGNMENT) // no '=', but ',' is allowed +#define EXPR_ANY() parsePrecedence(PREC_NONE) } void eatString(bool single_quote) { @@ -297,15 +301,15 @@ public: } - void exprAssign(){ + void exprAssign() { _TokenType op = parser->previous.type; if(op == TK("=")) { // a = (expr) - compileExpressionTuple(); + EXPR_TUPLE(); emitCode(OP_STORE_PTR); }else{ // a += (expr) -> a = a + (expr) // TODO: optimization is needed for inplace operators emitCode(OP_DUP_TOP); - compileExpression(); + EXPR(); switch (op) { case TK("+="): emitCode(OP_BINARY_OP, 0); break; case TK("-="): emitCode(OP_BINARY_OP, 1); break; @@ -318,6 +322,15 @@ public: } } + void exprComma() { + int size = 1; // an expr is in the stack now + do { + EXPR(); // NOTE: "1," will fail, "1,2" will be ok + size++; + } while(match(TK(","))); + emitCode(OP_BUILD_SMART_TUPLE, size); + } + void exprOr() { int patch = emitCode(OP_JUMP_IF_TRUE_OR_POP); parsePrecedence(PREC_LOGICAL_OR); @@ -371,7 +384,7 @@ public: void exprGrouping() { matchNewLines(); - compileExpressionTuple(); + EXPR_TUPLE(); matchNewLines(); consume(TK(")")); } @@ -386,8 +399,8 @@ public: do { matchNewLines(); if (peek() == TK("}")) break; - compileExpression();consume(TK(":"));compileExpression(); - emitCode(OP_BUILD_TUPLE, 2); + EXPR();consume(TK(":"));EXPR(); + emitCode(OP_BUILD_SMART_TUPLE, 2); size++; matchNewLines(); } while (match(TK(","))); @@ -425,17 +438,17 @@ public: if(match(TK("]"))){ emitCode(OP_LOAD_NONE); }else{ - compileExpression(); + EXPR(); consume(TK("]")); } emitCode(OP_BUILD_SLICE); }else{ - compileExpression(); + EXPR(); if(match(TK(":"))){ if(match(TK("]"))){ emitCode(OP_LOAD_NONE); }else{ - compileExpression(); + EXPR(); consume(TK("]")); } emitCode(OP_BUILD_SLICE); @@ -457,23 +470,6 @@ public: } } - void parsePrecedence(Precedence precedence) { - lexToken(); - GrammarFn prefix = rules[parser->previous.type].prefix; - - if (prefix == nullptr) { - throw SyntaxError(path, parser->previous, "expected an expression"); - } - - (this->*prefix)(); - while (rules[peek()].precedence >= precedence) { - lexToken(); - _TokenType op = parser->previous.type; - GrammarFn infix = rules[op].infix; - (this->*infix)(); - } - } - void keepOpcodeLine(){ int i = getCode()->co_code.size() - 1; getCode()->co_code[i].line = getCode()->co_code[i-1].line; @@ -527,29 +523,30 @@ public: } int index = getCode()->addName(tkmodule.str(), NAME_GLOBAL); emitCode(OP_STORE_NAME_PTR, index); - } while (match(TK(",")) && (matchNewLines(), true)); + } while (match(TK(","))); consumeEndStatement(); } - // Compiles an expression. An expression will result a value on top of the stack. - void compileExpression() { - parsePrecedence(PREC_LOWEST); - } + void parsePrecedence(Precedence precedence) { + lexToken(); + GrammarFn prefix = rules[parser->previous.type].prefix; - // Compiles an expression. Support tuple syntax. - void compileExpressionTuple() { - int size = 0; - while (true) { - compileExpression(); - size++; - if (!match(TK(","))) break; + if (prefix == nullptr) { + throw SyntaxError(path, parser->previous, "expected an expression"); + } + + (this->*prefix)(); + while (rules[peek()].precedence > precedence) { + lexToken(); + _TokenType op = parser->previous.type; + GrammarFn infix = rules[op].infix; + (this->*infix)(); } - if(size > 1) emitCode(OP_BUILD_TUPLE, size); } void compileIfStatement() { matchNewLines(); - compileExpression(); //< Condition. + EXPR_TUPLE(); int ifpatch = emitCode(OP_POP_JUMP_IF_FALSE); compileBlockBody(); @@ -583,7 +580,7 @@ public: void compileWhileStatement() { Loop& loop = enterLoop(false); - compileExpression(); + EXPR_TUPLE(); int patch = emitCode(OP_POP_JUMP_IF_FALSE); compileBlockBody(); emitCode(OP_JUMP_ABSOLUTE, loop.start); keepOpcodeLine(); @@ -598,7 +595,7 @@ public: codes.size()>1 ? NAME_LOCAL : NAME_GLOBAL ); consume(TK("in")); - compileExpressionTuple(); + EXPR_TUPLE(); emitCode(OP_GET_ITER); Loop& loop = enterLoop(true); int patch = emitCode(OP_FOR_ITER); @@ -628,7 +625,7 @@ public: if(matchEndStatement()){ emitCode(OP_LOAD_NONE); }else{ - compileExpressionTuple(); + EXPR_TUPLE(); consumeEndStatement(); } emitCode(OP_RETURN_VALUE); @@ -639,23 +636,23 @@ public: } else if (match(TK("for"))) { compileForStatement(); } else if(match(TK("assert"))){ - compileExpression(); + EXPR(); emitCode(OP_ASSERT); consumeEndStatement(); } else if(match(TK("raise"))){ consume(TK("@id")); // dummy exception type emitCode(OP_LOAD_CONST, getCode()->addConst(vm->PyStr(parser->previous.str()))); - consume(TK("("));compileExpression();consume(TK(")")); + consume(TK("("));EXPR();consume(TK(")")); emitCode(OP_RAISE_ERROR); consumeEndStatement(); } else if(match(TK("del"))){ - compileExpression(); + EXPR(); emitCode(OP_DELETE_PTR); consumeEndStatement(); } else if(match(TK("pass"))){ consumeEndStatement(); } else { - compileExpressionTuple(); + EXPR_ANY(); consumeEndStatement(); // If last op is not an assignment, pop the result. diff --git a/src/opcodes.h b/src/opcodes.h index 4de76db2..e86b894c 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -18,10 +18,8 @@ OPCODE(UNARY_NOT) OPCODE(DUP_TOP) OPCODE(BUILD_LIST) -OPCODE(BUILD_TUPLE) OPCODE(BUILD_MAP) OPCODE(BUILD_SLICE) -OPCODE(UNPACK_SEQUENCE) OPCODE(GET_ITER) OPCODE(FOR_ITER) @@ -49,4 +47,6 @@ OPCODE(STORE_NAME_PTR) // arg for the name_ptr, [expr], directly store to t OPCODE(STORE_PTR) // no arg, [ptr, expr] -> *ptr = expr OPCODE(DELETE_PTR) // no arg, [ptr] -> [] -> delete ptr +OPCODE(BUILD_SMART_TUPLE) // if all elements are pointers, build a compound pointer, otherwise build a tuple + #endif \ No newline at end of file diff --git a/src/parser.h b/src/parser.h index 64980c2a..f0eb238d 100644 --- a/src/parser.h +++ b/src/parser.h @@ -65,7 +65,8 @@ struct Token{ enum Precedence { PREC_NONE, - PREC_LOWEST, + PREC_ASSIGNMENT, // = + PREC_COMMA, // , PREC_LOGICAL_OR, // or PREC_LOGICAL_AND, // and PREC_EQUALITY, // == != diff --git a/src/pointer.h b/src/pointer.h index 5ab65494..2d6b27da 100644 --- a/src/pointer.h +++ b/src/pointer.h @@ -49,3 +49,13 @@ struct IndexPointer : BasePointer { void set(VM* vm, Frame* frame, PyVar val) const; void del(VM* vm, Frame* frame) const; }; + +struct CompoundPointer : BasePointer { + const std::vector<_Pointer> pointers; + CompoundPointer(std::vector<_Pointer> pointers) : pointers(pointers) {} + CompoundPointer(std::vector<_Pointer>&& pointers) : pointers(pointers) {} + + PyVar get(VM* vm, Frame* frame) const; + void set(VM* vm, Frame* frame, PyVar val) const; + void del(VM* vm, Frame* frame) const; +}; \ No newline at end of file diff --git a/src/vm.h b/src/vm.h index 0f6c33b5..2048c9ef 100644 --- a/src/vm.h +++ b/src/vm.h @@ -163,6 +163,27 @@ public: _Pointer p = PyPointer_AS_C(frame->__pop()); p->del(this, frame.get()); } break; + case OP_BUILD_SMART_TUPLE: + { + PyVarList items = frame->__popNReversed(byte.arg); + bool done = false; + for(auto& item : items){ + if(!item->isType(_tp_pointer)) { + done = true; + PyVarList values(items.size()); + for(int i=0; i__deref_pointer(this, items[i]); + } + frame->push(PyTuple(values)); + break; + } + } + if(done) break; + std::vector<_Pointer> pointers(items.size()); + for(int i=0; ipush(PyPointer(std::make_shared(pointers))); + } break; case OP_STORE_FUNCTION: { PyVar obj = frame->popValue(this); @@ -190,16 +211,6 @@ public: callstack.pop(); return ret; } break; - case OP_UNPACK_SEQUENCE: - { - PyVar seq = frame->popValue(this); - bool iterable = (seq->isType(_tp_tuple) || seq->isType(_tp_list)); - if(!iterable) _error("TypeError", "only tuple and list can be unpacked"); - const PyVarList& objs = std::get(seq->_native); - if(objs.size() > byte.arg) _error("ValueError", "too many values to unpack (expected " + std::to_string(byte.arg) + ")"); - if(objs.size() < byte.arg) _error("ValueError", "not enough values to unpack (expected " + std::to_string(byte.arg) + ", got " + std::to_string(objs.size()) + ")"); - for(auto it=objs.rbegin(); it!=objs.rend(); it++) frame->push(*it); - } break; case OP_PRINT_EXPR: { const PyVar& expr = frame->topValue(this); @@ -254,24 +265,19 @@ public: } break; case OP_BUILD_LIST: { - PyVarList items = frame->popNReversed(this, byte.arg); + PyVarList items = frame->popNValuesReversed(this, byte.arg); frame->push(PyList(items)); } break; case OP_BUILD_MAP: { - PyVarList items = frame->popNReversed(this, byte.arg); + PyVarList items = frame->popNValuesReversed(this, byte.arg); PyVar obj = call(builtins->attribs["dict"], {PyList(items)}); frame->push(obj); } break; - case OP_BUILD_TUPLE: - { - PyVarList items = frame->popNReversed(this, byte.arg); - frame->push(PyTuple(items)); - } break; case OP_DUP_TOP: frame->push(frame->topValue(this)); break; case OP_CALL: { - PyVarList args = frame->popNReversed(this, byte.arg); + PyVarList args = frame->popNValuesReversed(this, byte.arg); PyVar callable = frame->popValue(this); frame->push(call(callable, args)); } break; @@ -640,6 +646,30 @@ void IndexPointer::del(VM* vm, Frame* frame) const{ vm->call(obj, __delitem__, {index}); } +PyVar CompoundPointer::get(VM* vm, Frame* frame) const{ + PyVarList args(pointers.size()); + for (int i = 0; i < pointers.size(); i++) { + args[i] = pointers[i]->get(vm, frame); + } + return vm->PyTuple(args); +} + +void CompoundPointer::set(VM* vm, Frame* frame, PyVar val) const{ + if(!val->isType(vm->_tp_tuple) && !val->isType(vm->_tp_list)){ + vm->_error("TypeError", "only tuple or list can be unpacked"); + } + const PyVarList& args = std::get(val->_native); + if(args.size() > pointers.size()) vm->_error("ValueError", "too many values to unpack"); + if(args.size() < pointers.size()) vm->_error("ValueError", "not enough values to unpack"); + for (int i = 0; i < pointers.size(); i++) { + pointers[i]->set(vm, frame, args[i]); + } +} + +void CompoundPointer::del(VM* vm, Frame* frame) const{ + for (auto& ptr : pointers) ptr->del(vm, frame); +} + /**************** Frame ****************/ inline PyVar Frame::__deref_pointer(VM* vm, PyVar v){ if(v->isType(vm->_tp_pointer)) v = vm->PyPointer_AS_C(v)->get(vm, this);