fn rule_infix(
        &mut self,
        left: Spanned<AST>,
        trees: &TokenTrees,
        trees_idx: &mut usize,
    ) -> Result<Spanned<AST>, Syntax> {
        use ResOp::*;
        let tree: &Spanned<TokenTree> = &trees[*trees_idx];
        match &tree.item {
            TokenTree::Op(name) => match Parser::to_op(name, &tree.span)? {
                // Pattern-based
                Assign => self.assign(left, trees, trees_idx),
                Lambda => self.lambda(left, trees, trees_idx),

                // Simple binops
                Compose => self.binop(left, trees, trees_idx, true, Compose, |l, r| {
                    AST::Sugar(Sugar::comp(l, r))
                }),
                Is => self.binop(left, trees, trees_idx, true, Is, |l, r| {
                    AST::Sugar(Sugar::is(l, r))
                }),
                Field => self.binop(left, trees, trees_idx, true, Field, |l, r| {
                    AST::Sugar(Sugar::field(l, r))
                }),

                // Tuples
                Pair => {
                    // no expressions left to build
                    // handle the trailing comma
                    if trees.len() == *trees_idx + 1 {
                        *trees_idx += 1;
                        return Ok(left);
                    }

                    self.binop(left, trees, trees_idx, true, Pair, |l, r| {
                        let mut tuple = match l.item {
                            AST::Base(Base::Tuple(t)) => t,
                            _ => vec![l],
                        };
                        tuple.push(r);
                        AST::Base(Base::Tuple(tuple))
                    })
                }

                // Builtins
                Add => todo!(),
                Sub => todo!(),
                Mul => todo!(),
                Div => todo!(),
                Rem => todo!(),
                Equal => todo!(),
                Pow => todo!(),
            },

            _ => self.call(left, trees, trees_idx),
        }
    }
Looks at the current token and parses an infix
expression like an operator. Because an operator
can be used to split an expression across multiple
lines, this function ignores separator tokens
around the operator.
passerine/src/compiler/parse.rs