mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
impl unpack
This commit is contained in:
parent
c2fa538f44
commit
b5aba376fe
@ -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;
|
||||
}
|
||||
};
|
103
src/compiler.h
103
src/compiler.h
@ -31,14 +31,13 @@ struct Loop {
|
||||
do { \
|
||||
matchNewLines(); \
|
||||
if (peek() == TK(end)) break; \
|
||||
compileExpression(); \
|
||||
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) {
|
||||
@ -300,12 +304,12 @@ public:
|
||||
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;
|
||||
|
||||
if (prefix == nullptr) {
|
||||
throw SyntaxError(path, parser->previous, "expected an expression");
|
||||
}
|
||||
|
||||
// Compiles an expression. Support tuple syntax.
|
||||
void compileExpressionTuple() {
|
||||
int size = 0;
|
||||
while (true) {
|
||||
compileExpression();
|
||||
size++;
|
||||
if (!match(TK(","))) break;
|
||||
(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.
|
||||
|
@ -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
|
@ -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, // == !=
|
||||
|
@ -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;
|
||||
};
|
66
src/vm.h
66
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<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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user