feat: add parameter list parsing and up to 8 function call primitives
Extract shared parameter list parsing from namedSignature into a new parameterList function called by function, and replace single fn_call primitive with fn_call0 through fn_call8 primitives to support up to 8 arguments. Add comprehensive fn_parameters.wren test covering 0-8 parameter calls.
This commit is contained in:
+22
-21
@@ -658,6 +658,25 @@ typedef struct
|
||||
|
||||
GrammarRule rules[];
|
||||
|
||||
static void parameterList(Compiler* compiler, char* name, int* length)
|
||||
{
|
||||
// Parse the parameter list, if any.
|
||||
if (match(compiler, TOKEN_LEFT_PAREN))
|
||||
{
|
||||
do
|
||||
{
|
||||
// Define a local variable in the method for the parameter.
|
||||
declareVariable(compiler);
|
||||
|
||||
// Add a space in the name for the parameter.
|
||||
if (name != NULL) name[(*length)++] = ' ';
|
||||
// TODO(bob): Check for length overflow.
|
||||
}
|
||||
while (match(compiler, TOKEN_COMMA));
|
||||
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
static void grouping(Compiler* compiler, int allowAssignment)
|
||||
{
|
||||
expression(compiler, 0);
|
||||
@@ -705,6 +724,8 @@ static void function(Compiler* compiler, int allowAssignment)
|
||||
// that later locals have the correct slot indices.
|
||||
addSymbol(&fnCompiler.locals, "(this)", 6);
|
||||
|
||||
parameterList(&fnCompiler, NULL, NULL);
|
||||
|
||||
if (match(&fnCompiler, TOKEN_LEFT_BRACE))
|
||||
{
|
||||
// Block body.
|
||||
@@ -939,26 +960,6 @@ void infixOp(Compiler* compiler, int allowAssignment)
|
||||
emit(compiler, symbol);
|
||||
}
|
||||
|
||||
// Compiles a method signature for a regular named method.
|
||||
void namedSignature(Compiler* compiler, char* name, int* length)
|
||||
{
|
||||
// Parse the parameter list, if any.
|
||||
if (match(compiler, TOKEN_LEFT_PAREN))
|
||||
{
|
||||
do
|
||||
{
|
||||
// Define a local variable in the method for the parameter.
|
||||
declareVariable(compiler);
|
||||
|
||||
// Add a space in the name for the parameter.
|
||||
name[(*length)++] = ' ';
|
||||
// TODO(bob): Check for length overflow.
|
||||
}
|
||||
while (match(compiler, TOKEN_COMMA));
|
||||
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a method signature for an infix operator.
|
||||
void infixSignature(Compiler* compiler, char* name, int* length)
|
||||
{
|
||||
@@ -1038,7 +1039,7 @@ GrammarRule rules[] =
|
||||
/* TOKEN_THIS */ PREFIX(this_),
|
||||
/* TOKEN_TRUE */ PREFIX(boolean),
|
||||
/* TOKEN_VAR */ UNUSED,
|
||||
/* TOKEN_NAME */ { name, NULL, namedSignature, PREC_NONE, NULL },
|
||||
/* TOKEN_NAME */ { name, NULL, parameterList, PREC_NONE, NULL },
|
||||
/* TOKEN_NUMBER */ PREFIX(number),
|
||||
/* TOKEN_STRING */ PREFIX(string),
|
||||
/* TOKEN_LINE */ UNUSED,
|
||||
|
||||
+63
-6
@@ -54,16 +54,65 @@ DEF_PRIMITIVE(bool_toString)
|
||||
}
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call)
|
||||
// The call instruction leading to this primitive has one argument for the
|
||||
// receiver plus as many arguments as were passed. When we push the block onto
|
||||
// the callstack, we again use as many arguments. That ensures that the result
|
||||
// of evaluating the block goes into the slot that the caller of *this*
|
||||
// primitive is expecting.
|
||||
DEF_PRIMITIVE(fn_call0)
|
||||
{
|
||||
// The call instruction leading to this primitive has one argument. So when
|
||||
// we push the block onto the callstack, we again use one argument. That
|
||||
// ensures that the result of evaluating the block goes into the slot that
|
||||
// the caller of *this* primitive is expecting.
|
||||
callFunction(fiber, AS_FN(args[0]), 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call1)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call2)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 3);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call3)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 4);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call4)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 5);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call5)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 6);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call6)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 7);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call7)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call8)
|
||||
{
|
||||
callFunction(fiber, AS_FN(args[0]), 9);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_eqeq)
|
||||
{
|
||||
if (args[1]->type != OBJ_FN) return newBool(vm, 0);
|
||||
@@ -252,7 +301,15 @@ void loadCore(VM* vm)
|
||||
vm->classClass = AS_CLASS(findGlobal(vm, "Class"));
|
||||
|
||||
vm->fnClass = AS_CLASS(findGlobal(vm, "Function"));
|
||||
PRIMITIVE(vm->fnClass, "call", fn_call);
|
||||
PRIMITIVE(vm->fnClass, "call", fn_call0);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call1);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call2);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call3);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call4);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call5);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call6);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call7);
|
||||
PRIMITIVE(vm->fnClass, "call ", fn_call8);
|
||||
PRIMITIVE(vm->fnClass, "== ", fn_eqeq);
|
||||
PRIMITIVE(vm->fnClass, "!= ", fn_bangeq);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user