docs: def defined_names(code, free_format=False):
      """
      Parses a cobol document and build a name tree.
  
      For convenience, it also returns the list of variables (PIC) and
      procedures (paragraphs).
  
      :param code: cobol code to parse. Default is None.
      :param free_format: True if the source code must be considered as coded
          in free format.
  
      :return: A tuple made up of the name tree root node, the list of variables
          and  the list of paragraphs.
      :rtype: Name, list of Name, list of Name
      """
      root_node = Name(Name.Type.Root, 0, 0, 'root')
      variables = []
      paragraphs = []
      lines = code.splitlines()
      last_div_node = None
      last_section_node = None
      last_vars = {}
      last_par = None
  
      for i, line in enumerate(lines):
          if not free_format:
              if len(line) >= 6:
                  line = 6 * " " + line[6:]
          column = len(line) - len(line.lstrip())
          if not line.isspace() and not line.strip().startswith("*"):
              line = line.strip()
              # DIVISIONS
              if regex.DIVISION.indexIn(line.upper()) != -1 and 'EXIT' not in line.upper():
                  # remember
                  if last_div_node is not None:
                      last_div_node.end_line = i
                  last_div_node, last_section_node = parse_division(
                      i, column, line, root_node, last_section_node)
              # SECTIONS
              elif regex.SECTION.indexIn(line.upper()) != -1 and 'EXIT' not in line.upper():
                  if last_section_node:
                      last_section_node.end_line = i
                  if last_div_node is None:
                      name = 'PROCEDURE DIVISION'
                      for to_check in ['WORKING-STORAGE', 'LOCAL-STORAGE', 'LINKAGE', 'REPORT ', 'SCREEN']:
                          if to_check in line.upper():
                              name = 'DATA DIVISION'
                      last_div_node = Name(Name.Type.Division, -1, -1, name, name)
                      root_node.add_child(last_div_node)
                  last_section_node = parse_section(
                      i, column, last_div_node, last_vars, line)
              # VARIABLES
              # PARAGRAPHS
              elif (last_div_node is not None and
                    "PROCEDURE DIVISION" in last_div_node.name.upper()):
                  tokens = line.upper().split(" ")
                  if len(tokens) == 1 and not tokens[0] in ALL_KEYWORDS:
                      p = parse_paragraph(
                          i, column, last_div_node, last_section_node, line)
                      if p:
                          paragraphs.append(p)
                          if last_par:
                              last_par.end_line = i
                          last_par = p
              elif regex.VAR_PATTERN.indexIn(line.upper()) != -1 or line.upper().lstrip().startswith('FD'):
                  if last_div_node is None:
                      last_div_node = Name(Name.Type.Division, -1, -1, 'DATA DIVISION', '')
                      root_node.add_child(last_div_node)
                  if last_section_node is None:
                      last_section_node = Name(Name.Type.Section, -1, -1, 'WORKING-STORAGE SECTION', '')
                      last_div_node.add_child(last_section_node)
                  v = parse_pic_field(
                      i, column, last_section_node, last_vars, line)
                  if v:
                      variables.append(v)
  
      # close last div
      if last_par:
          last_par.end_line = len(lines) - 1
      if last_div_node:
          last_div_node.end_line = len(lines)
      if root_node and last_div_node:
          root_node.end_line = last_div_node.end_line
  
      return root_node, variables, paragraphs