function functionparams(context, options) {
    var next;
    var paramsIds = [];
    var ident;
    var tokens = [];
    var t;
    var pastDefault = false;
    var pastRest = false;
    var arity = 0;
    var loneArg = options && options.loneArg;
    var hasDestructuring = false;

    if (loneArg && loneArg.identifier === true) {
      state.funct["(scope)"].addParam(loneArg.value, loneArg);
      return { arity: 1, params: [ loneArg.value ], isSimple: true };
    }

    next = state.tokens.next;

    if (!options || !options.parsedOpening) {
      advance("(");
    }

    if (state.tokens.next.id === ")") {
      advance(")");
      return;
    }

    function addParam(addParamArgs) {
      state.funct["(scope)"].addParam.apply(state.funct["(scope)"], addParamArgs);
    }

    for (;;) {
      arity++;
      // are added to the param scope
      var currentParams = [];

      pastRest = spreadrest("rest");

      if (_.includes(["{", "["], state.tokens.next.id)) {
        hasDestructuring = true;
        tokens = destructuringPattern(context);
        for (t in tokens) {
          t = tokens[t];
          if (t.id) {
            paramsIds.push(t.id);
            currentParams.push([t.id, t.token]);
          }
        }
      } else {
        ident = identifier(context);

        if (ident) {
          paramsIds.push(ident);
          currentParams.push([ident, state.tokens.curr]);
        } else {
          // Skip invalid parameter.
          while (!checkPunctuators(state.tokens.next, [",", ")"])) advance();
        }
      }

      // It is valid to have a regular argument after a default argument
      // since undefined can be used for missing parameters. Still warn as it is
      // a possible code smell.
      if (pastDefault) {
        if (state.tokens.next.id !== "=") {
          error("W138", state.tokens.curr);
        }
      }
      if (state.tokens.next.id === "=") {
        if (!state.inES6()) {
          warning("W119", state.tokens.next, "default parameters", "6");
        }

        if (pastRest) {
          error("E062", state.tokens.next);
        }

        advance("=");
        pastDefault = true;
        expression(context, 10);
      }

      // now we have evaluated the default expression, add the variable to the param scope
      currentParams.forEach(addParam);
      if (state.tokens.next.id === ",") {
        if (pastRest) {
          warning("W131", state.tokens.next);
        }
        advance(",");
        checkComma({ allowTrailing: true });
      }

      if (state.tokens.next.id === ")") {
        if (state.tokens.curr.id === "," && !state.inES8()) {
          warning("W119", state.tokens.curr, "Trailing comma in function parameters", "8");
        }

        advance(")", next);
        return {
          arity: arity,
          params: paramsIds,
          isSimple: !hasDestructuring && !pastRest && !pastDefault
        };
      }
    }
  }
@param {Number} context The parsing context
@param {Object} [options]
@param {token} [options.loneArg] The argument to the function in cases
                                 where it was defined using the
                                 single-argument shorthand.
@param {bool} [options.parsedOpening] Whether the opening parenthesis has
                                      already been parsed.

@returns {{ arity: number, params: Array.<string>, isSimple: boolean }}