fn binop<T>(
        &mut self,
        left: Spanned<T>,
        trees: &TokenTrees,
        trees_idx: &mut usize,
        is_left: bool,
        op: ResOp,
        make_ast: impl Fn(Spanned<T>, Spanned<AST>) -> AST,
    ) -> Result<Spanned<AST>, Syntax> {
        let prec = Parser::op_prec(op);
        let prec = if is_left { prec.left() } else { prec };
        *trees_idx += 1; // move on from operator
        let right = self.expr(trees, trees_idx, prec)?;

        let combined = Span::combine(&left.span, &right.span);
        Ok(Spanned::new(make_ast(left, right), combined))
    }
TODO: just specify precedence directly?
Parses a binary operation.
Takes the left side of the operation,
whether or not the operation is left-associative,
the operator precedence,
and a function that creates the AST node.
passerine/src/compiler/parse.rs