Add argument expansions...

This commit is contained in:
K. Lange 2021-01-03 17:59:50 +09:00
parent 0a042e7a38
commit 2ee154ecf7
7 changed files with 174 additions and 41 deletions

View File

@ -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,

View File

@ -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) {

View File

@ -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)

View 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")

View File

@ -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
View File

@ -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
View File

@ -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));