/*
 * xtc - The eXTensible Compiler
 * Copyright (C) 2007 New York University
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */  
package xtc.typical; 

import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import xtc.tree.Comment;
import xtc.tree.GNode;
import xtc.tree.Visitor;
import xtc.tree.Node;

import xtc.util.SymbolTable;
import xtc.util.Runtime;
import xtc.util.Pair;

/**  
 * Visitor to translate Typical ASTs into Java ASTs.
 * 
 * @author Laune Harris, Anh Le
 * @version $Revision: 1.378 $
 */
public class Transformer extends Visitor {

  /** A Typical attribute. */
  static class Attribute { 

    /** The name. */
    public String name;

    /** The type (as a type expression node). */
    public Node type;
    
    /**
     * Create a new attribute.
     *
     * @param name The name.
     * @param type The type.
     */

    Attribute(String name, Node type) {
      this.name = name;
      this.type = type;
    }

  } 

  // =========================================================================

  /** A Typical equality definition. */
  static class Equality {

    /** The constructor name */
    public String name;

    /** The positions of the arguments to consider for equality. */
    public List<Integer> positions;

    /**
     * Create a new equality definition.
     *
     * @param name The constructor name.
     * @param positions The relevant positions.
     */
    Equality(String name, List<Integer> positions) {
      this.name      = name;
      this.positions = positions;
    }

  } 

  // =========================================================================
  
  /** A primitive operation instance class. */
  static class PrimitiveInstance {
    
    /** The name of the operation. */
    public String name;

    /** The specific types of the instance. */
    public List<String> types;

    /** The name of the instance. */
    public String instanceName;

    /**
     * Create a new primitive instance.
     *
     * @param name The name of the primitive operation
     * @param types The types of the instance
     */
    public PrimitiveInstance(String name, List<String> types, String instanceName) {
      this.name = name;
      this.types = types;
      this.instanceName = instanceName;
    }
  
  }

  // =========================================================================

  /** Representation of a matching clause. */
  static class Match {
    
    /** The argument type. */
    private Object type;
    
    /** The condition on the argument. */
    private Node condition;

    /** 
     * Create a new match. 
     *
     * @param type The argument type. 
     * @param condition The condition.
     */
    public Match(Object type, Node condition) {
      this.type = type;
      this.condition = condition;
    }
    
    public int hashCode() {
      return 0;
      
    }
    
    public boolean equals(Object o) {
      if (this == o) return true;
      if (!(o instanceof Match)) return false;
      
      Match other = (Match)o;
      return (type.equals(other.type) && condition.equals(other.condition));    
    }
    
  }
  
  // =========================================================================
  
  /** Representation of a variable binding in a let expression. */
  static class LetBinding {
  
    /** The name of the variable. */
    public String name;

    public Object typeObject;
  
    /** The type node of the variable. */  
    public Node type;

    /** The value node of the variable. */
    public Node value;

    /**
     * Create a new let binding.
     *
     * @param name The name of the variable.
     * @param type The type of the variable.
     * @param value The value of the variable.
     */  
    public LetBinding(String name, Object typeObject, Node type, Node value){
      this.name = name;
      this.typeObject = typeObject;
      this.type = type;
      this.value = value;
    }  

  }

  // =========================================================================

  /** The type annotation name. */
  protected static final String TYPE = "__type";
  
  /** The property indicating the top level of a pattern. */
  protected static final String TOP = "top";

  /** The property indicating the right hand side of a binding. */
  protected static final String RHS = "rhs";

  /** The property representing the match argument. */
  protected static final String MATCHARG = "match_arg";
  
  /** The property representing the block contains the bindings argument. */
  protected static final String BINDINGS = "bindings";

  /** The annotation name to remember it is currently in analyze function. */
  protected static final String INANALYZE = "inAnalyze";

  /** The annotation name to indicate it is needed to process scope. */
  protected static final String SCOPE = "process_scope";

  /** The annotation name to indicate it is needed to annotate the node. */
  protected static final String ANNOTATE = "annotate_node";

  /** The property indicating the variables from upper levels of binding. */
  protected static final String UPVARS = "up_variables";

  /** The property indicating the variable bindings from an expression. */
  protected static final String LETBINDINGS = "let_bindings";

  /** The property indicating the body of a function definition. */
  protected static final String BODY = "body";

  /** The property indicating a new instance of Let must be created. */
  protected static final String NEWLET = "new_let";

  /** The tree factory. */
  protected final TreeFactory factory;
   
  /** The symbol table for this typical file. */
  protected final SymbolTable table;

  /** The root of the incoming typical abstract syntax tree. */
  protected final Node typical;

  /** The name of the node type. */
  protected final String nodeTypeName;
  
  /** The name of the checker to be output. */
  protected final String output;

  /** The root of the generated java ast for the type checker. */
  protected GNode transformed; 

  /** The root of the types file. */
  protected GNode typesAST;

  /** The root of the support file. */
  protected GNode supportAST; 

  /** The class body of the generated typechecker. */
  protected Node cbody; 

  /** The class body of the types file. */
  protected Node tbody;

  /** The class body of the support file. */
  protected Node sbody;
  
  /** The type enumerations. */
  protected Node tags;

  /** This Transformers runtime. */ 
  protected final Runtime runtime; 

  /** The cached variables. */
  protected final 
    HashMap<Integer, String> typicalVars = new HashMap<Integer,String>();

  /** The Type mapper. */
  protected TypeMapper mapper;

  /** Flag indicating that a scope definition has been seen. */  
  protected boolean seenScope = false;

  /** The set of seen match conditions */
  protected HashMap<Match, String> matches = 
    new HashMap<Match, String>();

  /** The set of seen match conditions */
  protected HashMap<Node, String> nodeMatches = 
    new HashMap<Node, String>();

  /** Empty modifier node. */
  final protected Node  mod = GNode.create("Modifiers");

  /** Final modifier node. */
  final protected Node fmod = toModifiers("final");

  /** Public modifier node. */
  final protected Node pmod = toModifiers("public");

  /** Nullliteral node. */
  final protected Node nullNode = GNode.create("NullLiteral");
 
  /** Node type. */
  final protected Node nodeType = GNode.create("Type",
                            GNode.create("QualifiedIdentifier", "Node"),
                            null);
  /** GNode type. */
  final protected Node gnodeType = GNode.create("Type",
                             GNode.create("QualifiedIdentifier", "GNode"),
                             null);  
  
  /** The list of field that go into the. */
  final protected List<Node> staticFields = new ArrayList<Node>();

  /** The list of function definitions. */
  final protected List<Node> functionDefinitions = new ArrayList<Node>();

  /** A spare variable used in binding to a wildcard. */
  final protected String spareVar;

  /** Flag indicating that a namespace declaration has been seen. */
  protected boolean seenNameSpace = false;  
  
  /** A list to store all node names in scope definition. */
  protected ArrayList<String> processScopeNodes = new ArrayList<String>();
  
  /** List of equal structure. */
  protected ArrayList<Equality> equalities = new ArrayList<Equality>();

  /** The package name. */
  protected String packageName;

  /** The package node. */
  protected Node packageNode;

  /** A list to store all defined attributes. */
  protected List<Attribute> attributeList = new ArrayList<Attribute>();

  /** A list to store all defined equal attributes. */
  protected List<Attribute> eqAttributeList = new ArrayList<Attribute>();

  /** A variable to remember if type is optimized. */
  protected boolean replaceType;

  /** A variable to remember if let is optimized. */
  protected boolean optimizeLet;

  /** A variable to check if List is used. */
  private boolean isListUsed;

  /** A variable to check if ArrayList is used. */
  private boolean isArrayListUsed;  

  /** A variable to check if BigInteger is used. */
  private boolean isBigIntegerUsed;

  /** A variable to check if Pair is used. */
  private boolean isPairUsed;

  /** A variable to check if Node is used. */
  private boolean isNodeUsed;

  /** A variable to check if GNode is used. */
  private boolean isGNodeUsed;

  /** A variable to check if Primitives is used. */
  private boolean isPrimitivesUsed;

  /** A variable to check if Record is used. */
  private boolean isRecordUsed;

  /** A variable to check if Variant is used. */
  private boolean isVariantUsed;

  /** A variable to check if Tuple is used. */
  private boolean isTupleUsed;

  /** A variable to check if Reduction is used. */
  private boolean isReductionUsed;

  /** A variable to check if Name is used. */
  private boolean isNameUsed;

  /** A variable to check if Scope is used. */
  private boolean isScopeUsed;

  /** A variable to check if ScopeKind is used. */
  private boolean isScopeKindUsed;

  /** A variable to check if Analyzer is used. */
  private boolean isAnalyzerUsed;
  
  /** 
   * A list to store all declarations of 
   *   instances of primitive list operations. 
   */
  protected List<Node> primitiveDeclList = new ArrayList<Node>();

  /** A list to store all primitive instances. */
  protected List<PrimitiveInstance> primitiveInsList = 
    new ArrayList<PrimitiveInstance>(); 

  /** A list of types that are nodes. */
  protected Pair<String> nodeTypes = null;

  /**
   * Initialise this transformer.
   *
   * @param ast the root of the typical ast
   * @param s the name of the checker to be generated
   * @param runt the runtime
   */
  public Transformer(GNode ast, SymbolTable st, String s, Runtime runt) {
    factory = new TreeFactory();
    typical = ast; 
    output = s;
    table = st;
    runtime = runt;

    // Set replaceType
    if (runtime.test("optimizeType")) replaceType = true;

    // Set optimizeLet
    if (runtime.test("optimizeLet")) optimizeLet = true;
    else optimizeLet = false;

    spareVar = table.freshJavaId("spareVar");

    // Get the name of the node type
    String tempName = (String)runtime.getValue("optionNodeType"); 
    if (null == tempName) nodeTypeName = "node";
    else nodeTypeName = tempName;

    /* Process the module declaration. */
    dispatch(typical.getGeneric(0));
    cbody = makeClassBody();
    cbody = GNode.ensureVariable((GNode)cbody);
    // Add spare variable declaration
    if (optimizeLet) cbody.add(factory.fieldDecl3(spareVar));

    tbody = GNode.create("ClassBody");
    tbody = GNode.ensureVariable((GNode)tbody);

    sbody = GNode.create("ClassBody");
    sbody = GNode.ensureVariable((GNode)sbody);
  }
  
  /**
   * Process the module.
   *
   * @param n The module node.
   */
  public void visitModule(GNode n) {
    boolean hasAttributes = false;
    
    final int size = n.size();
    for (int i = 0; i < size; i++) {
      Node node = n.getGeneric(i);
      if (node.hasName("AttributeDefinition") ||
          node.hasName("EqualAttributeDefinition")) {
        hasAttributes = true;
        new TypeCollector().dispatch(node);
      } else if (node.hasName("NameSpaceDefinition") ||
                 (node.hasName("TypeDefinition") &&
                    "raw_type".equals(node.getString(1)))) {
        new TypeCollector().dispatch(node);
      }                          
    }

    if (hasAttributes && replaceType) {
      replaceType = false;
    }

    // Get the list of types that are nodes
    @SuppressWarnings("unchecked")
    Object ob = n.getProperty("__node_types");
    Pair<String> nodeTypes = TypeMapper.getAnnotatedStringList(ob);
    
    mapper = new TypeMapper(runtime, output + "Types", replaceType, nodeTypes);
    
    /* Process attribute, equality, and namespaced  definitions first. */
    for (int j = 0; j < size; j++) {
      Node node = n.getGeneric(j);    
      if(node.hasName("AttributeDefinition") ||
         node.hasName("EqualAttributeDefinition") ||
         node.hasName("EqualityDefinition") ||
         node.hasName("NameSpaceDefinition")) {
        dispatch(node);        
      } 
    }

    for (int i = 0; i < size; i++) {
      Node node = n.getGeneric(i);
      
      /* Skip the node, attribute, equality and namespace declarations. */
      if ((node.hasName("TypeDefinition") && 
           nodeTypes.contains(node.getString(1))) ||
          node.hasName("AttributeDefinition") || 
          node.hasName("EqualAttributeDefinition") ||
          node.hasName("EqualityDefinition") ||
          node.hasName("NameSpaceDefinition")) {
        continue;        
      }       
      
      dispatch(node); 
      
      if (node.hasName("TypeDefinition") &&
          "raw_type".equals(node.getString(1))) {
        dispatch(processRawTypeDefinition());           
      } 
    }
    
    /* Add code to process scopes. */
    processScopeSpace();
    cbody.addAll(functionDefinitions);

    sbody.addAll(primitiveDeclList);
    sbody.addAll(staticFields);
    tbody.add(factory.typesConstr(output + "Types"));
    sbody.add(factory.typesConstr(output + "Support"));
    
    // Make skeletons of generated files
    transformed = GNode.cast(makeSkeleton());   
    typesAST = GNode.cast(makeTypesSkeleton());
    supportAST = GNode.cast(makeSupportSkeleton());

  }
  
  /**
   * Process a module declaration.
   *
   * @param n The ModuleDeclaration node.
   */
  public void visitModuleDeclaration(GNode n) {
    Node qid = GNode.create("QualifiedIdentifier");

    StringBuilder buf = new StringBuilder();
    for (int i = 0; i < n.size() - 1; i++) {
      qid.add(n.getString(i));
      buf.append(n.getString(i));
      if (i < n.size() - 2) buf.append('.');
    }
    
    packageName = buf.toString();
    packageNode = GNode.create("PackageDeclaration", null, qid);
  }
  
  /**
   * Process a fun expression.
   * 
   * @param n The fun expression node.
   * @return The java code for the fun expression.
   */
  public Node visitFunExpression(GNode n) { 
    Object t = n.getProperty(TYPE);
    if (null == t) throw new AssertionError("Null type");
    
    if (mapper.isFunctionType(t)) {
      Node params = n.getGeneric(0);
      Node value = n.getGeneric(1);

      if (n.hasProperty(INANALYZE)) {
        params.setProperty(INANALYZE, Boolean.TRUE);
        value.setProperty(INANALYZE, Boolean.TRUE);
      }  

      final String scopename = (String)n.getProperty("enterScope");
      boolean didEnter = false;
      if (n.hasProperty("enterScope")) {     
        if (!table.current().getName().equals(scopename)) {
          enterScope(scopename);
          didEnter = true;
        }
      }
    
      // Get the number of type variables from the function type
      final int typeVarNumber = mapper.processTypeVariables(t, 0); 
      // Check if this function is generic
      final boolean isGeneric = typeVarNumber > 0;
      
      Node returnTypeNode = mapper.getReturnTypeNode(t);      
      List<Node> paramTypeNodes = mapper.getParameterTypeNodes(t);      
      Node functionTypeNode = mapper.toTypeNode(t,false);
      
      value.setProperty(TYPE, returnTypeNode);
      
      //populate the formal parameters for the "apply" method
      Node formalParameters = GNode.create("FormalParameters", params.size());      
      for (int i = 0; i < params.size(); i++) {
        formalParameters.add(GNode.create("FormalParameter", fmod, 
          paramTypeNodes.get(i), null,
          params.getGeneric(i).getString(0), null));
      }
      
      // Type parameters node
      Node typeParas = null;
      if (isGeneric) {
        typeParas = GNode.create("TypeParameters");
        for (int i = 0; i < typeVarNumber; i ++) {
          typeParas.add(GNode.create("TypeParameter", "T" + i, null));
        }
      }

      // Make a new let for the body if necessary
      Object returnType = mapper.getReturnTypeObject(t);
      Node valueExpr = (Node)dispatch(value);      
      valueExpr = checkToLet(valueExpr, returnType);
      

     
      Node classBody = GNode.create("ClassBody", 
        GNode.create("MethodDeclaration", pmod, typeParas,
        returnTypeNode, "apply", formalParameters, null, null,
         GNode.create("Block", factory.ret(valueExpr))));
      
      if (didEnter) exitScope(scopename);
      
      return toNewExpression2(functionTypeNode,null,classBody);
        
    } else throw new AssertionError("Function type is required");
  }

  /**
   * Process a value definition.
   * 
   * @param n The value binding node.
   * @return The java code for the funciton
   */
  public Node visitValueDefinition(GNode n) {

    String name = n.getString(0);
    Node params = n.getGeneric(1);
    Node value = n.getGeneric(2);

    String nsname = SymbolTable.toNameSpace(name, "value");
    Object t = n.getProperty(TYPE);
    
    if (null == t) t = table.current().lookup(nsname);

    if (mapper.isFunctionType(t)) {
      // Get the number of type variables from the function type
      final int typeVarNumber = mapper.processTypeVariables(t, 0); 
      // Check if this function is generic
      final boolean isGeneric = typeVarNumber > 0;

      Node returnTypeNode = mapper.getReturnTypeNode(t);
            
      List<Node> paramTypeNodes = mapper.getParameterTypeNodes(t);

      Node functionTypeNode = mapper.toTypeNode(t, false);
      //desugar function expressions
      if (value.hasName("FunctionExpression")) {
        String arg = table.freshJavaId("arg");        
        value = GNode.create("MatchExpression", 
          GNode.create("LowerID", arg), value.get(0));
        value.setProperty("__arg_type",paramTypeNodes.get(0));
       

        n.set(1, GNode.create("Parameters",
          GNode.create("Parameter", arg , null)));
        params = n.getGeneric(1);        
      } else if (value.hasName("ReduceExpression")) {
        String arg = "lst";
        n.set(1, GNode.create("Parameters",
          GNode.create("Parameter", arg , null)));
        params = n.getGeneric(1);        
      }
      

      // Set property to remember value is in analyze function
      if ("analyze".equals(name)) value.setProperty(INANALYZE, Boolean.TRUE); 
      
      value.setProperty(TYPE, returnTypeNode);
      
      //populate the formal parameters for the "apply" method
      Node formalParameters = GNode.create("FormalParameters", params.size());
      
      for (int i = 0; i < params.size(); i++) {
        formalParameters.add(GNode.create("FormalParameter", fmod, 
          paramTypeNodes.get(i), null,
          params.getGeneric(i).getString(0), null));
      }
       
      //enter the scope and process this bindings value     
      if (!"getNameSpace".equals(name)) enterScope(name);

      // Type parameters node
      Node typeParas = null;
      if (isGeneric) {
        typeParas = GNode.create("TypeParameters");
        for (int i = 0; i < typeVarNumber; i ++) {
          typeParas.add(GNode.create("TypeParameter", "T" + i, null));
        }
      }

      // The body of the function
      Node block;
      if (!optimizeLet) {
        block = GNode.create("Block", factory.ret((Node)dispatch(value))); 
      } else {
        block = GNode.create("Block");
        block = GNode.ensureVariable(GNode.cast(block));
        Node valueExpr = (Node)dispatch(value);

        List<LetBinding> bindList = getBindings(valueExpr);
        if (null != bindList) {
          for (LetBinding bind : bindList) {
            if (!bind.name.equals(spareVar)) {
              if (mapper.hasTypeVariables(bind.typeObject)) {
                block.add(factory.fieldDecl2(bind.type, bind.name, bind.value));
              } else {
                block.add(factory.fieldDecl2(bind.type, bind.name, 
                                             factory.cast(bind.value)));
              }
            } else {
              if (mapper.hasTypeVariables(bind.typeObject)) {
                block.add(factory.assign(toIdentifier(bind.name), bind.value));
              } else {
                block.add(factory.assign(toIdentifier(bind.name), 
                                         factory.cast(bind.value)));
              }
            }
          }
        }
        block.add(factory.castReturn(valueExpr));
      } 
     
      Node classBody = GNode.create("ClassBody", 
        GNode.create("MethodDeclaration", pmod, typeParas,
        returnTypeNode, "apply", formalParameters, null, null, block)); 
      
      if (!"getNameSpace".equals(name)) exitScope(name);
          
      //create and add the fielddeclaration to the typechecker
      if ("getNameSpace".equals(name) || "getScope".equals(name)) {
        return makeVarDec2(name,functionTypeNode,
                          toNewExpression2(functionTypeNode,null,classBody));
      } else {
        // If not a generic function, create a field declaration
        if (!isGeneric){
          functionDefinitions.add(makeVarDec2(name,functionTypeNode,
            toNewExpression2(functionTypeNode,null,classBody)));
        } else { // create a class
          Node classNode = factory.classDecl3(name);
          classNode.set(5, classBody);
          functionDefinitions.add(classNode);
          functionDefinitions.add(
            factory.instanceDecl(GNode.create("Type",
                                   GNode.create("QualifiedIdentifier",name),null),
                                 name,
                                 GNode.create("QualifiedIdentifier",name)));         
        }
      }
    } else {
      //no function needed, just field declaration
      //value.setProperty(NEWLET, Boolean.TRUE);
      Node valueExpr = (Node)dispatch(value);
      valueExpr = checkToLet(valueExpr, t);
      // fix cast here
      if (mapper.hasTypeVariables(t)) {
        functionDefinitions.add(makeVarDec2(name, mapper.toTypeNode(t, false),
                                            valueExpr));
      } else {      
        functionDefinitions.add(makeVarDec2(name, mapper.toTypeNode(t, false),
          factory.cast(valueExpr)));
      }     
    }

    // Note: in general, we modify the AST in place and are done.  For
    // getNameSpace() and getScope(), however, we need to do further
    // processing; so they are returned above.
    
    return null;
  }

