Support /,*,kws in function definitions

This commit is contained in:
K. Lange 2022-09-29 17:34:05 +09:00
parent 29b9db0079
commit 7d57acd49a
12 changed files with 198 additions and 129 deletions

View File

@ -852,9 +852,15 @@ KRK_Function(locals) {
/* First, we'll populate with arguments */ /* First, we'll populate with arguments */
size_t slot = 0; size_t slot = 0;
for (short int i = 0; i < func->requiredArgs; ++i) { for (short int i = 0; i < func->potentialPositionals; ++i) {
krk_tableSet(AS_DICT(dict), krk_tableSet(AS_DICT(dict),
func->requiredArgNames.values[i], func->positionalArgNames.values[i],
krk_currentThread.stack[frame->slots + slot]);
slot++;
}
if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
krk_tableSet(AS_DICT(dict),
func->positionalArgNames.values[func->potentialPositionals],
krk_currentThread.stack[frame->slots + slot]); krk_currentThread.stack[frame->slots + slot]);
slot++; slot++;
} }
@ -864,12 +870,6 @@ KRK_Function(locals) {
krk_currentThread.stack[frame->slots + slot]); krk_currentThread.stack[frame->slots + slot]);
slot++; slot++;
} }
if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
krk_tableSet(AS_DICT(dict),
func->requiredArgNames.values[func->requiredArgs],
krk_currentThread.stack[frame->slots + slot]);
slot++;
}
if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) { if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) {
krk_tableSet(AS_DICT(dict), krk_tableSet(AS_DICT(dict),
func->keywordArgNames.values[func->keywordArgs], func->keywordArgNames.values[func->keywordArgs],

View File

@ -223,6 +223,7 @@ typedef struct Compiler {
int delSatisfied; /**< Flag indicating if a 'del' target has been completed. */ int delSatisfied; /**< Flag indicating if a 'del' target has been completed. */
size_t optionsFlags; /**< Special __options__ imports; similar to __future__ in Python */ size_t optionsFlags; /**< Special __options__ imports; similar to __future__ in Python */
int unnamedArgs;
} Compiler; } Compiler;
#define OPTIONS_FLAG_COMPILE_TIME_BUILTINS (1 << 0) #define OPTIONS_FLAG_COMPILE_TIME_BUILTINS (1 << 0)
@ -344,6 +345,8 @@ static void rewindChunk(KrkChunk * out, ChunkRecorder from) {
out->constants.count = from.constants; out->constants.count = from.constants;
} }
static size_t renameLocal(struct GlobalState * state, size_t ind, KrkToken name);
static void initCompiler(struct GlobalState * state, Compiler * compiler, FunctionType type) { static void initCompiler(struct GlobalState * state, Compiler * compiler, FunctionType type) {
compiler->enclosing = state->current; compiler->enclosing = state->current;
state->current = compiler; state->current = compiler;
@ -368,6 +371,7 @@ static void initCompiler(struct GlobalState * state, Compiler * compiler, Functi
compiler->properties = NULL; compiler->properties = NULL;
compiler->annotationCount = 0; compiler->annotationCount = 0;
compiler->delSatisfied = 0; compiler->delSatisfied = 0;
compiler->unnamedArgs = 0;
compiler->optionsFlags = compiler->enclosing ? compiler->enclosing->optionsFlags : 0; compiler->optionsFlags = compiler->enclosing ? compiler->enclosing->optionsFlags : 0;
if (type != TYPE_MODULE) { if (type != TYPE_MODULE) {
@ -382,7 +386,9 @@ static void initCompiler(struct GlobalState * state, Compiler * compiler, Functi
local->isCaptured = 0; local->isCaptured = 0;
local->name.start = "self"; local->name.start = "self";
local->name.length = 4; local->name.length = 4;
renameLocal(state, 0, local->name);
state->current->codeobject->requiredArgs = 1; state->current->codeobject->requiredArgs = 1;
state->current->codeobject->potentialPositionals = 1;
} }
if (isCoroutine(type)) state->current->codeobject->obj.flags |= KRK_OBJ_FLAGS_CODEOBJECT_IS_COROUTINE; if (isCoroutine(type)) state->current->codeobject->obj.flags |= KRK_OBJ_FLAGS_CODEOBJECT_IS_COROUTINE;
@ -551,42 +557,50 @@ static void emitReturn(struct GlobalState * state) {
static KrkCodeObject * endCompiler(struct GlobalState * state) { static KrkCodeObject * endCompiler(struct GlobalState * state) {
KrkCodeObject * function = state->current->codeobject; KrkCodeObject * function = state->current->codeobject;
for (size_t i = 0; i < state->current->codeobject->localNameCount; i++) { for (size_t i = 0; i < function->localNameCount; i++) {
if (state->current->codeobject->localNames[i].deathday == 0) { if (function->localNames[i].deathday == 0) {
state->current->codeobject->localNames[i].deathday = currentChunk()->count; function->localNames[i].deathday = currentChunk()->count;
} }
} }
state->current->codeobject->localNames = GROW_ARRAY(KrkLocalEntry, state->current->codeobject->localNames, \ function->localNames = GROW_ARRAY(KrkLocalEntry, function->localNames, \
state->current->localNameCapacity, state->current->codeobject->localNameCount); /* Shorten this down for runtime */ state->current->localNameCapacity, function->localNameCount); /* Shorten this down for runtime */
if (state->current->continueCount) { state->parser.previous = state->current->continues[0].token; error("continue without loop"); } if (state->current->continueCount) { state->parser.previous = state->current->continues[0].token; error("continue without loop"); }
if (state->current->breakCount) { state->parser.previous = state->current->breaks[0].token; error("break without loop"); } if (state->current->breakCount) { state->parser.previous = state->current->breaks[0].token; error("break without loop"); }
emitReturn(state); emitReturn(state);
/* Attach contants for arguments */ /* Attach contants for arguments */
for (int i = 0; i < function->requiredArgs; ++i) { for (int i = 0; i < function->potentialPositionals; ++i) {
if (i < state->current->unnamedArgs) {
krk_writeValueArray(&function->positionalArgNames, NONE_VAL());
continue;
}
KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[i].name.start, state->current->locals[i].name.length)); KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[i].name.start, state->current->locals[i].name.length));
krk_push(value); krk_push(value);
krk_writeValueArray(&function->requiredArgNames, value); krk_writeValueArray(&function->positionalArgNames, value);
krk_pop(); krk_pop();
} }
size_t args = function->potentialPositionals;
if (function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[args].name.start,
state->current->locals[args].name.length));
krk_push(value);
krk_writeValueArray(&function->positionalArgNames, value);
krk_pop();
args++;
}
for (int i = 0; i < function->keywordArgs; ++i) { for (int i = 0; i < function->keywordArgs; ++i) {
KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[i+function->requiredArgs].name.start, KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[i+args].name.start,
state->current->locals[i+function->requiredArgs].name.length)); state->current->locals[i+args].name.length));
krk_push(value); krk_push(value);
krk_writeValueArray(&function->keywordArgNames, value); krk_writeValueArray(&function->keywordArgNames, value);
krk_pop(); krk_pop();
} }
size_t args = state->current->codeobject->requiredArgs + state->current->codeobject->keywordArgs; args += function->keywordArgs;
if (state->current->codeobject->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[args].name.start, if (function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) {
state->current->locals[args].name.length));
krk_push(value);
krk_writeValueArray(&function->requiredArgNames, value);
krk_pop();
args++;
}
if (state->current->codeobject->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) {
KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[args].name.start, KrkValue value = OBJECT_VAL(krk_copyString(state->current->locals[args].name.start,
state->current->locals[args].name.length)); state->current->locals[args].name.length));
krk_push(value); krk_push(value);
@ -595,8 +609,7 @@ static KrkCodeObject * endCompiler(struct GlobalState * state) {
args++; args++;
} }
state->current->codeobject->potentialPositionals = state->current->codeobject->requiredArgs + state->current->codeobject->keywordArgs; function->totalArguments = function->potentialPositionals + function->keywordArgs + !!(function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
state->current->codeobject->totalArguments = state->current->codeobject->potentialPositionals + !!(state->current->codeobject->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(state->current->codeobject->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
#ifndef KRK_NO_DISASSEMBLY #ifndef KRK_NO_DISASSEMBLY
if ((krk_currentThread.flags & KRK_THREAD_ENABLE_DISASSEMBLY) && !state->parser.hadError) { if ((krk_currentThread.flags & KRK_THREAD_ENABLE_DISASSEMBLY) && !state->parser.hadError) {
@ -1351,7 +1364,7 @@ static void hideLocal(struct GlobalState * state) {
state->current->locals[state->current->localCount - 1].depth = -2; state->current->locals[state->current->localCount - 1].depth = -2;
} }
static void argumentDefinition(struct GlobalState * state) { static void argumentDefinition(struct GlobalState * state, int hasCollectors) {
if (match(TOKEN_EQUAL)) { if (match(TOKEN_EQUAL)) {
/* /*
* We inline default arguments by checking if they are equal * We inline default arguments by checking if they are equal
@ -1370,13 +1383,27 @@ static void argumentDefinition(struct GlobalState * state) {
EMIT_OPERAND_OP(OP_SET_LOCAL_POP, myLocal); EMIT_OPERAND_OP(OP_SET_LOCAL_POP, myLocal);
endScope(state); endScope(state);
patchJump(jumpIndex); patchJump(jumpIndex);
state->current->codeobject->keywordArgs++; if (hasCollectors) {
} else { state->current->codeobject->keywordArgs++;
if (state->current->codeobject->keywordArgs) { } else {
error("non-keyword argument follows keyword argument"); state->current->codeobject->potentialPositionals++;
return; }
} else {
if (hasCollectors) {
size_t myLocal = state->current->localCount - 1;
EMIT_OPERAND_OP(OP_GET_LOCAL, myLocal);
int jumpIndex = emitJump(OP_TEST_ARG);
EMIT_OPERAND_OP(OP_MISSING_KW, state->current->codeobject->keywordArgs);
patchJump(jumpIndex);
state->current->codeobject->keywordArgs++;
} else {
if (state->current->codeobject->potentialPositionals != state->current->codeobject->requiredArgs) {
error("non-default argument follows default argument");
return;
}
state->current->codeobject->requiredArgs++;
state->current->codeobject->potentialPositionals++;
} }
state->current->codeobject->requiredArgs++;
} }
} }
@ -1413,11 +1440,19 @@ static int argumentList(struct GlobalState * state, FunctionType type) {
typeHint(state, name); typeHint(state, name);
} }
if (check(TOKEN_EQUAL)) { if (check(TOKEN_EQUAL)) {
errorAtCurrent("'self' can not be a keyword argument."); errorAtCurrent("'self' can not be a default argument.");
return 1; return 1;
} }
continue; continue;
} }
if (match(TOKEN_SOLIDUS)) {
if (hasCollectors || state->current->unnamedArgs || !state->current->codeobject->potentialPositionals) {
error("Syntax error.");
return 1;
}
state->current->unnamedArgs = state->current->codeobject->potentialPositionals;
continue;
}
if (match(TOKEN_ASTERISK) || check(TOKEN_POW)) { if (match(TOKEN_ASTERISK) || check(TOKEN_POW)) {
if (match(TOKEN_POW)) { if (match(TOKEN_POW)) {
if (hasCollectors == 2) { if (hasCollectors == 2) {
@ -1432,6 +1467,9 @@ static int argumentList(struct GlobalState * state, FunctionType type) {
return 1; return 1;
} }
hasCollectors = 1; hasCollectors = 1;
if (check(TOKEN_COMMA)) {
continue;
}
state->current->codeobject->obj.flags |= KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS; state->current->codeobject->obj.flags |= KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS;
} }
/* Collect a name, specifically "args" or "kwargs" are commont */ /* Collect a name, specifically "args" or "kwargs" are commont */
@ -1463,8 +1501,8 @@ static int argumentList(struct GlobalState * state, FunctionType type) {
patchJump(jumpIndex); patchJump(jumpIndex);
continue; continue;
} }
if (hasCollectors) { if (hasCollectors == 2) {
error("arguments follow catch-all collector"); error("arguments follow catch-all keyword collector");
break; break;
} }
ssize_t paramConstant = parseVariable(state, "Expected parameter name."); ssize_t paramConstant = parseVariable(state, "Expected parameter name.");
@ -1475,7 +1513,7 @@ static int argumentList(struct GlobalState * state, FunctionType type) {
match(TOKEN_COLON); match(TOKEN_COLON);
typeHint(state, name); typeHint(state, name);
} }
argumentDefinition(state); argumentDefinition(state, hasCollectors);
defineVariable(state, paramConstant); defineVariable(state, paramConstant);
} while (match(TOKEN_COMMA)); } while (match(TOKEN_COMMA));

View File

@ -31,27 +31,19 @@ void krk_debug_dumpStack(FILE * file, KrkCallFrame * frame) {
size_t relative = i - f->slots; size_t relative = i - f->slots;
/* Figure out the name of this value */ /* Figure out the name of this value */
if (relative < (size_t)f->closure->function->requiredArgs) { int found = 0;
fprintf(file, "%s=", AS_CSTRING(f->closure->function->requiredArgNames.values[relative])); for (size_t j = 0; j < f->closure->function->localNameCount; ++j) {
break; if (relative == f->closure->function->localNames[j].id
} else if (relative < (size_t)f->closure->function->requiredArgs + (size_t)f->closure->function->keywordArgs) { /* Only display this name if it's currently valid */
fprintf(file, "%s=", AS_CSTRING(f->closure->function->keywordArgNames.values[relative - f->closure->function->requiredArgs])); && f->closure->function->localNames[j].birthday <= (size_t)(f->ip - f->closure->function->chunk.code)
break; && f->closure->function->localNames[j].deathday >= (size_t)(f->ip - f->closure->function->chunk.code)
} else { ) {
int found = 0; fprintf(file, "%s=", f->closure->function->localNames[j].name->chars);
for (size_t j = 0; j < f->closure->function->localNameCount; ++j) { found = 1;
if (relative == f->closure->function->localNames[j].id break;
/* Only display this name if it's currently valid */
&& f->closure->function->localNames[j].birthday <= (size_t)(f->ip - f->closure->function->chunk.code)
&& f->closure->function->localNames[j].deathday >= (size_t)(f->ip - f->closure->function->chunk.code)
) {
fprintf(file, "%s=", f->closure->function->localNames[j].name->chars);
found = 1;
break;
}
} }
if (found) break;
} }
if (found) break;
} }
krk_printValueSafe(file, *slot); krk_printValueSafe(file, *slot);
@ -69,20 +61,24 @@ void krk_disassembleCodeObject(FILE * f, KrkCodeObject * func, const char * name
KrkChunk * chunk = &func->chunk; KrkChunk * chunk = &func->chunk;
/* Function header */ /* Function header */
fprintf(f, "<%s(", name); fprintf(f, "<%s(", name);
for (int i = 0; i < func->requiredArgs; ++i) { int j = 0;
fprintf(f,"%s",AS_CSTRING(func->requiredArgNames.values[i])); for (int i = 0; i < func->potentialPositionals; ++i) {
if (i + 1 < func->requiredArgs || func->keywordArgs || !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) || !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS)) fprintf(f,","); fprintf(f,"%s",func->localNames[j].name->chars);
} if (j + 1 < func->totalArguments) fprintf(f,",");
for (int i = 0; i < func->keywordArgs; ++i) { j++;
fprintf(f,"%s=...",AS_CSTRING(func->keywordArgNames.values[i]));
if (i + 1 < func->keywordArgs || !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) || !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS)) fprintf(f,",");
} }
if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) { if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
fprintf(f,"*%s", AS_CSTRING(func->requiredArgNames.values[func->requiredArgs])); fprintf(f,"*%s",func->localNames[j].name->chars);
if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) fprintf(f,","); if (j + 1 < func->totalArguments) fprintf(f,",");
j++;
}
for (int i = 0; i < func->keywordArgs; ++i) {
fprintf(f,"%s=",func->localNames[j].name->chars);
if (j + 1 < func->totalArguments) fprintf(f,",");
j++;
} }
if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) { if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) {
fprintf(f,"**%s", AS_CSTRING(func->keywordArgNames.values[func->keywordArgs])); fprintf(f,"**%s",func->localNames[j].name->chars);
} }
fprintf(f, ") from %s>\n", chunk->filename->chars); fprintf(f, ") from %s>\n", chunk->filename->chars);
for (size_t offset = 0; offset < chunk->count;) { for (size_t offset = 0; offset < chunk->count;) {
@ -233,16 +229,16 @@ static void _format_value_more(OPARGS, size_t operand) {
#define LOCAL_MORE _local_more #define LOCAL_MORE _local_more
static void _local_more(OPARGS, size_t operand) { static void _local_more(OPARGS, size_t operand) {
if ((short int)operand < (func->requiredArgs)) { for (size_t i = 0; i < func->localNameCount; ++i) {
fprintf(f, " (%s, arg)", AS_CSTRING(func->requiredArgNames.values[operand])); if (func->localNames[i].id == operand && func->localNames[i].birthday <= *offset && func->localNames[i].deathday >= *offset) {
} else if ((short int)operand < (func->requiredArgs + func->keywordArgs)) { fprintf(f, " (%s", func->localNames[i].name->chars);
fprintf(f, " (%s, kwarg))", AS_CSTRING(func->keywordArgNames.values[operand-func->requiredArgs])); if ((short int) operand < func->potentialPositionals) {
} else { fprintf(f, ", arg");
for (size_t i = 0; i < func->localNameCount; ++i) { } else if ((short int)operand < func->potentialPositionals + func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS)) {
if (func->localNames[i].id == operand && func->localNames[i].birthday <= *offset && func->localNames[i].deathday >= *offset) { fprintf(f, ", kwarg");
fprintf(f, " (%s)", func->localNames[i].name->chars);
break;
} }
fprintf(f, ")");
break;
} }
} }
} }
@ -727,17 +723,11 @@ static KrkValue _examineInternal(KrkCodeObject* func) {
} else if (jump != 0) { } else if (jump != 0) {
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump); newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump);
} else if (local != -1) { } else if (local != -1) {
if ((short int)local < func->requiredArgs) { newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
newTuple->values.values[newTuple->values.count++] = func->requiredArgNames.values[local]; for (size_t i = 0; i < func->localNameCount; ++i) {
} else if ((short int)local < func->requiredArgs + func->keywordArgs) { if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
newTuple->values.values[newTuple->values.count++] = func->keywordArgNames.values[local - func->requiredArgs]; newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
} else { break;
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
for (size_t i = 0; i < func->localNameCount; ++i) {
if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
break;
}
} }
} }
} else if (operand != -1) { } else if (operand != -1) {

View File

@ -151,8 +151,8 @@ typedef struct {
KrkChunk chunk; /**< @brief Bytecode data */ KrkChunk chunk; /**< @brief Bytecode data */
KrkString * name; /**< @brief Name of the function */ KrkString * name; /**< @brief Name of the function */
KrkString * docstring; /**< @brief Docstring attached to the function */ KrkString * docstring; /**< @brief Docstring attached to the function */
KrkValueArray requiredArgNames; /**< @brief Array of names for required arguments (and *args) */ KrkValueArray positionalArgNames; /**< @brief Array of names for positional arguments (and *args) */
KrkValueArray keywordArgNames; /**< @brief Array of names for keyword arguments (and **kwargs) */ KrkValueArray keywordArgNames; /**< @brief Array of names for keyword-only arguments (and **kwargs) */
size_t localNameCapacity; /**< @brief Capacity of @ref localNames */ size_t localNameCapacity; /**< @brief Capacity of @ref localNames */
size_t localNameCount; /**< @brief Number of entries in @ref localNames */ size_t localNameCount; /**< @brief Number of entries in @ref localNames */
KrkLocalEntry * localNames; /**< @brief Stores the names of local variables used in the function, for debugging */ KrkLocalEntry * localNames; /**< @brief Stores the names of local variables used in the function, for debugging */

View File

@ -210,7 +210,7 @@ static void freeObject(KrkObj * object) {
case KRK_OBJ_CODEOBJECT: { case KRK_OBJ_CODEOBJECT: {
KrkCodeObject * function = (KrkCodeObject*)object; KrkCodeObject * function = (KrkCodeObject*)object;
krk_freeChunk(&function->chunk); krk_freeChunk(&function->chunk);
krk_freeValueArray(&function->requiredArgNames); krk_freeValueArray(&function->positionalArgNames);
krk_freeValueArray(&function->keywordArgNames); krk_freeValueArray(&function->keywordArgNames);
FREE_ARRAY(KrkLocalEntry, function->localNames, function->localNameCount); FREE_ARRAY(KrkLocalEntry, function->localNames, function->localNameCount);
function->localNameCount = 0; function->localNameCount = 0;
@ -356,7 +356,7 @@ static void blackenObject(KrkObj * object) {
krk_markObject((KrkObj*)function->qualname); krk_markObject((KrkObj*)function->qualname);
krk_markObject((KrkObj*)function->docstring); krk_markObject((KrkObj*)function->docstring);
krk_markObject((KrkObj*)function->chunk.filename); krk_markObject((KrkObj*)function->chunk.filename);
markArray(&function->requiredArgNames); markArray(&function->positionalArgNames);
markArray(&function->keywordArgNames); markArray(&function->keywordArgNames);
markArray(&function->chunk.constants); markArray(&function->chunk.constants);
for (size_t i = 0; i < function->localNameCount; ++i) { for (size_t i = 0; i < function->localNameCount; ++i) {

View File

@ -14,21 +14,21 @@ static KrkValue nativeFunctionName(KrkValue func) {
} }
static KrkTuple * functionArgs(KrkCodeObject * _self) { static KrkTuple * functionArgs(KrkCodeObject * _self) {
KrkTuple * tuple = krk_newTuple(_self->requiredArgs + _self->keywordArgs + !!(_self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(_self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS)); KrkTuple * tuple = krk_newTuple(_self->totalArguments);
krk_push(OBJECT_VAL(tuple)); krk_push(OBJECT_VAL(tuple));
for (short i = 0; i < _self->requiredArgs; ++i) { for (short i = 0; i < _self->potentialPositionals; ++i) {
tuple->values.values[tuple->values.count++] = _self->requiredArgNames.values[i]; tuple->values.values[tuple->values.count++] = _self->positionalArgNames.values[i];
}
if (_self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
tuple->values.values[tuple->values.count++] = krk_stringFromFormat("*%S", AS_STRING(_self->positionalArgNames.values[_self->potentialPositionals]));
} }
for (short i = 0; i < _self->keywordArgs; ++i) { for (short i = 0; i < _self->keywordArgs; ++i) {
tuple->values.values[tuple->values.count++] = krk_stringFromFormat("%S=", AS_STRING(_self->keywordArgNames.values[i])); tuple->values.values[tuple->values.count++] = krk_stringFromFormat("%S=", AS_STRING(_self->keywordArgNames.values[i]));
} }
if (_self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
tuple->values.values[tuple->values.count++] = krk_stringFromFormat("*%S", AS_STRING(_self->requiredArgNames.values[_self->requiredArgs]));
}
if (_self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) { if (_self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) {
tuple->values.values[tuple->values.count++] = krk_stringFromFormat("**%S", AS_STRING(_self->keywordArgNames.values[_self->keywordArgs])); tuple->values.values[tuple->values.count++] = krk_stringFromFormat("**%S", AS_STRING(_self->keywordArgNames.values[_self->keywordArgs]));
} }
@ -239,6 +239,18 @@ KRK_Method(codeobject,co_argcount) {
return INTEGER_VAL(self->potentialPositionals); return INTEGER_VAL(self->potentialPositionals);
} }
KRK_Method(codeobject,co_kwonlyargcount) {
return INTEGER_VAL(self->keywordArgs);
}
KRK_Method(codeobject,co_posonlyargcount) {
/* This is tricky because we don't store it anywhere */
for (size_t i = 0; i < self->potentialPositionals; ++i) {
if (!IS_NONE(self->positionalArgNames.values[i])) return INTEGER_VAL(i);
}
return INTEGER_VAL(0);
}
KRK_Method(codeobject,__locals__) { KRK_Method(codeobject,__locals__) {
krk_push(OBJECT_VAL(krk_newTuple(self->localNameCount))); krk_push(OBJECT_VAL(krk_newTuple(self->localNameCount)));
for (size_t i = 0; i < self->localNameCount; ++i) { for (size_t i = 0; i < self->localNameCount; ++i) {
@ -384,6 +396,8 @@ void _createAndBind_functionClass(void) {
BIND_PROP(codeobject,co_flags); BIND_PROP(codeobject,co_flags);
BIND_PROP(codeobject,co_code); BIND_PROP(codeobject,co_code);
BIND_PROP(codeobject,co_argcount); BIND_PROP(codeobject,co_argcount);
BIND_PROP(codeobject,co_kwonlyargcount);
BIND_PROP(codeobject,co_posonlyargcount);
BIND_PROP(codeobject,__locals__); BIND_PROP(codeobject,__locals__);
BIND_PROP(codeobject,__args__); BIND_PROP(codeobject,__args__);
krk_defineNative(&codeobject->methods, "__repr__", FUNC_NAME(codeobject,__str__)); krk_defineNative(&codeobject->methods, "__repr__", FUNC_NAME(codeobject,__str__));

View File

@ -262,12 +262,13 @@ KrkCodeObject * krk_newCodeObject(void) {
KrkCodeObject * codeobject = ALLOCATE_OBJECT(KrkCodeObject, KRK_OBJ_CODEOBJECT); KrkCodeObject * codeobject = ALLOCATE_OBJECT(KrkCodeObject, KRK_OBJ_CODEOBJECT);
codeobject->requiredArgs = 0; codeobject->requiredArgs = 0;
codeobject->keywordArgs = 0; codeobject->keywordArgs = 0;
codeobject->potentialPositionals = 0;
codeobject->upvalueCount = 0; codeobject->upvalueCount = 0;
codeobject->name = NULL; codeobject->name = NULL;
codeobject->docstring = NULL; codeobject->docstring = NULL;
codeobject->localNameCount = 0; codeobject->localNameCount = 0;
codeobject->localNames = NULL; codeobject->localNames = NULL;
krk_initValueArray(&codeobject->requiredArgNames); krk_initValueArray(&codeobject->positionalArgNames);
krk_initValueArray(&codeobject->keywordArgNames); krk_initValueArray(&codeobject->keywordArgNames);
krk_initChunk(&codeobject->chunk); krk_initChunk(&codeobject->chunk);
return codeobject; return codeobject;

View File

@ -112,3 +112,4 @@ SIMPLE(OP_INHERIT)
JUMP(OP_CALL_ITER,+) JUMP(OP_CALL_ITER,+)
JUMP(OP_JUMP_IF_TRUE_OR_POP,+) JUMP(OP_JUMP_IF_TRUE_OR_POP,+)
SIMPLE(OP_TRY_ELSE) SIMPLE(OP_TRY_ELSE)
OPERAND(OP_MISSING_KW, NOOP)

View File

@ -5,7 +5,7 @@
#define KRK_VERSION_MAJOR 1 #define KRK_VERSION_MAJOR 1
#define KRK_VERSION_MINOR 3 #define KRK_VERSION_MINOR 3
#define KRK_VERSION_PATCH 0 #define KRK_VERSION_PATCH 1
#define KRK_VERSION_LEVEL 0xF #define KRK_VERSION_LEVEL 0xF
#define KRK_VERSION_SERIAL 0x0 #define KRK_VERSION_SERIAL 0x0
@ -74,7 +74,7 @@ KRK_Function(getsizeof) {
mySize += sizeof(KrkLineMap) * self->chunk.linesCapacity; mySize += sizeof(KrkLineMap) * self->chunk.linesCapacity;
mySize += sizeof(KrkValue) * self->chunk.constants.capacity; mySize += sizeof(KrkValue) * self->chunk.constants.capacity;
/* requiredArgNames */ /* requiredArgNames */
mySize += sizeof(KrkValue) * self->requiredArgNames.capacity; mySize += sizeof(KrkValue) * self->positionalArgNames.capacity;
/* keywordArgNames */ /* keywordArgNames */
mySize += sizeof(KrkValue) * self->keywordArgNames.capacity; mySize += sizeof(KrkValue) * self->keywordArgNames.capacity;
/* Locals array */ /* Locals array */

View File

@ -368,12 +368,13 @@ int krk_isInstanceOf(KrkValue obj, const KrkClass * type) {
static inline int checkArgumentCount(const KrkClosure * closure, int argCount) { static inline int checkArgumentCount(const KrkClosure * closure, int argCount) {
int minArgs = closure->function->requiredArgs; int minArgs = closure->function->requiredArgs;
int maxArgs = minArgs + closure->function->keywordArgs; int maxArgs = closure->function->potentialPositionals;
if (unlikely(argCount < minArgs || argCount > maxArgs)) { if (unlikely(argCount < minArgs || argCount > maxArgs)) {
krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)", krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d %sargument%s (%d given)",
closure->function->name ? closure->function->name->chars : "<unnamed>", closure->function->name ? closure->function->name->chars : "<unnamed>",
(minArgs == maxArgs) ? "exactly" : (argCount < minArgs ? "at least" : "at most"), (minArgs == maxArgs) ? "exactly" : (argCount < minArgs ? "at least" : "at most"),
(argCount < minArgs) ? minArgs : maxArgs, (argCount < minArgs) ? minArgs : maxArgs,
closure->function->keywordArgs ? "positional " : "",
((argCount < minArgs) ? minArgs : maxArgs) == 1 ? "" : "s", ((argCount < minArgs) ? minArgs : maxArgs) == 1 ? "" : "s",
argCount); argCount);
return 0; return 0;
@ -384,8 +385,8 @@ static inline int checkArgumentCount(const KrkClosure * closure, int argCount) {
static void multipleDefs(const KrkClosure * closure, int destination) { static void multipleDefs(const KrkClosure * closure, int destination) {
krk_runtimeError(vm.exceptions->typeError, "%s() got multiple values for argument '%S'", krk_runtimeError(vm.exceptions->typeError, "%s() got multiple values for argument '%S'",
closure->function->name ? closure->function->name->chars : "<unnamed>", closure->function->name ? closure->function->name->chars : "<unnamed>",
(destination < closure->function->requiredArgs ? AS_STRING(closure->function->requiredArgNames.values[destination]) : (destination < closure->function->potentialPositionals ? AS_STRING(closure->function->positionalArgNames.values[destination]) :
(destination - closure->function->requiredArgs < closure->function->keywordArgs ? AS_STRING(closure->function->keywordArgNames.values[destination - closure->function->requiredArgs]) : (destination - closure->function->potentialPositionals < closure->function->keywordArgs ? AS_STRING(closure->function->keywordArgNames.values[destination - closure->function->potentialPositionals]) :
S("<unnamed>")))); S("<unnamed>"))));
} }
@ -546,6 +547,11 @@ static inline int _callManaged(KrkClosure * closure, int argCount, int returnDep
argCount++; argCount++;
} }
for (size_t i = 0; i < closure->function->keywordArgs; ++i) {
krk_push(KWARGS_VAL(0));
argCount++;
}
/* We're done with the positionals */ /* We're done with the positionals */
krk_currentThread.scratchSpace[0] = NONE_VAL(); krk_currentThread.scratchSpace[0] = NONE_VAL();
@ -556,8 +562,8 @@ static inline int _callManaged(KrkClosure * closure, int argCount, int returnDep
KrkValue name = entry->key; KrkValue name = entry->key;
KrkValue value = entry->value; KrkValue value = entry->value;
/* See if we can place it */ /* See if we can place it */
for (int j = 0; j < (int)closure->function->requiredArgs; ++j) { for (int j = 0; j < (int)closure->function->potentialPositionals; ++j) {
if (krk_valuesEqual(name, closure->function->requiredArgNames.values[j])) { if (krk_valuesSame(name, closure->function->positionalArgNames.values[j])) {
if (!IS_KWARGS(krk_currentThread.stackTop[-argCount + j])) { if (!IS_KWARGS(krk_currentThread.stackTop[-argCount + j])) {
multipleDefs(closure,j); multipleDefs(closure,j);
goto _errorAfterPositionals; goto _errorAfterPositionals;
@ -568,12 +574,12 @@ static inline int _callManaged(KrkClosure * closure, int argCount, int returnDep
} }
/* See if it's a keyword arg. */ /* See if it's a keyword arg. */
for (int j = 0; j < (int)closure->function->keywordArgs; ++j) { for (int j = 0; j < (int)closure->function->keywordArgs; ++j) {
if (krk_valuesEqual(name, closure->function->keywordArgNames.values[j])) { if (krk_valuesSame(name, closure->function->keywordArgNames.values[j])) {
if (!IS_KWARGS(krk_currentThread.stackTop[-argCount + j + closure->function->requiredArgs])) { if (!IS_KWARGS(krk_currentThread.stackTop[-argCount + j + closure->function->potentialPositionals + !!(closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS)])) {
multipleDefs(closure, j + closure->function->requiredArgs); multipleDefs(closure, j + closure->function->potentialPositionals);
goto _errorAfterPositionals; goto _errorAfterPositionals;
} }
krk_currentThread.stackTop[-argCount + j + closure->function->requiredArgs] = value; krk_currentThread.stackTop[-argCount + j + closure->function->potentialPositionals + !!(closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS)] = value;
goto _finishKwarg; goto _finishKwarg;
} }
} }
@ -603,14 +609,21 @@ _finishKwarg:
for (size_t i = 0; i < (size_t)closure->function->requiredArgs; ++i) { for (size_t i = 0; i < (size_t)closure->function->requiredArgs; ++i) {
if (IS_KWARGS(krk_currentThread.stackTop[-argCount + i])) { if (IS_KWARGS(krk_currentThread.stackTop[-argCount + i])) {
krk_runtimeError(vm.exceptions->typeError, "%s() missing required positional argument: '%S'", if (i < closure->function->localNameCount) {
closure->function->name ? closure->function->name->chars : "<unnamed>", krk_runtimeError(vm.exceptions->typeError, "%s() %s: '%S'",
AS_STRING(closure->function->requiredArgNames.values[i])); closure->function->name ? closure->function->name->chars : "<unnamed>",
"missing required positional argument",
closure->function->localNames[i].name);
} else {
krk_runtimeError(vm.exceptions->typeError, "%s() %s",
closure->function->name ? closure->function->name->chars : "<unnamed>",
"missing required positional argument");
}
goto _errorAfterKeywords; goto _errorAfterKeywords;
} }
} }
argCountX = argCount - (!!(closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS)); argCountX = argCount - closure->function->keywordArgs - (!!(closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS));
} else if ((size_t)argCount > potentialPositionalArgs && (closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS)) { } else if ((size_t)argCount > potentialPositionalArgs && (closure->function->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS)) {
KrkValue * startOfPositionals = &krk_currentThread.stackTop[-argCount]; KrkValue * startOfPositionals = &krk_currentThread.stackTop[-argCount];
KrkValue tmp = krk_callNativeOnStack(argCount - potentialPositionalArgs, KrkValue tmp = krk_callNativeOnStack(argCount - potentialPositionalArgs,
@ -3018,6 +3031,16 @@ _finishPopBlock:
break; break;
} }
case OP_MISSING_KW_LONG:
THREE_BYTE_OPERAND;
case OP_MISSING_KW: {
ONE_BYTE_OPERAND;
krk_runtimeError(vm.exceptions->typeError, "%s() missing required keyword-only argument: %R",
frame->closure->function->name ? frame->closure->function->name->chars : "<unnamed>",
frame->closure->function->keywordArgNames.values[OPERAND]);
break;
}
default: default:
__builtin_unreachable(); __builtin_unreachable();
} }

View File

@ -32,7 +32,7 @@ try:
dis.build(template.replace('__args__','self=42')) dis.build(template.replace('__args__','self=42'))
print('fail') print('fail')
except SyntaxError as e: except SyntaxError as e:
print('pass' if 'keyword argument' in str(e) else 'fail') print('pass' if 'default argument' in str(e) else 'fail')
# for that matter, neither can star args # for that matter, neither can star args
try: try:

View File

@ -24,7 +24,7 @@
struct MarshalHeader { struct MarshalHeader {
uint8_t magic[4]; /* K R K B */ uint8_t magic[4]; /* K R K B */
uint8_t version[4]; /* 1 0 1 1 */ uint8_t version[4]; /* 1 0 1 2 */
} __attribute__((packed)); } __attribute__((packed));
struct FunctionHeader { struct FunctionHeader {
@ -33,6 +33,7 @@ struct FunctionHeader {
uint32_t qualInd; uint32_t qualInd;
uint16_t reqArgs; uint16_t reqArgs;
uint16_t kwArgs; uint16_t kwArgs;
uint16_t posArgs;
uint16_t upvalues; uint16_t upvalues;
uint32_t locals; uint32_t locals;
uint32_t bcSize; uint32_t bcSize;
@ -219,8 +220,8 @@ static int doFirstPass(FILE * out) {
if (func->docstring) internString(func->docstring); if (func->docstring) internString(func->docstring);
if (func->qualname) internString(func->qualname); if (func->qualname) internString(func->qualname);
for (size_t i = 0; i < (size_t)func->requiredArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); ++i) { for (size_t i = 0; i < (size_t)func->potentialPositionals + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); ++i) {
internString(AS_STRING(func->requiredArgNames.values[i])); internString(AS_STRING(func->positionalArgNames.values[i]));
} }
for (size_t i = 0; i < (size_t)func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) { for (size_t i = 0; i < (size_t)func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) {
@ -267,6 +268,7 @@ static int doSecondPass(FILE * out) {
func->qualname ? internString(func->qualname) : UINT32_MAX, func->qualname ? internString(func->qualname) : UINT32_MAX,
func->requiredArgs, func->requiredArgs,
func->keywordArgs, func->keywordArgs,
func->potentialPositionals,
func->upvalueCount, func->upvalueCount,
func->localNameCount, func->localNameCount,
func->chunk.count, func->chunk.count,
@ -278,8 +280,8 @@ static int doSecondPass(FILE * out) {
fwrite(&header, 1, sizeof(struct FunctionHeader), out); fwrite(&header, 1, sizeof(struct FunctionHeader), out);
/* Argument names first */ /* Argument names first */
for (size_t i = 0; i < (size_t)func->requiredArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); ++i) { for (size_t i = 0; i < (size_t)func->potentialPositionals + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); ++i) {
WRITE_STRING(AS_STRING(func->requiredArgNames.values[i])); WRITE_STRING(AS_STRING(func->positionalArgNames.values[i]));
} }
for (size_t i = 0; i < (size_t)func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) { for (size_t i = 0; i < (size_t)func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) {
@ -377,7 +379,7 @@ static int compileFile(char * fileName) {
/* Start with the primary header */ /* Start with the primary header */
struct MarshalHeader header = { struct MarshalHeader header = {
{'K','R','K','B'}, {'K','R','K','B'},
{'1','0','1','1'}, {'1','0','1','2'},
}; };
fwrite(&header, 1, sizeof(header), out); fwrite(&header, 1, sizeof(header), out);
@ -466,7 +468,7 @@ static int readFile(char * fileName) {
if (memcmp(header.magic,(uint8_t[]){'K','R','K','B'},4) != 0) if (memcmp(header.magic,(uint8_t[]){'K','R','K','B'},4) != 0)
return fprintf(stderr, "Invalid header.\n"), 1; return fprintf(stderr, "Invalid header.\n"), 1;
if (memcmp(header.version,(uint8_t[]){'1','0','1','1'},4) != 0) if (memcmp(header.version,(uint8_t[]){'1','0','1','2'},4) != 0)
return fprintf(stderr, "Bytecode is for a different version.\n"), 2; return fprintf(stderr, "Bytecode is for a different version.\n"), 2;
/* Read string table */ /* Read string table */
@ -544,14 +546,14 @@ static int readFile(char * fileName) {
self->keywordArgs = function.kwArgs; self->keywordArgs = function.kwArgs;
self->obj.flags = function.flags; self->obj.flags = function.flags;
self->upvalueCount = function.upvalues; self->upvalueCount = function.upvalues;
self->potentialPositionals = function.posArgs;
self->potentialPositionals = self->requiredArgs + self->keywordArgs;
self->totalArguments = self->potentialPositionals + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); self->totalArguments = self->potentialPositionals + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
/* Read argument names */ /* Read argument names */
DEBUGOUT(" [Required Arguments]\n"); DEBUGOUT(" [Positional Arguments]\n");
for (size_t i = 0; i < (size_t)function.reqArgs + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); i++) { for (size_t i = 0; i < (size_t)function.posArgs + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); i++) {
krk_writeValueArray(&self->requiredArgNames, valueFromConstant(i,inFile)); krk_writeValueArray(&self->positionalArgNames, valueFromConstant(i,inFile));
} }
DEBUGOUT(" [Keyword Arguments]\n"); DEBUGOUT(" [Keyword Arguments]\n");