/** @param moduleNode */
 public InstanceTransformation(ModuleNode moduleNode) {
   defs = moduleNode.getOpDefs();
   defsHash = new Hashtable<String, OpDefNode>();
   for (int i = 0; i < defs.length; i++) {
     OpDefNode def = defs[i];
     defsHash.put(def.getName().toString(), def);
   }
 }
  public void start() {
    for (int i = 0; i < defs.length; i++) {
      OpDefNode def = defs[i];
      if (def.getSource() != def && !BBuiltInOPs.contains(def.getSource().getName())) {
        // instance
        String defName = def.getName().toString();

        if (def.getBody() instanceof SubstInNode) {
          String prefix = defName.substring(0, defName.lastIndexOf('!') + 1);
          def.setParams(generateNewParams(def.getParams()));
          ExprNode body;
          try {
            body = generateNewExprNode(def.getBody(), prefix);
          } catch (AbortException e) {
            throw new RuntimeException();
          }
          def.setBody(body);
        }
      }
    }
  }
  /**
   * @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();
  }