  /**
   * Process a function expression
   *
   * @param n The function expression node
   * @return The translated node
   */
  public Node visitFunctionExpression(GNode n) {
    // Get the function type
    final Object t = n.getProperty(TYPE);
    final Node returnTypeNode = mapper.getReturnTypeNode(t);
         
    final List<Node> paramTypeNodes = mapper.getParameterTypeNodes(t);

    // Get the return type from the pattern match type
    final Object retType =
      mapper.getPatternMatchRightType(n.getGeneric(0).getProperty(TYPE));

    // Create a Typical match expression node
    Node value = GNode.create("MatchExpression", 
                   GNode.create("LowerID", "var"), n.getGeneric(0));
    value.setProperty("__arg_type", paramTypeNodes.get(0));
    value.setProperty(TYPE, retType);
    return factory.functionExpression(
      returnTypeNode, paramTypeNodes.get(0), (Node)dispatch(value));      
  }
  
  /**  
   * Process a type definition.
   *
   * @param n The type definition node.
   */
  public void visitTypeDefinition(GNode n) {
    n.getGeneric(2).setProperty("name", n.getString(1));
    dispatch(n.getGeneric(2));
  }
  
  /**
   * Process a string literal.
   *
   * @param n The string literal node.
   * @return n.
   */
  public GNode visitStringLiteral(GNode n) {
    return n;
  }

  /**
   * Process an integer literal.
   * 
   * @param n the integer literal node.
   * @return The BigInteger node.
   */
  public Node visitIntegerLiteral(GNode n) {
    return factory.createInteger(toLiteral("IntegerLiteral", n.getString(0)));
  }

  /**
   * Process a float literal.
   * 
   * @param n the float literal node.
   * @return The 'new Double' node.
   */
  public Node visitFloatingLiteral(GNode n) {
    return
      factory.createFloat(toLiteral("FloatingPointLiteral", n.getString(0)));
  }

  /**
   * Process a bottom node.
   *
   * @param n The bottom node.
   * @return A null literal.
   */
  public Node visitBottom(GNode n) {
    return GNode.create("NullLiteral");
  }

  /**
   * Process a bottom pattern.
   *
   * @param n The bottom pattern node.
   * @return A null literal.
   */
  public Node visitBottomPattern(GNode n) {
    return 
      factory.equalsBottom(toIdentifier((String)n.getProperty(MATCHARG)));
  }
  
  /**
   * Process a boolean literal.
   * 
   * @param n the boolean literal node.
   */
  public Node visitBooleanLiteral(GNode n) {
   return ("true".equals(n.getString(0))) ? toIdentifier("Boolean.TRUE") 
                                          : toIdentifier("Boolean.FALSE");
  }

