Add argument expansions...
This commit is contained in:
parent
0a042e7a38
commit
2ee154ecf7
2
chunk.h
2
chunk.h
@ -69,6 +69,8 @@ typedef enum {
|
||||
OP_INVOKE_SETTER,
|
||||
OP_INVOKE_GETSLICE,
|
||||
|
||||
OP_EXPAND_ARGS,
|
||||
|
||||
OP_CONSTANT_LONG = 128,
|
||||
OP_PRINT_LONG,
|
||||
OP_DEFINE_GLOBAL_LONG,
|
||||
|
49
compiler.c
49
compiler.c
@ -165,7 +165,6 @@ static void parsePrecedence(Precedence precedence);
|
||||
static ssize_t parseVariable(const char * errorMessage);
|
||||
static void variable(int canAssign);
|
||||
static void defineVariable(size_t global);
|
||||
static size_t argumentList();
|
||||
static ssize_t identifierConstant(KrkToken * name);
|
||||
static ssize_t resolveLocal(Compiler * compiler, KrkToken * name);
|
||||
static ParseRule * getRule(KrkTokenType type);
|
||||
@ -181,6 +180,7 @@ static void namedVariable(KrkToken name, int canAssign);
|
||||
static void addLocal(KrkToken name);
|
||||
static void string(int canAssign);
|
||||
static KrkToken decorator(size_t level, FunctionType type);
|
||||
static void call(int canAssign);
|
||||
|
||||
static void errorAt(KrkToken * token, const char * message) {
|
||||
if (parser.panicMode) return;
|
||||
@ -437,11 +437,6 @@ static void binary(int canAssign) {
|
||||
}
|
||||
}
|
||||
|
||||
static void call(int canAssign) {
|
||||
size_t argCount = argumentList();
|
||||
EMIT_CONSTANT_OP(OP_CALL, argCount);
|
||||
}
|
||||
|
||||
static int matchAssignment(void) {
|
||||
return match(TOKEN_EQUAL) || match(TOKEN_PLUS_EQUAL) || match(TOKEN_MINUS_EQUAL) ||
|
||||
match(TOKEN_PLUS_PLUS) || match(TOKEN_MINUS_MINUS);
|
||||
@ -1786,10 +1781,27 @@ static void defineVariable(size_t global) {
|
||||
EMIT_CONSTANT_OP(OP_DEFINE_GLOBAL, global);
|
||||
}
|
||||
|
||||
static size_t argumentList() {
|
||||
size_t argCount = 0, keywordArgs = 0;
|
||||
static void call(int canAssign) {
|
||||
size_t argCount = 0, specialArgs = 0, keywordArgs = 0, seenKeywordUnpacking = 0;
|
||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||
do {
|
||||
if (match(TOKEN_ASTERISK)) {
|
||||
specialArgs++;
|
||||
if (match(TOKEN_ASTERISK)) {
|
||||
seenKeywordUnpacking = 1;
|
||||
emitBytes(OP_EXPAND_ARGS, 2); /* Outputs something special */
|
||||
expression(); /* Expect dict */
|
||||
continue;
|
||||
} else {
|
||||
if (seenKeywordUnpacking) {
|
||||
error("Iterable expansion follows keyword argument unpacking.");
|
||||
return;
|
||||
}
|
||||
emitBytes(OP_EXPAND_ARGS, 1); /* outputs something special */
|
||||
expression();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (match(TOKEN_IDENTIFIER)) {
|
||||
KrkToken argName = parser.previous;
|
||||
if (check(TOKEN_EQUAL)) {
|
||||
@ -1800,6 +1812,7 @@ static size_t argumentList() {
|
||||
EMIT_CONSTANT_OP(OP_CONSTANT, ind);
|
||||
expression();
|
||||
keywordArgs++;
|
||||
specialArgs++;
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
@ -1809,32 +1822,40 @@ static size_t argumentList() {
|
||||
krk_ungetToken(parser.current);
|
||||
parser.current = argName;
|
||||
}
|
||||
} else if (seenKeywordUnpacking) {
|
||||
error("positional argument follows keyword argument unpacking");
|
||||
return;
|
||||
} else if (keywordArgs) {
|
||||
error("Keyword arguments must appear after positional arguments in function call.");
|
||||
return 0;
|
||||
error("Positional argument follows keyword argument");
|
||||
return;
|
||||
} else if (specialArgs) {
|
||||
emitBytes(OP_EXPAND_ARGS, 0);
|
||||
expression();
|
||||
specialArgs++;
|
||||
continue;
|
||||
}
|
||||
expression();
|
||||
argCount++;
|
||||
} while (match(TOKEN_COMMA));
|
||||
}
|
||||
consume(TOKEN_RIGHT_PAREN, "Expected ')' after arguments.");
|
||||
if (keywordArgs) {
|
||||
if (specialArgs) {
|
||||
/*
|
||||
* Creates a sentinel at the top of the stack to tell the CALL instruction
|
||||
* how many keyword arguments are at the top of the stack. This value
|
||||
* triggers special handling in the CALL that processes the keyword arguments,
|
||||
* which is relatively slow, so only use keyword arguments if you have to!
|
||||
*/
|
||||
EMIT_CONSTANT_OP(OP_KWARGS, keywordArgs);
|
||||
EMIT_CONSTANT_OP(OP_KWARGS, specialArgs);
|
||||
/*
|
||||
* We added two elements - name and value - for each keyword arg,
|
||||
* plus the sentinel object that will show up at the end after the
|
||||
* OP_KWARGS instruction complets, so make sure we have the
|
||||
* right depth into the stack when we execute CALL
|
||||
*/
|
||||
argCount += 1 /* for the sentinel */ + 2 * keywordArgs;
|
||||
argCount += 1 /* for the sentinel */ + 2 * specialArgs;
|
||||
}
|
||||
return argCount;
|
||||
EMIT_CONSTANT_OP(OP_CALL, argCount);
|
||||
}
|
||||
|
||||
static void and_(int canAssign) {
|
||||
|
1
debug.c
1
debug.c
@ -96,6 +96,7 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
|
||||
SIMPLE(OP_INVOKE_GETSLICE)
|
||||
SIMPLE(OP_SWAP)
|
||||
OPERANDB(OP_DUP)
|
||||
OPERANDB(OP_EXPAND_ARGS)
|
||||
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
||||
CONSTANT(OP_CONSTANT,(void)0)
|
||||
CONSTANT(OP_GET_GLOBAL,(void)0)
|
||||
|
6
test/testArgumentExpansions.krk
Normal file
6
test/testArgumentExpansions.krk
Normal file
@ -0,0 +1,6 @@
|
||||
def func(a,b,*args,**kwargs):
|
||||
print "a =",a,"b =",b
|
||||
print "args =",args,"kwargs =",kwargs
|
||||
|
||||
print "\[[31mCall starts here.\[[0m"
|
||||
func(1,*[1,2,3],**{"foo":"bar"},stuff="things")
|
@ -104,3 +104,23 @@ class Bar():
|
||||
|
||||
let b = Bar()
|
||||
b.superDecoratedMethod("this arg goes to the wrapper")
|
||||
|
||||
def genericDecorator(func):
|
||||
def wrapper(*args,**kwargs):
|
||||
print "I am a generic wrapper, I will pass all of my args along."
|
||||
func(*args,**kwargs)
|
||||
print "And I am done."
|
||||
return wrapper
|
||||
|
||||
@genericDecorator
|
||||
def aMethod(with,args=None):
|
||||
print "I got",with,"and",args
|
||||
|
||||
try:
|
||||
aMethod()
|
||||
except:
|
||||
print exception
|
||||
|
||||
aMethod("just the first")
|
||||
aMethod("the first","and the second")
|
||||
aMethod(args="hello",with="world")
|
||||
|
16
value.c
16
value.c
@ -1,3 +1,4 @@
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include "memory.h"
|
||||
#include "value.h"
|
||||
@ -34,7 +35,20 @@ void krk_printValue(FILE * f, KrkValue printable) {
|
||||
case VAL_FLOATING: fprintf(f, "%g", AS_FLOATING(printable)); break;
|
||||
case VAL_NONE: fprintf(f, "None"); break;
|
||||
case VAL_HANDLER: fprintf(f, "{try->%ld}", AS_HANDLER(printable)); break;
|
||||
case VAL_KWARGS: fprintf(f, "{sentinel=%ld}", AS_INTEGER(printable)); break;
|
||||
case VAL_KWARGS: {
|
||||
if (AS_INTEGER(printable) == LONG_MAX) {
|
||||
fprintf(f, "{unpack single}");
|
||||
} else if (AS_INTEGER(printable) == LONG_MAX-1) {
|
||||
fprintf(f, "{unpack list}");
|
||||
} else if (AS_INTEGER(printable) == LONG_MAX-2) {
|
||||
fprintf(f, "{unpack dict}");
|
||||
} else if (AS_INTEGER(printable) == 0) {
|
||||
fprintf(f, "{unset default}");
|
||||
} else {
|
||||
fprintf(f, "{sentinel=%ld}",AS_INTEGER(printable));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return;
|
||||
|
121
vm.c
121
vm.c
@ -1,3 +1,4 @@
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@ -693,6 +694,13 @@ static int checkArgumentCount(KrkClosure * closure, int argCount) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void multipleDefs(KrkClosure * closure, int 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])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a managed method.
|
||||
* Takes care of argument count checking, default argument filling,
|
||||
@ -744,19 +752,8 @@ static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
size_t existingPositionalArgs = argCount - kwargsCount * 2;
|
||||
int found = 0;
|
||||
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 (size_t availableSlots = argCount; availableSlots < (totalArguments); ++availableSlots) {
|
||||
krk_push(KWARGS_VAL(0)); /* Make sure we definitely have enough space */
|
||||
}
|
||||
@ -764,8 +761,14 @@ static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
for (long i = 0; i < kwargsCount; ++i) {
|
||||
KrkValue name = endOfPositionals[i*2];
|
||||
KrkValue value = endOfPositionals[i*2+1];
|
||||
if (IS_KWARGS(name)) {
|
||||
krk_push(name);
|
||||
krk_push(value);
|
||||
found++;
|
||||
goto _finishArg;
|
||||
}
|
||||
/* First, see if it's a positional arg. */
|
||||
for (int j = 0; j < (int)closure->function->requiredArgNames.count; ++j) {
|
||||
for (int j = 0; j < (int)closure->function->requiredArgs; ++j) {
|
||||
if (krk_valuesEqual(name, closure->function->requiredArgNames.values[j])) {
|
||||
krk_push(INTEGER_VAL(j));
|
||||
krk_push(value);
|
||||
@ -774,7 +777,7 @@ static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
}
|
||||
}
|
||||
/* See if it's a keyword arg. */
|
||||
for (int j = 0; j < (int)closure->function->keywordArgNames.count; ++j) {
|
||||
for (int j = 0; j < (int)closure->function->keywordArgs; ++j) {
|
||||
if (krk_valuesEqual(name, closure->function->keywordArgNames.values[j])) {
|
||||
krk_push(INTEGER_VAL(j + closure->function->requiredArgs));
|
||||
krk_push(value);
|
||||
@ -797,23 +800,89 @@ static int call(KrkClosure * closure, int argCount, int extra) {
|
||||
_finishArg:
|
||||
continue;
|
||||
}
|
||||
for (long clearSlots = existingPositionalArgs; clearSlots < argCount; ++clearSlots) {
|
||||
|
||||
size_t destination = existingPositionalArgs;
|
||||
for (long i = 0; i < found; ++i) {
|
||||
/* Check for specials */
|
||||
KrkValue name = startOfExtras[i*2];
|
||||
KrkValue value = startOfExtras[i*2+1];
|
||||
if (IS_KWARGS(name)) {
|
||||
if (AS_INTEGER(name) == LONG_MAX-1) {
|
||||
KrkValue _list_internal;
|
||||
krk_tableGet(&AS_INSTANCE(value)->fields, vm.specialMethodNames[METHOD_LIST_INT], &_list_internal);
|
||||
for (size_t i = 0; i < AS_LIST(_list_internal)->count; ++i) {
|
||||
startOfPositionals[destination] = AS_LIST(_list_internal)->values[i];
|
||||
destination++;
|
||||
}
|
||||
} else if (AS_INTEGER(name) == LONG_MAX) {
|
||||
startOfPositionals[destination] = value;
|
||||
destination++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (destination > potentialPositionalArgs) {
|
||||
if (!closure->function->collectsArguments) {
|
||||
checkArgumentCount(closure,destination);
|
||||
return 0;
|
||||
}
|
||||
krk_push(NONE_VAL()); krk_push(NONE_VAL()); krk_pop(); krk_pop();
|
||||
startOfPositionals[offsetOfExtraArgs] = krk_list_of(destination - potentialPositionalArgs,
|
||||
&startOfPositionals[potentialPositionalArgs]);
|
||||
destination = potentialPositionalArgs + 1;
|
||||
}
|
||||
|
||||
for (long clearSlots = destination; clearSlots < startOfExtras - startOfPositionals; ++clearSlots) {
|
||||
startOfPositionals[clearSlots] = KWARGS_VAL(0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < found; ++i) {
|
||||
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])));
|
||||
multipleDefs(closure, destination);
|
||||
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 if (IS_KWARGS(startOfExtras[i*2])) {
|
||||
if (AS_INTEGER(startOfExtras[i*2]) == LONG_MAX-2) {
|
||||
KrkValue _dict_internal;
|
||||
krk_tableGet(&AS_INSTANCE(startOfExtras[i*2+1])->fields, vm.specialMethodNames[METHOD_DICT_INT], &_dict_internal);
|
||||
for (size_t j = 0; j < AS_DICT(_dict_internal)->capacity; ++j) {
|
||||
KrkTableEntry entry = AS_DICT(_dict_internal)->entries[j];
|
||||
if (entry.key.type == VAL_NONE) continue;
|
||||
KrkValue name = entry.key;
|
||||
KrkValue value = entry.value;
|
||||
for (int j = 0; j < (int)closure->function->requiredArgNames.count; ++j) {
|
||||
if (krk_valuesEqual(name, closure->function->requiredArgNames.values[j])) {
|
||||
int destination = j;
|
||||
if (!IS_KWARGS(startOfPositionals[destination])) {
|
||||
multipleDefs(closure, destination);
|
||||
}
|
||||
startOfPositionals[destination] = value;
|
||||
goto _finishDictEntry;
|
||||
}
|
||||
}
|
||||
/* See if it's a keyword arg. */
|
||||
for (int j = 0; j < (int)closure->function->keywordArgNames.count; ++j) {
|
||||
if (krk_valuesEqual(name, closure->function->keywordArgNames.values[j])) {
|
||||
int destination = j + closure->function->requiredArgs;
|
||||
if (!IS_KWARGS(startOfPositionals[destination])) {
|
||||
multipleDefs(closure, destination);
|
||||
}
|
||||
startOfPositionals[destination] = value;
|
||||
goto _finishDictEntry;
|
||||
}
|
||||
}
|
||||
krk_push(name);
|
||||
krk_push(value);
|
||||
extraKwargs++;
|
||||
_finishDictEntry: continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
krk_runtimeError(vm.exceptions.typeError, "Internal error?");
|
||||
return 0;
|
||||
@ -822,14 +891,9 @@ _finishArg:
|
||||
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) {
|
||||
for (clearSlots = destination; clearSlots < closure->function->requiredArgs; ++clearSlots) {
|
||||
if (IS_KWARGS(startOfPositionals[clearSlots])) {
|
||||
krk_runtimeError(vm.exceptions.typeError, "%s() missing required positional argument: '%s'",
|
||||
closure->function->name ? closure->function->name->chars : "<unnamed function>",
|
||||
@ -854,7 +918,7 @@ _finishArg:
|
||||
if (!checkArgumentCount(closure, argCountX)) {
|
||||
return 0;
|
||||
}
|
||||
while (argCount < (closure->function->requiredArgs + closure->function->keywordArgs)) {
|
||||
while (argCount < (int)totalArguments) {
|
||||
krk_push(KWARGS_VAL(0));
|
||||
argCount++;
|
||||
}
|
||||
@ -2120,6 +2184,11 @@ static KrkValue run() {
|
||||
frame = &vm.frames[vm.frameCount - 1];
|
||||
break;
|
||||
}
|
||||
case OP_EXPAND_ARGS: {
|
||||
int type = READ_BYTE();
|
||||
krk_push(KWARGS_VAL(LONG_MAX-type));
|
||||
break;
|
||||
}
|
||||
case OP_CLOSURE_LONG:
|
||||
case OP_CLOSURE: {
|
||||
KrkFunction * function = AS_FUNCTION(READ_CONSTANT(operandWidth));
|
||||
|
Loading…
Reference in New Issue
Block a user