Parse reaction string.

def parse(self, s):
        """Parse reaction string."""

        global_comp = None
        if self._parse_global:
            # Split by colon for global compartment information
            m = re.match(r'^\s*\[(\w+)\]\s*:\s*(.*)', s)
            if m is not None:
                global_comp = m.group(1)
                s = m.group(2)

        expect_operator = False
        direction = None
        left = []
        right = []
        current_side = left
        saved_token = None

        def tokenize():
            for match in re.finditer(self._scanner, s):
                for i, group in enumerate(match.groups()):
                    if group is not None:
                        token = self._tokens[i][1]
                        span = match.start(), match.end()
                        if token is None:
                            raise ParseError(
                                'Invalid token in expression string:'
                                ' {!r}'.format(group), span=span)
                        yield self._tokens[i][1], group, span
                        break

        for token, value, span in tokenize():
            # Handle partially parsed compound.
            if saved_token is not None and (
                    token in (_ReactionToken.Plus,
                              _ReactionToken.Arrow,
                              _ReactionToken.End)):
                compound = parse_compound(saved_token, global_comp)
                current_side.append((compound, 1))
                expect_operator = True
                saved_token = None

            if token == _ReactionToken.Plus:
                # Compound separator. Expect compound name or compound count
                # next.
                if not expect_operator:
                    raise ParseError('Unexpected token: {!r}'.format(value),
                                     span=span)
                expect_operator = False
            elif token == _ReactionToken.Arrow:
                # Reaction arrow. Expect compound name or compound count next.
                if direction is not None:
                    raise ParseError(
                        'More than one equation arrow: {!r}'.format(value),
                        span=span)

                if not expect_operator and len(left) > 0:
                    raise ParseError('Unexpected token: {!r}'.format(value),
                                     span=span)

                expect_operator = False
                direction = self._arrows[value]
                current_side = right
            elif token == _ReactionToken.Group:
                # Compound count. Expect compound name next.
                if expect_operator:
                    raise ParseError('Expected plus or arrow: {!r}'.format(
                        value), span=span)
                if saved_token is not None:
                    raise ParseError('Expected compound name: {!r}'.format(
                        value), span=span)
                saved_token = value
            elif token == _ReactionToken.Quoted:
                # Compound name. Expect operator next.
                if expect_operator:
                    raise ParseError('Expected plus or arrow: {!r}'.format(
                        value), span=span)
                if saved_token is not None:
                    try:
                        count = parse_compound_count(saved_token)
                    except ValueError as e:
                        raise_from(ParseError(
                            'Unable to parse compound count: {!r}'.format(
                                saved_token),
                            span=span), e)
                else:
                    count = 1
                compound = parse_compound(value, global_comp)
                current_side.append((compound, count))
                expect_operator = True
                saved_token = None
            elif token == _ReactionToken.Other:
                # Could be count or compound name. Store and expect other,
                # quoted, operator or end.
                if expect_operator:
                    raise ParseError('Expected plus or arrow: {!r}'.format(
                        value), span=span)
                if saved_token is not None:
                    try:
                        count = parse_compound_count(saved_token)
                    except ValueError as e:
                        raise_from(ParseError(
                            'Unable to parse compound count: {!r}'.format(
                                saved_token),
                            span=span), e)
                    compound = parse_compound(value, global_comp)
                    current_side.append((compound, count))
                    expect_operator = True
                    saved_token = None
                else:
                    saved_token = value

        if direction is None:
            raise ParseError('Missing equation arrow')

        return Reaction(direction, left, right)