  /**
   * Process a cons expression.
   *
   * @param n The cons expression node.
   * @return The java equivalent.
   */
  public Node visitConsExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    }
    if (!optimizeLet) { 
      return factory.consWrapper((Node)dispatch(n.getGeneric(0)),
                                 (Node)dispatch(n.getGeneric(1)));
    } else {
      List<String> upVars = getUpVariables(n);
      Node left = n.getGeneric(0);
      Node right = n.getGeneric(1);

      if (null != upVars) left.setProperty(UPVARS, upVars);
      Node leftExpr = (Node)dispatch(left);
      List<LetBinding> bl1 = getBindings(leftExpr);      
      List<String> leftVars = extractVariables(bl1);
      List<String> upVars2 = groupList(upVars,leftVars);

      if (null != upVars2) right.setProperty(UPVARS, upVars2);
      Node rightExpr = (Node)dispatch(right);

      List<LetBinding> bl2 = getBindings(rightExpr);
      List<LetBinding> bl = groupList(bl1,bl2);

      Node ret = factory.consWrapper(leftExpr, rightExpr);
      if (null != bl) ret.setProperty(LETBINDINGS, bl);
      return ret;      
    }      
  }
  
  /**
   * Transform a wildcard.
   *
   * @param n The wildcard node.
   * @return A wildcard primary identifier.
   */
  public Node visitWildCard(GNode n) {
    return toLiteral("BooleanLiteral", "true");
  }
  
  /**
   * Process a field expression.
   *
   * @param n The field expression node.
   * @return A java field access node (with null pointer checks inserted).
   */
  public Node visitFieldExpression(GNode n) {
    Node expr = n.getGeneric(0);

    if (n.hasProperty(INANALYZE)) {
      expr.setProperty(INANALYZE, Boolean.TRUE);
    }   
    if ("TupleConstructor".equals(expr.getName())) {
      String convertedName = Primitives.convertName(n.getString(1)); 	
      if ("Limits".equals(expr.getString(0))) {
    	if (Primitives.hasIntegerType(convertedName)) {
    	  return factory.createInteger(toIdentifier(
    			                         "xtc.Limits." + convertedName));
    	}   
    	return toIdentifier("xtc.Limits." + convertedName);
      }	
      return toIdentifier("Primitives." + convertedName);
    } 

    passVariables(n, expr);
    
    // Check replaceType and the field name
    if (replaceType && "type".equals(n.getString(1))) {
      return (Node)dispatch(expr);
    }
 
    Node exprNode = (Node)dispatch(expr);
    Node ret = factory.fieldExpression(exprNode, n.getString(1));
    passBindings(exprNode, ret);
    return ret;
  }
  
  /**
   * Process a tuple literal.
   *
   * @param n The tuple literal node.
   * @return The java equivalent.
   */
  public Node visitTupleLiteral(GNode n) {
    return makeTuple(n);
  }  

  /**
   * Process a tuple pattern.
   *
   * @param n The tuple pattern node.
   * @return The java equivalent.
   */ 
  public Node visitTuplePattern(GNode n) {
    String matchArg = (String)n.getProperty(MATCHARG);
    
    Node condition = 
      factory.jand(factory.notEqualsBottom(toIdentifier(matchArg)),
        factory.sizeEqual(toIdentifier(matchArg), toLiteral("IntegerLiteral",
          Integer.toString(n.size()))));
   
    for (int i = 0; i < n.size(); i++) {
      Node node = n.getGeneric(i);
      
      if (node.hasName("WildCard")) continue;
      
      if (node.hasName("Variable")) {
        //make the binding
        node.setProperty(RHS, matchArg +  ".get" + (i + 1) + "()");
        node.setProperty(BINDINGS, n.getProperty(BINDINGS));
        dispatch(node);
        continue;
      }
      
      if (isLiteral(node)) {
        condition = factory.jand(condition, 
         factory.jequals2((Node)dispatch(node), 
         toIdentifier(matchArg + ".get" + (i + 1) + "()")));
        continue;
      }
        
      node.setProperty(RHS, matchArg +  ".get" + (i + 1) + "()");  
      node.setProperty(BINDINGS, (n.getProperty(BINDINGS)));
      node.setProperty(MATCHARG, (String)n.getProperty(MATCHARG) + 
                       ".get" + (i + 1) + "()");
      condition = factory.jand(condition, (Node)dispatch(node));      
    } 
    
    if (n.hasProperty(TOP)) {
      String matchName = table.freshJavaId("match");
      condition = replaceMatchArg(condition, matchArg);

      Match ms = new Match(mapper.toTypeNode(n.getProperty(TYPE), false),
                           condition);
      
      if (matches.containsKey(ms)) {
        matchName = matches.get(ms);
      } else {
        matches.put(ms, matchName);
        Object t = n.getProperty(TYPE);
        Node tNode = null;
        if (mapper.hasTypeVariables(t)) {
          tNode = mapper.toTypeNode(t, true); 
        } else {
          tNode = mapper.toTypeNode(t, false);
        }
        Node matchFunction = 
          factory.matchFunction(matchName, tNode, condition);
        
        matchFunction.getGeneric(4).getGeneric(0).set(3, "m");
        staticFields.add(matchFunction);
      }    
      
      return factory.matchCall(toIdentifier(output + "Support"), matchName, 
        toIdentifier((String)n.getProperty(MATCHARG)));
    } else {
      return condition;
    }
  }
  
  /**
   * Process a List literal.
   *
   * @param n The list literal node.
   * @return The java equivalent.
   */
  public Node visitListLiteral(GNode n) {
    return makeList(n);
  }

  /**
   * Make conditions identical with respect to argument name so we can generate
   * unique match functions;
   *
   * @param n The node to process.
   * @param id The name of the id to replace.
   * @return The processed node.
   */
  private Node replaceMatchArg(Node n, String id) {

    for (int i = 0; i < n.size(); i++) {
      Object o = n.get(i);
      if (o instanceof Node) {
        Node node = (Node)o;

        if (node.hasName("PrimaryIdentifier") &&
            node.getString(0).equals(id)) {
          node.set(0, node.getString(0).replace(id,"m"));
        } else if (node.hasName("PrimaryIdentifier") &&
                   node.getString(0).startsWith(id + ".")) {             
          node.set(0, node.getString(0).replace(id + ".", "m."));
        } else {
          replaceMatchArg(node, id);
        }       
      } else if ((o instanceof String) && id.equals(o)) {
        n.set(i, id);
      }      
    }

    return n;
  }

  /**
   * Process a List pattern.
   *
   * @param n The list pattern node.
   * @return The java equivalent.
   */
  public Node visitListPattern(GNode n) {
    checkTypeAnnotation(n);
   
    String matchArg = (String)n.getProperty(MATCHARG);
    
    Node condition = (0 == n.size()) 
      ? factory.isEmptyCall(toIdentifier(matchArg)) 
      : factory.sizeEqual(toIdentifier(matchArg), 
          toLiteral("IntegerLiteral", Integer.toString(n.size())));
    
    for (int i = 0; i < n.size(); i++) {
      Node node = n.getGeneric(i);
      
      if (node.hasName("WildCard")) continue;
      
      if (node.hasName("Variable")) {
        // fix cast here
        Object t = n.getProperty(TYPE);
        if (mapper.hasTypeVariables(t)) {
          ((Node)n.getProperty(BINDINGS)).add(makeVarDec2(node.getString(0), 
          mapper.toTypeNode(mapper.getBase(t), false), 
          toIdentifier(matchArg + ".get(" + i + ")")));
        } else {
          ((Node)n.getProperty(BINDINGS)).add(makeVarDec2(node.getString(0), 
          mapper.toTypeNode(mapper.getBase(t), false), 
          factory.cast(toIdentifier(matchArg + ".get(" + i + ")"))));
        }
        continue;
      }
      
      if (isLiteral(node)) {
        condition = factory.jand(condition, 
         factory.jequals2((Node)dispatch(node), 
                          toIdentifier(matchArg + ".get(" + i + ")")));
        continue;
      }

      node.setProperty(BINDINGS, n.getProperty(BINDINGS));
      node.setProperty(MATCHARG, matchArg + ".get(" + i + ")");
      condition = factory.jand(condition, (Node)dispatch(node));
    } 

    if (n.hasProperty(TOP)) {
      Object t = n.getProperty(TYPE);
      Node listTypeNode = null;
      if (mapper.hasTypeVariables(t)) {
        listTypeNode = mapper.toTypeNode(t, true);
      } else {
        listTypeNode = mapper.toTypeNode(t, false);
      }  

      condition = replaceMatchArg(condition, matchArg);
      
      String matchName = table.freshJavaId("match");
      Match ms = null;
      
      ms = new Match(listTypeNode, condition);
            
      if (matches.containsKey(ms)) {
        matchName = matches.get(ms);        
      } else {
        matches.put(ms, matchName);
        Node matchFunction = 
          factory.matchFunction(matchName, listTypeNode, condition);
     
        matchFunction.getGeneric(4).getGeneric(0).set(3, "m");
        staticFields.add(matchFunction);
      } 
      
      return factory.matchCall(toIdentifier(output + "Support"), matchName, 
        toIdentifier((String)n.getProperty(MATCHARG)));
    } else {
      return condition;
    }
  }

  /**
   * Transform a predicate expression.
   *
   * @param n The predicate expression.
   * @return The java equivalent.
   */
  public Node visitPredicateExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    }
 
    Node arg = n.getGeneric(0).getGeneric(0);
    
    for (int i = 0; i < arg.size(); i++) {
      Node node = arg.getGeneric(i);
      node.setProperty(RHS, RHS);
      node.setProperty(BINDINGS, GNode.create("Block"));
      node.setProperty("enterScope", "scope" + i);
    }

    Node wild = GNode.create("WildCard");
    wild.setProperty("enterScope", "ihavenoscope");
   
    Node last = GNode.create("PatternMatch", 
      GNode.create("Patterns",wild),GNode.create("BooleanLiteral", "false"));
    Node first = GNode.create("PatternMatch", n.getGeneric(0).getGeneric(0), 
                              GNode.create("BooleanLiteral", "true"));
    
    Node pmatching = GNode.create("PatternMatching", first, last);
    
    Object pt = mapper.makePatternMatchType(arg.getProperty(TYPE),
                                            n.getProperty(TYPE));

    first.setProperty(TYPE, pt);
    last.setProperty(TYPE, pt);
    pmatching.setProperty(TYPE, pt);
    first.setProperty("enterScope", "predicatescope");
    last.setProperty("enterScope", "predicatescope");
    

    Node match = GNode.create("MatchExpression", n.getGeneric(1), pmatching);
    match.setProperty("__arg_type", n.getProperty("__arg_type"));
    
    match.setProperty(TYPE, n.getProperty(TYPE));
       
    return 
    factory.jequals2((Node)dispatch(match),toLiteral("BooleanLiteral","true"));
  }
  
  /**
   * Transform a guard expression.
   * 
   * @param n
   */
  public Node visitGuardExpression(GNode n) {
    //we need to collect all the variables and test for nullity
    Set<String> variables = new FreeVariableCollector(n).getIdentifiers();
    
    List<Node> statements = new ArrayList<Node>();
    
    Object t = n.getGeneric(0).getProperty(TYPE);
    
    Node typeNode = mapper.toTypeNode(t, false);

    String name = table.freshJavaId("result");
    
    for (String variable : variables) {
      statements.add(factory.
        ifStatement(factory.isNull(toIdentifier(variable)),
                    factory.ret(toIdentifier("null"))));
      
    }

    Node no = n.getGeneric(0);
    if (n.hasProperty(INANALYZE)) {
      no.setProperty(INANALYZE, Boolean.TRUE);      
    }    
    passVariables(n, no);
    Node expr = (Node)dispatch(no);
    statements.add(factory.fieldDecl2(typeNode, name,expr));
    // fix cast here
    if (mapper.hasTypeVariables(t)) {                                      
      statements.add(factory.ifStatement(factory.isNull(toIdentifier(name)),
              factory.ret((Node)dispatch(n.getGeneric(1)))));
    } else {
      statements.add(factory.ifStatement(factory.isNull(toIdentifier(name)),
              factory.ret(factory.cast((Node)dispatch(n.getGeneric(1))))));
    }        
    statements.add(factory.ret(toIdentifier(name)));

    Node ret = factory.guardExpression(typeNode, statements);
    passBindings(expr, ret); 
    return ret;
  }
  
  /**
   * Transform a reduce expression.
   *
   * @param n The predicate expression.
   * @return The java equivalent.
   */
  public Node visitReduceExpression(GNode n) {

    Node arg = toIdentifier("lst");
    Node runtimeNode = toIdentifier("runtime");

    List<Node> initList = new ArrayList<Node>();
       
    Node options = n.getGeneric(0);
    
    for (int i = 0; i < options.size(); i++) {
      String opt = options.getString(i);

      if ("required".equals(opt)) {
        initList.add(factory.reduceReq());
      } else if ("optional".equals(opt)) {
        initList.add(factory.reduceOpt());
      } else if ("list".equals(opt)) {
        initList.add(factory.reduceList());
      } else if ("set".equals(opt)) {
        initList.add(factory.reduceSet());
      } else if ("dup".equals(opt)) {
        initList.add(factory.reduceDup());
      } else if ("nodup".equals(opt)) {
        initList.add(factory.reduceNodup());
      } else if ("singleton".equals(opt)) {
        initList.add(factory.reduceSing());
      }
    }
    
    //set the tag
    initList.add(factory.reduceTag(n.getGeneric(1)));
                 
    Node patternMatching = n.getGeneric(2);
                 
    for (int i = 0; i < patternMatching.size(); i++) {
      Node patternMatch = patternMatching.getGeneric(i);
      
      ArrayList<Node> addArgs = new ArrayList<Node>();

      ArrayList<Node> block = new ArrayList<Node>();

      addArgs.add((Node)dispatch(patternMatch.getGeneric(1)));
      Node lpatterns = patternMatch.getGeneric(0).getGeneric(0);
      for (int j = 0; j < lpatterns.size(); j++) {
        Node node = lpatterns.getGeneric(j);
        
        if (node.hasName("AsPattern")) {
          String binding = node.getString(1);
          
          node = node.getGeneric(0);
          node.setProperty("ancestor", true);
          node.setProperty("top", true);
          node.setProperty(MATCHARG, table.freshJavaId("arg"));
          
          block.add(factory.reduceGetMatch(binding, (Node)dispatch(node)));
        }

        node.setProperty("ancestor", true);
        node.setProperty("top", true);
        node.setProperty(MATCHARG, table.freshJavaId("arg"));
        addArgs.add((Node)dispatch(node));
      }     
      
      block.add(factory.reduceAddPatterns(addArgs));
      initList.add(factory.block(block));
   } 
    
    return factory.cast(factory.reduceExpression(arg, runtimeNode, initList));
  }

  /**
   * Process a function application. 
   *
   * @param n The function application node.
   * @return The java equivalent of the function application node.
   */
  public Node visitFunctionApplication(GNode n) {
    // Get function arguments
    Node funcArgs = n.getGeneric(n.size() - 1);
    
    Node id = n.getGeneric(0);

    if ("ancestor".equals(id.getString(0)) || 
        "parent".equals(id.getString(0))) {
      
      Node arg = funcArgs.getGeneric(0);
      arg.setProperty("ancestor", true);
      arg.setProperty(MATCHARG, table.freshJavaId("arg"));
      arg = (Node)dispatch(arg);
      
      List<Node> nlist = new ArrayList<Node>(1);
      nlist.add(arg);

      return factory.apply(toIdentifier(id.getString(0)),nlist);
    }
    //special case

    // Annotate if this function application is in analyze function
    if (n.hasProperty(INANALYZE)) {
      for (int i = 0; i < funcArgs.size(); i++) {
        funcArgs.getGeneric(i).setProperty(INANALYZE, Boolean.TRUE);
      }      
    }

    //process arguments
    Node args = GNode.create("Arguments");
    
    List<LetBinding> bl = null;
    List<String> upVars = getUpVariables(n);
        
    for (int i = 0; i < funcArgs.size(); i++) {
      Node arg = funcArgs.getGeneric(i);
      if (null != upVars) arg.setProperty(UPVARS, upVars);
      Node expr = (Node)dispatch(arg);
      List<LetBinding> l = getBindings(expr);
      List<String> vars = extractVariables(l);
      upVars = groupList(upVars, vars);
      bl = groupList(bl,l); 
      args.add(expr);      
    }      

    // Name of this function
    final String funcName;
    // Name is this function in Java's style
    final String newName;
    
    // Type of this function
    final Object funcType;
    
    // Parameter types of this function
    final List<String> paramTypes;
    final List<Node> paramTypeNodes;
   
    // Return type of this function
    final Node retTypeNode;

    Node ret = null;

    // Process function application of the form Name.name ...
    if (3 == n.size()) {
      // All functions here are in Primitives
      // Module name
      String moduleName = n.getGeneric(0).getString(0);

      if ("Prelude".equals(moduleName)) {
        // Functions of this form: Prelude.name
        funcName = n.getGeneric(1).getString(0);
      } else {        
        funcName = moduleName + "." + n.getGeneric(1).getString(0);
      }
    
      // Get type of this function
      funcType = table.current().lookup("value(" + funcName + ")");
      // Get parameter type list
      paramTypes = mapper.getParameterTypes(funcType);
      paramTypeNodes = mapper.getParameterTypeNodes(funcType);
      // Get return type
      retTypeNode = mapper.getReturnTypeNode(funcType);
      
      // Java's style of the function name
      newName = Primitives.convertName(n.getGeneric(1).getString(0)); 
      
      // Process get and put here
      if ("Map".equals(moduleName)) {
        List<Node> args1 = new ArrayList<Node>();
        if ("get".equals(newName)) {
          // This must be a complete function application
          args1.add(args.getGeneric(0));  
          args1.add(toIdentifier("hashTable"));
          ret = factory.castInvocation(toIdentifier("Primitives.get"),
                                        "apply", args1); 
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        } else {
          if (2 == args.size()) {
            // complete application
            args1.add(args.getGeneric(0)); 
            args1.add(args.getGeneric(1));   
            args1.add(toIdentifier("hashTable"));
            ret = factory.castInvocation(toIdentifier("Primitives.put"),
                                          "apply", args1); 
            if (null != bl) ret.setProperty(LETBINDINGS, bl);
            return ret;
          } else {
            // incomplete application
            String tempVar = table.freshJavaId("var");
            ret = factory.curryingPut(toIdentifier(tempVar), args.getGeneric(0));
            if (null != bl) ret.setProperty(LETBINDINGS, bl);
            return ret;
          }
        }
        
      }      
      
      //check if this is a complete or imcomplete function application
      if (args.size() == paramTypes.size()) {
        // This is a complete function application
        // Special case the pair ops are generic
        final String newInstance;
        Node newIns = null;
        
        // Instances with one type parameter
        if ("head".equals(newName) || "tail".equals(newName) ||
            "append".equals(newName) || "union".equals(newName) ||
            "cons".equals(newName) || "nth".equals(newName) ||
            "intersection".equals(newName) || "subtraction".equals(newName) ||
            "flatten".equals(newName)) {
          
          final Object t = 
            mapper.getBase(funcArgs.getGeneric(0).getProperty(TYPE));                    
          final String typeName = mapper.toTypeString(t);
          final Node typeNode = mapper.toTypeNode(t, false);

          // Check for type variables
          if (mapper.hasTypeVariables(t)) {
            if ("head".equals(newName)) {
              ret = factory.newApplyHead(typeNode, makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("tail".equals(newName)) {
              ret = factory.newApplyTail(typeNode, makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret; 
            } else if ("append".equals(newName)) {
              ret = factory.newApplyAppend(typeNode, makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret; 
            } else if ("union".equals(newName)) {
              ret = factory.newApplyUnion(typeNode, makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("cons".equals(newName)) {
              ret = factory.newApplyCons(typeNode, makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("nth".equals(newName)) {
              ret = factory.newApplyNth(typeNode, makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("intersection".equals(newName)) {
              ret = factory.newApplyIntersection(typeNode, 
                                                  makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("subtraction".equals(newName)) {
              ret = factory.newApplySubtraction(typeNode, 
                                                 makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("flatten".equals(newName)) {
              Node ttNode = mapper.toTypeNode(mapper.getBase(t), false);
              ret = factory.newApplyFlatten(ttNode, 
                                             makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            }

          }
          
          List<String> instanceTypes = new ArrayList<String>();
          instanceTypes.add(typeName);

          String instanceName = getInstanceName(newName, instanceTypes);

          if (null == instanceName) {
            newInstance = table.freshJavaId(newName);
            primitiveInsList.add(new PrimitiveInstance(newName, instanceTypes,
                                                       newInstance));
          } else {
            newInstance = instanceName;
          }

          // Create a new instances with one type parameter
          
          if (null == instanceName && "head".equals(newName)) {
            newIns = factory.newHead(typeNode, newInstance);
          } else if (null == instanceName && "tail".equals(newName)) {
            newIns = factory.newTail(typeNode, newInstance);
          } else if (null == instanceName && "append".equals(newName)){
            newIns = factory.newAppend(typeNode, newInstance);
          } else if (null == instanceName && "union".equals(newName)) {
            newIns = factory.newUnion(typeNode, newInstance);
          } else if (null == instanceName && "cons".equals(newName)) {
            newIns = factory.newCons(typeNode, newInstance);
          } else if (null == instanceName && "nth".equals(newName)) {
            newIns = factory.newNth(typeNode, newInstance);
          } else if (null == instanceName && "intersection".equals(newName)) {
            newIns = factory.newIntersection(typeNode, newInstance);
          } else if (null == instanceName && "subtraction".equals(newName)) {
            newIns = factory.newSubtraction(typeNode, newInstance);
          } else if (null == instanceName && "flatten".equals(newName)) {
            Object tt = mapper.getBase(funcArgs.getGeneric(0).getProperty(TYPE));                    
            Node ttNode = mapper.toTypeNode(mapper.getBase(tt), false);
            newIns = factory.newFlatten(ttNode, newInstance);
          }
   
          if (null == instanceName) primitiveDeclList.add(newIns);

          ret = factory.apply(toIdentifier(output + "Support." + newInstance),
                               makeArgumentList(args));
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        }  

        if ("exists".equals(newName)) {
          final Object t = 
            mapper.getBase(funcArgs.getGeneric(1).getProperty(TYPE));
          final String typeName = mapper.toTypeString(t);
          final Node typeNode = mapper.toTypeNode(t, false);
          // check for type variables
          if (mapper.hasTypeVariables(t)) {
            ret = factory.newApplyExists(typeNode, makeArgumentList(args)); 
            if (null != bl) ret.setProperty(LETBINDINGS, bl);
            return ret;
          }
          
          List<String> instanceTypes = new ArrayList<String>();
          instanceTypes.add(typeName);
          
          String instanceName = getInstanceName(newName, instanceTypes);
  
          if (null == instanceName) {
            newInstance = table.freshJavaId(newName);
            primitiveInsList.add(new PrimitiveInstance(newName, instanceTypes,
                                                       newInstance));
            newIns = factory.newExists(typeNode, newInstance);
            primitiveDeclList.add(newIns);
          } else {
            newInstance = instanceName;
          }                    
          ret = factory.apply(toIdentifier(output + "Support." + newInstance),
                               makeArgumentList(args));
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        }
        
        // Instances with two type parameters
        if ("map".equals(newName) || "iter".equals(newName)) {
          final Object t1 = funcArgs.getGeneric(0).getProperty(TYPE);
          final String typeName1 = mapper.getReturnType(t1);
          final Node typeNode1 = mapper.getReturnTypeNode(t1);
          final Object t2 = 
            mapper.getBase(funcArgs.getGeneric(1).getProperty(TYPE));
          final String typeName2 = mapper.toTypeString(t2);
          final Node typeNode2 = mapper.toTypeNode(t2, false);
          
          // Check for type variables
          if (mapper.hasTypeVariables(t1) || mapper.hasTypeVariables(t2)) {
            if ("map".equals(newName)) {
              ret = factory.newApplyMap(typeNode1, typeNode2, 
                                         makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } else if ("iter".equals(newName)) {
              ret = factory.newApplyIter(typeNode1, typeNode2, 
                                         makeArgumentList(args));
              if (null != bl) ret.setProperty(LETBINDINGS, bl);
              return ret;
            } 
          }
          
          List<String> instanceTypes = new ArrayList<String>();
          instanceTypes.add(typeName1);
          instanceTypes.add(typeName2);

          String instanceName = getInstanceName(newName, instanceTypes);
          if (null == instanceName) {
            newInstance = table.freshJavaId(newName);
            primitiveInsList.add(new PrimitiveInstance(newName, instanceTypes,
                                                       newInstance));
          } else {
            newInstance = instanceName;
          }
          
          if (null == instanceName && "map".equals(newName)) {
            newIns = factory.newMap(typeNode1, typeNode2, newInstance);
          } else if (null == instanceName && "iter".equals(newName)) {
            newIns = factory.newIter(typeNode1, typeNode2, newInstance);
          } 
          if (null == instanceName) primitiveDeclList.add(newIns);
          ret = factory.apply(toIdentifier(output + "Support." + newInstance),
                               makeArgumentList(args));
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        }  

        if ("foldl".equals(newName)) {
          final Object t1 = funcArgs.getGeneric(2).getProperty(TYPE);
          final String typeName1 = mapper.toTypeString(t1);
          final Node typeNode1 = mapper.toTypeNode(t1, false);
          final Object t2 = 
            mapper.getBase(funcArgs.getGeneric(1).getProperty(TYPE));
          final String typeName2 = mapper.toTypeString(t2);
          final Node typeNode2 = mapper.toTypeNode(t2, false);
          
          // Check for type variables
          if (mapper.hasTypeVariables(t1) || mapper.hasTypeVariables(t2)) {
            ret = factory.newApplyFoldl(typeNode1, typeNode2,
                                         makeArgumentList(args)); 
            if (null != bl) ret.setProperty(LETBINDINGS, bl);
            return ret;
          }
          List<String> instanceTypes = new ArrayList<String>();
          instanceTypes.add(typeName1);
          instanceTypes.add(typeName2);

          String instanceName = getInstanceName(newName, instanceTypes);
          if (null == instanceName) {
            newInstance = table.freshJavaId(newName);
            primitiveInsList.add(new PrimitiveInstance(newName, instanceTypes,
                                                       newInstance));
            newIns = factory.newFoldl(typeNode1, typeNode2, newInstance);
            primitiveDeclList.add(newIns);
          } else {
            newInstance = instanceName;
          }
          ret = factory.apply(toIdentifier(output + "Support." + newInstance),
                               makeArgumentList(args));
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        }
        // Other primitive functions
        ret = factory.applyPrimitive(newName, makeArgumentList(args));  
        if (null != bl) ret.setProperty(LETBINDINGS, bl);
        return ret;
      } else {
        // This is an incomplete function application, process currying
        ret = makeCurry("Primitives." + newName, args, 
                         paramTypeNodes, retTypeNode, funcType);
        if (null != bl) ret.setProperty(LETBINDINGS, bl);
        return ret;
      }

    } else { // Process function application of this form: name ...  
     
      funcName = n.getGeneric(0).getString(0);
      // Java's style of the function name
      newName = Primitives.convertName(n.getGeneric(0).getString(0));
 
      List<Node> args1 = new ArrayList<Node>();  

      // Process symbol table function applications, they must be complete.
      if ("lookup".equals(newName)||"lookupLocally".equals(newName)) {
        if (1 == funcArgs.size()) {
          // If it has only one child, that must be a node
          args1.add((Node)dispatch(funcArgs.getGeneric(0)));
          args1.add(toIdentifier("getNameSpace"));        
          ret = factory.castInvocation(toIdentifier(newName + "2"),
                                        "apply", args1);
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
  
        } else if(2 == funcArgs.size()) {
          Node arg = (Node)dispatch(funcArgs.getGeneric(0));
                  
          if ("ErrorClause".equals(funcArgs.getGeneric(1).getName())) {
            // If the second child is a error clause.
            String tag = funcArgs.getGeneric(1).getGeneric(0).getString(0);
            args1.add(arg);
            args1.add(toLiteral("StringLiteral",  "\"" + tag + "\""));
            args1.add((Node)dispatch(funcArgs.getGeneric(1).getGeneric(1)));
            args1.add(toIdentifier("getNameSpace"));
            ret = factory.castInvocation(toIdentifier(newName + "4"),
                                      "apply", args1);
            if (null != bl) ret.setProperty(LETBINDINGS, bl);
            return ret;
          } else { 
            // The second child is a tag.
            args1.add(arg);
            args1.add(toIdentifier("getNameSpace"));
            ret = factory.castInvocation(toIdentifier(newName + "2"),
                                      "apply", args1);
            if (null != bl) ret.setProperty(LETBINDINGS, bl);
            return ret;
          }
        } else {
          // The arguments include: node, tag and error clause in that order.
          String tag = funcArgs.getGeneric(2).getGeneric(0).getString(0);
          
          args1.add((Node)dispatch(funcArgs.getGeneric(0)));
          args1.add(toLiteral("StringLiteral",  "\"" + tag + "\""));
          args1.add((Node)dispatch(funcArgs.getGeneric(2).getGeneric(1)));
          args1.add(toIdentifier("getNameSpace"));
          ret = factory.castInvocation(toIdentifier(newName + "4"),
                                        "apply", args1);        
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        } 
      }  
        // Process define.
      if ("define".equals(newName)) {
        if (2 == funcArgs.size()) {
          //  It is has 2 children, they must be a node and a type.
          args1.add((Node)dispatch(funcArgs.getGeneric(0)));
          args1.add((Node)dispatch(funcArgs.getGeneric(1)));
          args1.add(toIdentifier("getNameSpace"));
          
          ret = factory.apply(toIdentifier("define3"), args1);
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        } else {
          // It has 3 children: node, type and and error clause in that order.
          String tag = funcArgs.getGeneric(2).getGeneric(0).getString(0);
        
          args1.add((Node)dispatch(funcArgs.getGeneric(0)));
          args1.add((Node)dispatch(funcArgs.getGeneric(1)));
          args1.add(toLiteral("StringLiteral",  "\"" + tag + "\""));
          args1.add((Node)dispatch(funcArgs.getGeneric(2).getGeneric(1)));
          args1.add(toIdentifier("getNameSpace"));
          ret = factory.invocation(toIdentifier("define5"),
                                    "apply", args1);                    
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        } 
      }
    
      //process redefine, isDefined, and isDefinedLocally
      if ("isDefined".equals(newName) || "isDefinedLocally".equals(newName) 
          || "redefine".equals(newName)) {
        args.add(toIdentifier("getNameSpace"));
        ret = factory.apply(toIdentifier(newName),makeArgumentList(args));  
        if (null != bl) ret.setProperty(LETBINDINGS, bl);
        return ret;
      }
 
      // Get the type of this function
      funcType = table.current().lookup("value(" + funcName + ")");
      // Get parameter type
      paramTypes = mapper.getParameterTypes(funcType);
      paramTypeNodes = mapper.getParameterTypeNodes(funcType);
      // Get return type
      retTypeNode = mapper.getReturnTypeNode(funcType);

      // check for complete and incomplete function applications
      if (args.size() == paramTypes.size()) {
        // Complete function application
        if (Primitives.isPrimitive(newName)) {
          ret = factory.applyPrimitive(newName, makeArgumentList(args));  
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        } else {
          ret = factory.apply(toIdentifier(newName),makeArgumentList(args));          
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        }
      } else {
        // incomplete function application
        if (Primitives.isPrimitive(newName)) {
          ret = makeCurry("Primitives." + newName, args, 
                            paramTypeNodes, retTypeNode, funcType);  
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        } else {
          ret = makeCurry(newName, args, paramTypeNodes, 
                           retTypeNode, funcType);
          if (null != bl) ret.setProperty(LETBINDINGS, bl);
          return ret;
        }
      }             
    }       
  }    
  
  /**
   * Make a java expresson to represent a tuple.
   *
   * @param n The typical tuple node.
   * @return The java representation.
   */
  private Node makeTuple(GNode n) {    
    GNode args = GNode.create("Arguments");
    List<String> upVars = getUpVariables(n);
    List<LetBinding> bl = null;
 
    for (int i = 0; i < n.size(); i++) {
      Node no = n.getGeneric(i); 
      if (n.hasProperty(INANALYZE)) {
        no.setProperty(INANALYZE, Boolean.TRUE);
      }
      if (null != upVars) no.setProperty(UPVARS,upVars);
      Node expr = (Node)dispatch(no); 
      args.add(expr);
      List<LetBinding> l = getBindings(expr);
      List<String> vars = extractVariables(l);
      upVars = groupList(upVars, vars);
      bl = groupList(bl,l);      
    }  

    final List<Node> members = mapper.getMemberNodes(n.getProperty(TYPE));
    // Make type arguments        
    Node typeArgs = GNode.create("TypeArguments");                
    final Node typeNode;
    for (Node ob : members) typeArgs.add(ob);

    if (!members.isEmpty()) {        
      typeNode = GNode.create("Type",
                 GNode.create("InstantiatedType",
                   GNode.create("TypeInstantiation", "Tuple", null),
                   GNode.create("TypeInstantiation", "T" + members.size(), 
                                typeArgs)),
                 null);
    } else {
      typeNode = GNode.create("Type",
                   GNode.create("QualifiedIdentifier", "Tuple", "T0"),
                   null);  
    }

    Node ret = toNewExpression2(typeNode, args,null);
    if (null != bl) ret.setProperty(LETBINDINGS,bl);       
    return ret;
      
  }
   
  /** 
   * Create java code for a list literal.
   *
   * @param n The list expression or pattern node.
   * @return A Pair representing this list.
   */ 
  private Node makeList(GNode n) {
    List<Node> arguments = new ArrayList<Node>(n.size());

    List<LetBinding> bl = null;
    List<String> upVars = getUpVariables(n);
    
    //process each list element
    for (int i = 0; i < n.size(); i++) {
      Node no = n.getGeneric(i);
      if (n.hasProperty(INANALYZE)) {
        no.setProperty(INANALYZE, Boolean.TRUE);
      }
      if (null != upVars) no.setProperty(UPVARS, upVars);
      Node expr = (Node)dispatch(no);
      arguments.add(expr);
      List<LetBinding> l = getBindings(expr);
      List<String> vars = extractVariables(l);
      upVars = groupList(upVars, vars);
      bl = groupList(bl,l);      
    }
    
    if (arguments.isEmpty()) {
      final String typeName = getType(mapper.getBase(n.getProperty(TYPE)));
      final Node typeNode = mapper.toTypeNode(
                              mapper.getBase(n.getProperty(TYPE)), false);
      if ("Object".equals(typeName)) {
        return factory.invocation(toIdentifier("Pair"), "empty", arguments);
      } else {
        return factory.pairEmpty(typeNode);
      }
    }
    
    //cons up the list
    Node newPair = factory.newPair(
      mapper.toTypeNode(n.getProperty(TYPE), false), arguments.get(0));
       
    for (int i = 1; i < arguments.size(); i++) {
      newPair = factory.appendPair(newPair, 
      toNewExpression2(mapper.toTypeNode(n.getProperty(TYPE), false),
                      GNode.create("Arguments", arguments.get(i)), null));
    }
    if (null != bl) newPair.setProperty(LETBINDINGS, bl);
    return newPair;
  }


  /**
   * Process a cons pattern.
   *
   * @param n The cons pattern node.
   * @return The java equivalent.
   */
  public Node visitConsPattern(GNode n) {
    String name = table.freshJavaId("list");
    String rhs = (String)n.getProperty(RHS);
    
    String matchArg = (String)n.getProperty(MATCHARG); 
      
    // fix cast here
    Object t = n.getProperty(TYPE);
    if (mapper.hasTypeVariables(t)) {
      ((Node)n.getProperty(BINDINGS)).add(makeVarDec2(name, 
        mapper.toTypeNode(t, false),
        toIdentifier(rhs)));
    } else {
      ((Node)n.getProperty(BINDINGS)).add(makeVarDec2(name, 
        mapper.toTypeNode(t, false),
        factory.cast(toIdentifier(rhs))));
    }  

    Node head = n.getGeneric(0);
    head.setProperty(MATCHARG, "Primitives.wrapHead(" + matchArg + ")");
    head.setProperty(RHS, "Primitives.wrapHead(" + name + ")");
    head.setProperty(BINDINGS, n.getProperty(BINDINGS));
    
    Node tail = n.getGeneric(1); 
    tail.setProperty(MATCHARG,"Primitives.wrapTail(" + matchArg + ".tail()");
    tail.setProperty(RHS, "Primitives.wrapTail(" + name + ")");
    tail.setProperty(BINDINGS, n.getProperty(BINDINGS));
       
    
    if ((head.hasName("WildCard") || head.hasName("Variable")) &&
        (tail.hasName("WildCard") || tail.hasName("Variable"))) {
      dispatch(head);
      dispatch(tail);
      return factory.isNotEmptyCall(toIdentifier(matchArg));
    } else if (tail.hasName("WildCard") || tail.hasName("Variable")) {
      head = (Node)dispatch(head);
      dispatch(tail);
      return factory.jequals(head,factory.headWrapper(toIdentifier(matchArg)));
    } else if (head.hasName("WildCard") || head.hasName("Variable")) {
      tail = (Node)dispatch(tail);
      dispatch(head);
      return factory.jequals(tail,factory.tailWrapper(toIdentifier(matchArg)));
    }
        
    return factory.jand(factory.jequals((Node)dispatch(head),
                           factory.headWrapper(toIdentifier(matchArg))), 
                        factory.jequals((Node)dispatch(tail), 
                                factory.tailWrapper(toIdentifier(matchArg))));
  }
      
  /**
   * Process a match expression type.
   * 
   * @param n The match expression node.
   * @return The java equivalent.
   */
  public Node visitMatchExpression(GNode n) {
    
    Object argType =
      mapper.getPatternMatchLeftType(n.getGeneric(1).getProperty(TYPE));
   
    String matchArg = table.freshJavaId("arg");

    n.getGeneric(1).setProperty(TOP, null);
    n.getGeneric(1).setProperty(MATCHARG, matchArg);
    n.getGeneric(1).setProperty(RHS, matchArg); 
    
    List<Node> nodes = new ArrayList<Node>();
    
    final Node argTypeNode = mapper.toTypeNode(argType, false);

    if (nodeType.equals(argTypeNode) ||
        gnodeType.equals(argTypeNode)) {
      nodes.add(makeVarDec2(matchArg, argTypeNode,
                           factory.gnodeCast((Node)dispatch(n.getGeneric(0)))));
      if (n.hasProperty(INANALYZE)) {
        n.getGeneric(1).setProperty(ANNOTATE, Boolean.TRUE);
      } else {
        n.getGeneric(1).setProperty(SCOPE, Boolean.TRUE);
      }
    } else {
      // fix cast here
      if (mapper.hasTypeVariables(argType)) {
        nodes.add(makeVarDec2(matchArg, argTypeNode,
                           (Node)dispatch(n.getGeneric(0))));
      } else {
        nodes.add(makeVarDec2(matchArg, argTypeNode,
                           factory.cast((Node)dispatch(n.getGeneric(0)))));
      }                  
    }
    
    // Checking and return null
    if (!containsBottomMatch(n.getGeneric(1))) {
      nodes.add(factory.ifStatement4(toIdentifier(matchArg)));                   
    }
    
    @SuppressWarnings("unchecked")
    List<Node> matches = (List<Node>)dispatch(n.getGeneric(1));
   
    nodes.addAll(matches);
    nodes.add(factory.ret(nullNode));
        
    Node match = factory.matchExpression(
      mapper.toTypeNode(n.getProperty(TYPE), false), nodes);  
    
    return match; 
  } 
  
  /** 
   * Check if a pattern matching contains an explicit match for bottom.
   *
   * @param n The pattern matching node.
   * @return <code> true </code> if it contains a match for bottom pattern. 
   *   <code> false</code> otherwise.   
   */
  private boolean containsBottomMatch(Node n) {
    for (int i = 0; i < n.size(); i++) {
      Node patterns = n.getGeneric(i).getGeneric(0);      
      for(int j= 0; j < patterns.size(); j++) { 
        Node pat = patterns.getGeneric(j);
        if (pat.hasName("BottomPattern")) return true;
      }
    }
    return false;  
  } 
  
  /**
   * Check if this pattern match is on non-node type constructors.
   * 
   * @param n the pattern match node.
   * @return <code> true </code> if type constructor match. <code> false</code>
   * otherwise.
   */
  private boolean isTypeConstructorMatch(Node n) {
    for (int i = 0; i < n.size(); i++) {
      if (n.getGeneric(i).getGeneric(0).getGeneric(0)
          .hasName("TypeConstructorPattern") &&
          !mapper.isNode(n.getGeneric(i).getGeneric(0).getGeneric(0).
                         getProperty(TYPE))) {
        return true;
      }      
    }
    return false;
  }
  
  /**
   * Process a pattern matching expression.
   *
   * @param n The pattern matching node.
   * @return A list with the unified types of the left and right sides
   *         of the pattern matching.
   */
  public List<Node> visitPatternMatching(GNode n) {

    int size = n.size();
    ArrayList<Node> nodes = new ArrayList<Node>();
    
    boolean tcmatch = isTypeConstructorMatch(n);
    
    for (int i = 0; i < size; i++) {
      Node patterns = n.getGeneric(i).getGeneric(0);
      
      for(int j= 0; j < patterns.size(); j++) {
        
        String foo = (String)n.getGeneric(i).getProperty("enterScope");
        if (null== foo) foo = (String)n.getGeneric(i).getProperty("enterScope");
        
        enterScope(foo);
        
        Node pmatch2 = GNode.create("PatternMatch",
          GNode.create("Patterns",patterns.getGeneric(j)),
                                    n.getGeneric(i).getGeneric(1));
        pmatch2.setProperty(TOP, null);
        pmatch2.setProperty(MATCHARG, n.getProperty(MATCHARG));
        pmatch2.setProperty(RHS, n.getProperty(MATCHARG));
        pmatch2.setProperty(TYPE,n.getProperty(TYPE));

        if (n.hasProperty(ANNOTATE)) { 
          pmatch2.setProperty(ANNOTATE, Boolean.TRUE);
        }  
        if (n.hasProperty(SCOPE)) { 
          pmatch2.setProperty(SCOPE, Boolean.TRUE);
        }  
        
        if (tcmatch) pmatch2.setProperty("TCMatch", null);
        
        nodes.add((Node)dispatch(pmatch2));
        
        exitScope(foo);
      }      
    }
 

    if (runtime.test("optimizeMatch")) {
      
      HashMap<String, ArrayList<Node>> bins =
        new HashMap<String, ArrayList<Node>>();
      
      bins.put("default", new ArrayList<Node>());
      
      
      for (int i = 0; i < nodes.size(); i++) {
        Node node = nodes.get(i);
        if (node.hasProperty("TName")) {
          String name = (String)node.getProperty("TName");
          
          if (bins.containsKey(name)) {
            bins.get(name).add(node);
          } else {
            ArrayList<Node> temp = new ArrayList<Node>();
            temp.add(node);
            bins.put(name, temp);        
          }
        } else {
          bins.get("default").add(node);
        }
      }
      
      ArrayList<Node> nodes2 = new ArrayList<Node>();
      Node switchn = 
        factory.switchStmnt(toIdentifier((String)n.getProperty(MATCHARG)));
      
      switchn = GNode.ensureVariable(GNode.cast(switchn));
      
      String defname = table.freshJavaId("default");
      
      for (String key : bins.keySet()) {
        if ("default".equals(key)) continue;
        switchn.add(makeCase(GNode.create("PrimaryIdentifier", key), 
                             bins.get(key),defname));
      }
      switchn.add(GNode.create("DefaultClause", 
                               GNode.create("BreakStatement", null)));
      
      nodes2.add(factory.switchWrap(
        toIdentifier((String)n.getProperty(MATCHARG)),switchn));
      
      if (bins.get("default").size() > 0) {
        nodes2.addAll(bins.get("default"));
      }
      
      if (tcmatch) {
        return nodes2;
      }
    }
    
    return nodes;
  }
        
  /**
   * Transform a typed pattern.
   *
   * @param n The typed pattern node.
   * @return The java code for the typed pattern.
   */
  public Node visitTypedPattern(GNode n) {
    return (Node)dispatch(n.getGeneric(0));
  }

  /**
   * Generate an as pattern.
   *
   * @param n The as pattern node.
   * @return The java as pattern node.
   */
  public Node visitAsPattern(GNode n) {
    Node pat = n.getGeneric(0);
    String rhs = (String)n.getProperty(RHS);
    String matchArg = (String)n.getProperty(MATCHARG);
    
    if (n.hasProperty(TOP)) pat.setProperty(TOP, null);

    pat.setProperty(MATCHARG, n.getProperty(MATCHARG));
    pat.setProperty(RHS, rhs);
    pat.setProperty(BINDINGS, n.getProperty(BINDINGS));
    pat.setProperty(TYPE,pat.getProperty(TYPE));
     
    // fix cast here
    Object t = pat.getProperty(TYPE);
    if (mapper.hasTypeVariables(t)) {               
      ((Node)n.getProperty(BINDINGS)).add(makeVarDec2(n.getString(1), 
        mapper.toTypeNode(t, false), 
        toIdentifier(matchArg)));
    } else {
      ((Node)n.getProperty(BINDINGS)).add(makeVarDec2(n.getString(1), 
        mapper.toTypeNode(t, false), 
        factory.cast(toIdentifier(matchArg))));
    } 
   
    if (isLiteral(n))
      return factory.jequals((Node)dispatch(pat),toIdentifier(matchArg));
    return (Node)dispatch(pat);
  }  

  /**
   * Generate a when pattern.
   *
   * @param n The when pattern node.
   * @return An if statement represeing the when expression.
   */
  public Node visitWhenPattern(GNode n) {
    Node pat = n.getGeneric(0);
    
    pat.setProperty(TOP, null);
    pat.setProperty(RHS, n.getProperty(RHS));
    pat.setProperty(BINDINGS, n.getProperty(BINDINGS));
    pat.setProperty(TYPE, n.getProperty(TYPE));
    pat.setProperty(MATCHARG, n.getProperty(MATCHARG));
    
    if (n.hasProperty(TOP)) pat.setProperty(TOP, null);
  
    //n.getGeneric(1).setProperty(NEWLET, Boolean.TRUE);
    Node expr = (Node)dispatch(n.getGeneric(1));
    expr = checkToLet(expr, n.getGeneric(1).getProperty(TYPE));
    
    return factory.ifStatement(expr,(Node)dispatch(pat));
  }


  //additional conditions that need to be met after the basic pattern
  //is matched and bound.
  //For example, in the pattern Foo(a, a) -> e, both a's are wildcards.
  //however the expression is only executed if the a's are equal.
  //we therefor translate that pattern match as follows
  // if ( Constructor.make("Foo", wild1, wild2).equals(arg)) {
  //   a = arg.get(1);
  //   if (a.equals(arg.get(2))) { ***conditions***
  //     return e; 
  //   }
  // }
  Node conditions = null;

  /**
   * Process a pattern match.
   *
   * @param n The pattern match node.
   * @return The if statement representing this pattern match.
   */
  public Node visitPatternMatch(GNode n) {
    Node savedConditions = conditions;

    String rhs = (String)n.getProperty(RHS);
    Node pattern = n.getGeneric(0);   
    
    Node expr    = n.getGeneric(1);
    String matchArg = (String)n.getProperty(MATCHARG);

    Node ifStmnt = toIfStatement(null, null);

    if (n.hasProperty(INANALYZE)) expr.setProperty(INANALYZE, Boolean.TRUE);

    pattern.setProperty(TOP, null);
    pattern.setProperty(BINDINGS, ifStmnt.getGeneric(1));
    pattern.setProperty(MATCHARG, matchArg);
    pattern.setProperty(RHS, rhs);
       
    if ("WhenPattern".equals(pattern.getGeneric(0).getName())) {
      Node when = (GNode)dispatch(pattern);
            
      if (pattern.getGeneric(0).getGeneric(0).hasName("WildCard") ||
          pattern.getGeneric(0).getGeneric(0).hasName("Variable")) {
        ifStmnt.set(0, toLiteral("BooleanLiteral", "true"));
      } else {
        ifStmnt.set(0, GNode.create(when.getGeneric(1).getGeneric(0)));
      }
    
      if (optimizeLet) {
        List<String> vars = getPatternVariables(pattern.getGeneric(0));
        if (null != vars && !vars.isEmpty()) expr.setProperty(UPVARS, vars);
      }
     
      Node exprNode = (Node)dispatch(expr);
      
      when.getGeneric(1).set(0, factory.ret(exprNode));
      ifStmnt.getGeneric(1).add(when);
   
      if (n.hasProperty(ANNOTATE) || n.hasProperty(SCOPE)) {        
        return augmentIfStatement(ifStmnt, matchArg, n, exprNode);
      }
      return addToIf(ifStmnt, exprNode);
    }
    
    if (pattern.hasName("WildCard") || pattern.hasName("Variable")) {
      ifStmnt.set(0, toLiteral("BooleanLiteral", "true"));
      dispatch(pattern);
    } else {
      ifStmnt.set(0, dispatch(pattern));
    }
    
    if (optimizeLet) {
      List<String> vars = getPatternVariables(pattern.getGeneric(0));
      if (null != vars && !vars.isEmpty()) expr.setProperty(UPVARS, vars);
    }

    Node res = (Node)dispatch(expr);
    
    // fix cast
    Object t = mapper.getPatternMatchRightType(n.getProperty(TYPE));    
    if(res.hasName("Block")) {
      ifStmnt.getGeneric(1).add(res);
    } else {  
      if(conditions == null) {
        if (mapper.hasTypeVariables(t)) {
          ifStmnt.getGeneric(1).
            add(factory.ret(res));
        } else {
          ifStmnt.getGeneric(1).
            add(factory.ret(factory.cast(res)));
        } 
      } else { 
        Node b = GNode.create("Block");
        Node newIf = toIfStatement(conditions, b);
        if (mapper.hasTypeVariables(t)) {
          b.add(factory.ret(res));
        } else {
          b.add(factory.ret(factory.cast(res)));
        }
        ifStmnt.getGeneric(1).add(newIf);
      }
    }
    conditions = savedConditions;
    
    if (n.hasProperty(ANNOTATE) || n.hasProperty(SCOPE)) {
      return augmentIfStatement(ifStmnt, matchArg, n, res);
    }
    
    if (n.hasProperty("TCMatch")) {
      if (n.getGeneric(0).getGeneric(0).hasName("TypeConstructorPattern")) {
        ifStmnt.setProperty("TName", 
                            n.getGeneric(0).getGeneric(0).getString(0));
      }
      if (n.getGeneric(0).getGeneric(0).hasName("AsPattern") &&
          n.getGeneric(0).getGeneric(0).getGeneric(0).hasName("TypeConstructorPattern")) {
        ifStmnt.setProperty("TName", 
           n.getGeneric(0).getGeneric(0).getGeneric(0).getString(0));
      }
    }

    return addToIf(ifStmnt, res);
  }
   

  /**
   * Process a type constructor pattern.
   *
   * @param n The type constructor node.
   * @return The java equivalent.
   */
  public Node visitTypeConstructorPattern(GNode n) {
 
    final String typeName = n.getString(0);
    final Object type = n.getProperty(TYPE);
    final Node typeNode = mapper.toTypeNode(type, false); 

    String matchArg = (String)n.getProperty(MATCHARG);
    
    
    Node matchArgNode = toIdentifier(matchArg);
    Node condition = null;
    Node inner = (Node)n.getProperty(BINDINGS);
    
    final int size = n.size();
 
    if (nodeType.equals(typeNode) || gnodeType.equals(typeNode)) {
      condition = factory.hasNameCall(toIdentifier(matchArg), 
        toLiteral("StringLiteral", "\"" + typeName + "\""));

      if ((size == 1) || n.getGeneric(1).hasName("WildCard")) {
        //do nothing since the initial condition is sufficient
      } else {
        Node params = n.getGeneric(1);
        List<String> members = mapper.getMembers(type);
        List<Object> memberObjects = mapper.getMemberObjects(type);
        List<Node> memberNodes = mapper.getMemberNodes(type);
        
        boolean hasVar = false;
        for (Object o : memberObjects) {
          if (mapper.isVariable(o)) hasVar = true;
        }
        
        //check the last item to see if it's a list variable
        //need to also check the last item to see if it's a node var
        
        if (members.get(members.size() -1).startsWith("Pair") || hasVar) {
          condition = factory.jand(condition, 
            factory.sizeGreaterEqual(matchArgNode, 
            toLiteral("IntegerLiteral", Integer.toString((params.size()- 1)))));
        } else {
          condition = factory.jand(condition, 
            factory.sizeEqual(matchArgNode, 
            toLiteral("IntegerLiteral", Integer.toString(params.size()))));
        } 
                
        for (int i = 0; i < params.size(); i++) {
          
          String tname = members.get(i);
          Node tNode = memberNodes.get(i);
          
          Node node = params.getGeneric(i);
          if (node.hasName("Variable")) {
            if ("String".equals(tname)) {
              inner.add(factory.makeNodeBinding2(node.getString(0),
                          toIdentifier(matchArg),
                          toLiteral("IntegerLiteral", Integer.toString(i))));
            } else if ( "Node".equals(members.get(i)) || 
                      "GNode".equals(members.get(i))) {
              inner.add(factory.makeNodeBinding(node.getString(0),
                          toIdentifier(matchArg),
                          toLiteral("IntegerLiteral", Integer.toString(i))));
            } else if (tname.startsWith("Pair")) {
              inner.add(makeVarDec2(node.getString(0), tNode,
                factory.cast(toIdentifier("Primitives.getChildren(" + matchArg +
                    ", " + i + ", " + matchArg + ".size())"))));                  
            }
            
            continue;
          } 
          
          if (node.hasName("WildCard")) continue;
          
          if (isLiteral(node)) {
            condition = factory.jand(condition, 
              factory.jequals2((Node)dispatch(node), 
              toIdentifier(matchArg + ".getString(" + i + ")")));
            continue;
          }
          
          node.setProperty(BINDINGS, inner);
          
          if ("String".equals(members.get(i))) {
            node.setProperty(MATCHARG, matchArg + ".getString(" + i + ")");
          } else if ( "Node".equals(members.get(i)) || 
                      "GNode".equals(members.get(i))) {
            node.setProperty(MATCHARG, matchArg + ".getGeneric(" + i + ")");
          } else if (members.get(i).startsWith("Pair")) {
            
            node.setProperty(MATCHARG, "Primitives.getChildren(" + matchArg +
                             ", " + i + ", " + matchArg + ".size())");         
          }
          
          condition = factory.jand(condition,(Node)dispatch(node));
        }
      }
    } else {
      condition = 
        factory.isMethodCall(matchArgNode, "is" + typeName);

      if (n.size() == 1 || n.getGeneric(1).hasName("WildCard")) {
        //do nothing since the initial condition is sufficient
      } else {
        Node params = n.getGeneric(1);
        
        List<Node> memberNodes = mapper.getMemberNodes(type);
                
        for (int i = 0; i < params.size(); i++) {
          Node node = params.getGeneric(i);
          
          if (node.hasName("WildCard")) {
            continue;
          }
          if (node.hasName("Variable")) {
            inner.add(makeVarDec2(node.getString(0), memberNodes.get(i),
                factory.cast(toIdentifier(matchArg + ".getTuple().get" + 
                                          (i + 1) + "()"))));
            continue;
          }

          if (isLiteral(node)) {
            condition = factory.jand(condition, 
              factory.jequals2((Node)dispatch(node), 
               toIdentifier(matchArg + ".getTuple().get" + (i + 1) + "()")));
            continue;
          }

          node.setProperty(MATCHARG, matchArg + ".getTuple().get" + (i + 1) +
                           "()");
          condition = factory.jand(condition,(Node)dispatch(node));
        }
      }    
    }  
    
   
    
    if (n.hasProperty(TOP) || n.hasProperty("ancestor")) {
      
   
      condition = replaceMatchArg(condition, matchArg);
      
      if (n.hasProperty("ancestor")) {
        Node node = factory.ancestorExpression(factory.ret(condition));
        
        if (nodeMatches.containsKey(node)) {
          return factory.support(toIdentifier(output + "Support"),
                                 nodeMatches.get(node));
        } else {
          String arg = table.freshJavaId("nodeMatch");
          staticFields.add(factory.supportNodeMatch(arg, node));
          nodeMatches.put(node, arg);
          return factory.support(toIdentifier(output + "Support"),arg);
        }
      }

      String matchName = table.freshJavaId("match");
      Match ms = new Match(mapper.toTypeNode(n.getProperty(TYPE), false),
                           condition);
      if (matches.containsKey(ms)) {
        matchName = matches.get(ms);
      } else {
        matches.put(ms, matchName);
        Node tNode = null;
        if (mapper.hasTypeVariables(type)) {
          tNode = mapper.toTypeNode(type, true);
        } else {
          tNode = mapper.toTypeNode(type, false);
        }
        Node matchFunction = 
          factory.matchFunction(matchName, tNode , 
                                condition);   
       
        matchFunction.getGeneric(4).getGeneric(0).set(3, "m");
        staticFields.add(matchFunction);
      } 
      
      

      return factory.matchCall(toIdentifier(output + "Support"), matchName, 
        toIdentifier((String)n.getProperty(MATCHARG)));
    } else {
      return condition;
    }
  } 

  /**
   * Process pattern parameters.
   *
   * @param n The pattern parameters node.
   * @return A new tuple expression.
   */
  public Node visitPatternParameters(GNode n) {
    return makeTuple(n);
  }
  
  /**
   * Process a variant type definition.
   *
   * @param n The variant type node.
   */
  public void visitVariantDeclaration(GNode n) {
    String baseName = (String)n.getProperty("name");
    Node baseBody = GNode.create("ClassBody");
   
    Node enums = GNode.create("EnumConstants");
    Node tag = GNode.create("EnumDeclaration", GNode.create("Modifiers", 
      GNode.create("Modifier", "public"), GNode.create("Modifier", "static")), 
      "Tag", null, enums, null);    
    
    baseBody.add(tag);
    baseBody.add(factory.defaultConstr(baseName));
    baseBody.add(factory.getTagAbstract());
    
    for (int i = 0; i < n.size(); i++) {
      Node tc = n.getGeneric(i);
      addEnum(enums, tc.getString(0));
      Object tcType = tc.getProperty(TYPE);
      
      List<String> types = mapper.getMembers(tcType);
      List<Node> typeNodes = mapper.getMemberNodes(tcType);
         
      int size = types.size();
      
      Node fparams = GNode.create("FormalParameters");
      Node classBody = GNode.create("ClassBody");

      //create a constructor
      Node createArgs = GNode.create("Arguments");
      for (int j = 0; j < size; j++) {
        Node fptype = typeNodes.get(j);
        String fpname = "member" + (j + 1);
        
        createArgs.add(toIdentifier(fpname));
        fparams.add(GNode.create("FormalParameter", null, fptype, null,
            fpname, null));
      }

      Node constrblock1  = GNode.create("Block");
      Node constr1 = GNode.create("ConstructorDeclaration", pmod, null,
                     tc.getString(0), fparams, null, constrblock1);

      // Make type arguments        
      Node typeArgs = GNode.create("TypeArguments");                

      final Node typeNode;
      for (Node ob : typeNodes) typeArgs.add(ob);

      if (!typeNodes.isEmpty()) {        
        typeNode = GNode.create("Type",
                   GNode.create("InstantiatedType",
                     GNode.create("TypeInstantiation", "Tuple", null),
                     GNode.create("TypeInstantiation", "T" + typeNodes.size(), 
                                  typeArgs)),
                   null);
      } else {
        typeNode = GNode.create("Type",
                     GNode.create("QualifiedIdentifier", "Tuple", "T0"),
                     null);  
      }        

      constrblock1.add(factory.assign(toIdentifier("tuple"),
         factory.newExpr(typeNode, makeArgumentList(createArgs))));
      
      classBody.add(constr1);
      classBody.add(factory.getTag(tc.getString(0)));
      
      //add an isXXX method to the base class
      Node isBase = factory.isMethod();
      isBase.set(3, "is" + tc.getString(0));
      baseBody.add(isBase);
      
      //add an isXXX method to the variant type class
      Node isVariant = factory.isMethod();
      isVariant.set(3, "is" + tc.getString(0));
      isVariant.getGeneric(7).getGeneric(0).getGeneric(0).set(0, "true");
      classBody.add(isVariant);
      
      // Add getName() method
      classBody.add(factory.getNameMethod(GNode.create("StringLiteral","\"" + 
                                                       tc.getString(0)+ "\"")));
      // ADD equals methods
      for (Equality e : equalities) {
        if (e.name.equals(tc.getString(0))) {
          classBody.add(createVariantEqualsMethod(tc.getString(0)));
          break;
        }
      }
      
      // Add toString()
      String toString = "\"" + tc.getString(0);
      if (size > 0) {
        toString += " of \" + tuple.toString()";
      } else {
        toString += "\"";
      }
      
      Node toStringNode = factory.toStringMethod();
      toStringNode.set(7, GNode.create("Block", 
        factory.ret(toLiteral("StringLiteral", toString))));
      
      classBody.add(toStringNode);

      final Node baseNameNode = GNode.create("Type",
                                  GNode.create("InstantiatedType",
                                    GNode.create("TypeInstantiation", baseName,
                                      GNode.create("TypeArguments", typeNode))),
                                  null);

      tbody.add(comment(makeExtendedClassDecl(tc.getString(0),
                                              baseNameNode, classBody),
                        "Implementation of constructor '"+ tc.getString(0) +
                        "' in variant '"+ baseName + "'."));
    }
    
    final String typeName = baseName + "<T extends Tuple>";
    final Node vNode = GNode.create("Type",
                         GNode.create("InstantiatedType",
                           GNode.create("TypeInstantiation", "Variant",
                             GNode.create("TypeArguments",
                               GNode.create("Type",
                                 GNode.create("QualifiedIdentifier", "T"),
                                 null)))),
                         null);
 
    tbody.add(comment(makeExtendedClassDecl2(typeName, vNode, baseBody),
                      "Superclass of all constructors in variant '" +
                      baseName + "'."));
  } 

  /**
   * Create an extends class declaration.
   *
   * @param n The name of the class.
   * @param b The base name node (the class that is extended).
   * @param classBody The class body.
   */
  private Node makeExtendedClassDecl(String n, Node b, Node classBody) {
    Node foo = factory.extendsDecl();
    foo.set(1, n);
    foo.getGeneric(3).set(0, b);
    foo.set(5, classBody);
    return foo;
  }
  
  /**
   * Create an abstract extends class declaration.
   *
   * @param n The name of the class.
   * @param b The base name node (the class that is extended).
   * @param classBody The class body.
   */
  private Node makeExtendedClassDecl2(String n, Node b, Node classBody) {
    Node foo = factory.extendsDecl2();
    foo.set(1, n);
    foo.getGeneric(3).set(0, b);
    foo.set(5, classBody);
    return foo;
  }

  /**
   * Create an implements class declaration.
   *
   * @param n The name of the class.
   * @param b The base name (the interface that is implemented).
   * @param classBody The class body.
   */
  private Node makeImplementedClassDecl(String n, String b, Node classBody) {
    Node foo = factory.implementsDecl();
    foo.set(1, n);
    foo.getGeneric(4).set(0, toType(b));
    foo.set(5, classBody);
    return foo;
  }
  
  /**
   * Process a record type definition.
   *
   * @param n The type information.
   */
  public void visitRecordDeclaration(GNode n) {
    String name = (String)n.getProperty("name");
    Object t = n.getProperty(TYPE);
    
    List<Node> fieldTypeNodes = mapper.getFieldTypeNodes(t);
    List<String> fieldNames = mapper.getFieldNames(t);
    Node classBody = GNode.create("ClassBody");
    StringBuilder toString = new StringBuilder("\"{\"");
    
    //ADD equals method for type record
    Node equals = null;
    Node eqBlock = GNode.create("Block");
    
    if("type".equals(name)) {
      equals = createTypeRecordEquals();
    } else {
      equals = factory.equalsMethod();
      equals.set(7, eqBlock);
      eqBlock.add(toIfStatement(factory.jnot(toInstanceOf(toIdentifier("o"),
        toType(name))), factory.ret(toLiteral("BooleanLiteral", "true"))));
      eqBlock.add(makeVarDec("r", name, factory.cast(toIdentifier("o"))));      
    }
    
    Node constrblk = GNode.create("Block");
    Node constr  = GNode.create("ConstructorDeclaration", pmod, null, name,
      GNode.create("FormalParameters"), null, constrblk);

    Node fps = constr.getGeneric(3);

    for (int i = 0; i < fieldNames.size(); i++) {
      String fname = fieldNames.get(i);
      Node ftype = fieldTypeNodes.get(i);
      toString.append(" + (null == "+fname+ " ? \"?\" : " +fname
                      + ".toString())");

      fps.add(GNode.create("FormalParameter", null, ftype, 
                           null, fname, null));
      constrblk.add(factory.assign(factory.thisExpr(fname), 
                                     toIdentifier(fname)));
       
      if (i < fieldNames.size() - 1) toString.append(" + \",\" ");
       
      classBody.add(factory.publicFieldDecl(ftype, fname));

      eqBlock.add(toIfStatement(
        factory.jnot(factory.jequals(toIdentifier(fname),
          factory.fieldExpression(toIdentifier("r"),
            fname))),
            factory.ret(toLiteral("BooleanLiteral", "false"))));
    }
    eqBlock.add(factory.ret(toLiteral("BooleanLiteral", "true")));

    toString.append(" + " + "\"}\"");
    
    //create the toString method for this record
    Node toStringNode = factory.toStringMethod();
    toStringNode.getGeneric(7).set(0, 
      factory.ret(toLiteral("StringLiteral",toString.toString())));
      
    classBody.add(constr);
    classBody.add(equals);
    classBody.add(toStringNode);
    
    tbody.add(comment(makeImplementedClassDecl(name, "Record", classBody),
                      "Implementation of record '" + name + "'."));
  }  
  
  /**
   * Create a new named tuple.
   *
   * @param n The tuple constructor node.
   * @return The java equivalent.
   */
  public Node visitTupleConstructor(GNode n) {
    Node args = GNode.create("Arguments");  

    if (n.hasProperty(INANALYZE)) {
      for (int j = 1; j < n.size(); j++) {
        n.getGeneric(j).setProperty(INANALYZE, Boolean.TRUE);
      }
    } 

    List<LetBinding> bl = null;
    List<String> upVars = getUpVariables(n);
    Node ret; 

    if (n.getProperty(TYPE)  != null && 
        gnodeType.equals(mapper.toTypeNode(n.getProperty(TYPE),false)) || 
        nodeType.equals(mapper.toTypeNode(n.getProperty(TYPE), false))) {      
      List<Node> args2 = new ArrayList<Node>(n.size());
      args2.add(toLiteral("StringLiteral", "\"" + n.getString(0) + "\"" ));
                
      for (int i = 1; i < n.size(); i++) {
        Node no = n.getGeneric(i);
        if (null != upVars) no.setProperty(UPVARS, upVars);
        Node expr = (Node)dispatch(no);
        List<LetBinding> l = getBindings(expr);
        List<String> vars = extractVariables(l);
        upVars = groupList(upVars, vars);
        bl = groupList(bl,l); 
        args2.add(expr);
      }
      
      ret = factory.gnodeCreate(args2);
      if (null != bl) ret.setProperty(LETBINDINGS, bl);
      return ret;
    }
    
    for (int i = 1; i < n.size(); i++) {
      Node no = n.getGeneric(i);
      if (null != upVars) no.setProperty(UPVARS, upVars);
      Node expr = (Node)dispatch(no);
      List<LetBinding> l = getBindings(expr);
      List<String> vars = extractVariables(l);
      upVars = groupList(upVars, vars);
      bl = groupList(bl,l); 
      args.add(expr);
    }

    ret = toNewExpression2(mapper.toTypeNode(n.getString(0), false), 
                            args,null);
    if (null != bl) ret.setProperty(LETBINDINGS, bl);
    return ret;
  }
  
  /**
   * Create an instanceof node.
   *
   * @param n1 The object node.
   * @param n2 The type node.
   * @return The instanceof expression
   */
  private Node toInstanceOf(Node n1, Node n2) {
    return GNode.create("InstanceOfExpression", n1, n2);
  }

  /**
   * Process a LowerID node.
   *
   * @return The primary identifier corresponding to this node.
   */
  public Node visitLowerID(GNode n) {
    final String newName = Primitives.convertName(n.getString(0));
    if (Primitives.isPrimitive(newName)) {
      if ("nonce".equals(newName)) {
        return factory.apply2(toIdentifier("Primitives." + newName));
      }
      return toIdentifier("Primitives." + newName);
    }    
    else return toIdentifier(newName);
  }
  
  /**
   * Process a variable.
   *
   * @param n The variable node.
   */
  public void visitVariable(GNode n) {
    String name = n.getString(0);
    //make bindings for this variable if necessary
    Object type = n.getProperty(TYPE);

    final Node typeNode = mapper.toTypeNode(type, false);     
    
    String rhs = (String)n.getProperty(RHS);
    
    if (table.current().isDefinedLocally(
        SymbolTable.toNameSpace(name, "value"))) {
      
      if (nodeType.equals(typeNode) || gnodeType.equals(typeNode)) {
        ((GNode)n.getProperty(BINDINGS)).add(makeVarDec2(name, typeNode,
                             factory.gnodeCast(toIdentifier(rhs))));
      } else {
        // fix cast here
        if (mapper.hasTypeVariables(type)) {
          ((GNode)n.getProperty(BINDINGS)).add(makeVarDec2(name, typeNode,
                               toIdentifier(rhs)));
        } else {
          ((GNode)n.getProperty(BINDINGS)).add(makeVarDec2(name, typeNode,
                               factory.cast(toIdentifier(rhs))));
        }                    
      }        
    } else {
      Node cond = factory.jequals(toIdentifier(rhs),toIdentifier(name));
      if (conditions == null) {
        conditions = cond;
      } else {
        conditions = factory.jand(conditions, cond);
      }
    }
  }
  
  /**
   * Transform a record expression. 
   *
   * @param n The record expression node.
   * @return A java equivalent.
   */
  public Node visitRecordExpression(GNode n) {
    
    boolean bottomWith = (null != n.getGeneric(0)) && 
      n.getGeneric(0).getGeneric(0).hasName("Bottom"); 

    if (n.hasProperty(INANALYZE)) {
      for (int j = 0; j < n.size(); j++) {
        n.getGeneric(j).setProperty(INANALYZE, Boolean.TRUE);
      }
    }  

    // Check the first field assign, field name and replaceType variable
    if (replaceType && "type".equals(n.getGeneric(1).getString(0))) {
      return (Node)dispatch(n.getGeneric(1).getGeneric(1));
    }
    
    Object rt = n.getProperty(TYPE);
       
    List<String> fieldNames = mapper.getFieldNames(rt);
    Node firstNode = n.getGeneric(0);

    Node args = GNode.create("Arguments");
    List<LetBinding> bl = null;
    List<String> upVars = getUpVariables(n);

    for (String s : fieldNames) {
      boolean found = false;
      for (int i = 1; i < n.size(); i++) {
        if (s.equals(n.getGeneric(i).getString(0))) {
          found = true;
          Node no = n.getGeneric(i).getNode(1);
          if (null != upVars) no.setProperty(UPVARS, upVars);
          Node expr = (Node)dispatch(no);
          List<LetBinding> l = getBindings(expr);
          List<String> vars = extractVariables(l);
          upVars = groupList(upVars, vars);
          bl = groupList(bl,l); 
          args.add(expr);
        }
      }
      if (!found && ("WithExpression".equals(firstNode.getName()))) {
        if (!bottomWith) {
          // expression with
          //firstNode.getGeneric(0).setProperty(NEWLET, Boolean.TRUE);
          Node letNode = (Node)dispatch(firstNode.getGeneric(0));
          letNode = checkToLet(letNode, firstNode.getGeneric(0).getProperty(TYPE)); 
          args.add(factory.fieldExpression(letNode, s)); 
        } else {
          args.add(GNode.create("NullLiteral"));
        }
      } else if (!found) {
        assert found : "cannot determine field value";
      }
    }
    Node ret = toNewExpression2(mapper.toTypeNode(rt,false), args, null);
    if (null != bl) ret.setProperty(LETBINDINGS, bl);
    return ret;
  }
  
  /**
   * Transform a field assignment.
   *
   * @param n The field assignment node.
   * @return The java assignment expression.
   */
  public Node visitFieldAssign(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    } 
    if (!optimizeLet) { 
      return factory.assignField(toIdentifier(n.getString(0)),
                               (Node)dispatch(n.getNode(1)));
    } else {
      passVariables(n, n.getGeneric(1));
      Node expr = (Node)dispatch(n.getNode(1));
      Node ret = factory.assignField(toIdentifier(n.getString(0)), expr);
      passBindings(expr, ret);
      return ret;
    }
  }

  /**
   * Process a let expression.
   *
   * @param n The let expression node.
   * @return An anonymous Let instance
   */
  public Node visitLetExpression(GNode n) {
    
    if (runtime.test("optimizeFoldLet")) {
      new LetFolder().collapseLet(n, table);      
    }
    
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    }  

    final String scopename = (String)n.getProperty("enterScope");

    boolean didEnter = false;
    if (n.hasProperty("enterScope")) {     
      if (!table.current().getName().equals(scopename)) {
        enterScope(scopename);
        didEnter = true;
      }
    }
    
    Node bindings = n.getGeneric(0);
    Node value = n.getGeneric(1);
    Object resultType = n.getProperty(TYPE);
    int size = bindings.size();
    Node block = null;
    Node res = null; 
    
    Object t = value.getProperty(TYPE);

    if (!optimizeLet) {
      res = (GNode)dispatch(value);
      if ("Block".equals(res.getName())) {
        block = GNode.create("Block", res);
      } else {
        // fix cast here
        if (mapper.hasTypeVariables(t)) {
          block = GNode.create("Block", factory.ret(res));
        } else {
          block = GNode.create("Block", factory.ret(factory.cast(res)));
        }
      }
 
      Node letclass = GNode.create("ClassBody");
      letclass = GNode.ensureVariable((GNode)letclass);
      GNode letbody = GNode.create("Block");
    
      for (int i = 0; i < size; i++) {
        Node binding = bindings.getGeneric(i);
        Node left = binding.getGeneric(0);
        Node right = binding.getGeneric(1);

        if (n.hasProperty(INANALYZE)) right.setProperty(INANALYZE, Boolean.TRUE); 
     
        String bname;
        Object btype;
  
        right = (Node)dispatch(right);
  
        if (left.hasName("Variable")) {
          bname = left.getString(0);
          btype = left.getProperty(TYPE);
          letclass.add(makeVarDec2(bname, mapper.toTypeNode(btype, false) , null));
          // fix cast here
          if (mapper.hasTypeVariables(btype)) {
            letbody.add(factory.assign(toIdentifier(bname), right));
          } else {
            letbody.add(factory.assign(toIdentifier(bname), factory.cast(right)));
          }
        
        } else if ("TypedPattern".equals(left.getName()) &&
                   "Variable".equals(left.getGeneric(0).getName())) {
          bname = left.getGeneric(0).getString(0);        
          btype = left.getProperty(TYPE);
          letclass.add(makeVarDec2(bname, mapper.toTypeNode(btype, false), null));
          // fix cast here
          if (mapper.hasTypeVariables(btype)) {
            letbody.add(factory.assign(toIdentifier(bname),right));
          } else {
            letbody.add(factory.assign(toIdentifier(bname),factory.cast(right)));
          }
        
        } else {
          if (right.hasName("ConditionalExpression")) {
            letbody.add(factory.discard(right));
          } else {
            letbody.add(factory.expressionStmnt(right));
          }
        }
      }       

      if (letbody.size() > 0) {
        letclass.add(letbody);
      }
    
      letclass.add(GNode.create("MethodDeclaration",
        toModifiers("public"),null, mapper.toTypeNode(resultType, false),
        "apply", GNode.create("FormalParameters"), null, null, block));
    
      Node let = factory.letExpression(mapper.toTypeNode(resultType, false));
      let.getGeneric(0).set(4, letclass);

      if (didEnter) exitScope(scopename);
      return let;

    } else { // optimizing Let
      List<String> upVars = getUpVariables(n);
      List<LetBinding> bl = null;

      // Get variables in let bindings
      List<String> vars = new ArrayList<String>();
      for (int i = 0; i < size; i++) {
        Node binding = bindings.getGeneric(i);
        Node left = binding.getGeneric(0);
        if (left.hasName("Variable")) {
          vars.add(left.getString(0));          
        
        } else if ("TypedPattern".equals(left.getName()) &&
                   "Variable".equals(left.getGeneric(0).getName())) {
          vars.add(left.getGeneric(0).getString(0));
        }  
      }
      // check for redefinitions
      boolean check = false;
      for (String v : vars) {
        if (null != upVars && upVars.contains(v)) {
          check = true;
          continue;
        }
      }

      if (check) upVars = vars;
      else upVars = groupList(upVars, vars);

      for (int i = 0; i < size; i++) {
        Node binding = bindings.getGeneric(i);
        Node left = binding.getGeneric(0);
        Node right = binding.getGeneric(1);
        String name;
        Object type;
        if (left.hasName("Variable")) {
          name = left.getString(0);
          type = left.getProperty(TYPE);        
        } else if ("TypedPattern".equals(left.getName()) &&
                   "Variable".equals(left.getGeneric(0).getName())) {
          name = left.getGeneric(0).getString(0);
          type = left.getProperty(TYPE);        
        } else {
          name = spareVar;
          type = right.getProperty(TYPE);        
        } 
        // process the value of a binding
        right.setProperty(UPVARS, upVars);
        Node expr = (Node)dispatch(right);
        List<LetBinding> l = getBindings(expr);
        List<String> vs = extractVariables(l);
        bl = groupList(bl,l);
        if (null == bl) {
          bl = new ArrayList<LetBinding>();
          bl.add(new LetBinding(name, type, mapper.toTypeNode(type, false), expr));
        } else {
          bl.add(new LetBinding(name, type, mapper.toTypeNode(type, false), expr));
        }
        upVars = groupList(upVars, vs);
      }
      
      // Process the expression
      value.setProperty(UPVARS, upVars);
      res = (Node)dispatch(value);
      List<LetBinding> l = getBindings(res);
      bl = groupList(bl,l);
      Node let = null; 
      if (check || n.hasProperty(NEWLET)) {
        let = convertToLet(res, bl, t);
      } else {
        res.setProperty(LETBINDINGS, bl);
        let = res;
      } 
      
      if (didEnter) exitScope(scopename);
      return let;
    }    
  }

  /**
   * Check and convert an expression to let expression if it contains bindings.
   *
   * @param n The expression node.
   * @param retType The type of that expression.
   * @return the converted node.
   */
  private Node checkToLet(Node n, Object retType) {
    List<LetBinding> bl = getBindings(n);
    if (null == bl || bl.isEmpty()) return n;

    Node block = null;
    if ("Block".equals(n.getName())) {
      block = GNode.create("Block", n);
    } else {
      // fix cast here
      if (mapper.hasTypeVariables(retType)) {
        block = GNode.create("Block", factory.ret(n));
      } else {
        block = GNode.create("Block", factory.ret(factory.cast(n)));
      }
    }
 
    Node letclass = GNode.create("ClassBody");
    letclass = GNode.ensureVariable((GNode)letclass);
    GNode letbody = GNode.create("Block");

    for (LetBinding bind : bl) {
      if (!bind.name.equals(spareVar)) {
        letclass.add(makeVarDec2(bind.name, bind.type , null));
      }
      if (mapper.hasTypeVariables(bind.typeObject)) {
        letbody.add(factory.assign(toIdentifier(bind.name), bind.value));
      } else {
        letbody.add(factory.assign(toIdentifier(bind.name), 
                                   factory.cast(bind.value)));
      }
    }

    if (letbody.size() > 0) {
      letclass.add(letbody);
    }
    
    letclass.add(GNode.create("MethodDeclaration",
      toModifiers("public"),null, mapper.toTypeNode(retType, false),
      "apply", GNode.create("FormalParameters"), null, null, block));
    
    Node let = factory.letExpression(mapper.toTypeNode(retType, false));
    let.getGeneric(0).set(4, letclass);
    return let;    
  }

  /**
   * Convert an expression to let expression if it contains bindings.
   *
   * @param n The expression node.
   * @param bl The list of variable bindings.
   * @param retType The type of that expression.
   * @return the converted node.
   */
  private Node convertToLet(Node n, List<LetBinding> bl, Object retType) {
    Node block = null;
    if ("Block".equals(n.getName())) {
      block = GNode.create("Block", n);
    } else {
      // fix cast here
      if (mapper.hasTypeVariables(retType)) {
        block = GNode.create("Block", factory.ret(n));
      } else {
        block = GNode.create("Block", factory.ret(factory.cast(n)));
      }
    }
 
    Node letclass = GNode.create("ClassBody");
    letclass = GNode.ensureVariable((GNode)letclass);
    GNode letbody = GNode.create("Block");

    for (LetBinding bind : bl) {
      if (!bind.name.equals(spareVar)) {
        letclass.add(makeVarDec2(bind.name, bind.type , null));
      }
      if (mapper.hasTypeVariables(bind.typeObject)) {
        letbody.add(factory.assign(toIdentifier(bind.name), bind.value));
      } else {
        letbody.add(factory.assign(toIdentifier(bind.name), 
                                   factory.cast(bind.value)));
      }
    }

    if (letbody.size() > 0) {
      letclass.add(letbody);
    }
    
    letclass.add(GNode.create("MethodDeclaration",
      toModifiers("public"),null, mapper.toTypeNode(retType, false),
      "apply", GNode.create("FormalParameters"), null, null, block));
    
    Node let = factory.letExpression(mapper.toTypeNode(retType, false));
    let.getGeneric(0).set(4, letclass);
    return let;    
  }
 
  /**
   * Process a Patterns node. 
   *
   * @return The patterns node corresponding to this node.
   */
  public Node visitPatterns(GNode n) {
    Node pat = n.getGeneric(0);
   
    if (n.hasProperty(TOP)) pat.setProperty(TOP, null);

    if (isLiteral(pat)) {
      return factory.jequals((Node)dispatch(pat), 
                             toIdentifier((String)n.getProperty(MATCHARG)));
    } else {
      pat.setProperty(MATCHARG, n.getProperty(MATCHARG));
      pat.setProperty(BINDINGS, n.getProperty(BINDINGS));
      pat.setProperty(RHS, n.getProperty(RHS));
      return (GNode)dispatch(pat);
    }
  }  
  
  /**
   * Test if a node is a literal.
   *
   * @param n The node to test.
   * @return True if literal false otherwise.
   */
  private boolean isLiteral(Node n) {
    if (null == n) {
      runtime.error("Calling isLiteral on null node", n);
    } else {
      String name = n.getName();
      return ("StringLiteral".equals(name) || "BooleanLiteral".equals(name) ||
              "FloatingLiteral".equals(name) || "IntegerLiteral".equals(name));
    }
      return false;
   }  

  /**
   * Process an additive expression.
   *
   * @param n The additive expression node.
   * @return The java additive expression.
   */
  public Node visitAdditiveExpression(GNode n) {
    String op = n.getString(1);
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(2).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(2).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(2));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret;    
    
    if ("+".equals(op))  ret = factory.addInt(left, right);
    else if ("-".equals(op))  ret = factory.subtractInt(left, right);
    else if ("+.".equals(op)) ret = factory.addFloat64(left, right);
    else ret = factory.subtractFloat64(left, right);
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }

  /**
   * Process a concatenation expression.
   *
   * @param n The concatenation expression node.
   * @return The java equivalent.
   */
  public Node visitConcatenationExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(2).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(2).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(2));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret;        
    
    if ("^".equals(n.getString(1))) ret = factory.concatStrings(left, right);
    else ret = factory.concatLists(left, right);

    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }

  /**
   * Process a multiplicative expression.
   *
   * @param n The multiplicative expression node.
   * @return The java multiplicatiee expression.
   */
  public Node visitMultiplicativeExpression(GNode n) {
    String op = n.getString(1);
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(2).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(2).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(2));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret;            
    
    if ("*".equals(op))  ret = factory.multiplyInt(left, right);
    else if ("/".equals(op))  ret = factory.divideInt(left, right);
    else if ("%".equals(op))  ret = factory.modInt(left, right);
    else if ("*.".equals(op)) ret = factory.multiplyFloat64(left, right);
    else ret = factory.divideFloat64(left, right);

    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }

  /**
   * Process a relational expression.
   *
   * @param n The relational expression node.
   * @return The java relational expression
   */
  public Node visitRelationalExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(2).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(2).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(2));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret;                
  
    String op = n.getString(1);
    String internal = null;
    
    if ("<".equals(op))   internal = "lessInt";
    if ("<=".equals(op))  internal = "lessEqualInt";
    if (">".equals(op))   internal = "greaterInt";
    if (">=".equals(op))  internal = "greaterEqualInt";
    if ("<.".equals(op))  internal = "lessFloat64";
    if ("<=.".equals(op)) internal = "lessEqualFloat64";
    if (">.".equals(op))  internal = "greaterFloat64";
    if (">=.".equals(op)) internal = "greaterEqualFloat64";
    assert null != internal : "undefined relational operator " + op;
     
    ret = 
      factory.relationalExpr(toIdentifier("Primitives." + internal),left,right);
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }

  /**
   * Process a logical or  expression.
   *
   * @param n The logical or expression node.
   * @return The java expression.
   */
  public Node visitLogicalOrExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(1).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(1));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret = factory.or(left, right);
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }
  
  /** 
   * Process a logical and expression.
   *
   * @param n The logical and expression node.
   * @return The java expression.
   */
  public Node visitLogicalAndExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(1).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(1));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret = factory.and(left, right);
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }
  
  /**
   * Process a logical negation expression.
   *
   * @param n The logical negation expression node.
   * @return n The java equivalent.
   */  
  public Node visitLogicalNegationExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);      
    }  
    passVariables(n, n.getGeneric(0));
    Node expr = (Node)dispatch(n.getGeneric(0));
    Node ret = factory.not(expr);
    passBindings(expr, ret);
    return ret;
  }
  
  /**
   * Process an equality expression.
   *
   * @param n The equality expression node.
   * @return The java equivalent.
   */
  public Node visitEqualityExpression(GNode n) {
    String op = n.getString(1);
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(2).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    List<String> vars = extractVariables(bl1);
    upVars = groupList(upVars,vars);

    if (null != upVars) n.getGeneric(2).setProperty(UPVARS, upVars);
    Node right = (Node)dispatch(n.getGeneric(2));
    List<LetBinding> bl2 = getBindings(right);
    bl1 = groupList(bl1, bl2);
    Node ret;            
    
    if ("=".equals(op)) {
      if ("Bottom".equals(n.getGeneric(0).getName())) {    
        ret = factory.equalsBottom(right);
        if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
        return ret;      
      } else if ("Bottom".equals(n.getGeneric(2).getName())) {
        ret = factory.equalsBottom(left);
        if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
        return ret;    
      }
    } else {
       if ("Bottom".equals(n.getGeneric(0).getName())) {    
        ret = factory.notEqualsBottom(right);
        if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
        return ret;      
      } else if ("Bottom".equals(n.getGeneric(2).getName())) {
        ret = factory.notEqualsBottom(left);
        if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
        return ret;    
      }
    }

    ret = 
      ("=".equals(n.getString(1))) ? factory.equal(left, right)
                                   : factory.not(factory.equal(left, right));
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }

  /**
   * Process an attribute declaration.
   *
   * @param n the attribute declaration node to process.
   */
  public void visitAttributeDefinition(GNode n) {
    Node typeNode = n.getGeneric(1);
    if ("ConstraintType".equals(typeNode.getName())) {
      typeNode = GNode.create("UserDefinedType","Node");
    }
    attributeList.add(new Attribute(n.getString(0), typeNode)); 
  }
  
  /**
   * Process an equal attribute definition.
   *
   * @param n the equal attribute definition node to process.
   */
  public void visitEqualAttributeDefinition(GNode n) {
    Node typeNode = n.getGeneric(1);
    if ("ConstraintType".equals(typeNode.getName())) {
      typeNode = GNode.create("UserDefinedType","Node");
    }
    eqAttributeList.add(new Attribute(n.getString(0), typeNode));     
  }

  /**
   * Process raw_type definition to create "type" record.
   *
   * @return The node of generated code for "type" record definition.
   */
  public Node processRawTypeDefinition() {
    //Create type
    Node typeInfo = 
      GNode.create("RecordDeclaration",GNode.create("FieldType", "type",
        GNode.create("UserDefinedType", "raw_type<?>")));

    typeInfo = GNode.ensureVariable((GNode)typeInfo);
      
    typeInfo.setProperty(TYPE, table.current().lookup("type(type)"));
    
    //Create equal attributes
    for (Attribute att : eqAttributeList) {
      typeInfo.add(GNode.create("FieldType", att.name, att.type));
    } 
   
    //Create attributes
    for (Attribute att : attributeList) {
      typeInfo.add(GNode.create("FieldType", att.name, att.type));
    } 
    
    return GNode.create("TypeDefinition", null, "type", typeInfo);   
  }
  
  /**
   * Process an equality definition.
   *
   * @param n The equality definition node.
   */
  public void visitEqualityDefinition(GNode n) {
    // process equal structure 
    for (int index = 1; index < n.size(); index ++) {
      boolean seenLowerID = false;
      Node child = (GNode)n.get(index);
      ArrayList<Integer> list = new ArrayList<Integer>();

      for (int i = 1; i < child.size(); i++) {
        if (!("WildCard".equals(child.getGeneric(i).getName()))) {
          list.add(i);
          seenLowerID = true;          
        }         
      }
      
      if (!seenLowerID) {
        throw new AssertionError("At least one identifier required");        
      } 
      equalities.add(new Equality((child.getGeneric(0)).getString(0),list));
    } 
  } 
  
  /**
   * Get node name from a pattern
   *
   * @param n The pattern node
   */
  protected String getNodeName(Node n) {
    return ("TypeConstructorPattern".equals(n.getName())) 
      ? n.getString(0) : getNodeName(n.getGeneric(0));
  }

  /**
   * Process scope definition
   *
   * @param n The scope definition node
   */  
  public void visitScopeDefinition(GNode n) {   
    // Adding code to build the list of node names that need to process scope
    Node pat = n.getGeneric(0);
    for (int i = 0; i < pat.size(); i++) {
      Node patterns = pat.getGeneric(i).getGeneric(0);
      for (int j = 0; j < patterns.size(); j++) {
        processScopeNodes.add(getNodeName(patterns.getGeneric(j)));         
      }
    } 
  
    Node match = GNode.create("MatchExpression",
       GNode.create("LowerID","n"), n.getGeneric(0));
    match.setProperty("__arg_type", n.getProperty("__arg_type"));
    match.setProperty(TYPE, 
      mapper.getPatternMatchRightType(n.getProperty(TYPE)));
    
    seenScope = true;

    Node pmatch = n.getGeneric(0);
    pmatch.setProperty(MATCHARG, "n");

    @SuppressWarnings("unchecked")
    List<Node> nodes = (List<Node>)dispatch(pmatch);
    
    nodes.add(factory.ret(GNode.create("NullLiteral")));
   
    // Create typical AST
    Node top = GNode.create("ValueDefinition",
                 "getScope",
                 GNode.create("Parameters",
                   GNode.create("Parameter",
                     "n",
                     GNode.create("UserDefinedType",
                       "node")
                   ) 
                 ),
                 GNode.create("BooleanLiteral", "true")
               );
            
    top.setProperty(TYPE, table.current().lookup("value(getScope)"));
    top.setProperty("__isfunction", null);
       
    Node block = GNode.create("Block");

    for (Node node : nodes) block.add(node);
    
    Node scopefunc = (Node)dispatch(top);

    scopefunc.getGeneric(2).getGeneric(0).getGeneric(2).getGeneric(4).
      getGeneric(0).set(7, block);

    functionDefinitions.add(scopefunc);
    return;
  }
 
  /**
   * Process  namespace definition.
   *
   * @param n The name space definition node.
   */  
  public void visitNameSpaceDefinition(GNode n) {   
    seenNameSpace = true;       
    Node pat = GNode.create("PatternMatching");
    pat.setProperty(TYPE, n.getProperty(TYPE));
    pat.setProperty(MATCHARG, "n");

    // Create the pattern matching node
    for(int i = 0; i < n.size(); i++) {
      Node child = n.getGeneric(i);
     
      for(int j = 0; j < child.getGeneric(2).size(); j++) {        
        Node grand = child.getGeneric(2).getGeneric(j);
        Node pattern = GNode.create("PatternMatch");
        
        pattern.add(grand.getGeneric(0));
        // Create Typical tuple
        Node tup = GNode.create("TupleLiteral", grand.getGeneric(1),
          GNode.create("StringLiteral", "\"" + child.getString(0) + "\""), 
          GNode.create("StringLiteral", "\"" + child.getString(1) + "\""));
        tup.setProperty(TYPE, TypeMapper.nameTupleT);
        pattern.add(tup);
      
        pattern.setProperty("enterScope",grand.getProperty("enterScope"));
        
        Node thepatterns = pattern.getGeneric(0);
        
        for (int k = 0; k < thepatterns.size(); k++) {
        thepatterns.getGeneric(k).setProperty("enterScope", 
          pattern.getProperty("enterScope"));
        }
        pat.add(pattern);
      }      
    }

    pat.setProperty(MATCHARG, "n");
    Node match = 
      GNode.create("MatchExpression", GNode.create("LowerID","n"),pat);
    
    // create the value definition node
    Node top = GNode.create("ValueDefinition",
      "getNameSpace", GNode.create("Parameters", GNode.create("Parameter", "n",
       GNode.create("UserDefinedType", "node"))), match);
    top.setProperty("__isfunction", null);
    match.setProperty("__arg_type", table.current().lookup("type(node)"));
    match.setProperty(TYPE, TypeMapper.nameTupleT);
    top.setProperty("__arg_type", n.getProperty("__arg_type"));
   
    top.setProperty(TYPE, table.current().lookup("value(getNameSpace)"));
    functionDefinitions.add((Node)dispatch(top));
  }

  /**
   * Transform an error clause into a function call to either or warning.
   *
   * @param n The expression node.
   * @return The function call java node.
   */
  public Node visitErrorClause(GNode n) {
    Node loc = (Node)dispatch(n.getGeneric(2));
    if (null == loc) loc = nullNode;
    //n.getGeneric(1).setProperty(NEWLET, Boolean.TRUE);
    Node letNode = (Node)dispatch(n.getGeneric(1));
    letNode = checkToLet(letNode, n.getGeneric(1).getProperty(TYPE));
    return factory.errorClause(n.getGeneric(0).getString(0), letNode,loc);
  }

  /**
   * Transform an assert clause into a function call to assert.
   *
   * @param n The assert clause node.
   * @return The function call java node.
   */
  public Node visitAssertClause(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      if (null != n.getGeneric(1)) {
        n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
      }
    }
    //n.getGeneric(0).setProperty(NEWLET, Boolean.TRUE);
    //n.getGeneric(1).setProperty(NEWLET, Boolean.TRUE);

    Node exp0 = (Node)dispatch(n.getGeneric(0));
    exp0 = checkToLet(exp0, n.getGeneric(0).getProperty(TYPE)); 

    Node exp1 = (Node)dispatch(n.getGeneric(1));
    exp1 = checkToLet(exp1, n.getGeneric(1).getProperty(TYPE)); 

    return (null == n.getGeneric(1)) 
      ? GNode.create("PostfixExpression", toIdentifier("assertion"),
          GNode.create("Arguments", exp0))
      : GNode.create("PostfixExpression", toIdentifier("assertion"),
          GNode.create("Arguments", exp0, exp1));
  }

  /**
   * Transform an if expression into a java conditional expression.
   *
   * @param n The if expression node.
   * @return The java conditional expression.
   */
  public Node visitIfExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
    }  

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
        
    Node right = (Node)dispatch(n.getGeneric(1));
    right = checkToLet(right, n.getGeneric(1).getProperty(TYPE));
    Node ret = factory.ifExpression(left, right);
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }
  
  /**
   * Transform an if expression into a java conditional expression.
   *
   * @param n The if expression node.
   * @return The java conditional expression.
   */
  public Node visitIfElseExpression(GNode n) {
    if (n.hasProperty(INANALYZE)) {
      n.getGeneric(0).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(1).setProperty(INANALYZE, Boolean.TRUE);
      n.getGeneric(2).setProperty(INANALYZE, Boolean.TRUE);
    }

    List<String> upVars = getUpVariables(n);
    if (null != upVars) n.getGeneric(0).setProperty(UPVARS, upVars); 
    Node left = (Node)dispatch(n.getGeneric(0));
    List<LetBinding> bl1 = getBindings(left);
    

    Node middle = (Node)dispatch(n.getGeneric(1));
    middle = checkToLet(middle, n.getGeneric(1).getProperty(TYPE));

    Node right = (Node)dispatch(n.getGeneric(2));
    right = checkToLet(right, n.getGeneric(2).getProperty(TYPE));

    Node ret = factory.ifElseExpression(left, middle, right);
    if (null != bl1) ret.setProperty(LETBINDINGS, bl1);
    return ret;
  }
  
  /**
   * Process require expression.
   *
   * @param n the require expression node
   * @return The java code for the require expression.
   */  
  public Node visitRequireExpression(GNode n) {
    
    int nodeSize = n.size();
 
    // get the expression
    Node expr = n.getGeneric(nodeSize -1);
    passVariables(n, expr);

    if (n.hasProperty(INANALYZE)) expr.setProperty(INANALYZE, Boolean.TRUE);
 
    //dispatch this node to translate
    Node valExpr = (Node)dispatch(expr);

    final Node genericType;
    if (expr.hasProperty(TYPE)) {
      genericType = mapper.toTypeNode(expr.getProperty(TYPE), false);
    } else { 
      genericType = GNode.create("Type",
                      GNode.create("QualifiedIdentifier", "Object"),
                      null);
    }
    
    ArrayList<Node> list = new ArrayList<Node>();
    
    for (int id = 0; id < nodeSize - 1; id++) {
      Node no = n.getGeneric(id);
      list.add(no);
    }

    // Create require block
    List<Node> requireBlock = new ArrayList<Node>();

    /* A list of translated expressions */
    ArrayList<Node> listExpr = new ArrayList<Node>();
    
    // Add statement
    for (Node node: list) {      
      Node boolNode = node.getGeneric(0);
      //boolNode.setProperty(NEWLET, Boolean.TRUE);
      if (n.hasProperty(INANALYZE)) {
        boolNode.setProperty(INANALYZE, Boolean.TRUE);
      }
      Node boolExpr = (Node)dispatch(boolNode);
      boolExpr = checkToLet(boolExpr, boolNode.getProperty(TYPE));

      final String newVar = table.freshJavaId("var");

      requireBlock.add(factory.boolVar(newVar, boolExpr));
      
      listExpr.add(toIdentifier(newVar));

      Node tagNode = node.getGeneric(1);
      String tag = tagNode.getString(0);

      Node msgNode = node.getGeneric(2);
      Node msgExpr = (Node)dispatch(msgNode);
  
      Node atNode = node.getGeneric(3);
      Node atExpr;
      if (null == atNode) {
        atExpr = nullNode;
      } else {
        atExpr = (Node)dispatch(atNode);
      }  
    
      Node ifNode = factory.ifStatement3(toIdentifier(newVar), 
                                         GNode.create("StringLiteral",
                                           "\"" + tag + "\""), msgExpr,atExpr); 
      requireBlock.add(ifNode);
    }

    // check for returning null (bottom)    
    if (1 == listExpr.size()) {
      Node checkNode = factory.ifStatement4(listExpr.get(0));
      
      requireBlock.add(checkNode);
    } else {
      Node logicalOr = GNode.create("LogicalOrExpression");
      logicalOr.add(factory.isNull(listExpr.get(0)));
      logicalOr.add(factory.isNull(listExpr.get(1)));
      
      for (int i = 2; i < listExpr.size(); i++ ) {
        logicalOr = GNode.create("LogicalOrExpression", logicalOr, 
                                  factory.isNull(listExpr.get(i)));
      }
      
      Node checkNode = factory.ifStatement5(logicalOr);
      requireBlock.add(checkNode);
    }
    
    // Check to return expr

    if (1 == listExpr.size()) {
      Node checkNode;
      if(!"Block".equals(valExpr.getName())) {
        checkNode = factory.ifStatement(listExpr.get(0), factory.ret(valExpr)); 
      } else {
        checkNode = toIfStatement(listExpr.get(0), valExpr);
      }
      requireBlock.add(checkNode);
    } else {
      Node logicalAnd = GNode.create("LogicalAndExpression");
      logicalAnd.add(listExpr.get(0));
      logicalAnd.add(listExpr.get(1));
      
      for(int i = 2; i < listExpr.size(); i++) {
        logicalAnd = GNode.create("LogicalAndExpression", logicalAnd,
                                  listExpr.get(i));
      }
      Node checkNode;
      if (!"Block".equals(valExpr.getName())) {
        checkNode = factory.ifStatement(logicalAnd, factory.ret(valExpr));     
      } else {
        checkNode = toIfStatement(logicalAnd, valExpr);
      }
      requireBlock.add(checkNode);
    }
   
    // return null at the end
    requireBlock.add(factory.ret(nullNode));
    Node ret = factory.requireExpression(genericType, requireBlock);
    passBindings(valExpr, ret);
    return ret;
  }
  
  /** Store nodes that need to process scopes. */
  public void processScopeSpace() {
    Node block = GNode.create("Block");
    
    for (int i = 0; i < processScopeNodes.size(); i++) {
      Node add = factory.addScopeNode(toIdentifier(
                                      "\"" + processScopeNodes.get(i) + "\""));
      block.add(add);

    }   
  
    Node getNodes = factory.getScopeNodesMethod();
    getNodes.set(7, block);
    
    cbody.add(getNodes);

    // if scope definition is not used
    if(!seenScope) {
      cbody.add(factory.getScopeClass());
      cbody.add(factory.getScopeObject()); 
     
    } 
    if(!seenNameSpace) {
      throw new AssertionError("Name space must be defined");      
    }
  }
   
  /**
   * Create equals method for the record "type".
   * 
   * @return The equals method for the type record.
   */
  public Node createTypeRecordEquals() {

    Node block = GNode.create("Block");

    block.add(toIfStatement(factory.equality(toIdentifier("o"), nullNode),
      factory.ret(GNode.create("BooleanLiteral","false"))));
                        
    block.add(toIfStatement(GNode.create("LogicalNegationExpression",
      GNode.create("InstanceOfExpression", toIdentifier("o"), toType("type"))),
        factory.ret(toLiteral("BooleanLiteral","false"))) );
 
    block.add(factory.recordFieldEqual());
    
    block.add(factory.recordEqualReturn());

    block.add(factory.compareTypes());
    
    // Add equal attribute comparisons.
    for (Attribute att : eqAttributeList) {
      block.add(factory.compareAttributes(toIdentifier(att.name)));      
    }
    block.add(factory.ret(toIdentifier("res")));

    Node retNode = factory.equalsMethod();
    retNode.set(7, block);
    return retNode;
  }

  /**
   * Create equals method for a constructor that is in equality definition.
   *
   * @param variantName The name of the contructor.
   * @return The equals method for the variant.
   */
  public Node createVariantEqualsMethod(String variantName) {
    Equality constr = null;
    for (Equality e : equalities) {
      if (variantName.equals(e.name)) {
        constr = e;
        break;
      }
    }
    
    assert null != constr : "null value for constr";  
         
    Node block = GNode.create("Block");
    block.add(factory.ifStatement1(toIdentifier("o")));

    Node secondIf = factory.ifStatement2(toIdentifier("o"));
    secondIf.getGeneric(0).getGeneric(0).set(1, toType(variantName));
    block.add(secondIf);
    block.add(makeDec("other", variantName, GNode.create("CastExpression",
         toType(variantName), toIdentifier("o"))));
    block.add(makeDec("res", "boolean", toLiteral("BooleanLiteral","true")));
       
    for (Integer pos : constr.positions) {
      block.add(factory.compareMembers(
                  "getTuple().get" + pos + "()"));      
    }
    block.add(factory.ret(toIdentifier("res")));
    Node foo = factory.equalsMethod();
    foo.set(7, block);
    return foo;
  }

  /**
   * Augment a translated IfStatement of a pattern match to process scope.
   *
   * @param n The IfStatement node.
   * @param matchArg The argument of the match expression.
   * @param no The pattern match node.
   * @param bn The node to get bindings from (if exists).
   * @return The augmented IfStatement. 
   */
  public Node augmentIfStatement(Node n, String matchArg, GNode no, Node bn) {
    Node block = n.getGeneric(1);
    GNode pattern = no.getGeneric(0).getGeneric(0);
    List<Integer> indexList = getIndexList(pattern);

    int size = block.size();
    // Get the last return statement of the block
    Node retNode = block.getGeneric(size - 1);

    assert ("ReturnStatement" == retNode.getName()) : 
      "The last statement of the if block is not a return statement";

    // Update matching_nodes.
    block.set(size - 1, factory.matchingNodesAdd(toIdentifier(matchArg)));
    // Check if this node needs to process scope
    block.add(factory.processScope(toIdentifier(matchArg)));
    // Check if enter a new scope
    block.add(factory.checkEnterScope(toIdentifier(matchArg)));
    // Check if needed to process scope of the offsprings
    String nodeName = table.freshJavaId("nodeName");
    String listName = table.freshJavaId("listName");
    if (null != indexList && indexList.size() > 1) {
      block.add(factory.spOffspringList(listName));
      block.add(factory.spRunNode(nodeName, toIdentifier(matchArg)));
      int index;
      for (int ind = 0; ind < indexList.size() - 1; ind++){
        index = indexList.get(ind);
        block.add(factory.spGetGeneric(toIdentifier(nodeName),
          toLiteral("IntegerLiteral", "" + index)));
        block.add(factory.processScope(toIdentifier(nodeName)));
        block.add(factory.checkEnterScope(toIdentifier(nodeName)));
        block.add(factory.spOffspringListAdd(toIdentifier(listName), 
                                             toIdentifier(nodeName)));
      }
    }
    // Add bindings if exists
    List<LetBinding> bl = getBindings(bn);
      
    if (null != bl) {
      for (LetBinding bind : bl) {
        if (!bind.name.equals(spareVar)) {
          if (mapper.hasTypeVariables(bind.typeObject)) { 
            block.add(factory.fieldDecl2(bind.type, bind.name, bind.value));
          } else {
            block.add(factory.fieldDecl2(bind.type, bind.name, 
                      factory.cast(bind.value)));
          }
        } else {
          if (mapper.hasTypeVariables(bind.typeObject)) {
            block.add(factory.assign(toIdentifier(bind.name), bind.value));
          } else {
            block.add(factory.assign(toIdentifier(bind.name), 
                      factory.cast(bind.value)));
          }
        }
      }
    }

    // Store the return value
    String freshId = table.freshJavaId("retValue");
    block.add(factory.storeValue(freshId, retNode.getGeneric(0)));
    // Check to exit scope
    if (null != indexList && indexList.size() > 1) {
      block.add(factory.spForLoop(toIdentifier(listName)));
    }
    block.add(factory.checkExitScope(toIdentifier(matchArg)));
    // Update matching_nodes
    block.add(factory.matchingNodesRemove());
    // Annotate this node with the type
    if (no.hasProperty(ANNOTATE)) {
      block.add(factory.annotateType(toIdentifier(matchArg), 
                                     toIdentifier(freshId)));
    }
    // return
    Object t = mapper.getPatternMatchRightType(no.getProperty(TYPE));
    if (mapper.hasTypeVariables(t)) {
      block.add(factory.castReturn(toIdentifier(freshId)));
    } else {
      block.add(factory.castReturn(toIdentifier(freshId)));
    }
    return n;    
  } 

  /**
   * Add bindings to an if statement
   *
   * @param n The if statement node
   * @param no The node annotated with bindings
   * @return The if statement with bindings added
   */
  public Node addToIf(Node n, Node no) {
    Node block = n.getGeneric(1);
    int size = block.size();
    // Get the last return statement of the block
    Node retNode = block.getGeneric(size - 1);
    block.set(size - 1, null);

    List<LetBinding> bl = getBindings(no);

    if (null != bl) {
      for (LetBinding bind : bl) {
        if (!bind.name.equals(spareVar)) {
          if (mapper.hasTypeVariables(bind.typeObject)) { 
            block.add(factory.fieldDecl2(bind.type, bind.name, bind.value));
          } else {
            block.add(factory.fieldDecl2(bind.type, bind.name, 
                      factory.cast(bind.value)));
          }
        } else {
          if (mapper.hasTypeVariables(bind.typeObject)) {
            block.add(factory.assign(toIdentifier(bind.name), bind.value));
          } else {
            block.add(factory.assign(toIdentifier(bind.name), 
                      factory.cast(bind.value)));
          }
        }
      }
    }	
    block.add(retNode);
    return n; 
  } 

  /**
   * Get a list of indice that corresspond to nodes needed to process scope
   *
   * @param node The pattern node
   * @return The list of indice
   */
  public static List<Integer> getIndexList(GNode node){
    List<Integer> res;
    final String nodeName = node.getName();
    if ("WhenPattern".equals(nodeName) ||
        "AsPattern".equals(nodeName) ||
        "TypedPattern".equals(nodeName)) {
      return getIndexList(node.getGeneric(0));
      
    } else if ("TypeConstructorPattern".equals(nodeName)) {
      if (1 == node.size() || "WidlCard".equals(node.getGeneric(1).getName())) {
        return null;
      } else {
        Node paras = node.getGeneric(1);
        boolean hasBinding = false;

        for (int index = 0; index < paras.size(); index++) {
          res = getIndexList(paras.getGeneric(index));
          if (null != res && !res.isEmpty()) {
            if ("ConsPattern".equals(paras.getGeneric(index).getName())||
                "ListPattern".equals(paras.getGeneric(index).getName())) {
              int pos = res.remove(0);
              res.add(0, pos + index);
              return res;
            } else {
              res.add(0, index);
              return res;
            }
          } else if (null != res) {
            hasBinding = true;
          }          
        }
        if (hasBinding) {
          res = new ArrayList<Integer>();
          res.add(0);
          return res;
        } else return null;
      }  
    } else if ("ConsPattern".equals(nodeName)) {
      res = getIndexList(node.getGeneric(0));
      if (null == res) {
        res = getIndexList(node.getGeneric(1));
        if (null == res || res.isEmpty()) return res;
        else {
          int pos = res.remove(0);
          res.add(0, pos + 1);
          return res;
        }
      } else if (res.isEmpty()) {
        return res;        
      } else {
        res.add(0,0);
        return res;
      }

    } else if ("ListPattern".equals(nodeName)) {
      boolean hasBinding = false;
      for (int index = 0; index < node.size(); index++){
        res = getIndexList(node.getGeneric(index));
        if (null != res && !res.isEmpty()) {
          res.add(0, index);
          return res;
        } else if (null != res){
          hasBinding = true;
        }       
      }
      return hasBinding? new ArrayList<Integer>() : null;
    } else if ("Variable".equals(nodeName)) {
      return new ArrayList<Integer>();
    } else if ("WildCard".equals(nodeName) ||
               "BottomPattern".equals(nodeName)) {
      return null;
    } 
    return null;
  }

  /**
   * Process currying in function applications
   *
   * @param funcName The name of the function
   * @param args The node that contains all arguments
   * @param paramTypes The list of parameter type nodes of the function
   * @param retType The return type node of the function
   * @param funcType The whole function type
   * @return A node that is a new function
   */
  public Node makeCurry(String funcName, Node args, 
                        List<Node> paramTypes, Node retType, Object funcType) {
      
    //create the function name and parameterise it with the return type
    //and the argument types
    final int paraSize = paramTypes.size() - args.size();

    Node typeArgs = GNode.create("TypeArguments");
    typeArgs.add(retType);

    for (int i = args.size(); i < paramTypes.size(); i++) {
      typeArgs.add(paramTypes.get(i));
    }      
    
    Node functionTypeNode = GNode.create("Type",
                 GNode.create("InstantiatedType",
                   GNode.create("TypeInstantiation", "Function", null),
                   GNode.create("TypeInstantiation", "F" + paraSize, 
                                typeArgs)),
                 null);

    // Populate the formal parameters for the "apply" method
    Node formalParameters = GNode.create("FormalParameters", paraSize);
    // Names of formal parameters
    List<String> formalParams = new ArrayList<String>();
  
    for (int i = args.size(); i < paramTypes.size(); i++) {
      String newPara = table.freshJavaId("para");
      formalParams.add(newPara); 
      formalParameters.add(GNode.create("FormalParameter", fmod, 
        paramTypes.get(i), null, newPara, null));
    }
    
    // Create block for apply method
    Node block = GNode.create("Block");
    // Names of temporary variables
    List<String> tempVars = new ArrayList<String>();
    
    for (int i = 0; i < args.size(); i++) {
      // Assign to a temporary variable
      String tempVar = table.freshJavaId("var");
      tempVars.add(tempVar);
      block.add(factory.fieldDecl2(paramTypes.get(i),
                                   tempVar, args.getGeneric(i)));      
    }
  
    // Node of new arguments
    Node newArgs = GNode.create("Arguments");
    
    for (String s : tempVars) newArgs.add(toIdentifier(s));
    for (String s : formalParams) newArgs.add(toIdentifier(s));
    
    // Create return statement
    Node ret = factory.ret(factory.apply(toIdentifier(funcName),
                                         makeArgumentList(newArgs)));
    block.add(ret);

    // Create body of the class
    Node classBody = GNode.create("ClassBody", 
      GNode.create("MethodDeclaration", pmod, null,
      retType, "apply", formalParameters, null, null,
      block));
 
    return toNewExpression2(functionTypeNode,null,classBody);        
  }

  /** 
   * Get the index of a primitive instance in the list
   * 
   * @param name The name of the instance
   * @param types The types of the instance
   * @return The index of the instance, -1 if not exists
   */
  public String getInstanceName(String name, List<String> types) {
    for (PrimitiveInstance ins : primitiveInsList) {
      if (!name.equals(ins.name) || types.size() != ins.types.size()) continue;
      boolean check = true;
      for (int j = 0; j < types.size(); j++) {
        if (!types.get(j).equals(ins.types.get(j))) {
          check = false;
          break;
        }
      }
      if (check) return ins.instanceName;
    }
    return null;
  }

  /**
   * Make a variable declaration.
   *
   * @param s The variable name.
   * @param t The type.
   * @param d The declarator value.
   * @return The variable declaration node.
   */
  private Node makeVarDec(String s, String t, Node d) {  
    Node decl = factory.fieldDecl(toType(t), d);
    //set the name
    decl.getGeneric(2).getGeneric(0).set(0, s);
    return decl;
  }

  /**
   * Make a variable declaration with type is a node.
   *
   * @param s The variable name.
   * @param t The type node.
   * @param d The declarator value.
   * @return The variable declaration node.
   */
  private Node makeVarDec2(String s, Node t, Node d) {  
    Node decl = factory.fieldDecl(t, d);
    //set the name
    decl.getGeneric(2).getGeneric(0).set(0, s);
    return decl;
  }

  /**
   * Make a variable declaration.
   *
   * @param s The variable name.
   * @param t The type.
   * @param d The declarator value.
   * @return The variable declaration node.
   */
  @SuppressWarnings ("unused")
  private Node makeStaticVarDec(String s, String t, Node d) {  
    Node decl = factory.staticFieldDecl(toType(t), d);
    //set the name
    decl.getGeneric(2).getGeneric(0).set(0, s);
    return decl;
  }
  
  /**
   * Make a variable declaration.
   *
   * @param s The variable name.
   * @param t The type.
   * @param d The declarator value.
   * @return The variable declaration node.
   */
  private Node makeDec(String s, String t, Node d) {  
    Node decl = factory.fieldDecl1(toType(t), d);
    //set the name
    decl.getGeneric(2).getGeneric(0).set(0, s);
    return decl;
  }

  /**
   * Get variale bindings from a node.
   * 
   * @param n The node to get variable bindings.
   * @return A list of bindings.
   */
  @SuppressWarnings("unchecked")
  private List<LetBinding> getBindings(Node n) {    
    return (List<LetBinding>)n.getProperty(LETBINDINGS);
  }

  /**
   * Get annotated variales from a node.
   * 
   * @param n The node to get variables bindings.
   * @return A list of variables (string).
   */
  @SuppressWarnings("unchecked")
  private List<String> getUpVariables(Node n) {
    return (List<String>)n.getProperty(UPVARS);
  }

  /**
   * Get variables from a list of bindings.
   *
   * @param l The list of bindings.
   * @return A list of variables.
   */
  private List<String> extractVariables(List<LetBinding> l) {
    if (null == l) return null;
    List<String> ret = new ArrayList<String>();
    for (LetBinding bind : l) {
      ret.add(bind.name);
    }
    return ret;
  }

  /**
   * Group variables from 2 lists of variables.
   * @param l1 The first list.
   * @param l2 The second list.
   * @return The resulted list.
   */
  private <T0> List<T0> groupList(List<T0> l1, List<T0> l2) {
    if (null == l1) return l2;
    if (null == l2) return l1;
    List<T0> ret = l1;
    ret.addAll(l2);
    return ret;
  }

  /**
   * Pass annotated variables from a node to another.
   *
   * @param from The node to get annotated variables.
   * @param to The node to pass variables to.
   */
  private void passVariables(Node from, Node to) {
    List<String> vars = getUpVariables(from);
    if (null != vars) to.setProperty(UPVARS, vars);
  }

  /**
   * Pass annotated bindings from a node to another.
   *
   * @param from The node to get annotated bindings.
   * @param to The node to pass bindings to.
   */
  private void passBindings(Node from, Node to) {
    List<LetBinding> bl = getBindings(from);
    if (null != bl) to.setProperty(LETBINDINGS, bl);
  }

  /** 
   * Get variables from a pattern.
   *
   * @param n The pattern node.
   * @return A list of variables.
   */ 
  private List<String> getPatternVariables(Node n) {
    String name = n.getName();
    
    if ("TuplePattern".equals(name)) {
      List<String> res = new ArrayList<String>();
      for (int i = 0; i < n.size(); i++) {
        List<String> vars = getPatternVariables(n.getGeneric(i));
        if (null != vars) res.addAll(vars);
      }
      return res;
      
    } else if ("WhenPattern".equals(name)) {
      return getPatternVariables(n.getGeneric(0));

    } else if ("AsPattern".equals(name)) {
      List<String> vars = getPatternVariables(n.getGeneric(0));
      vars.add(n.getString(1));
      return vars; 

    } else if ("TypedPattern".equals(name)) {
      return getPatternVariables(n.getGeneric(0));

    } else if ("ConsPattern".equals(name)) {
      List<String> res = getPatternVariables(n.getGeneric(0));
      res.addAll(getPatternVariables(n.getGeneric(1)));
      return res;

    } else if ("Variable".equals(name)) {
      List<String> res = new ArrayList<String>(); 
      res.add(n.getString(0));
      return res;

    } else if ("TypeConstructorPattern".equals(name)) {
      if (1 < n.size()) return getPatternVariables(n.getGeneric(1));
      else return new ArrayList<String>(); 

    } else if ("PatternParameters".equals(name)) {
      List<String> res = new ArrayList<String>();
      for (int i = 0; i < n.size(); i++) {
        List<String> vars = getPatternVariables(n.getGeneric(i));
        if (null != vars) res.addAll(vars);
      }
      return res;

    } else if ("ListPattern".equals(name)) {
      List<String> res = new ArrayList<String>();
      for (int i = 0; i < n.size(); i++) {
        List<String> vars = getPatternVariables(n.getGeneric(i));
        if (null != vars) res.addAll(vars);
      }
      return res;

    } else if ("RecordPattern".equals(name)) {
      List<String> res = new ArrayList<String>();
      for (int i = 0; i < n.size(); i++) {
        List<String> vars = getPatternVariables(n.getGeneric(i));
        if (null != vars) res.addAll(vars);
      }
      return res;

    } else if ("FieldPattern".equals(name)) {
      return getPatternVariables(n.getGeneric(1));

    } else return new ArrayList<String>();
  }

  /** Run this transformer. */
  public void run() {     
    dispatch(typical);
  }

  /**
   * Return the ast of the generated program.
   *
   * @return The ast of the output type checker.
   */
  public GNode getCheckerAST() {
    return transformed;
  }
   
  /**
   * Return the ast of the generated types file.
   *
   * @return The ast of the output types file.
   */
  public GNode getTypesAST() {
    return typesAST;
  }  

  /**
   * Return the ast of the generated support file.
   *
   * @return The ast of the output support file.
   */
  public GNode getSupportAST() {
    return supportAST;
  } 

  /**
   * Check which libraries are used, set the corresponding flag variables.
   *
   * @param n The root of the generated code to check.
   */
  private void setFlagVariables(Node n) {
        
    FlagSetter set = new FlagSetter((GNode)n);
        
    isListUsed       = set.isListUsed;
    isArrayListUsed  = set.isArrayListUsed;
    isBigIntegerUsed = set.isBigIntegerUsed;
    isPairUsed       = set.isPairUsed;
    isNodeUsed       = set.isNodeUsed;
    isGNodeUsed      = set.isGNodeUsed;
    isPrimitivesUsed = set.isPrimitivesUsed;
    isRecordUsed     = set.isRecordUsed;
    isVariantUsed    = set.isVariantUsed;
    isTupleUsed      = set.isTupleUsed;
    isReductionUsed  = set.isReductionUsed;
    isNameUsed       = set.isNameUsed;
    isScopeUsed      = set.isScopeUsed;
    isScopeKindUsed  = set.isScopeKindUsed; 
    isAnalyzerUsed   = set.isAnalyzerUsed;    
  }

  /**
   * Import libraries that are used.
   *
   * @param compUnit The CompilationUnit node.
   * @param n The node to check which libraries are used.
   * @param fileName The importing file, XXXAnalyzer, XXXTypes or XXXSuppport
   */
  private void addImports(Node compUnit, Node n, String fileName) {

    setFlagVariables(n);
    Node importNode; 
    
    if (isBigIntegerUsed) {
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "java", "math", "BigInteger"),
        null);
      compUnit.add(importNode);
    }
    
    if (isListUsed) {
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "java", "util", "List"),null);
      compUnit.add(importNode);
    }
   
    if (isArrayListUsed) { 
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "java", "util", "ArrayList"),
        null);
      compUnit.add(importNode); 
    } 
    
    if (isPairUsed) {
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "xtc", "util", "Pair"), null);
      compUnit.add(importNode);
    }
    
    if ("Analyzer".equals(fileName)) {
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "xtc", "util", "Runtime"), null);
      compUnit.add(importNode);
     
      importNode = GNode.create("ImportDeclaration", null,
       GNode.create("QualifiedIdentifier", "xtc", "util", "Function"), null);
      compUnit.add(importNode);
    }

    if (isNodeUsed) {
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "xtc", "tree", "Node"), null);
      compUnit.add(importNode);
    }
    
    if (isGNodeUsed) {
      importNode = GNode.create("ImportDeclaration", null,
        GNode.create("QualifiedIdentifier", "xtc", "tree", "GNode"), null);
      compUnit.add(importNode);
    } 
    
    // Check and import xtc.typical.XXX
    if (!"xtc.typical".equals(packageName)) {
      if ("Analyzer".equals(fileName) || isAnalyzerUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Analyzer"), 
          (GNode)null);
        compUnit.add(importNode);
      }
     
      if (isPrimitivesUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Primitives"), 
          (GNode)null);
        compUnit.add(importNode);
      }

      if (isRecordUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Record"), 
          (GNode)null);
        compUnit.add(importNode);
      }

      if (isVariantUsed) {    
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Variant"), 
          (GNode)null);
        compUnit.add(importNode);
      }
  
      if (isTupleUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Tuple"), 
          (GNode)null);
        compUnit.add(importNode);
      }
    
      if (isReductionUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Reduction"),
          (GNode)null);
        compUnit.add(importNode);
      }
   
      if (isNameUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Name"),
          (GNode)null);
        compUnit.add(importNode);
      }
    
      if (isScopeUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "Scope"),
          (GNode)null);
        compUnit.add(importNode);
      }
 
      if (isScopeKindUsed) {
        importNode = GNode.create("ImportDeclaration", null,
          GNode.create("QualifiedIdentifier", "xtc", "typical", "ScopeKind"),
          (GNode)null);
        compUnit.add(importNode);
      }
    }
  }

  /**
   * Make the class body for the XXXAnalyzer
   *
   * @return The node of the class body
   */
  private Node makeClassBody() {
    // Make the constructor
    Node constructor;
    if ("Typical".equals(output)) {
      constructor = factory.makeConstructor(output + "Analyzer"); 
      Node fieldDecl = factory.nodeTypeDecl();
      // return the class body node
      return GNode.create("ClassBody", fieldDecl, constructor);
    } else {
      constructor = factory.makeConstructor2(output + "Analyzer");
      // return the class body node
      return GNode.create("ClassBody", constructor); 
    }    
  } 

  /**
   * Create the typechecker skeleton.
   *
   * @return The root of the type checker ast.
   */
  private Node makeSkeleton() {    
    Node classDec = GNode.create("ClassDeclaration",
      toModifiers("public"),
      output + "Analyzer",
      null,
      GNode.create("Extension", GNode.create("Type",
        GNode.create("QualifiedIdentifier", "Analyzer"),null)),null, 
      cbody);

    Node compUnit = GNode.create("CompilationUnit", 8);
    compUnit.add(packageNode);
    addImports(compUnit, cbody, "Analyzer");      
    compUnit.add(comment(classDec,
                         "Type checker for " + output + "."));
    return compUnit; 
  }  

  /**
   * Create the skeleton for the xxxTypes.java file.
   *
   * @return The root of the XXXTypes.java ast.
   */
  private Node makeTypesSkeleton() {
    Node compUnit = GNode.create("CompilationUnit", 8);
    compUnit.add(packageNode);
    addImports(compUnit, tbody, "Types");
   
    Node classDecl = factory.classDecl2(output + "Types");
    classDecl.set(5, tbody);

    compUnit.add(comment(classDecl,
                         "Types for " + output + "."));
    return compUnit;    
  }

  /**
   * Create the skeleton for the xxxSupport.java file.
   *
   * @return The root of the XXXSupport.java ast.
   */
  private Node makeSupportSkeleton() {
    Node compUnit = GNode.create("CompilationUnit", 8);
    compUnit.add(packageNode);
    addImports(compUnit, sbody, "Support");
    
    Node classDecl = factory.classDecl2(output + "Support");
    classDecl.set(5, sbody);

    compUnit.add(comment(classDecl,
                         "Helper functionality for " + output + "."));
    return compUnit;    
  }

  /** 
   * Add an enumeration constant. 
   * 
   * @param s The name of the enum.
   */
  private void addEnum(Node enums, String s) {
    enums.add(GNode.create("EnumConstant", null, s, null, null));
  }
   
  /**
   * Enter the scoped named.
   *
   * @param n The scope name.
   */
  private void enterScope(String n) {
    table.enter(n);
  }

  /**
   * Exit the scope named n.
   *
   * @param n The scope name.
   */
  private void exitScope(String n) {
    if (!(n.equals(table.current().getName()))) {
      throw new AssertionError("mismatched scope exit " + n);      
    } else {
      table.exit();
    }
  }
  
  /**
   * Debug function to check for type annotations.
   *
   * @param n The node to check.
   */
  private void checkTypeAnnotation(GNode n) {
    if (!n.hasProperty(TYPE)) {
      throw new AssertionError("no type annotation for " + n.getName());      
    } else if (n.getProperty(TYPE) == null) {
      throw new AssertionError(n.getName() + " has null type");
    }
  }  
 
  /**
   * Utility function to print a nicely formatted ast for debugging.
   * 
   * @param n The ast root.
   */
  @SuppressWarnings("unused")
  private final void printAST(Node n) {
    runtime.console().pln().format(n).pln().flush();
  }
  
  /** Print the symbol table. */
  @SuppressWarnings("unused")
  private final void printSymbolTable() {
    if (null != table) {
      Visitor visitor = runtime.console().visitor();
      try {
        table.root().dump(runtime.console());
      } finally {
        runtime.console().register(visitor);
      } 
      runtime.console().flush();
    } else {
      throw new AssertionError("Symbol table not initialized");      
    }
  }

  /**
   * Return the qualified name of a type object.
   *
   * @param o The type object.
   * @return The (possibly prefixed) type.
   */
  private String getType(Object o) {
    return mapper.toTypeString(o);    
  }

  /**
   * Wrap the specified node in a documentation comment.
   *
   * @param node The node.
   * @param text The comment's text.
   * @return The commented node.
   */
  public static Node comment(Node node, String... text) {
    final List<String> l = new ArrayList<String>(text.length);
    for (String s : text) l.add(s);
    return new Comment(Comment.Kind.DOCUMENTATION, l, node);
  }

  /**
   * Convert the specified string to a modifier in a list of modifiers.
   *
   * @param mod The modifier
   * @return The corresponding node.
   */
  public static Node toModifiers(String mod) {
    return GNode.create("Modifiers", GNode.create("Modifier", mod));
  }

  /**
   * Convert the specified string to a literal node.
   *
   * @param name The node name.
   * @param literal The literal as a string.
   * @return The corresponding literal node.
   */
  public static Node toLiteral(String name, String literal) {
    return GNode.create(name, literal);
  }
  
  /**
   * Create an identifier node with the specified name.
   *
   * @param name The name.
   * @return The corresponding identifier node.
   */
  public static Node toIdentifier(String name) {
    if (null == name) throw new AssertionError("null name in toIdentifier");
    return GNode.create("PrimaryIdentifier", name);
  }

  /**
   * Create a conditional statement node.
   *
   * @param condition The condition node.
   * @param action The action statement or block.
   * @return The conditional node.
   */  
  public Node toIfStatement(Node condition, Node action) {
    
    action = (null == action) ? GNode.create("Block") : action;
    
    Node block = (action.hasName("Block")) 
        ? GNode.ensureVariable(GNode.cast(action))
        : GNode.ensureVariable(GNode.create("Block", action));
    
    return GNode.create("ConditionalStatement", condition, block, null);
  }
  
  /**
   * Create a case statemnt
   * 
   * @param clause
   * @param action
   */
  private Node makeCase(Node clause, List<Node> actions, String s) {
    Node c = factory.caseStmnt(clause, actions).getGeneric(1);    
    return c;
  }

  /**
   * Create a type node.
   *
   * @param s The name of the type.
   * @return The type node.
   */
  private static Node toType(String s) {
    assert null != s :"null string"; 
    return 
      GNode.create("Type",GNode.create("QualifiedIdentifier",s), null); 
  }

  /**
   * Create new expression with type is a node.
   *   If args is null empty arguments will be used.
   *
   * @param name The type node.
   * @param args The arguments.
   * @param body The option anonymous class body.
   */
  private Node toNewExpression2(Node name, Node args, Node body) {
    if (null == args) args = GNode.create("Arguments");
 
    List<Object> arglisto = new ArrayList<Object>(args.size());
    args.addAllTo(arglisto);
    
    Object o = arglisto; // Hack to enable cast from List<Object> to
                         // List<Node>.

    @SuppressWarnings("unchecked")
    List<Node> arglistn = (List<Node>)o;
    Node newNode = factory.newExpr(name, arglistn);
    newNode.set(4, body);
    return newNode;
  }  

  /**
   * Extract the list of arguments from an Arguments node.
   *
   * @param args The arguments node.
   * @return The list of arguments.
   */
  @SuppressWarnings("unchecked")
  private static List<Node> makeArgumentList(Node args) {
    if (null == args) args = GNode.create("Arguments");
    List<Object> arguments = new ArrayList<Object>(args.size());
    args.addAllTo(arguments);

    Object o = arguments; // Hack to enable cast from List<Object> to
                          // List<Node>.
    return (List<Node>)(o);
  }
    
}
