Skip to content
68 changes: 61 additions & 7 deletions ASTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1565,13 +1565,67 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
ASTFunction::defarg_t defArgs, kwDefArgs;
const int defCount = operand & 0xFF;
const int kwDefCount = (operand >> 8) & 0xFF;
for (int i = 0; i < defCount; ++i) {
defArgs.push_front(stack.top());
stack.pop();
}
for (int i = 0; i < kwDefCount; ++i) {
kwDefArgs.push_front(stack.top());
stack.pop();
const int annotationCount = (operand >> 16) & 0x7FFF;

/* Docs for 3.3 say KW Pairs come first, but reality disagrees */
if (mod->verCompare(3, 3) <= 0) {
for (int i = 0; i < defCount; ++i) {
defArgs.push_front(stack.top());
stack.pop();
}
/* KW Defaults not mentioned in docs, but they come after the positional args */
for (int i = 0; i < kwDefCount; ++i) {
kwDefArgs.push_front(stack.top());
stack.pop(); // KW Pair object
stack.pop(); // KW Pair name
}
} else if (mod->verCompare(3, 5) <= 0) {
/* From Py 3.4 there can now be annotation params
The order has also switched so Kwargs come before Pos Args */
if(annotationCount) {
stack.pop(); // Tuple of param names for annotations
for (int i = 0; i < annotationCount; ++i) {
stack.pop(); // Pop annotation objects and ignore
}
}
for (int i = 0; i < kwDefCount; ++i) {
kwDefArgs.push_front(stack.top());
stack.pop(); // KW Pair object
stack.pop(); // KW Pair name
}
for (int i = 0; i < defCount; ++i) {
defArgs.push_front(stack.top());
stack.pop();
}
} else {
/* From Py 3.6 the operand stopped being an argument count
and changed to a flag that indicates what is represented by
preceding tuples on the stack. Docs for 3.7 are clearer,
docs for 3.6 may have not been correctly updated */
if(operand & 0x08) { // Cells for free vars to create a closure
stack.pop(); // Ignore these for syntax generation
}
if(operand & 0x04) { // Annotation dict (3.6-9) or string (3.10+)
stack.pop(); // Ignore annotations
}
if(operand & 0x02) { // Kwarg Defaults
PycRef<ASTNode> kw_tuple = stack.top();
stack.pop();
std::vector<PycRef<ASTNode>> kw_values = kw_tuple.cast<ASTConstMap>()->values();

for(const PycRef<ASTNode>& kw : kw_values) {
kwDefArgs.push_front(kw);
}
}
if(operand & 0x01) { // Positional Defaults (including positional-or-KW args)
PycRef<ASTNode> pos_tuple = stack.top();
stack.pop();
std::vector<PycRef<PycObject>> pos_values = pos_tuple.cast<ASTObject>()->object().cast<PycTuple>()->values();

for(const PycRef<PycObject>& pos : pos_values) {
defArgs.push_back(new ASTObject(pos));
}
}
}
stack.push(new ASTFunction(fun_code, defArgs, kwDefArgs));
}
Expand Down
Binary file modified tests/compiled/test_functions_py3.3.0.pyc
Binary file not shown.
Binary file modified tests/compiled/test_functions_py3.3.4.pyc
Binary file not shown.
Binary file not shown.
3 changes: 3 additions & 0 deletions tests/input/test_functions_py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ def x7c(foo = 1, *, bar, **kwargs):

def x7d(foo = 1, *, bar = 2, **kwargs):
pass

def x7e(foo = 1, *, bar = 2, baz = 3, **kwargs):
pass
40 changes: 36 additions & 4 deletions tests/tokenized/test_functions_py3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,50 @@ def x4c ( foo , bar = 1 , bla = 2 , * args , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x5a ( * , bar = 1 ) : <EOL>
def x5a ( * , bar ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x5b ( * , bar = 1 , ** kwargs ) : <EOL>
def x5b ( * , bar = 1 ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x6a ( foo , * , bar = 1 ) : <EOL>
def x5c ( * , bar = 1 , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x7a ( foo , * , bar = 1 , ** kwargs ) : <EOL>
def x6a ( foo , * , bar ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x6b ( foo , * , bar = 1 ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x6c ( foo = 1 , * , bar ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x6d ( foo = 1 , * , bar = 2 ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x7a ( foo , * , bar , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x7b ( foo , * , bar = 1 , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x7c ( foo = 1 , * , bar , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x7d ( foo = 1 , * , bar = 2 , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>
<OUTDENT>
def x7e ( foo = 1 , * , bar = 2 , baz = 3 , ** kwargs ) : <EOL>
<INDENT>
pass <EOL>