'Parse an algebraic expression into a tree.
Store a `pyparsing.ParseResult` in `self.tree` with proper groupings to
reflect parenthesis and order of operations. Leave all operators in the
tree and do not parse any strings of numbers into their float versions.
Adding the groups and result names makes the `repr()` of the result
really gross. For debugging, use something like
print OBJ.tree.asXML()'

def parse_algebra(self):

    number_part = Word(nums)
    inner_number = ((number_part + Optional(('.' + Optional(number_part)))) | ('.' + number_part))
    inner_number = Combine(inner_number)
    number_suffix = MatchFirst((Literal(k) for k in SUFFIXES.keys()))
    plus_minus = (Literal('+') | Literal('-'))
    number = Group((((Optional(plus_minus) + inner_number) + Optional(((CaselessLiteral('E') + Optional(plus_minus)) + number_part))) + Optional(number_suffix)))
    number = number('number')
    expr = Forward()
    inner_varname = Word((alphas + '_'), (alphanums + '_'))
    varname = Group(inner_varname)('variable')
    varname.setParseAction(self.variable_parse_action)
    function = Group((((inner_varname + Suppress('(')) + expr) + Suppress(')')))('function')
    function.setParseAction(self.function_parse_action)
    atom = (((number | function) | varname) | (('(' + expr) + ')'))
    atom = Group(atom)('atom')
    pow_term = (atom + ZeroOrMore(('^' + atom)))
    pow_term = Group(pow_term)('power')
    par_term = (pow_term + ZeroOrMore(('||' + pow_term)))
    par_term = Group(par_term)('parallel')
    prod_term = (par_term + ZeroOrMore(((Literal('*') | Literal('/')) + par_term)))
    prod_term = Group(prod_term)('product')
    sum_term = ((Optional(plus_minus) + prod_term) + ZeroOrMore((plus_minus + prod_term)))
    sum_term = Group(sum_term)('sum')
    (expr << sum_term)
    self.tree = (expr + stringEnd).parseString(self.math_expr)[0]
