@SuppressWarnings("unchecked")
  private static ListASTNode<? extends ASTNode> searchList(ASTNode top) {
    if (top instanceof ListASTNode) return (ListASTNode<? extends ASTNode>) top;

    ASTNode parent = top.getParent();
    if (parent == null) return null;

    return searchList(parent);
  }
  /**
   * Searches documentation for <code>field</code>.
   *
   * @param field the identifier of some field
   * @param region additional information about the document
   * @return the documentation or <code>null</code>
   */
  protected String getFieldDocumentation(Identifier field, DocumentRegionInformation region) {
    ASTNode node = field;

    while (node != null) {
      if (node instanceof FunctionDefinition) {
        FunctionDefinition definition = (FunctionDefinition) node;
        FieldModelNode fieldNode = definition.resolveNode();
        if (fieldNode != null) {
          Name[] arguments = fieldNode.getArgumentNames();
          if (arguments != null) {
            for (Name name : arguments) {
              if (name != null) {
                if (name.toIdentifier().equals(field.getName())) {
                  NesCDocComment comment = getFunctionDocumentation(definition, region);
                  if (comment == null) return null;
                  return comment.getAnalysis().getParameterDescription(field.getName());
                }
              }
            }
          }
        }
      }

      NesCDocComment[] comments = node.getComments();
      if (comments != null) {
        for (int i = comments.length - 1; i >= 0; i--) {
          String documentation = comments[i].getAnalysis().getParameterDescription(field.getName());
          if (documentation != null) return documentation;
        }
      }

      node = node.getParent();
    }

    return null;
  }
  /**
   * Method to retrieve as much as possible from the generated Abstract Syntax Tree even if the
   * parser stopped with a heavy error.
   *
   * @param result the result of the parser
   * @param stack the parsers stack
   * @param remaining the remaining errors on the parser
   * @return a node that somehow combines all remaining elements
   */
  public static ASTNode parseAST(Symbol result, Stack<?> stack, List<ErrorASTNode> remaining) {
    ASTNode root;
    if (result != null && result.value instanceof ASTNode) {
      root = (ASTNode) result.value;
    } else {
      root = new TranslationUnit();
    }

    while (root.getParent() != null) root = root.getParent();

    // search for nodes that are still on the stack (but should not be there)
    List<ASTNode> begin = new ArrayList<ASTNode>();
    for (int i = 0, n = stack.size() - 1; i < n; i++) {
      Symbol next = (Symbol) stack.get(i);
      if (next.value instanceof ASTNode) {
        if (next.value != root) {
          begin.add((ASTNode) next.value);
        }
      }
    }

    if (!begin.isEmpty()) {
      // the stack was not built correct, try put these things together
      begin.add(root);
      Collections.sort(
          begin,
          new Comparator<ASTNode>() {
            public int compare(ASTNode a, ASTNode b) {
              if (a == b) return 0;

              Range rangeA = a.getRange();
              Range rangeB = b.getRange();

              if (rangeA.getLeft() < rangeB.getLeft()) return -1;

              if (rangeA.getLeft() > rangeB.getLeft()) return 1;

              if (rangeA.getRight() < rangeB.getRight()) return -1;

              if (rangeA.getRight() > rangeB.getRight()) return 1;

              return 0;
            }
          });

      root = begin.get(begin.size() - 1);
      for (int i = begin.size() - 2; i >= 0; i--) {
        ASTNode next = begin.get(i);
        root = merge(next, root);
      }
    }

    if (!remaining.isEmpty()) {
      if (!(root instanceof ListASTNode)) {
        root = new ListErrorASTNode(root);
      }

      ListASTNode<?> list = (ListASTNode<?>) root;

      for (ErrorASTNode error : remaining) list.addError(error);
    }

    return root;
  }
  private static ASTNode parent(ASTNode node) {
    while (node.getParent() != null) node = node.getParent();

    return node;
  }