Support * and ** in function signatures for collections of additional arguments and keyword args
This commit is contained in:
parent
3f848dba19
commit
36716e3508
58
compiler.c
58
compiler.c
@ -310,6 +310,23 @@ static KrkFunction * endCompiler() {
|
||||
krk_writeValueArray(&function->keywordArgNames, value);
|
||||
krk_pop();
|
||||
}
|
||||
size_t args = current->function->requiredArgs + current->function->keywordArgs;
|
||||
if (current->function->collectsArguments) {
|
||||
KrkValue value = OBJECT_VAL(krk_copyString(current->locals[args].name.start,
|
||||
current->locals[args].name.length));
|
||||
krk_push(value);
|
||||
krk_writeValueArray(&function->keywordArgNames, value);
|
||||
krk_pop();
|
||||
args++;
|
||||
}
|
||||
if (current->function->collectsKeywords) {
|
||||
KrkValue value = OBJECT_VAL(krk_copyString(current->locals[args].name.start,
|
||||
current->locals[args].name.length));
|
||||
krk_push(value);
|
||||
krk_writeValueArray(&function->keywordArgNames, value);
|
||||
krk_pop();
|
||||
args++;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DISASSEMBLY
|
||||
if ((vm.flags & KRK_ENABLE_DISASSEMBLY) && !parser.hadError) {
|
||||
@ -687,6 +704,8 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
|
||||
if (type == TYPE_METHOD || type == TYPE_INIT) current->function->requiredArgs = 1;
|
||||
|
||||
int hasCollectors = 0;
|
||||
|
||||
consume(TOKEN_LEFT_PAREN, "Expected start of parameter list after function name.");
|
||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||
do {
|
||||
@ -696,6 +715,44 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (match(TOKEN_ASTERISK)) {
|
||||
if (match(TOKEN_ASTERISK)) {
|
||||
if (hasCollectors == 2) {
|
||||
error("Duplicate ** in parameter list.");
|
||||
return;
|
||||
}
|
||||
hasCollectors = 2;
|
||||
current->function->collectsKeywords = 1;
|
||||
} else {
|
||||
if (hasCollectors) {
|
||||
error("Syntax error.");
|
||||
return;
|
||||
}
|
||||
hasCollectors = 1;
|
||||
current->function->collectsArguments = 1;
|
||||
}
|
||||
/* Collect a name, specifically "args" or "kwargs" are commont */
|
||||
ssize_t paramConstant = parseVariable("Expect parameter name.");
|
||||
defineVariable(paramConstant);
|
||||
/* Make that a valid local for this function */
|
||||
size_t myLocal = current->localCount - 1;
|
||||
EMIT_CONSTANT_OP(OP_GET_LOCAL, myLocal);
|
||||
/* Check if it's equal to the unset-kwarg-sentinel value */
|
||||
emitConstant(KWARGS_VAL(0));
|
||||
emitByte(OP_EQUAL);
|
||||
int jumpIndex = emitJump(OP_JUMP_IF_FALSE);
|
||||
/* And if it is, set it to the appropriate type */
|
||||
beginScope();
|
||||
KrkToken synth = syntheticToken(hasCollectors == 1 ? "listOf" : "dictOf");
|
||||
namedVariable(synth, 0);
|
||||
emitBytes(OP_CALL, 0);
|
||||
EMIT_CONSTANT_OP(OP_SET_LOCAL, myLocal);
|
||||
endScope();
|
||||
/* Otherwise pop the comparison. */
|
||||
patchJump(jumpIndex);
|
||||
emitByte(OP_POP); /* comparison value */
|
||||
continue;
|
||||
}
|
||||
ssize_t paramConstant = parseVariable("Expect parameter name.");
|
||||
defineVariable(paramConstant);
|
||||
if (match(TOKEN_EQUAL)) {
|
||||
@ -718,6 +775,7 @@ static void function(FunctionType type, size_t blockWidth) {
|
||||
EMIT_CONSTANT_OP(OP_SET_LOCAL, myLocal);
|
||||
endScope();
|
||||
patchJump(jumpIndex);
|
||||
emitByte(OP_POP);
|
||||
current->function->keywordArgs++;
|
||||
} else {
|
||||
current->function->requiredArgs++;
|
||||
|
2
object.c
2
object.c
@ -68,6 +68,8 @@ KrkFunction * krk_newFunction() {
|
||||
function->upvalueCount = 0;
|
||||
function->name = NULL;
|
||||
function->docstring = NULL;
|
||||
function->collectsArguments = 0;
|
||||
function->collectsKeywords = 0;
|
||||
krk_initValueArray(&function->requiredArgNames);
|
||||
krk_initValueArray(&function->keywordArgNames);
|
||||
krk_initChunk(&function->chunk);
|
||||
|
2
object.h
2
object.h
@ -65,6 +65,8 @@ typedef struct {
|
||||
KrkString * docstring;
|
||||
KrkValueArray requiredArgNames;
|
||||
KrkValueArray keywordArgNames;
|
||||
unsigned char collectsArguments:1;
|
||||
unsigned char collectsKeywords:1;
|
||||
} KrkFunction;
|
||||
|
||||
typedef struct {
|
||||
|
24
test/testArgsKwargs.krk
Normal file
24
test/testArgsKwargs.krk
Normal file
@ -0,0 +1,24 @@
|
||||
def anything(*args, **kwargs):
|
||||
print "Positionals:", args
|
||||
print "Keywords:", kwargs
|
||||
|
||||
anything(1,2,3,"a","b",foo="bar",biz=42)
|
||||
|
||||
def func(a,b,*args,**kwargs):
|
||||
print a, b, args
|
||||
let x = 'hello'
|
||||
print x
|
||||
|
||||
func(1,2,3,4,5,6,foo="bar")
|
||||
|
||||
def last(a,b,c=1,d=2,**kwargs):
|
||||
print "Main:", a, b, c, d
|
||||
print "Keyword:", kwargs
|
||||
|
||||
last(1,2)
|
||||
last(1,2,7,3)
|
||||
last(1,2,test="thing")
|
||||
try:
|
||||
last(1,2,'c','d',7,8,extra='foo')
|
||||
except:
|
||||
print exception.arg
|
78
vm.c
78
vm.c
@ -687,6 +687,13 @@ static int checkArgumentCount(KrkClosure * closure, int argCount) {
|
||||
* where we need to restore the stack to when we return from this call.
|
||||
*/
|
||||
static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
KrkValue * startOfPositionals = &vm.stackTop[-argCount];
|
||||
size_t potentialPositionalArgs = closure->function->requiredArgs + closure->function->keywordArgs;
|
||||
size_t totalArguments = closure->function->requiredArgs + closure->function->keywordArgs + closure->function->collectsArguments + closure->function->collectsKeywords;
|
||||
size_t offsetOfExtraArgs = closure->function->requiredArgs + closure->function->keywordArgs;
|
||||
size_t offsetOfExtraKeys = offsetOfExtraArgs + closure->function->collectsArguments;
|
||||
size_t argCountX = argCount;
|
||||
|
||||
if (argCount && IS_KWARGS(vm.stackTop[-1])) {
|
||||
/**
|
||||
* Process keyword arguments.
|
||||
@ -717,11 +724,23 @@ static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
long kwargsCount = AS_INTEGER(vm.stackTop[-1]);
|
||||
krk_pop(); /* Pop the arg counter */
|
||||
argCount--;
|
||||
long existingPositionalArgs = argCount - kwargsCount * 2;
|
||||
size_t existingPositionalArgs = argCount - kwargsCount * 2;
|
||||
int found = 0;
|
||||
KrkValue * startOfPositionals = &vm.stackTop[-argCount];
|
||||
int extraKwargs = 0;
|
||||
|
||||
if (existingPositionalArgs > potentialPositionalArgs) {
|
||||
if (!closure->function->collectsArguments) {
|
||||
checkArgumentCount(closure,existingPositionalArgs);
|
||||
return 0;
|
||||
}
|
||||
krk_push(NONE_VAL()); krk_push(NONE_VAL()); krk_pop(); krk_pop();
|
||||
startOfPositionals[offsetOfExtraArgs] = krk_list_of(existingPositionalArgs - potentialPositionalArgs,
|
||||
&startOfPositionals[potentialPositionalArgs]);
|
||||
existingPositionalArgs = potentialPositionalArgs + 1;
|
||||
}
|
||||
|
||||
KrkValue * endOfPositionals = &vm.stackTop[-kwargsCount * 2];
|
||||
for (long availableSlots = argCount; availableSlots < (closure->function->requiredArgs + closure->function->keywordArgs); ++availableSlots) {
|
||||
for (size_t availableSlots = argCount; availableSlots < (totalArguments); ++availableSlots) {
|
||||
krk_push(KWARGS_VAL(0)); /* Make sure we definitely have enough space */
|
||||
}
|
||||
KrkValue * startOfExtras = vm.stackTop;
|
||||
@ -747,6 +766,13 @@ static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
}
|
||||
}
|
||||
/* If we got to this point, it's not a recognized argument for this function. */
|
||||
if (closure->function->collectsKeywords) {
|
||||
krk_push(name);
|
||||
krk_push(value);
|
||||
found++;
|
||||
extraKwargs++;
|
||||
continue;
|
||||
}
|
||||
krk_runtimeError(vm.exceptions.typeError, "%s() got an unexpected keyword argument '%s'",
|
||||
closure->function->name ? closure->function->name->chars : "<unnamed function>",
|
||||
AS_CSTRING(name));
|
||||
@ -758,15 +784,32 @@ _finishArg:
|
||||
startOfPositionals[clearSlots] = KWARGS_VAL(0);
|
||||
}
|
||||
for (int i = 0; i < found; ++i) {
|
||||
int destination = AS_INTEGER(startOfExtras[i*2]);
|
||||
if (!IS_KWARGS(startOfPositionals[destination])) {
|
||||
krk_runtimeError(vm.exceptions.typeError, "%s() got multiple values for argument '%s'",
|
||||
closure->function->name ? closure->function->name->chars : "<unnamed function>",
|
||||
(destination < closure->function->requiredArgs ? AS_CSTRING(closure->function->requiredArgNames.values[destination]) :
|
||||
AS_CSTRING(closure->function->keywordArgNames.values[destination - closure->function->requiredArgs])));
|
||||
if (IS_INTEGER(startOfExtras[i*2])) {
|
||||
int destination = AS_INTEGER(startOfExtras[i*2]);
|
||||
if (!IS_KWARGS(startOfPositionals[destination])) {
|
||||
krk_runtimeError(vm.exceptions.typeError, "%s() got multiple values for argument '%s'",
|
||||
closure->function->name ? closure->function->name->chars : "<unnamed function>",
|
||||
(destination < closure->function->requiredArgs ? AS_CSTRING(closure->function->requiredArgNames.values[destination]) :
|
||||
AS_CSTRING(closure->function->keywordArgNames.values[destination - closure->function->requiredArgs])));
|
||||
return 0;
|
||||
}
|
||||
startOfPositionals[destination] = startOfExtras[i*2+1];
|
||||
} else if (IS_STRING(startOfExtras[i*2])) {
|
||||
krk_push(startOfExtras[i*2]);
|
||||
krk_push(startOfExtras[i*2+1]);
|
||||
} else {
|
||||
krk_runtimeError(vm.exceptions.typeError, "Internal error?");
|
||||
return 0;
|
||||
}
|
||||
startOfPositionals[destination] = startOfExtras[i*2+1];
|
||||
}
|
||||
if (extraKwargs) {
|
||||
krk_push(NONE_VAL()); krk_push(NONE_VAL()); krk_pop(); krk_pop();
|
||||
startOfPositionals[offsetOfExtraKeys] = krk_dict_of(extraKwargs*2,&startOfExtras[found*2]);
|
||||
while (extraKwargs) {
|
||||
krk_pop();
|
||||
krk_pop();
|
||||
extraKwargs--;
|
||||
}
|
||||
}
|
||||
long clearSlots;
|
||||
for (clearSlots = existingPositionalArgs; clearSlots < closure->function->requiredArgs; ++clearSlots) {
|
||||
@ -777,10 +820,21 @@ _finishArg:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
argCount = closure->function->requiredArgs + closure->function->keywordArgs;
|
||||
argCount = totalArguments;
|
||||
argCountX = argCount - (closure->function->collectsArguments + closure->function->collectsKeywords);
|
||||
while (vm.stackTop > startOfPositionals + argCount) krk_pop();
|
||||
} else {
|
||||
/* We can't have had any kwargs. */
|
||||
if ((size_t)argCount > potentialPositionalArgs && closure->function->collectsArguments) {
|
||||
krk_push(NONE_VAL()); krk_push(NONE_VAL()); krk_pop(); krk_pop();
|
||||
startOfPositionals[offsetOfExtraArgs] = krk_list_of(argCount - potentialPositionalArgs,
|
||||
&startOfPositionals[potentialPositionalArgs]);
|
||||
argCount = closure->function->requiredArgs + 1;
|
||||
argCountX = argCount - 1;
|
||||
while (vm.stackTop > startOfPositionals + argCount) krk_pop();
|
||||
}
|
||||
}
|
||||
if (!checkArgumentCount(closure, argCount)) {
|
||||
if (!checkArgumentCount(closure, argCountX)) {
|
||||
return 0;
|
||||
}
|
||||
while (argCount < (closure->function->requiredArgs + closure->function->keywordArgs)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user