def _subspan(self, s, span, nextspan):
        """Recursively subdivide spans based on a series of rules."""
        text = s[span[0]:span[1]]
        lowertext = text.lower()

        # Skip if only a single character or a split sequence
        if span[1] - span[0] < 2 or text in self.SPLIT or text in self.SPLIT_END_WORD or text in self.SPLIT_START_WORD or lowertext in self.NO_SPLIT:
            return [span]

        # Skip if it looks like URL
        if text.startswith('http://') or text.startswith('ftp://') or text.startswith('www.'):
            return [span]

        # Split full stop at end of final token (allow certain characters to follow) unless ellipsis
        if self.split_last_stop and nextspan is None and text not in self.NO_SPLIT_STOP and not text[-3:] == '...':
            if text[-1] == '.':
                return self._split_span(span, -1)
            ind = text.rfind('.')
            if ind > -1 and all(t in '\'‘’"“”)]}' for t in text[ind + 1:]):
                return self._split_span(span, ind, 1)

        # Split off certain sequences at the end of a token
        for spl in self.SPLIT_END:
            if text.endswith(spl) and len(text) > len(spl):
                return self._split_span(span, -len(spl), 0)

        # Split off certain sequences at the end of a word
        for spl in self.SPLIT_END_WORD:
            if text.endswith(spl) and len(text) > len(spl) and text[-len(spl) - 1].isalpha():
                return self._split_span(span, -len(spl), 0)

        # Split off certain sequences at the end of a word
        for spl in self.SPLIT_START_WORD:
            if text.startswith(spl) and len(text) > len(spl) and text[-len(spl) - 1].isalpha():
                return self._split_span(span, len(spl), 0)

        # Split around certain sequences
        for spl in self.SPLIT:
            ind = text.find(spl)
            if ind > -1:
                return self._split_span(span, ind, len(spl))

        # Split around certain sequences unless followed by a digit
        # - We skip this because of difficulty with chemical names.
        # for spl in self.SPLIT_NO_DIGIT:
        #     ind = text.rfind(spl)
        #     if ind > -1 and (len(text) <= ind + len(spl) or not text[ind + len(spl)].isdigit()):
        #         return self._split_span(span, ind, len(spl))

        # Split off certain sequences at the end of a token unless preceded by a digit
        for spl in self.SPLIT_END_NO_DIGIT:
            if text.endswith(spl) and len(text) > len(spl) and not text[-len(spl) - 1].isdigit():
                return self._split_span(span, -len(spl), 0)

        # Regular Bracket at both start and end, break off both provided they correspond
        if text.startswith('(') and text.endswith(')') and self._closing_bracket_index(text) == len(text) - 1:
            return self._split_span(span, 1, len(text)-2)

        # Split things like IR(KBr)
        if text.startswith('IR(') and text.endswith(')'):
            return self._split_span(span, 2, 1)

        # Split things like \d+\.\d+([a-z]+) e.g. UV-vis/IR peaks with bracketed strength/shape
        m = re.match('^(\d+\.\d+|\d{3,})(\([a-z]+\))$', text, re.I)
        if m:
            return self._split_span(span, m.start(2), 1)

        # Split brackets off start and end if the corresponding bracket isn't within token
        for bpair in [('(', ')'), ('{', '}'), ('[', ']')]:
            #level = bracket_level(text, open=[bpair[0]], close=[bpair[1]])
            # Bracket at start, bracketlevel > 0, break it off
            if text.startswith(bpair[0]) and self._closing_bracket_index(text, bpair=bpair) is None:
                return self._split_span(span, 1, 0)
            # Bracket at end, bracketlevel < 0, break it off
            if text.endswith(bpair[1]) and self._opening_bracket_index(text, bpair=bpair) is None:
                return self._split_span(span, -1, 0)

        # TODO: Consider splitting around comma in limited circumstances. Mainly to fix whitespace errors.

        # Characters to split around, but with exceptions
        for i, char in enumerate(text):
            before = text[:i]
            after = text[i+1:]
            if char in {':', ';'}:
                # Split around colon unless it looks like we're in a chemical name
                if not (before and after and after[0].isdigit() and before.rstrip('′\'')[-1:].isdigit() and '-' in after) and not (self.NO_SPLIT_CHEM.search(before) and self.NO_SPLIT_CHEM.search(after)):
                    return self._split_span(span, i, 1)
            elif char in {'x', '+', '−'}:
                # Split around x, +, − (\u2212 minus) between two numbers or at start followed by numbers
                if (i == 0 or self._is_number(before)) and self._is_number(after):
                    return self._split_span(span, i, 1)
                # Also plit around − (\u2212 minus) between two letters
                if char == '−' and before and before[-1].isalpha() and after and after[0].isalpha():
                    return self._split_span(span, i, 1)
            elif char == '±':
                # Split around ± unless surrounded by brackets
                if not (before and after and before[-1] == '(' and after[0] == ')'):
                    return self._split_span(span, i, 1)
            elif char == '/':
                # Split around / unless '+/-' or '-/-' etc.
                if not (before and after and before[-1] in self.NO_SPLIT_SLASH and after[0] in self.NO_SPLIT_SLASH):
                    return self._split_span(span, i, 1)
            elif char == '>':
                if not (before and before[-1] == '-'):
                    # Split if preceding is not -
                    return self._split_span(span, i, 1)
                if before and before[-1] == '-':
                    # If preceding is -, split around -> unless in chemical name
                    if not text == '->' and not self._is_saccharide_arrow(before[:-1], after):
                        return self._split_span(span, i-1, 2)
            elif char is '→' and not self._is_saccharide_arrow(before, after):
                # TODO: 'is' should be '=='... this never splits!?
                # Split around → unless in chemical name
                return self._split_span(span, i, 1)
            elif char == '(' and self._is_number(before) and not '(' in after and not ')' in after:
                # Split around open bracket after a number
                return self._split_span(span, i, 1)
            elif char == '-':
                lowerbefore = lowertext[:i]
                lowerafter = lowertext[i+1:]
                # Always split on -of-the- -to- -in- -by- -of- -or- -and- -per- -the-
                if lowerafter[:7] == 'of-the-':
                    return [(span[0], span[0] + i), (span[0] + i, span[0] + i + 1), (span[0] + i + 1, span[0] + i + 3), (span[0] + i + 3, span[0] + i + 4), (span[0] + i + 4, span[0] + i + 7), (span[0] + i + 7, span[0] + i + 8), (span[0] + i + 8, span[1])]
                if lowerafter[:5] in {'on-a-', 'of-a-'}:
                    return [(span[0], span[0] + i), (span[0] + i, span[0] + i + 1), (span[0] + i + 1, span[0] + i + 3), (span[0] + i + 3, span[0] + i + 4), (span[0] + i + 4, span[0] + i + 5), (span[0] + i + 5, span[0] + i + 6), (span[0] + i + 6, span[1])]
                if lowerafter[:3] in {'to-', 'in-', 'by-', 'of-', 'or-', 'on-'}:
                    return [(span[0], span[0] + i), (span[0] + i, span[0] + i + 1), (span[0] + i + 1, span[0] + i + 3), (span[0] + i + 3, span[0] + i + 4), (span[0] + i + 4, span[1])]
                if lowerafter[:4] in {'and-', 'per-', 'the-'}:
                    return [(span[0], span[0] + i), (span[0] + i, span[0] + i + 1), (span[0] + i + 1, span[0] + i + 4), (span[0] + i + 4, span[0] + i + 5), (span[0] + i + 5, span[1])]

                # By default we split on hyphens
                split = True
                if lowerafter == 'nmr':
                    split = True  # Always split NMR off end
                elif bracket_level(text) == 0 and (not bracket_level(after) == 0 or not bracket_level(before) == 0):
                    split = False  # Don't split if within brackets
                elif after and after[0] == '>':
                    split = False  # Don't split if followed by >
                elif lowerbefore in self.NO_SPLIT_PREFIX or lowerafter in self.NO_SPLIT_SUFFIX:
                    split = False  # Don't split if prefix or suffix in list
                elif self.NO_SPLIT_PREFIX_ENDING.search(lowerbefore):
                    split = False  # Don't split if prefix ends with pattern
                elif lowerafter in self.SPLIT_SUFFIX:
                    split = True  # Do split if suffix in list
                elif len(before) <= 1 or len(after) <= 2:
                    split = False  # Don't split if not at least 2 char before and 3 after
                elif self.NO_SPLIT_CHEM.search(lowerbefore) or self.NO_SPLIT_CHEM.search(lowerafter):
                    split = False  # Don't split if prefix or suffix match chem regex
                if split:
                    return self._split_span(span, i, 1)

                # TODO: Errors:
                # [³H]-choline
                # S,S'-...
                # 1,4-di-substituted
                # 11-β - hydroxysteroid
                # Spelt out greek: 11beta - hydroxysteroid
                # ...-N-substituted like 2,5-dimethyl-N-substituted pyrroles
                # 4-(2-Butyl-6,7-dichloro-2-cyclopentyl-indan-1-on-5-yl) oxobutyric acid
                # Adenosine - monophosphate
                # Consistency for amino acids: Arg-Arg and Arg-Arg-Asp... probably always split
                # D,L-α-peptide?
                # N'-formylkynurenine
                # poly(D,L-lactic acid )?
                # poly(methyl metha-acrylate )?
                # Poly(N - alkyl Acrylamide )
                # poly(N - isopropyl acrylamide )
                # R,S - lorazepam
                # S,S - dioxide

        # Split units off the end of a numeric value
        quantity = self.QUANTITY_RE.search(text)
        if quantity:
            return self._split_span(span, len(quantity.group(6) or quantity.group(3) or quantity.group(2)), 0)

        # Split pH off the start of a numeric value
        if text.startswith('pH') and self._is_number(text[2:]):
            return self._split_span(span, 2, 0)

        # Split contraction words
        for contraction in self.CONTRACTIONS:
            if lowertext == contraction[0]:
                return self._split_span(span, contraction[1])

        if nextspan:
            nexttext = s[nextspan[0]:nextspan[1]]
            # Split NMR isotope whitespace errors (joined with previous sentence full stop)
            if nexttext == 'NMR':
                ind = text.rfind('.')
                if ind > -1 and text[ind + 1:] in {'1H', '13C', '15N', '31P', '19F', '11B', '29Si', '170', '73Ge', '195Pt', '33S', '13C{1H}'}:
                    return self._split_span(span, ind, 1)


        return [span]