docs: def loads(data):
      """Parser entry point. Parses the output of a traceroute execution"""
      data += "\n_EOS_" # Append EOS token. Helps to match last RE_HOP
  
      # Get headers
      match_dest = RE_HEADER.search(data)
      dest_name = match_dest.group(1)
      dest_ip = match_dest.group(2)
  
      # The Traceroute is the root of the tree.
      traceroute = Traceroute(dest_name, dest_ip)
  
      # Get hops
      matches_hop = RE_HOP.findall(data)
  
      for match_hop in matches_hop:
          # Initialize a hop
          idx = int(match_hop[0])
          if match_hop[1]:
              asn = int(match_hop[1])
          else:
              asn = None
          hop = Hop(idx, asn)
  
          # Parse probes data: <name> | <(IP)> | <rtt> | 'ms' | '*'
          probes_data = match_hop[2].split()
          # Get rid of 'ms': <name> | <(IP)> | <rtt> | '*'
          probes_data = filter(lambda s: s.lower() != 'ms', probes_data)
  
          i = 0
          while i < len(probes_data):
              # For each hop parse probes
              name = None
              ip = None
              rtt = None
              anno = ''
  
              # RTT check comes first because RE_PROBE_NAME can confuse rtt with an IP as name
              # The regex RE_PROBE_NAME can be improved
              if RE_PROBE_RTT.match(probes_data[i]):
                  # Matched rtt, so name and IP have been parsed before
                  rtt = float(probes_data[i])
                  i += 1
              elif RE_PROBE_NAME.match(probes_data[i]):
                  # Matched a name, so next elements are IP and rtt
                  name = probes_data[i]
                  ip = probes_data[i+1].strip('()')
                  rtt = float(probes_data[i+2])
                  i += 3
              elif RE_PROBE_TIMEOUT.match(probes_data[i]):
                  # Its a timeout, so maybe name and IP have been parsed before
                  # or maybe not. But it's Hop job to deal with it
                  rtt = None
                  i += 1
              else:
                  ext = "i: %d\nprobes_data: %s\nname: %s\nip: %s\nrtt: %s\nanno: %s" % (i, probes_data, name, ip, rtt, anno)
                  raise ParseError("Parse error \n%s" % ext)
              # Check for annotation
              try:
                  if RE_PROBE_ANNOTATION.match(probes_data[i]):
                      anno = probes_data[i]
                      i += 1
              except IndexError:
                  pass
  
              probe = Probe(name, ip, rtt, anno)
              hop.add_probe(probe)
  
          traceroute.add_hop(hop)
  
      return traceroute