mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
...
This commit is contained in:
parent
19c0e8cac6
commit
b87cd6ed52
@ -20,17 +20,16 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
|
|||||||
|
|
||||||
## Unimplemented features
|
## Unimplemented features
|
||||||
|
|
||||||
1. `**kwargs` in function definition.
|
1. `__getattr__` and `__setattr__`.
|
||||||
2. `__getattr__` and `__setattr__`.
|
2. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
|
||||||
3. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
|
3. `__slots__` in class definition.
|
||||||
4. `__slots__` in class definition.
|
4. One element tuple. `(1,)` is not supported.
|
||||||
5. One element tuple. `(1,)` is not supported.
|
5. Unpacking in `list` and `dict` literals, e.g. `[1, 2, *a]`.
|
||||||
6. Unpacking in `list` and `dict` literals, e.g. `[1, 2, *a]`.
|
6. Access the exception object in try..except.
|
||||||
7. Access the exception object in try..except.
|
7. `else` clause in try..except.
|
||||||
8. `else` clause in try..except.
|
8. Inplace methods like `__iadd__` and `__imul__`.
|
||||||
9. Inplace methods like `__iadd__` and `__imul__`.
|
9. `__del__` in class definition.
|
||||||
10. `__del__` in class definition.
|
10. Multiple inheritance.
|
||||||
11. Multiple inheritance.
|
|
||||||
|
|
||||||
## Different behaviors
|
## Different behaviors
|
||||||
|
|
||||||
|
78
src/ceval.h
78
src/ceval.h
@ -91,7 +91,7 @@ __NEXT_STEP:;
|
|||||||
TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
|
TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
|
||||||
TARGET(LOAD_FUNCTION) {
|
TARGET(LOAD_FUNCTION) {
|
||||||
FuncDecl_ decl = co->func_decls[byte.arg];
|
FuncDecl_ decl = co->func_decls[byte.arg];
|
||||||
bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator;
|
bool is_simple = decl->starred_kwarg==-1 && decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator;
|
||||||
int argc = decl->args.size();
|
int argc = decl->args.size();
|
||||||
PyObject* obj;
|
PyObject* obj;
|
||||||
if(decl->nested){
|
if(decl->nested){
|
||||||
@ -236,6 +236,11 @@ __NEXT_STEP:;
|
|||||||
}
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
|
TARGET(BUILD_TUPLE)
|
||||||
|
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
|
||||||
|
STACK_SHRINK(byte.arg);
|
||||||
|
PUSH(_0);
|
||||||
|
DISPATCH();
|
||||||
TARGET(BUILD_LIST)
|
TARGET(BUILD_LIST)
|
||||||
_0 = VAR(STACK_VIEW(byte.arg).to_list());
|
_0 = VAR(STACK_VIEW(byte.arg).to_list());
|
||||||
STACK_SHRINK(byte.arg);
|
STACK_SHRINK(byte.arg);
|
||||||
@ -263,11 +268,6 @@ __NEXT_STEP:;
|
|||||||
_0 = POPX(); // start
|
_0 = POPX(); // start
|
||||||
PUSH(VAR(Slice(_0, _1, _2)));
|
PUSH(VAR(Slice(_0, _1, _2)));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(BUILD_TUPLE)
|
|
||||||
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
|
|
||||||
STACK_SHRINK(byte.arg);
|
|
||||||
PUSH(_0);
|
|
||||||
DISPATCH();
|
|
||||||
TARGET(BUILD_STRING) {
|
TARGET(BUILD_STRING) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ArgsView view = STACK_VIEW(byte.arg);
|
ArgsView view = STACK_VIEW(byte.arg);
|
||||||
@ -276,6 +276,40 @@ __NEXT_STEP:;
|
|||||||
PUSH(VAR(ss.str()));
|
PUSH(VAR(ss.str()));
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
|
TARGET(BUILD_TUPLE_UNPACK) {
|
||||||
|
auto _lock = heap.gc_scope_lock();
|
||||||
|
List list;
|
||||||
|
_unpack_as_list(STACK_VIEW(byte.arg), list);
|
||||||
|
STACK_SHRINK(byte.arg);
|
||||||
|
_0 = VAR(Tuple(std::move(list)));
|
||||||
|
PUSH(_0);
|
||||||
|
} DISPATCH();
|
||||||
|
TARGET(BUILD_LIST_UNPACK) {
|
||||||
|
auto _lock = heap.gc_scope_lock();
|
||||||
|
List list;
|
||||||
|
_unpack_as_list(STACK_VIEW(byte.arg), list);
|
||||||
|
STACK_SHRINK(byte.arg);
|
||||||
|
_0 = VAR(std::move(list));
|
||||||
|
PUSH(_0);
|
||||||
|
} DISPATCH();
|
||||||
|
TARGET(BUILD_DICT_UNPACK) {
|
||||||
|
auto _lock = heap.gc_scope_lock();
|
||||||
|
Dict dict(this);
|
||||||
|
_unpack_as_dict(STACK_VIEW(byte.arg), dict);
|
||||||
|
STACK_SHRINK(byte.arg);
|
||||||
|
_0 = VAR(std::move(dict));
|
||||||
|
PUSH(_0);
|
||||||
|
} DISPATCH();
|
||||||
|
TARGET(BUILD_SET_UNPACK) {
|
||||||
|
auto _lock = heap.gc_scope_lock();
|
||||||
|
List list;
|
||||||
|
_unpack_as_list(STACK_VIEW(byte.arg), list);
|
||||||
|
STACK_SHRINK(byte.arg);
|
||||||
|
_0 = VAR(std::move(list));
|
||||||
|
_0 = call(builtins->attr(set), _0);
|
||||||
|
PUSH(_0);
|
||||||
|
} DISPATCH();
|
||||||
|
/*****************************************/
|
||||||
#define PREDICT_INT_OP(op) \
|
#define PREDICT_INT_OP(op) \
|
||||||
if(is_both_int(TOP(), SECOND())){ \
|
if(is_both_int(TOP(), SECOND())){ \
|
||||||
_1 = POPX(); \
|
_1 = POPX(); \
|
||||||
@ -426,9 +460,6 @@ __NEXT_STEP:;
|
|||||||
frame->jump_abs_break(index);
|
frame->jump_abs_break(index);
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(BEGIN_CALL)
|
|
||||||
PUSH(PY_BEGIN_CALL);
|
|
||||||
DISPATCH();
|
|
||||||
TARGET(CALL)
|
TARGET(CALL)
|
||||||
_0 = vectorcall(
|
_0 = vectorcall(
|
||||||
byte.arg & 0xFFFF, // ARGC
|
byte.arg & 0xFFFF, // ARGC
|
||||||
@ -438,6 +469,23 @@ __NEXT_STEP:;
|
|||||||
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
|
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
|
||||||
PUSH(_0);
|
PUSH(_0);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
TARGET(CALL_TP)
|
||||||
|
// [callable, <self>, args: tuple, kwargs: dict]
|
||||||
|
_2 = POPX();
|
||||||
|
_1 = POPX();
|
||||||
|
for(PyObject* obj: _CAST(Tuple&, _1)) PUSH(obj);
|
||||||
|
_CAST(Dict&, _2).apply([this](PyObject* k, PyObject* v){
|
||||||
|
PUSH(VAR(StrName(CAST(Str&, k)).index));
|
||||||
|
PUSH(v);
|
||||||
|
});
|
||||||
|
_0 = vectorcall(
|
||||||
|
_CAST(Tuple&, _1).size(), // ARGC
|
||||||
|
_CAST(Dict&, _2).size(), // KWARGC
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
|
||||||
|
PUSH(_0);
|
||||||
|
DISPATCH();
|
||||||
TARGET(RETURN_VALUE)
|
TARGET(RETURN_VALUE)
|
||||||
_0 = POPX();
|
_0 = POPX();
|
||||||
_pop_frame();
|
_pop_frame();
|
||||||
@ -471,6 +519,9 @@ __NEXT_STEP:;
|
|||||||
TARGET(UNARY_NOT)
|
TARGET(UNARY_NOT)
|
||||||
TOP() = VAR(!py_bool(TOP()));
|
TOP() = VAR(!py_bool(TOP()));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
TARGET(UNARY_STAR)
|
||||||
|
TOP() = VAR(StarWrapper(byte.arg, TOP()));
|
||||||
|
DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(GET_ITER)
|
TARGET(GET_ITER)
|
||||||
TOP() = py_iter(TOP());
|
TOP() = py_iter(TOP());
|
||||||
@ -528,15 +579,6 @@ __NEXT_STEP:;
|
|||||||
}
|
}
|
||||||
PUSH(VAR(extras));
|
PUSH(VAR(extras));
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
TARGET(UNPACK_UNLIMITED) {
|
|
||||||
auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!!
|
|
||||||
_0 = py_iter(POPX());
|
|
||||||
_1 = py_next(_0);
|
|
||||||
while(_1 != StopIteration){
|
|
||||||
PUSH(_1);
|
|
||||||
_1 = py_next(_0);
|
|
||||||
}
|
|
||||||
} DISPATCH();
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(BEGIN_CLASS)
|
TARGET(BEGIN_CLASS)
|
||||||
_name = StrName(byte.arg);
|
_name = StrName(byte.arg);
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#define PK_VERSION "1.0.1"
|
#define PK_VERSION "1.0.2"
|
||||||
|
|
||||||
// debug macros
|
// debug macros
|
||||||
#define DEBUG_NO_BUILTIN_MODULES 0
|
#define DEBUG_NO_BUILTIN_MODULES 0
|
||||||
@ -153,6 +153,8 @@ struct Type {
|
|||||||
#define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!");
|
#define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define PK_ASSERT(x) if(!(x)) FATAL_ERROR();
|
||||||
|
|
||||||
inline const float kInstAttrLoadFactor = 0.67f;
|
inline const float kInstAttrLoadFactor = 0.67f;
|
||||||
inline const float kTypeAttrLoadFactor = 0.5f;
|
inline const float kTypeAttrLoadFactor = 0.5f;
|
||||||
|
|
||||||
@ -173,7 +175,6 @@ inline bool is_both_int(PyObject* a, PyObject* b) noexcept {
|
|||||||
|
|
||||||
// special singals, is_tagged() for them is true
|
// special singals, is_tagged() for them is true
|
||||||
inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null
|
inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null
|
||||||
inline PyObject* const PY_BEGIN_CALL = (PyObject*)0b010011;
|
|
||||||
inline PyObject* const PY_OP_CALL = (PyObject*)0b100011;
|
inline PyObject* const PY_OP_CALL = (PyObject*)0b100011;
|
||||||
inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011;
|
inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011;
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class Compiler {
|
|||||||
rules[TK("*")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_FACTOR };
|
rules[TK("*")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_FACTOR };
|
||||||
rules[TK("/")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
|
rules[TK("/")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
|
||||||
rules[TK("//")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
|
rules[TK("//")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
|
||||||
rules[TK("**")] = { nullptr, METHOD(exprBinaryOp), PREC_EXPONENT };
|
rules[TK("**")] = { METHOD(exprUnaryOp), METHOD(exprBinaryOp), PREC_EXPONENT };
|
||||||
rules[TK(">")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
rules[TK(">")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||||
rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||||
rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||||
@ -280,7 +280,10 @@ class Compiler {
|
|||||||
ctx()->s_expr.push(make_expr<NegatedExpr>(ctx()->s_expr.popx()));
|
ctx()->s_expr.push(make_expr<NegatedExpr>(ctx()->s_expr.popx()));
|
||||||
break;
|
break;
|
||||||
case TK("*"):
|
case TK("*"):
|
||||||
ctx()->s_expr.push(make_expr<StarredExpr>(ctx()->s_expr.popx()));
|
ctx()->s_expr.push(make_expr<StarredExpr>(1, ctx()->s_expr.popx()));
|
||||||
|
break;
|
||||||
|
case TK("**"):
|
||||||
|
ctx()->s_expr.push(make_expr<StarredExpr>(2, ctx()->s_expr.popx()));
|
||||||
break;
|
break;
|
||||||
default: FATAL_ERROR();
|
default: FATAL_ERROR();
|
||||||
}
|
}
|
||||||
@ -387,10 +390,16 @@ class Compiler {
|
|||||||
EXPR();
|
EXPR();
|
||||||
e->kwargs.push_back({key, ctx()->s_expr.popx()});
|
e->kwargs.push_back({key, ctx()->s_expr.popx()});
|
||||||
} else{
|
} else{
|
||||||
if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument");
|
|
||||||
EXPR();
|
EXPR();
|
||||||
|
if(ctx()->s_expr.top()->star_level() == 2){
|
||||||
|
// **kwargs
|
||||||
|
e->kwargs.push_back({"**", ctx()->s_expr.popx()});
|
||||||
|
}else{
|
||||||
|
// positional argument
|
||||||
|
if(!e->kwargs.empty()) SyntaxError("positional argument follows keyword argument");
|
||||||
e->args.push_back(ctx()->s_expr.popx());
|
e->args.push_back(ctx()->s_expr.popx());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
match_newlines_repl();
|
match_newlines_repl();
|
||||||
} while (match(TK(",")));
|
} while (match(TK(",")));
|
||||||
consume(TK(")"));
|
consume(TK(")"));
|
||||||
@ -876,6 +885,7 @@ __SUBSCR_END:
|
|||||||
void _compile_f_args(FuncDecl_ decl, bool enable_type_hints){
|
void _compile_f_args(FuncDecl_ decl, bool enable_type_hints){
|
||||||
int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs
|
int state = 0; // 0 for args, 1 for *args, 2 for k=v, 3 for **kwargs
|
||||||
do {
|
do {
|
||||||
|
if(state > 3) SyntaxError();
|
||||||
if(state == 3) SyntaxError("**kwargs should be the last argument");
|
if(state == 3) SyntaxError("**kwargs should be the last argument");
|
||||||
match_newlines();
|
match_newlines();
|
||||||
if(match(TK("*"))){
|
if(match(TK("*"))){
|
||||||
@ -894,14 +904,17 @@ __SUBSCR_END:
|
|||||||
SyntaxError("duplicate argument name");
|
SyntaxError("duplicate argument name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){
|
|
||||||
SyntaxError("duplicate argument name");
|
|
||||||
}
|
|
||||||
for(auto& kv: decl->kwargs){
|
for(auto& kv: decl->kwargs){
|
||||||
if(decl->code->varnames[kv.key] == name){
|
if(decl->code->varnames[kv.key] == name){
|
||||||
SyntaxError("duplicate argument name");
|
SyntaxError("duplicate argument name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){
|
||||||
|
SyntaxError("duplicate argument name");
|
||||||
|
}
|
||||||
|
if(decl->starred_kwarg!=-1 && decl->code->varnames[decl->starred_kwarg] == name){
|
||||||
|
SyntaxError("duplicate argument name");
|
||||||
|
}
|
||||||
|
|
||||||
// eat type hints
|
// eat type hints
|
||||||
if(enable_type_hints && match(TK(":"))) consume_type_hints();
|
if(enable_type_hints && match(TK(":"))) consume_type_hints();
|
||||||
@ -924,7 +937,10 @@ __SUBSCR_END:
|
|||||||
}
|
}
|
||||||
decl->kwargs.push_back(FuncDecl::KwArg{index, value});
|
decl->kwargs.push_back(FuncDecl::KwArg{index, value});
|
||||||
} break;
|
} break;
|
||||||
case 3: SyntaxError("**kwargs is not supported yet"); break;
|
case 3:
|
||||||
|
decl->starred_kwarg = index;
|
||||||
|
state+=1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} while (match(TK(",")));
|
} while (match(TK(",")));
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,14 @@ struct Dict{
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename __Func>
|
||||||
|
void apply(__Func f) const {
|
||||||
|
for(int i=0; i<_capacity; i++){
|
||||||
|
if(_items[i].first == nullptr) continue;
|
||||||
|
f(_items[i].first, _items[i].second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clear(){
|
void clear(){
|
||||||
memset(_items, 0, _capacity * sizeof(Item));
|
memset(_items, 0, _capacity * sizeof(Item));
|
||||||
_size = 0;
|
_size = 0;
|
||||||
|
60
src/expr.h
60
src/expr.h
@ -19,11 +19,12 @@ struct Expr{
|
|||||||
virtual void emit(CodeEmitContext* ctx) = 0;
|
virtual void emit(CodeEmitContext* ctx) = 0;
|
||||||
virtual std::string str() const = 0;
|
virtual std::string str() const = 0;
|
||||||
|
|
||||||
virtual bool is_starred() const { return false; }
|
|
||||||
virtual bool is_literal() const { return false; }
|
virtual bool is_literal() const { return false; }
|
||||||
virtual bool is_json_object() const { return false; }
|
virtual bool is_json_object() const { return false; }
|
||||||
virtual bool is_attrib() const { return false; }
|
virtual bool is_attrib() const { return false; }
|
||||||
virtual bool is_compare() const { return false; }
|
virtual bool is_compare() const { return false; }
|
||||||
|
virtual int star_level() const { return 0; }
|
||||||
|
bool is_starred() const { return star_level() > 0; }
|
||||||
|
|
||||||
// for OP_DELETE_XXX
|
// for OP_DELETE_XXX
|
||||||
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
|
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
|
||||||
@ -183,24 +184,25 @@ struct NameExpr: Expr{
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct StarredExpr: Expr{
|
struct StarredExpr: Expr{
|
||||||
|
int level;
|
||||||
Expr_ child;
|
Expr_ child;
|
||||||
StarredExpr(Expr_&& child): child(std::move(child)) {}
|
StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
|
||||||
std::string str() const override { return "Starred()"; }
|
std::string str() const override { return fmt("Starred(level=", level, ")"); }
|
||||||
|
|
||||||
bool is_starred() const override { return true; }
|
int star_level() const override { return level; }
|
||||||
|
|
||||||
void emit(CodeEmitContext* ctx) override {
|
void emit(CodeEmitContext* ctx) override {
|
||||||
child->emit(ctx);
|
child->emit(ctx);
|
||||||
ctx->emit(OP_UNPACK_UNLIMITED, BC_NOARG, line);
|
ctx->emit(OP_UNARY_STAR, level, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emit_store(CodeEmitContext* ctx) override {
|
bool emit_store(CodeEmitContext* ctx) override {
|
||||||
|
if(level != 1) return false;
|
||||||
// simply proxy to child
|
// simply proxy to child
|
||||||
return child->emit_store(ctx);
|
return child->emit_store(ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct NotExpr: Expr{
|
struct NotExpr: Expr{
|
||||||
Expr_ child;
|
Expr_ child;
|
||||||
NotExpr(Expr_&& child): child(std::move(child)) {}
|
NotExpr(Expr_&& child): child(std::move(child)) {}
|
||||||
@ -265,16 +267,13 @@ struct LiteralExpr: Expr{
|
|||||||
if(std::holds_alternative<i64>(value)){
|
if(std::holds_alternative<i64>(value)){
|
||||||
return std::to_string(std::get<i64>(value));
|
return std::to_string(std::get<i64>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(std::holds_alternative<f64>(value)){
|
if(std::holds_alternative<f64>(value)){
|
||||||
return std::to_string(std::get<f64>(value));
|
return std::to_string(std::get<f64>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(std::holds_alternative<Str>(value)){
|
if(std::holds_alternative<Str>(value)){
|
||||||
Str s = std::get<Str>(value).escape();
|
Str s = std::get<Str>(value).escape();
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
FATAL_ERROR();
|
FATAL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,38 +643,55 @@ struct AttribExpr: Expr{
|
|||||||
struct CallExpr: Expr{
|
struct CallExpr: Expr{
|
||||||
Expr_ callable;
|
Expr_ callable;
|
||||||
std::vector<Expr_> args;
|
std::vector<Expr_> args;
|
||||||
|
// **a will be interpreted as a special keyword argument: {"**": a}
|
||||||
std::vector<std::pair<Str, Expr_>> kwargs;
|
std::vector<std::pair<Str, Expr_>> kwargs;
|
||||||
std::string str() const override { return "Call()"; }
|
std::string str() const override { return "Call()"; }
|
||||||
|
|
||||||
bool need_unpack() const {
|
|
||||||
for(auto& item: args) if(item->is_starred()) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit(CodeEmitContext* ctx) override {
|
void emit(CodeEmitContext* ctx) override {
|
||||||
VM* vm = ctx->vm;
|
bool vargs = false;
|
||||||
if(need_unpack()) ctx->emit(OP_BEGIN_CALL, BC_NOARG, line);
|
bool vkwargs = false;
|
||||||
|
for(auto& arg: args) if(arg->is_starred()) vargs = true;
|
||||||
|
for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true;
|
||||||
|
|
||||||
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
|
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
|
||||||
if(callable->is_attrib()){
|
if(callable->is_attrib()){
|
||||||
auto p = static_cast<AttribExpr*>(callable.get());
|
auto p = static_cast<AttribExpr*>(callable.get());
|
||||||
p->emit_method(ctx);
|
p->emit_method(ctx); // OP_LOAD_METHOD
|
||||||
}else{
|
}else{
|
||||||
callable->emit(ctx);
|
callable->emit(ctx);
|
||||||
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
|
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
// emit args
|
|
||||||
|
if(vargs || vkwargs){
|
||||||
for(auto& item: args) item->emit(ctx);
|
for(auto& item: args) item->emit(ctx);
|
||||||
// emit kwargs
|
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
|
||||||
|
|
||||||
for(auto& item: kwargs){
|
for(auto& item: kwargs){
|
||||||
int index = StrName::get(item.first.sv()).index;
|
item.second->emit(ctx);
|
||||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(index)), line);
|
if(item.second->is_starred()){
|
||||||
|
if(item.second->star_level() != 2) FATAL_ERROR();
|
||||||
|
}else{
|
||||||
|
// k=v
|
||||||
|
int index = ctx->add_const(py_var(ctx->vm, item.first));
|
||||||
|
ctx->emit(OP_LOAD_CONST, index, line);
|
||||||
|
ctx->emit(OP_BUILD_TUPLE, 2, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->emit(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line);
|
||||||
|
ctx->emit(OP_CALL_TP, BC_NOARG, line);
|
||||||
|
}else{
|
||||||
|
// vectorcall protocal
|
||||||
|
for(auto& item: args) item->emit(ctx);
|
||||||
|
for(auto& item: kwargs){
|
||||||
|
int index = StrName(item.first.sv()).index;
|
||||||
|
ctx->emit(OP_LOAD_INTEGER, index, line);
|
||||||
item.second->emit(ctx);
|
item.second->emit(ctx);
|
||||||
}
|
}
|
||||||
int KWARGC = (int)kwargs.size();
|
int KWARGC = (int)kwargs.size();
|
||||||
int ARGC = (int)args.size();
|
int ARGC = (int)args.size();
|
||||||
if(need_unpack()) ARGC = 0xFFFF;
|
|
||||||
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
|
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BinaryExpr: Expr{
|
struct BinaryExpr: Expr{
|
||||||
|
@ -238,6 +238,7 @@ struct MemoryPool{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: make them thread-safe
|
||||||
inline MemoryPool<64> pool64;
|
inline MemoryPool<64> pool64;
|
||||||
inline MemoryPool<128> pool128;
|
inline MemoryPool<128> pool128;
|
||||||
|
|
||||||
|
19
src/obj.h
19
src/obj.h
@ -60,8 +60,9 @@ struct FuncDecl {
|
|||||||
};
|
};
|
||||||
CodeObject_ code; // code object of this function
|
CodeObject_ code; // code object of this function
|
||||||
pod_vector<int> args; // indices in co->varnames
|
pod_vector<int> args; // indices in co->varnames
|
||||||
int starred_arg = -1; // index in co->varnames, -1 if no *arg
|
|
||||||
pod_vector<KwArg> kwargs; // indices in co->varnames
|
pod_vector<KwArg> kwargs; // indices in co->varnames
|
||||||
|
int starred_arg = -1; // index in co->varnames, -1 if no *arg
|
||||||
|
int starred_kwarg = -1; // index in co->varnames, -1 if no **kwarg
|
||||||
bool nested = false; // whether this function is nested
|
bool nested = false; // whether this function is nested
|
||||||
void _gc_mark() const;
|
void _gc_mark() const;
|
||||||
};
|
};
|
||||||
@ -101,6 +102,12 @@ struct Range {
|
|||||||
i64 step = 1;
|
i64 step = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StarWrapper{
|
||||||
|
int level; // either 1 or 2
|
||||||
|
PyObject* obj;
|
||||||
|
StarWrapper(int level, PyObject* obj) : level(level), obj(obj) {}
|
||||||
|
};
|
||||||
|
|
||||||
struct Bytes{
|
struct Bytes{
|
||||||
std::vector<char> _data;
|
std::vector<char> _data;
|
||||||
bool _ok;
|
bool _ok;
|
||||||
@ -335,6 +342,16 @@ struct Py_<BoundMethod> final: PyObject {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Py_<StarWrapper> final: PyObject {
|
||||||
|
StarWrapper _value;
|
||||||
|
void* value() override { return &_value; }
|
||||||
|
Py_(Type type, StarWrapper val): PyObject(type), _value(val) {}
|
||||||
|
void _obj_gc_mark() override {
|
||||||
|
OBJ_MARK(_value.obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct Py_<Property> final: PyObject {
|
struct Py_<Property> final: PyObject {
|
||||||
Property _value;
|
Property _value;
|
||||||
|
@ -38,13 +38,18 @@ OPCODE(DELETE_GLOBAL)
|
|||||||
OPCODE(DELETE_ATTR)
|
OPCODE(DELETE_ATTR)
|
||||||
OPCODE(DELETE_SUBSCR)
|
OPCODE(DELETE_SUBSCR)
|
||||||
/**************************/
|
/**************************/
|
||||||
|
OPCODE(BUILD_TUPLE)
|
||||||
OPCODE(BUILD_LIST)
|
OPCODE(BUILD_LIST)
|
||||||
OPCODE(BUILD_DICT)
|
OPCODE(BUILD_DICT)
|
||||||
OPCODE(BUILD_SET)
|
OPCODE(BUILD_SET)
|
||||||
OPCODE(BUILD_SLICE)
|
OPCODE(BUILD_SLICE)
|
||||||
OPCODE(BUILD_TUPLE)
|
|
||||||
OPCODE(BUILD_STRING)
|
OPCODE(BUILD_STRING)
|
||||||
/**************************/
|
/**************************/
|
||||||
|
OPCODE(BUILD_TUPLE_UNPACK)
|
||||||
|
OPCODE(BUILD_LIST_UNPACK)
|
||||||
|
OPCODE(BUILD_DICT_UNPACK)
|
||||||
|
OPCODE(BUILD_SET_UNPACK)
|
||||||
|
/**************************/
|
||||||
OPCODE(BINARY_TRUEDIV)
|
OPCODE(BINARY_TRUEDIV)
|
||||||
OPCODE(BINARY_POW)
|
OPCODE(BINARY_POW)
|
||||||
|
|
||||||
@ -81,8 +86,8 @@ OPCODE(LOOP_CONTINUE)
|
|||||||
OPCODE(LOOP_BREAK)
|
OPCODE(LOOP_BREAK)
|
||||||
OPCODE(GOTO)
|
OPCODE(GOTO)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(BEGIN_CALL)
|
|
||||||
OPCODE(CALL)
|
OPCODE(CALL)
|
||||||
|
OPCODE(CALL_TP)
|
||||||
OPCODE(RETURN_VALUE)
|
OPCODE(RETURN_VALUE)
|
||||||
OPCODE(YIELD_VALUE)
|
OPCODE(YIELD_VALUE)
|
||||||
/**************************/
|
/**************************/
|
||||||
@ -92,6 +97,7 @@ OPCODE(SET_ADD)
|
|||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(UNARY_NEGATIVE)
|
OPCODE(UNARY_NEGATIVE)
|
||||||
OPCODE(UNARY_NOT)
|
OPCODE(UNARY_NOT)
|
||||||
|
OPCODE(UNARY_STAR)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(GET_ITER)
|
OPCODE(GET_ITER)
|
||||||
OPCODE(FOR_ITER)
|
OPCODE(FOR_ITER)
|
||||||
@ -102,7 +108,6 @@ OPCODE(IMPORT_STAR)
|
|||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(UNPACK_SEQUENCE)
|
OPCODE(UNPACK_SEQUENCE)
|
||||||
OPCODE(UNPACK_EX)
|
OPCODE(UNPACK_EX)
|
||||||
OPCODE(UNPACK_UNLIMITED)
|
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(BEGIN_CLASS)
|
OPCODE(BEGIN_CLASS)
|
||||||
OPCODE(END_CLASS)
|
OPCODE(END_CLASS)
|
||||||
|
22
src/str.h
22
src/str.h
@ -124,19 +124,37 @@ struct Str{
|
|||||||
return memcmp(data, other.data, size) != 0;
|
return memcmp(data, other.data, size) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==(const std::string_view other) const {
|
||||||
|
if(size != (int)other.size()) return false;
|
||||||
|
return memcmp(data, other.data(), size) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const std::string_view other) const {
|
||||||
|
if(size != (int)other.size()) return true;
|
||||||
|
return memcmp(data, other.data(), size) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const char* p) const {
|
||||||
|
return *this == std::string_view(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const char* p) const {
|
||||||
|
return *this != std::string_view(p);
|
||||||
|
}
|
||||||
|
|
||||||
bool operator<(const Str& other) const {
|
bool operator<(const Str& other) const {
|
||||||
int ret = strncmp(data, other.data, std::min(size, other.size));
|
int ret = strncmp(data, other.data, std::min(size, other.size));
|
||||||
if(ret != 0) return ret < 0;
|
if(ret != 0) return ret < 0;
|
||||||
return size < other.size;
|
return size < other.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const std::string_view& other) const {
|
bool operator<(const std::string_view other) const {
|
||||||
int ret = strncmp(data, other.data(), std::min(size, (int)other.size()));
|
int ret = strncmp(data, other.data(), std::min(size, (int)other.size()));
|
||||||
if(ret != 0) return ret < 0;
|
if(ret != 0) return ret < 0;
|
||||||
return size < (int)other.size();
|
return size < (int)other.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator<(const std::string_view& other, const Str& str){
|
friend bool operator<(const std::string_view other, const Str& str){
|
||||||
return str > other;
|
return str > other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
src/vm.h
75
src/vm.h
@ -134,7 +134,7 @@ public:
|
|||||||
Type tp_function, tp_native_func, tp_bound_method;
|
Type tp_function, tp_native_func, tp_bound_method;
|
||||||
Type tp_slice, tp_range, tp_module;
|
Type tp_slice, tp_range, tp_module;
|
||||||
Type tp_super, tp_exception, tp_bytes, tp_mappingproxy;
|
Type tp_super, tp_exception, tp_bytes, tp_mappingproxy;
|
||||||
Type tp_dict, tp_property;
|
Type tp_dict, tp_property, tp_star_wrapper;
|
||||||
|
|
||||||
const bool enable_os;
|
const bool enable_os;
|
||||||
|
|
||||||
@ -637,6 +637,8 @@ public:
|
|||||||
#if DEBUG_CEVAL_STEP
|
#if DEBUG_CEVAL_STEP
|
||||||
void _log_s_data(const char* title = nullptr);
|
void _log_s_data(const char* title = nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
void _unpack_as_list(ArgsView args, List& list);
|
||||||
|
void _unpack_as_dict(ArgsView args, Dict& dict);
|
||||||
PyObject* vectorcall(int ARGC, int KWARGC=0, bool op_call=false);
|
PyObject* vectorcall(int ARGC, int KWARGC=0, bool op_call=false);
|
||||||
CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false);
|
CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false);
|
||||||
PyObject* py_negate(PyObject* obj);
|
PyObject* py_negate(PyObject* obj);
|
||||||
@ -686,6 +688,7 @@ DEF_NATIVE_2(Bytes, tp_bytes)
|
|||||||
DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
|
DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
|
||||||
DEF_NATIVE_2(Dict, tp_dict)
|
DEF_NATIVE_2(Dict, tp_dict)
|
||||||
DEF_NATIVE_2(Property, tp_property)
|
DEF_NATIVE_2(Property, tp_property)
|
||||||
|
DEF_NATIVE_2(StarWrapper, tp_star_wrapper)
|
||||||
|
|
||||||
#undef DEF_NATIVE_2
|
#undef DEF_NATIVE_2
|
||||||
|
|
||||||
@ -1063,7 +1066,6 @@ inline void VM::_log_s_data(const char* title) {
|
|||||||
if(sp_bases[p] > 0) ss << " ";
|
if(sp_bases[p] > 0) ss << " ";
|
||||||
PyObject* obj = *p;
|
PyObject* obj = *p;
|
||||||
if(obj == nullptr) ss << "(nil)";
|
if(obj == nullptr) ss << "(nil)";
|
||||||
else if(obj == PY_BEGIN_CALL) ss << "BEGIN_CALL";
|
|
||||||
else if(obj == PY_NULL) ss << "NULL";
|
else if(obj == PY_NULL) ss << "NULL";
|
||||||
else if(is_int(obj)) ss << CAST(i64, obj);
|
else if(is_int(obj)) ss << CAST(i64, obj);
|
||||||
else if(is_float(obj)) ss << CAST(f64, obj);
|
else if(is_float(obj)) ss << CAST(f64, obj);
|
||||||
@ -1121,6 +1123,7 @@ inline void VM::init_builtin_types(){
|
|||||||
tp_mappingproxy = _new_type_object("mappingproxy");
|
tp_mappingproxy = _new_type_object("mappingproxy");
|
||||||
tp_dict = _new_type_object("dict");
|
tp_dict = _new_type_object("dict");
|
||||||
tp_property = _new_type_object("property");
|
tp_property = _new_type_object("property");
|
||||||
|
tp_star_wrapper = _new_type_object("_star_wrapper");
|
||||||
|
|
||||||
this->None = heap._new<Dummy>(_new_type_object("NoneType"), {});
|
this->None = heap._new<Dummy>(_new_type_object("NoneType"), {});
|
||||||
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
|
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
|
||||||
@ -1154,21 +1157,47 @@ inline void VM::init_builtin_types(){
|
|||||||
this->_main = new_module("__main__");
|
this->_main = new_module("__main__");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
// `heap.gc_scope_lock();` needed before calling this function
|
||||||
bool is_varargs = ARGC == 0xFFFF;
|
inline void VM::_unpack_as_list(ArgsView args, List& list){
|
||||||
PyObject** p0;
|
for(PyObject* obj: args){
|
||||||
PyObject** p1 = s_data._sp - KWARGC*2;
|
if(is_non_tagged_type(obj, tp_star_wrapper)){
|
||||||
if(is_varargs){
|
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
||||||
p0 = p1 - 1;
|
// maybe this check should be done in the compile time
|
||||||
while(*p0 != PY_BEGIN_CALL) p0--;
|
if(w.level != 1) TypeError("expected level 1 star wrapper");
|
||||||
// [BEGIN_CALL, callable, <self>, args..., kwargs...]
|
PyObject* _0 = py_iter(w.obj);
|
||||||
// ^p0 ^p1 ^_sp
|
PyObject* _1 = py_next(_0);
|
||||||
ARGC = p1 - (p0 + 3);
|
while(_1 != StopIteration){
|
||||||
|
list.push_back(_1);
|
||||||
|
_1 = py_next(_0);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
p0 = p1 - ARGC - 2 - (int)is_varargs;
|
list.push_back(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `heap.gc_scope_lock();` needed before calling this function
|
||||||
|
inline void VM::_unpack_as_dict(ArgsView args, Dict& dict){
|
||||||
|
for(PyObject* obj: args){
|
||||||
|
if(is_non_tagged_type(obj, tp_star_wrapper)){
|
||||||
|
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
||||||
|
// maybe this check should be done in the compile time
|
||||||
|
if(w.level != 2) TypeError("expected level 2 star wrapper");
|
||||||
|
const Dict& other = CAST(Dict&, w.obj);
|
||||||
|
dict.update(other);
|
||||||
|
}else{
|
||||||
|
const Tuple& t = CAST(Tuple&, obj);
|
||||||
|
if(t.size() != 2) TypeError("expected tuple of length 2");
|
||||||
|
dict.set(t[0], t[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
||||||
|
PyObject** p1 = s_data._sp - KWARGC*2;
|
||||||
|
PyObject** p0 = p1 - ARGC - 2;
|
||||||
// [callable, <self>, args..., kwargs...]
|
// [callable, <self>, args..., kwargs...]
|
||||||
// ^p0 ^p1 ^_sp
|
// ^p0 ^p1 ^_sp
|
||||||
}
|
|
||||||
PyObject* callable = p1[-(ARGC + 2)];
|
PyObject* callable = p1[-(ARGC + 2)];
|
||||||
bool method_call = p1[-(ARGC + 1)] != PY_NULL;
|
bool method_call = p1[-(ARGC + 1)] != PY_NULL;
|
||||||
|
|
||||||
@ -1249,12 +1278,28 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
|||||||
if(i < args.size()) TypeError(fmt("too many arguments", " (", fn.decl->code->name, ')'));
|
if(i < args.size()) TypeError(fmt("too many arguments", " (", fn.decl->code->name, ')'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject* vkwargs;
|
||||||
|
if(fn.decl->starred_kwarg != -1){
|
||||||
|
vkwargs = VAR(Dict(this));
|
||||||
|
buffer[fn.decl->starred_kwarg] = vkwargs;
|
||||||
|
}else{
|
||||||
|
vkwargs = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
for(int i=0; i<kwargs.size(); i+=2){
|
for(int i=0; i<kwargs.size(); i+=2){
|
||||||
StrName key = CAST(int, kwargs[i]);
|
StrName key = CAST(int, kwargs[i]);
|
||||||
int index = co->varnames_inv.try_get(key);
|
int index = co->varnames_inv.try_get(key);
|
||||||
if(index<0) TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
|
if(index < 0){
|
||||||
|
if(vkwargs == nullptr){
|
||||||
|
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
|
||||||
|
}else{
|
||||||
|
Dict& dict = _CAST(Dict&, vkwargs);
|
||||||
|
dict.set(VAR(key.sv()), kwargs[i+1]);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
buffer[index] = kwargs[i+1];
|
buffer[index] = kwargs[i+1];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(co->is_generator){
|
if(co->is_generator){
|
||||||
s_data.reset(p0);
|
s_data.reset(p0);
|
||||||
|
@ -47,6 +47,9 @@ assert f(10, 1, 2, 3) == 18
|
|||||||
def f(a, b, *c, d=2, e=5):
|
def f(a, b, *c, d=2, e=5):
|
||||||
return a + b + d + e + sum(c)
|
return a + b + d + e + sum(c)
|
||||||
|
|
||||||
|
def g(*args, **kwargs):
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
assert f(1, 2, 3, 4) == 17
|
assert f(1, 2, 3, 4) == 17
|
||||||
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 62
|
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 62
|
||||||
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1, e=2) == 58
|
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1, e=2) == 58
|
||||||
@ -56,6 +59,13 @@ assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=1) == 58
|
|||||||
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) == 217
|
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) == 217
|
||||||
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, d=1, e=2) == 213
|
assert f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, d=1, e=2) == 213
|
||||||
|
|
||||||
|
assert g(1, 2, 3, 4) == 17
|
||||||
|
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 62
|
||||||
|
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1, e=2) == 58
|
||||||
|
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=1, d=2) == 58
|
||||||
|
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, d=1) == 61
|
||||||
|
assert g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=1) == 58
|
||||||
|
|
||||||
a = 1
|
a = 1
|
||||||
b = 2
|
b = 2
|
||||||
|
|
||||||
@ -67,3 +77,18 @@ def f():
|
|||||||
f()
|
f()
|
||||||
assert a == 3
|
assert a == 3
|
||||||
assert b == 4
|
assert b == 4
|
||||||
|
|
||||||
|
def g(a, b, *args, c=1, d=2, **kwargs):
|
||||||
|
S = a + b + c + d + sum(args)
|
||||||
|
return S, kwargs
|
||||||
|
|
||||||
|
S, kwargs = g(1, 2, 3, 4, 5, c=4, e=5, f=6)
|
||||||
|
# a = 1
|
||||||
|
# b = 2
|
||||||
|
# c = 4
|
||||||
|
# d = 2
|
||||||
|
# sum(args) = 3 + 4 + 5 = 12
|
||||||
|
# S = 1 + 2 + 4 + 2 + 12 = 21
|
||||||
|
|
||||||
|
assert S == 21
|
||||||
|
assert kwargs == {'e': 5, 'f': 6}
|
Loading…
x
Reference in New Issue
Block a user