Lambda should be able to accept *args and **kwargs
This commit is contained in:
parent
65f7ae3f22
commit
bbfe948b86
@ -1309,38 +1309,40 @@ static void argumentDefinition(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void function(FunctionType type, size_t blockWidth) {
|
||||
Compiler compiler;
|
||||
initCompiler(&compiler, type);
|
||||
compiler.codeobject->chunk.filename = compiler.enclosing->codeobject->chunk.filename;
|
||||
|
||||
beginScope();
|
||||
|
||||
if (isMethod(type)) current->codeobject->requiredArgs = 1;
|
||||
if (isCoroutine(type)) current->codeobject->flags |= KRK_CODEOBJECT_FLAGS_IS_COROUTINE;
|
||||
static void functionPrologue(Compiler * compiler) {
|
||||
KrkCodeObject * func = endCompiler();
|
||||
if (compiler->annotationCount) {
|
||||
EMIT_OPERAND_OP(OP_MAKE_DICT, compiler->annotationCount * 2);
|
||||
}
|
||||
size_t ind = krk_addConstant(currentChunk(), OBJECT_VAL(func));
|
||||
EMIT_OPERAND_OP(OP_CLOSURE, ind);
|
||||
doUpvalues(compiler, func);
|
||||
if (compiler->annotationCount) {
|
||||
emitByte(OP_ANNOTATE);
|
||||
}
|
||||
freeCompiler(compiler);
|
||||
}
|
||||
|
||||
static int argumentList(FunctionType type) {
|
||||
int hasCollectors = 0;
|
||||
KrkToken self = syntheticToken("self");
|
||||
|
||||
consume(TOKEN_LEFT_PAREN, "Expected start of parameter list after function name.");
|
||||
startEatingWhitespace();
|
||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||
do {
|
||||
if (isMethod(type) && check(TOKEN_IDENTIFIER) &&
|
||||
identifiersEqual(&parser.current, &self)) {
|
||||
if (hasCollectors || current->codeobject->requiredArgs != 1) {
|
||||
errorAtCurrent("Argument name 'self' in a method signature is reserved for the implicit first argument.");
|
||||
goto _bail;
|
||||
return 1;
|
||||
}
|
||||
advance();
|
||||
if (check(TOKEN_COLON)) {
|
||||
if (type != TYPE_LAMBDA && check(TOKEN_COLON)) {
|
||||
KrkToken name = parser.previous;
|
||||
match(TOKEN_COLON);
|
||||
typeHint(name);
|
||||
}
|
||||
if (check(TOKEN_EQUAL)) {
|
||||
errorAtCurrent("'self' can not be a keyword argument.");
|
||||
goto _bail;
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -1348,14 +1350,14 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
if (match(TOKEN_POW)) {
|
||||
if (hasCollectors == 2) {
|
||||
error("Duplicate ** in parameter list.");
|
||||
goto _bail;
|
||||
return 1;
|
||||
}
|
||||
hasCollectors = 2;
|
||||
current->codeobject->flags |= KRK_CODEOBJECT_FLAGS_COLLECTS_KWS;
|
||||
} else {
|
||||
if (hasCollectors) {
|
||||
error("Syntax error.");
|
||||
goto _bail;
|
||||
return 1;
|
||||
}
|
||||
hasCollectors = 1;
|
||||
current->codeobject->flags |= KRK_CODEOBJECT_FLAGS_COLLECTS_ARGS;
|
||||
@ -1363,10 +1365,14 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
/* Collect a name, specifically "args" or "kwargs" are commont */
|
||||
ssize_t paramConstant = parseVariable(
|
||||
(hasCollectors == 1) ? "Expected parameter name after '*'." : "Expected parameter name after '**'.");
|
||||
if (parser.hadError) goto _bail;
|
||||
if (parser.hadError) return 1;
|
||||
defineVariable(paramConstant);
|
||||
if (check(TOKEN_COLON)) {
|
||||
KrkToken name = parser.previous;
|
||||
if (isMethod(type) && identifiersEqual(&name,&self)) {
|
||||
errorAtCurrent("Argument name 'self' in a method signature is reserved for the implicit first argument.");
|
||||
return 1;
|
||||
}
|
||||
if (type != TYPE_LAMBDA && check(TOKEN_COLON)) {
|
||||
match(TOKEN_COLON);
|
||||
typeHint(name);
|
||||
}
|
||||
@ -1392,9 +1398,9 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
break;
|
||||
}
|
||||
ssize_t paramConstant = parseVariable("Expected parameter name.");
|
||||
if (parser.hadError) goto _bail;
|
||||
if (parser.hadError) return 1;
|
||||
hideLocal();
|
||||
if (check(TOKEN_COLON)) {
|
||||
if (type != TYPE_LAMBDA && check(TOKEN_COLON)) {
|
||||
KrkToken name = parser.previous;
|
||||
match(TOKEN_COLON);
|
||||
typeHint(name);
|
||||
@ -1402,6 +1408,24 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
argumentDefinition();
|
||||
defineVariable(paramConstant);
|
||||
} while (match(TOKEN_COMMA));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void function(FunctionType type, size_t blockWidth) {
|
||||
Compiler compiler;
|
||||
initCompiler(&compiler, type);
|
||||
compiler.codeobject->chunk.filename = compiler.enclosing->codeobject->chunk.filename;
|
||||
|
||||
beginScope();
|
||||
|
||||
if (isMethod(type)) current->codeobject->requiredArgs = 1;
|
||||
if (isCoroutine(type)) current->codeobject->flags |= KRK_CODEOBJECT_FLAGS_IS_COROUTINE;
|
||||
|
||||
consume(TOKEN_LEFT_PAREN, "Expected start of parameter list after function name.");
|
||||
startEatingWhitespace();
|
||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||
if (argumentList(type)) goto _bail;
|
||||
}
|
||||
stopEatingWhitespace();
|
||||
consume(TOKEN_RIGHT_PAREN, "Expected end of parameter list.");
|
||||
@ -1413,19 +1437,7 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
consume(TOKEN_COLON, "Expected colon after function signature.");
|
||||
block(blockWidth,"def");
|
||||
_bail: (void)0;
|
||||
KrkCodeObject * function = endCompiler();
|
||||
if (compiler.annotationCount) {
|
||||
EMIT_OPERAND_OP(OP_MAKE_DICT, compiler.annotationCount * 2);
|
||||
}
|
||||
size_t ind = krk_addConstant(currentChunk(), OBJECT_VAL(function));
|
||||
EMIT_OPERAND_OP(OP_CLOSURE, ind);
|
||||
doUpvalues(&compiler, function);
|
||||
|
||||
if (compiler.annotationCount) {
|
||||
emitByte(OP_ANNOTATE);
|
||||
}
|
||||
|
||||
freeCompiler(&compiler);
|
||||
functionPrologue(&compiler);
|
||||
}
|
||||
|
||||
static void classBody(size_t blockWidth) {
|
||||
@ -1610,24 +1622,15 @@ static void lambda(int exprType) {
|
||||
beginScope();
|
||||
|
||||
if (!check(TOKEN_COLON)) {
|
||||
do {
|
||||
ssize_t paramConstant = parseVariable("Expected parameter name.");
|
||||
if (parser.hadError) goto _bail;
|
||||
hideLocal();
|
||||
argumentDefinition();
|
||||
defineVariable(paramConstant);
|
||||
} while (match(TOKEN_COMMA));
|
||||
if (argumentList(TYPE_LAMBDA)) goto _bail;
|
||||
}
|
||||
|
||||
consume(TOKEN_COLON, "Expected ':' after lambda arguments");
|
||||
expression();
|
||||
|
||||
_bail: (void)0;
|
||||
KrkCodeObject * lambda = endCompiler();
|
||||
size_t ind = krk_addConstant(currentChunk(), OBJECT_VAL(lambda));
|
||||
EMIT_OPERAND_OP(OP_CLOSURE, ind);
|
||||
doUpvalues(&lambdaCompiler, lambda);
|
||||
freeCompiler(&lambdaCompiler);
|
||||
_bail:
|
||||
functionPrologue(&lambdaCompiler);
|
||||
|
||||
invalidTarget(exprType, "lambda");
|
||||
}
|
||||
|
||||
|
48
test/testBadSelfAsMethodArg.krk
Normal file
48
test/testBadSelfAsMethodArg.krk
Normal file
@ -0,0 +1,48 @@
|
||||
import dis
|
||||
|
||||
let template = '''
|
||||
class Foo:
|
||||
def method(__args__):
|
||||
pass
|
||||
'''
|
||||
|
||||
# 'self' can only be the first argument
|
||||
try:
|
||||
dis.build(template.replace('__args__','a,self'))
|
||||
print('fail')
|
||||
except SyntaxError as e:
|
||||
print('pass' if 'implicit' in str(e) else 'fail')
|
||||
|
||||
# 'self' can not be a star arg
|
||||
try:
|
||||
dis.build(template.replace('__args__','a,*self'))
|
||||
print('fail')
|
||||
except SyntaxError as e:
|
||||
print('pass' if 'implicit' in str(e) else 'fail')
|
||||
|
||||
# 'self' can not be a star-star arg
|
||||
try:
|
||||
dis.build(template.replace('__args__','a,**self'))
|
||||
print('fail')
|
||||
except SyntaxError as e:
|
||||
print('pass' if 'implicit' in str(e) else 'fail')
|
||||
|
||||
# 'self' can not take a default value
|
||||
try:
|
||||
dis.build(template.replace('__args__','self=42'))
|
||||
print('fail')
|
||||
except SyntaxError as e:
|
||||
print('pass' if 'keyword argument' in str(e) else 'fail')
|
||||
|
||||
# for that matter, neither can star args
|
||||
try:
|
||||
dis.build(template.replace('__args__','*args=[]'))
|
||||
print('fail')
|
||||
except SyntaxError as e:
|
||||
print('pass' if 'end of' in str(e) else 'fail')
|
||||
|
||||
try:
|
||||
dis.build(template.replace('__args__','**args=[]'))
|
||||
print('fail')
|
||||
except SyntaxError as e:
|
||||
print('pass' if 'end of' in str(e) else 'fail')
|
6
test/testBadSelfAsMethodArg.krk.expect
Normal file
6
test/testBadSelfAsMethodArg.krk.expect
Normal file
@ -0,0 +1,6 @@
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
pass
|
8
test/testLambdaTakesStars.krk
Normal file
8
test/testLambdaTakesStars.krk
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
let star = lambda *args: ', '.join(str(x) for x in args)
|
||||
|
||||
print(star(1,2,3,'a','b','c'))
|
||||
|
||||
let kws = lambda **kwargs: ', '.join(sorted(f'{x}: {y}' for x, y in kwargs.items()))
|
||||
|
||||
print(kws(a=42,b='hi',c=None))
|
2
test/testLambdaTakesStars.krk.expect
Normal file
2
test/testLambdaTakesStars.krk.expect
Normal file
@ -0,0 +1,2 @@
|
||||
1, 2, 3, a, b, c
|
||||
a: 42, b: hi, c: None
|
Loading…
Reference in New Issue
Block a user