"eats" up the string until it hits an ending character to get valid leaf expressions.
        For example, given \\Phi_{z}(L) = \\sum_{i=1}^{N} \\frac{1}{C_{i} \\times V_{\\rm max, i}},
        this function would pull out \\Phi, stopping at _
        @ string: str
        returns a tuple of (expression [ex: \\Phi], remaining_chars [ex: _{z}(L) = \\sum_{i=1}^{N}...])

def _get_next_token(self, string):
        """
        "eats" up the string until it hits an ending character to get valid leaf expressions.
        For example, given \\Phi_{z}(L) = \\sum_{i=1}^{N} \\frac{1}{C_{i} \\times V_{\\rm max, i}},
        this function would pull out \\Phi, stopping at _
        @ string: str
        returns a tuple of (expression [ex: \\Phi], remaining_chars [ex: _{z}(L) = \\sum_{i=1}^{N}...])
        """
        STOP_CHARS = "_ {}^ \n ,()="
        UNARY_CHARS = "^_"
        # ^ and _ are valid leaf expressions--just ones that should be handled on their own
        if string[0] in STOP_CHARS:
            return string[0], string[1:]
        
        expression = []
        for i, c in enumerate(string):
            if c in STOP_CHARS:
                break
            else:
                expression.append(c)
        
        return "".join(expression), string[i:]