/**
   * Expects that the identifier of the macro expansion has been consumed. Expands the macro
   * consuming tokens from the input (to read the parameters) and stores the resulting tokens
   * together with boundary markers in the result token list. Returns the last token of the
   * expansion.
   *
   * @throws AbortMacroExpansionException
   */
  private Token expandOne(
      Token lastConsumed,
      PreprocessorMacro macro,
      IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden,
      TokenSource input,
      TokenList result,
      MacroExpansionTracker tracker)
      throws OffsetLimitReachedException {
    if (macro.isFunctionStyle()) {
      final int paramCount = macro.getParameterPlaceholderList().length;
      final TokenSource[] argInputs = new TokenSource[paramCount];
      final BitSet paramUsage = getParamUsage(macro);
      if (tracker != null) {
        tracker.startFunctionStyleMacro((Token) lastConsumed.clone());
      }
      try {
        lastConsumed =
            parseArguments(input, (FunctionStyleMacro) macro, forbidden, argInputs, tracker);
      } catch (AbortMacroExpansionException e) {
        // ignore this macro expansion
        for (int i = 0; i < argInputs.length; i++) {
          TokenSource argInput = argInputs[i];
          executeScopeMarkers(argInput, forbidden);
          if (tracker != null) {
            tracker.setExpandedMacroArgument(null);
          }
        }
        if (tracker != null) {
          if (tracker.isRequestedStep()) {
            tracker.storeFunctionStyleMacroReplacement(macro, new TokenList(), result);
          } else if (tracker.isDone()) {
            tracker.appendFunctionStyleMacro(result);
          }
          tracker.endFunctionStyleMacro();
        }
        return null;
      }

      TokenList[] clonedArgs = new TokenList[paramCount];
      TokenList[] expandedArgs = new TokenList[paramCount];
      for (int i = 0; i < paramCount; i++) {
        final TokenSource argInput = argInputs[i];
        final boolean needCopy = paramUsage.get(2 * i);
        final boolean needExpansion = paramUsage.get(2 * i + 1);
        clonedArgs[i] = needCopy ? argInput.cloneTokens() : EMPTY_TOKEN_LIST;
        expandedArgs[i] =
            needExpansion ? expandAll(argInput, forbidden, false, tracker) : EMPTY_TOKEN_LIST;
        if (!needExpansion) {
          executeScopeMarkers(argInput, forbidden);
        }

        if (tracker != null) {
          tracker.setExpandedMacroArgument(needExpansion ? expandedArgs[i] : null);
          // make sure that the trailing arguments do not get
          // expanded.
          if (tracker.isDone()) {
            paramUsage.clear();
          }
        }
      }
      if (tracker == null) {
        replaceArgs(macro, clonedArgs, expandedArgs, result);
      } else {
        if (tracker.isRequestedStep()) {
          TokenList replacement = new TokenList();
          replaceArgs(macro, clonedArgs, expandedArgs, replacement);
          tracker.storeFunctionStyleMacroReplacement(macro, replacement, result);
        } else if (tracker.isDone()) {
          tracker.appendFunctionStyleMacro(result);
        } else {
          replaceArgs(macro, clonedArgs, expandedArgs, result);
        }
        tracker.endFunctionStyleMacro();
      }
    } else {
      if (tracker == null) {
        objStyleTokenPaste(macro, result);
      } else {
        if (tracker.isRequestedStep()) {
          TokenList replacement = new TokenList();
          objStyleTokenPaste(macro, replacement);
          tracker.storeObjectStyleMacroReplacement(macro, lastConsumed, replacement, result);
        } else {
          objStyleTokenPaste(macro, result);
        }
        tracker.endObjectStyleMacro();
      }
    }
    return lastConsumed;
  }