From 090cc62cce7bc1e76e490c1471c245648b19f143 Mon Sep 17 00:00:00 2001 From: CPunch Date: Sat, 26 Dec 2020 22:01:22 -0600 Subject: [PATCH] Added variadic functions TOKEN_DOT_DOT_DOT was added to the lexer variadic.cosmo was added to the examples directory --- examples/variadic.cosmo | 15 +++++++++++++++ src/clex.c | 2 +- src/clex.h | 1 + src/cobj.c | 1 + src/cobj.h | 1 + src/cparse.c | 15 +++++++++++++-- src/cvm.c | 34 ++++++++++++++++++++++++++-------- 7 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 examples/variadic.cosmo diff --git a/examples/variadic.cosmo b/examples/variadic.cosmo new file mode 100644 index 0000000..cab52a6 --- /dev/null +++ b/examples/variadic.cosmo @@ -0,0 +1,15 @@ +// adds all args passed (expects numbers) +function add(start, ...args) + // starting at `start`, add up all numbers passed + local total = start + for val in args do + total = total + val + end + + return total +end + +print("add(100) -> " .. add(100)) +print("add(100, 1, 2, 3, 4) -> " .. add(100, 1, 2, 3, 4)) +print("add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) -> " .. add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) +print("add(-54, 2, 3, 4, 5, 6, 7, 8, 9, 10) -> " .. add(-54, 2, 3, 4, 5, 6, 7, 8, 9, 10)) \ No newline at end of file diff --git a/src/clex.c b/src/clex.c index 1a0f018..fdaaa33 100644 --- a/src/clex.c +++ b/src/clex.c @@ -316,7 +316,7 @@ CToken cosmoL_scanToken(CLexState *state) { case '-': return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS); case '.': - return match(state, '.') ? makeToken(state, TOKEN_DOT_DOT) : makeToken(state, TOKEN_DOT); + return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT); case '!': return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG); case '=': diff --git a/src/clex.h b/src/clex.h index 2195edc..7f020a6 100644 --- a/src/clex.h +++ b/src/clex.h @@ -15,6 +15,7 @@ typedef enum { TOKEN_COLON, TOKEN_DOT, TOKEN_DOT_DOT, + TOKEN_DOT_DOT_DOT, TOKEN_MINUS, TOKEN_MINUS_MINUS, TOKEN_PLUS, diff --git a/src/cobj.c b/src/cobj.c index 2527b16..e5cdc36 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -128,6 +128,7 @@ CObjFunction *cosmoO_newFunction(CState *state) { CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); func->args = 0; func->upvals = 0; + func->variadic = false; func->name = NULL; func->module = NULL; diff --git a/src/cobj.h b/src/cobj.h index 19ac243..9e9a38a 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -64,6 +64,7 @@ typedef struct CObjFunction { CChunk chunk; int args; int upvals; + int variadic; CObjString *name; CObjString *module; // name of the "module" } CObjFunction; diff --git a/src/cparse.c b/src/cparse.c index fad0a97..6c3b144 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -713,7 +713,6 @@ static void predecrement(CParseState *pstate, bool canAssign) { increment(pstate, -1); } - ParseRule ruleTable[] = { [TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL}, [TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE}, @@ -725,6 +724,7 @@ ParseRule ruleTable[] = { [TOKEN_COLON] = {NULL, NULL, PREC_NONE}, [TOKEN_DOT] = {NULL, dot, PREC_CALL}, [TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT}, + [TOKEN_DOT_DOT_DOT] = {NULL, NULL, PREC_NONE}, [TOKEN_MINUS] = {unary, binary, PREC_TERM}, [TOKEN_MINUS_MINUS] = {predecrement, NULL, PREC_TERM}, [TOKEN_PLUS] = {NULL, binary, PREC_TERM}, @@ -1051,6 +1051,9 @@ static void function(CParseState *pstate, FunctionType type) { consume(pstate, TOKEN_LEFT_PAREN, "Expected '(' after identifier."); if (!check(pstate, TOKEN_RIGHT_PAREN)) { do { + if (check(pstate, TOKEN_DOT_DOT_DOT)) + break; + // add arg to function compiler.function->args++; if (compiler.function->args > UINT16_MAX - 1) { // -1 since the function would already be on the stack @@ -1058,11 +1061,19 @@ static void function(CParseState *pstate, FunctionType type) { } // parse identifier for param (force them to be a local) - uint16_t funcIdent = parseVariable(pstate, "Expected identifier for function!", true); + uint16_t funcIdent = parseVariable(pstate, "Expected identifier for parameter!", true); defineVariable(pstate, funcIdent, true); valuePushed(pstate, 1); // they *will* be populated during runtime } while (match(pstate, TOKEN_COMMA)); } + + if (match(pstate, TOKEN_DOT_DOT_DOT)) { // marks a function as variadic, now we expect an identifer for the populated variadic dictionary + uint16_t vari = parseVariable(pstate, "Expected identifier for variadic dictionary!", true); + defineVariable(pstate, vari, true); + valuePushed(pstate, 1); + compiler.function->variadic = true; + } + consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after parameters."); // compile function block diff --git a/src/cvm.c b/src/cvm.c index be5ac48..ba70e29 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -141,7 +141,7 @@ static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args, nres = nresults; // remember where the return values are - CValue* results = cosmoV_getTop(state, nres-1); + StkPtr results = cosmoV_getTop(state, nres-1); state->top = savedBase + offset; // set stack @@ -155,14 +155,32 @@ static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args, } bool call(CState *state, CObjClosure *closure, int args, int nresults, int offset) { - // missmatched args, thats an obvious user error, so error. - if (args != closure->function->args) { - cosmoV_error(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args); + CObjFunction *func = closure->function; + + // if the function is variadic and theres more args than parameters, push the args into a dictionary + if (func->variadic && args >= func->args) { + int extraArgs = args - func->args; + StkPtr variStart = cosmoV_getTop(state, extraArgs-1); + + // push key & value pairs + for (int i = 0; i < extraArgs; i++) { + cosmoV_pushNumber(state, i); + cosmoV_pushValue(state, *(variStart + i)); + } + + cosmoV_makeDictionary(state, extraArgs); + *variStart = *cosmoV_getTop(state, 0); // move dict on the stack to the vari local + state->top -= extraArgs; + + pushCallFrame(state, closure, func->args + 1); + } else if (args < func->args) { // too few args passed, obvious user error + cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args); return false; + } else { + // load function into callframe + pushCallFrame(state, closure, func->args); } - // load function into callframe - pushCallFrame(state, closure, closure->function->args); // execute int nres = cosmoV_execute(state); @@ -173,12 +191,12 @@ bool call(CState *state, CObjClosure *closure, int args, int nresults, int offse nres = nresults; // remember where the return values are - CValue* results = cosmoV_getTop(state, nres-1); + StkPtr results = cosmoV_getTop(state, nres-1); // pop the callframe and return results :) popCallFrame(state, offset); - // push the return value back onto the stack + // push the return values back onto the stack memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack state->top += nres; // and make sure to move state->top to match