mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10:05 +00:00
Added variadic functions
TOKEN_DOT_DOT_DOT was added to the lexer variadic.cosmo was added to the examples directory
This commit is contained in:
parent
db8ed21746
commit
090cc62cce
15
examples/variadic.cosmo
Normal file
15
examples/variadic.cosmo
Normal file
@ -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))
|
@ -316,7 +316,7 @@ CToken cosmoL_scanToken(CLexState *state) {
|
|||||||
case '-':
|
case '-':
|
||||||
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS);
|
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS);
|
||||||
case '.':
|
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 '!':
|
case '!':
|
||||||
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG);
|
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG);
|
||||||
case '=':
|
case '=':
|
||||||
|
@ -15,6 +15,7 @@ typedef enum {
|
|||||||
TOKEN_COLON,
|
TOKEN_COLON,
|
||||||
TOKEN_DOT,
|
TOKEN_DOT,
|
||||||
TOKEN_DOT_DOT,
|
TOKEN_DOT_DOT,
|
||||||
|
TOKEN_DOT_DOT_DOT,
|
||||||
TOKEN_MINUS,
|
TOKEN_MINUS,
|
||||||
TOKEN_MINUS_MINUS,
|
TOKEN_MINUS_MINUS,
|
||||||
TOKEN_PLUS,
|
TOKEN_PLUS,
|
||||||
|
@ -128,6 +128,7 @@ CObjFunction *cosmoO_newFunction(CState *state) {
|
|||||||
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
||||||
func->args = 0;
|
func->args = 0;
|
||||||
func->upvals = 0;
|
func->upvals = 0;
|
||||||
|
func->variadic = false;
|
||||||
func->name = NULL;
|
func->name = NULL;
|
||||||
func->module = NULL;
|
func->module = NULL;
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ typedef struct CObjFunction {
|
|||||||
CChunk chunk;
|
CChunk chunk;
|
||||||
int args;
|
int args;
|
||||||
int upvals;
|
int upvals;
|
||||||
|
int variadic;
|
||||||
CObjString *name;
|
CObjString *name;
|
||||||
CObjString *module; // name of the "module"
|
CObjString *module; // name of the "module"
|
||||||
} CObjFunction;
|
} CObjFunction;
|
||||||
|
15
src/cparse.c
15
src/cparse.c
@ -713,7 +713,6 @@ static void predecrement(CParseState *pstate, bool canAssign) {
|
|||||||
increment(pstate, -1);
|
increment(pstate, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ParseRule ruleTable[] = {
|
ParseRule ruleTable[] = {
|
||||||
[TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL},
|
[TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL},
|
||||||
[TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
|
[TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
|
||||||
@ -725,6 +724,7 @@ ParseRule ruleTable[] = {
|
|||||||
[TOKEN_COLON] = {NULL, NULL, PREC_NONE},
|
[TOKEN_COLON] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_DOT] = {NULL, dot, PREC_CALL},
|
[TOKEN_DOT] = {NULL, dot, PREC_CALL},
|
||||||
[TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT},
|
[TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT},
|
||||||
|
[TOKEN_DOT_DOT_DOT] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_MINUS] = {unary, binary, PREC_TERM},
|
[TOKEN_MINUS] = {unary, binary, PREC_TERM},
|
||||||
[TOKEN_MINUS_MINUS] = {predecrement, NULL, PREC_TERM},
|
[TOKEN_MINUS_MINUS] = {predecrement, NULL, PREC_TERM},
|
||||||
[TOKEN_PLUS] = {NULL, binary, 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.");
|
consume(pstate, TOKEN_LEFT_PAREN, "Expected '(' after identifier.");
|
||||||
if (!check(pstate, TOKEN_RIGHT_PAREN)) {
|
if (!check(pstate, TOKEN_RIGHT_PAREN)) {
|
||||||
do {
|
do {
|
||||||
|
if (check(pstate, TOKEN_DOT_DOT_DOT))
|
||||||
|
break;
|
||||||
|
|
||||||
// add arg to function
|
// add arg to function
|
||||||
compiler.function->args++;
|
compiler.function->args++;
|
||||||
if (compiler.function->args > UINT16_MAX - 1) { // -1 since the function would already be on the stack
|
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)
|
// 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);
|
defineVariable(pstate, funcIdent, true);
|
||||||
valuePushed(pstate, 1); // they *will* be populated during runtime
|
valuePushed(pstate, 1); // they *will* be populated during runtime
|
||||||
} while (match(pstate, TOKEN_COMMA));
|
} 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.");
|
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after parameters.");
|
||||||
|
|
||||||
// compile function block
|
// compile function block
|
||||||
|
34
src/cvm.c
34
src/cvm.c
@ -141,7 +141,7 @@ static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args,
|
|||||||
nres = nresults;
|
nres = nresults;
|
||||||
|
|
||||||
// remember where the return values are
|
// 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
|
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) {
|
bool call(CState *state, CObjClosure *closure, int args, int nresults, int offset) {
|
||||||
// missmatched args, thats an obvious user error, so error.
|
CObjFunction *func = closure->function;
|
||||||
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);
|
// 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;
|
return false;
|
||||||
|
} else {
|
||||||
|
// load function into callframe
|
||||||
|
pushCallFrame(state, closure, func->args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load function into callframe
|
|
||||||
pushCallFrame(state, closure, closure->function->args);
|
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
int nres = cosmoV_execute(state);
|
int nres = cosmoV_execute(state);
|
||||||
@ -173,12 +191,12 @@ bool call(CState *state, CObjClosure *closure, int args, int nresults, int offse
|
|||||||
nres = nresults;
|
nres = nresults;
|
||||||
|
|
||||||
// remember where the return values are
|
// 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 :)
|
// pop the callframe and return results :)
|
||||||
popCallFrame(state, offset);
|
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
|
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
|
state->top += nres; // and make sure to move state->top to match
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user