code: func (p *Parser) ParseExpr() (Expr, error) {
  	var err error
  	// Dummy root node.
  	root := &BinaryExpr{}
  
  	// Parse a non-binary expression type to start.
  	// This variable will always be the root of the expression tree.
  	root.RHS, err = p.parseUnaryExpr()
  	if err != nil {
  		return nil, err
  	}
  
  	// Loop over operations and unary exprs and build a tree based on precendence.
  	for {
  		// If the next token is NOT an operator then return the expression.
  		op, _, _ := p.ScanIgnoreWhitespace()
  		if !op.isOperator() {
  			p.Unscan()
  			return root.RHS, nil
  		}
  
  		// Otherwise parse the next expression.
  		var rhs Expr
  		if IsRegexOp(op) {
  			// RHS of a regex operator must be a regular expression.
  			if rhs, err = p.parseRegex(); err != nil {
  				return nil, err
  			}
  			// parseRegex can return an empty type, but we need it to be present
  			if rhs.(*RegexLiteral) == nil {
  				tok, pos, lit := p.ScanIgnoreWhitespace()
  				return nil, newParseError(tokstr(tok, lit), []string{"regex"}, pos)
  			}
  		} else {
  			if rhs, err = p.parseUnaryExpr(); err != nil {
  				return nil, err
  			}
  		}
  
  		// Find the right spot in the tree to add the new expression by
  		// descending the RHS of the expression tree until we reach the last
  		// BinaryExpr or a BinaryExpr whose RHS has an operator with
  		// precedence >= the operator being added.
  		for node := root; ; {
  			r, ok := node.RHS.(*BinaryExpr)
  			if !ok || r.Op.Precedence() >= op.Precedence() {
  				// Add the new expression here and break.
  				node.RHS = &BinaryExpr{LHS: node.RHS, RHS: rhs, Op: op}
  				break
  			}
  			node = r
  		}
  	}
  }
docs: // ParseExpr parses an expression.