docs: def _parse_formula(s):
      """Parse formula string."""
      scanner = re.compile(r'''
          (\s+) |         # whitespace
          (\(|\)) |       # group
          ([A-Z][a-z]*) | # element
          (\d+) |         # number
          ([a-z]) |       # variable
          (\Z) |          # end
          (.)             # error
      ''', re.DOTALL | re.VERBOSE)
  
      def transform_subformula(form):
          """Extract radical if subformula is a singleton with a radical."""
          if isinstance(form, dict) and len(form) == 1:
              # A radical in a singleton subformula is interpreted as a
              # numbered radical.
              element, value = next(iteritems(form))
              if isinstance(element, Radical):
                  return Radical('{}{}'.format(element.symbol, value))
          return form
  
      stack = []
      formula = {}
      expect_count = False
  
      def close(formula, count=1):
          if len(stack) == 0:
              raise ParseError('Unbalanced parenthesis group in formula')
          subformula = transform_subformula(formula)
          if isinstance(subformula, dict):
              subformula = Formula(subformula)
  
          formula = stack.pop()
          if subformula not in formula:
              formula[subformula] = 0
          formula[subformula] += count
          return formula
  
      for match in re.finditer(scanner, s):
          (whitespace, group, element, number, variable, end,
              error) = match.groups()
  
          if error is not None:
              raise ParseError(
                  'Invalid token in formula string: {!r}'.format(match.group(0)),
                  span=(match.start(), match.end()))
          elif whitespace is not None:
              continue
          elif group is not None and group == '(':
              if expect_count:
                  formula = close(formula)
              stack.append(formula)
              formula = {}
              expect_count = False
          elif group is not None and group == ')':
              if expect_count:
                  formula = close(formula)
              expect_count = True
          elif element is not None:
              if expect_count:
                  formula = close(formula)
              stack.append(formula)
              if element in 'RX':
                  formula = Radical(element)
              else:
                  formula = Atom(element)
              expect_count = True
          elif number is not None and expect_count:
              formula = close(formula, int(number))
              expect_count = False
          elif variable is not None and expect_count:
              formula = close(formula, Expression(variable))
              expect_count = False
          elif end is not None:
              if expect_count:
                  formula = close(formula)
          else:
              raise ParseError(
                  'Invalid token in formula string: {!r}'.format(match.group(0)),
                  span=(match.start(), match.end()))
  
      if len(stack) > 0:
          raise ParseError('Unbalanced parenthesis group in formula')
  
      return Formula(formula)