/**
   * @param body
   * @param prefix
   * @return
   * @throws AbortException
   */
  private ExprNode generateNewExprNode(ExprNode n, String prefix) throws AbortException {
    switch (n.getKind()) {
      case OpApplKind:
        {
          return generateNewOpApplNode((OpApplNode) n, prefix);
        }

      case NumeralKind:
        {
          NumeralNode num = (NumeralNode) n;
          return new NumeralNode(num.toString(), n.getTreeNode());
        }

      case StringKind:
        {
          StringNode str = (StringNode) n;
          return new StringNode(str.getTreeNode(), false);
        }

      case SubstInKind:
        {
          SubstInNode substInNode = (SubstInNode) n;

          Subst[] subs = substInNode.getSubsts();
          for (int i = 0; i < subs.length; i++) {
            OpDeclNode op = subs[i].getOp();
            ExprOrOpArgNode expr = subs[i].getExpr();
            op.setToolObject(substitutionId, expr);
          }
          return generateNewExprNode(substInNode.getBody(), prefix);
        }
      case AtNodeKind:
        { // @
          AtNode old = (AtNode) n;
          OpApplNode oldExcept = old.getExceptRef();
          OpApplNode newExcept = (OpApplNode) oldExcept.getToolObject(substitutionId);
          OpApplNode oldComponent = old.getExceptComponentRef();
          OpApplNode newComponent = (OpApplNode) oldComponent.getToolObject(substitutionId);
          return new AtNode(newExcept, newComponent);
        }

      case LetInKind:
        {
          LetInNode oldLetNode = (LetInNode) n;
          OpDefNode[] newLets = new OpDefNode[oldLetNode.getLets().length];
          Context cc = oldLetNode.context;
          for (int i = 0; i < oldLetNode.getLets().length; i++) {
            OpDefNode let = oldLetNode.getLets()[i];
            UniqueString newName = UniqueString.uniqueStringOf(prefix + let.getName().toString());
            OpDefNode newLet =
                new OpDefNode(
                    newName,
                    let.getKind(),
                    generateNewParams(let.getParams()),
                    let.isLocal(),
                    generateNewExprNode(let.getBody(), prefix),
                    let.getOriginallyDefinedInModuleNode(),
                    null,
                    let.getTreeNode(),
                    true,
                    let.getSource());
            let.setToolObject(substitutionId, newLet);
            newLets[i] = newLet;
            cc.addSymbolToContext(newName, newLet);
          }

          LetInNode newLetNode =
              new LetInNode(
                  oldLetNode.getTreeNode(),
                  newLets,
                  null,
                  generateNewExprNode(oldLetNode.getBody(), prefix),
                  cc);
          return newLetNode;
        }
    }
    throw new RuntimeException();
  }