impl unpack

This commit is contained in:
blueloveTH 2022-11-07 18:42:27 +08:00
parent c2fa538f44
commit b5aba376fe
6 changed files with 130 additions and 86 deletions

View File

@ -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;
}
};

View File

@ -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> 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.

View File

@ -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

View File

@ -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, // == !=

View File

@ -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;
};

View File

@ -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<items.size(); i++){
values[i] = frame->__deref_pointer(this, items[i]);
}
frame->push(PyTuple(values));
break;
}
}
if(done) break;
std::vector<_Pointer> pointers(items.size());
for(int i=0; i<items.size(); i++)
pointers[i] = PyPointer_AS_C(items[i]);
frame->push(PyPointer(std::make_shared<CompoundPointer>(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<PyVarList>(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<PyVarList>(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);