private Expr visitAccumulator(
      Span span,
      List<GeneratorClause> gens,
      Op op,
      Expr body,
      List<StaticArg> staticArgs,
      boolean isParen) {
    body = visitGenerators(span, gens, body);
    /**
     * * If the accumulation is a nested reduction like BIG OP [ys <- gg] BIG OT <| f y | y <- ys |>
     * , visitGenerators returns a tuple of ((BIG OT, f), gg) (this should be refactored, though)
     */
    Expr res;
    if (body instanceof FnExpr) {
      Expr opexp = ExprFactory.makeOpExpr(span, op, staticArgs);
      res =
          ExprFactory.make_RewriteFnApp(
              span, BIGOP_NAME, ExprFactory.makeTupleExpr(span, opexp, body));
    } else if (body instanceof TupleExpr) {
      /**
       * * For BIG OP [ys <- gg] BIG OT <| f y | y <- ys |> The nested reduction is replaced with
       * __bigOperator2(BIG OP, BIG OT, gg)
       *
       * <p>This is similar to forOpExpr(OpExpr that) .
       *
       * <p>by Kento
       */
      // a tuple of the inner Accumulator (op, body) and the gg
      TupleExpr tuple = (TupleExpr) body;
      TupleExpr innerAccumTuple = (TupleExpr) tuple.getExprs().get(0);
      Expr opexpI = (Expr) innerAccumTuple.getExprs().get(0);
      Expr innerBody = (Expr) innerAccumTuple.getExprs().get(1);
      Expr opexpO = ExprFactory.makeOpExpr(span, op, staticArgs);
      Expr gg = tuple.getExprs().get(1);

      res =
          ExprFactory.make_RewriteFnApp(
              span, BIGOP2_NAME, ExprFactory.makeTupleExpr(span, opexpO, opexpI, gg, innerBody));
    } else res = bug(body, "Function expressions or tuple expressions are expected.");
    if (isParen) res = ExprFactory.makeInParentheses(res);
    return (Expr) recur(res);
  }
  @Override
  public Node forOpExpr(OpExpr that) {
    FunctionalRef op_result = (FunctionalRef) recur(that.getOp());

    /**
     * * For BIG OP <| BIG OT <| f y | y <- ys |> | ys <- gg |> Is this case, BIG <||> is being
     * removed. The nested reduction is replaced with __bigOperator2(BIG OP, BIG OT, gg)
     *
     * <p>by Kento
     */
    String str = op_result.toString();
    String theListEnclosingOperatorName = "BIG <| BIG |>";
    String someBigOperatorName = "BIG";

    // make sure the body is of application of some big operator
    if ((str.length() >= someBigOperatorName.length()
        && str.substring(0, someBigOperatorName.length()).equals(someBigOperatorName))) {
      // make sure that BIG OP (Accumulator (BIG <||>, gs))
      if (that.getArgs().size() == 1
          && that.getArgs().get(0) instanceof Accumulator
          && ((Accumulator) that.getArgs().get(0))
              .getAccOp()
              .toString()
              .equals(theListEnclosingOperatorName)) {

        Accumulator acc = (Accumulator) that.getArgs().get(0);
        Expr body = visitGenerators(NodeUtil.getSpan(acc), acc.getGens(), acc.getBody());
        /**
         * * If the accumulation is a nested reduction like <| BIG OT <| f y | y <- ys |> | ys <- gg
         * |> , visitGenerators returns a tuple of ((BIG OT, f), gg) (this should be refactored,
         * though) In this case, the nested reduction is replaced with __bigOperator2
         */
        if (body instanceof TupleExpr) {
          // a tuple of the inner Accumulator (op, body) and the gg
          TupleExpr tuple = (TupleExpr) body;
          TupleExpr innerAccumTuple = (TupleExpr) tuple.getExprs().get(0);
          Expr opexpI = (Expr) innerAccumTuple.getExprs().get(0);
          Expr innerBody = (Expr) innerAccumTuple.getExprs().get(1);
          FunctionalRef ref = (FunctionalRef) op_result;
          IdOrOp name = ref.getNames().get(0);
          // make sure the operator is actually an operator
          if (!(name instanceof Op)) return null;
          Expr opexpO =
              ExprFactory.makeOpExpr(NodeUtil.getSpan(that), (Op) name, ref.getStaticArgs());
          Expr gg = tuple.getExprs().get(1);
          Expr res =
              ExprFactory.make_RewriteFnApp(
                  NodeUtil.getSpan(that),
                  BIGOP2_NAME,
                  ExprFactory.makeTupleExpr(NodeUtil.getSpan(body), opexpO, opexpI, gg, innerBody));
          return (Expr) recur(res);
        }
      }
    }

    List<Expr> args_result = recurOnListOfExpr(that.getArgs());

    OpExpr new_op;
    if (op_result == that.getOp() && args_result == that.getArgs()) {
      new_op = that;
    } else {
      new_op =
          ExprFactory.makeOpExpr(
              NodeUtil.getSpan(that),
              NodeUtil.isParenthesized(that),
              NodeUtil.getExprType(that),
              op_result,
              args_result);
    }
    return cleanupOpExpr(new_op);
  }