docs: def from_string(string, conv, bound=r'.+?', disj=r' ?\| ?', sep=r', ?', left_open=r'\(',
                  left_closed=r'\[', right_open=r'\)', right_closed=r'\]', pinf=r'\+inf', ninf=r'-inf'):
      """
      Parse given string and create an Interval instance.
      A converter function has to be provided to convert a bound (as string) to a value.
  
      :param string: string to parse.
      :param conv: function that converts a bound (as string) to an object.
      :param bound: pattern that matches a value.
      :param disj: pattern that matches the disjunctive operator (default matches '|').
      :param sep: pattern that matches a bounds separator (default matches ',').
      :param left_open: pattern that matches a left open boundary (default matches '(').
      :param left_closed: pattern that matches a left closed boundary (default matches '[').
      :param right_open: pattern that matches a right open boundary (default matches ')').
      :param right_closed: pattern that matches a right closed boundary (default matches ']').
      :param pinf: pattern that matches a positive infinity (default matches '+inf').
      :param ninf: pattern that matches a negative infinity (default matches '-inf').
      :return: an Interval instance.
      """
  
      re_left_boundary = r'(?P<left>{}|{})'.format(left_open, left_closed)
      re_right_boundary = r'(?P<right>{}|{})'.format(right_open, right_closed)
      re_bounds = r'(?P<lower>{bound})({sep}(?P<upper>{bound}))?'.format(bound=bound, sep=sep)
      re_interval = r'{}(|{}){}'.format(re_left_boundary, re_bounds, re_right_boundary)
      re_intervals = r'{}(?P<disj>{})?'.format(re_interval, disj)
  
      intervals = []
      has_more = True
  
      def _convert(bound):
          if re.match(pinf, bound):
              return inf
          elif re.match(ninf, bound):
              return -inf
          else:
              return conv(bound)
  
      while has_more:
          match = re.match(re_intervals, string)
          if match is None:
              has_more = False
          else:
              group = match.groupdict()
  
              left = re.match(left_closed + '$', group['left']) is not None
              right = re.match(right_closed + '$', group['right']) is not None
  
              lower = group.get('lower', None)
              upper = group.get('upper', None)
              lower = _convert(lower) if lower is not None else inf
              upper = _convert(upper) if upper is not None else lower
  
              intervals.append(AtomicInterval(left, lower, upper, right))
              string = string[match.end():]
  
      return Interval(*intervals)