Variable Arguments

We’ve defined some of our builtin functions so they can take in a variable number of arguments. Functions like + and join can take any number of arguments, and operate on them logically. We should find a way to let user defined functions work on multiple arguments also.

Unfortunately there isn’t an elegant way for us to allow for this, without adding in some special syntax. So we’re going to hard-code some system into our language using a special symbol &.

We are going to let users define formal arguments that look like {x & xs}, which means that a function will take in a single argument x, followed by zero or more other arguments, joined together into a list called xs. This is a bit like the ellipsis we used to declare variable arguments in C.

When assigning our formal arguments we’re going to look for a & symbol and if it exists take the next formal argument and assign it any remaining supplied arguments we’ve been passed. It’s important we convert this argument list to a Q-Expression. We need to also remember to check that & is followed by a real symbol, and if it isn’t we should throw an error.

Just after the first symbol is popped from the formals in the while loop of lval_call we can add this special case.

  1. /* Special Case to deal with '&' */
  2. if (strcmp(sym->sym, "&") == 0) {
  3. /* Ensure '&' is followed by another symbol */
  4. if (f->formals->count != 1) {
  5. lval_del(a);
  6. return lval_err("Function format invalid. "
  7. "Symbol '&' not followed by single symbol.");
  8. }
  9. /* Next formal should be bound to remaining arguments */
  10. lval* nsym = lval_pop(f->formals, 0);
  11. lenv_put(f->env, nsym, builtin_list(e, a));
  12. lval_del(sym); lval_del(nsym);
  13. break;
  14. }

Suppose when calling the function the user doesn’t supply any variable arguments, but only the first named ones. In this case we need to set the symbol following & to the empty list. Just after we delete the argument list, and before we check to see if all the formals have been evaluated, add in this special case.

  1. /* If '&' remains in formal list bind to empty list */
  2. if (f->formals->count > 0 &&
  3. strcmp(f->formals->cell[0]->sym, "&") == 0) {
  4. /* Check to ensure that & is not passed invalidly. */
  5. if (f->formals->count != 2) {
  6. return lval_err("Function format invalid. "
  7. "Symbol '&' not followed by single symbol.");
  8. }
  9. /* Pop and delete '&' symbol */
  10. lval_del(lval_pop(f->formals, 0));
  11. /* Pop next symbol and create empty list */
  12. lval* sym = lval_pop(f->formals, 0);
  13. lval* val = lval_qexpr();
  14. /* Bind to environment and delete */
  15. lenv_put(f->env, sym, val);
  16. lval_del(sym); lval_del(val);
  17. }