class_docstr: A NodeVisitor specialized for constructing a libcudf expression tree.
  
  This visitor is designed to handle AST nodes that have libcudf equivalents.
  It constructs column references from names and literals from constants,
  then builds up operations. The final result can be accessed using the
  `expression` property. The visitor must be kept in scope for as long as the
  expression is needed because all of the underlying libcudf expressions will
  be destroyed when the libcudfASTVisitor is.
  
  Parameters
  ----------
  col_names : Tuple[str]
      The column names used to map the names in an expression.
class_docstr_tok: ["A","NodeVisitor","specialized","for","constructing","a","libcudf","expression","tree",".","This","visitor","is","designed","to","handle","AST","nodes","that","have","libcudf","equivalents",".","It","constructs","column","references","from","names","and","literals","from","constants",",","then","builds","up","operations",".","The","final","result","can","be","accessed","using","the","`","expression","`","property",".","The","visitor","must","be","kept","in","scope","for","as","long","as","the","expression","is","needed","because","all","of","the","underlying","libcudf","expressions","will","be","destroyed","when","the","libcudfASTVisitor","is",".","Parameters","--","--","--","--","--","col_names",":","Tuple","[","str","]","The","column","names","used","to","map","the","names","in","an","expression","."]
code: def _visit_BoolOp_Compare(
      self, operators, operands, has_multiple_ops
  ):
      # Helper function handling the common components of parsing BoolOp and
      # Compare AST nodes. These two types of nodes both support chaining
      # (e.g. `a > b > c` is equivalent to `a > b and b > c`, so this
      # function helps standardize that.
  
      # TODO: Whether And/Or and BitAnd/BitOr actually correspond to
      # logical or bitwise operators depends on the data types that they
      # are applied to. We'll need to add logic to map to that.
      inner_ops = []
      for op, (left, right) in zip(operators, operands):
          # Note that this will lead to duplicate nodes, e.g. if
          # the comparison is `a < b < c` that will be encoded as
          # `a < b and b < c`. We could potentially optimize by caching
          # expressions by name so that we only construct them once.
          self.visit(left)
          self.visit(right)
  
          self.nodes.append(self.stack.pop())
          self.nodes.append(self.stack.pop())
  
          op = python_cudf_operator_map[type(op)]
          inner_ops.append(
              Operation(op, self.nodes[-1], self.nodes[-2])
          )
  
      self.nodes.extend(inner_ops)
  
      # If we have more than one comparator, we need to link them
      # together with LOGICAL_AND operators.
      if has_multiple_ops:
          op = ASTOperator.LOGICAL_AND
  
          def _combine_compare_ops(left, right):
              self.nodes.append(Operation(op, left, right))
              return self.nodes[-1]
  
          functools.reduce(_combine_compare_ops, inner_ops)
  
      self.stack.append(self.nodes[-1])
  
file_path: cudf/python/cudf/cudf/core/_internals/expressions.py
imports: import ast
  import functools
  from typing import List, Tuple
  from cudf._lib.expressions import ASTOperator, ColumnReference, Expression, Literal, Operation
label_desc: Use image node_id 7 for calling the libcudfASTVisitor obj's underlying member method code with example usage: obj._visit_BoolOp_Compare(operators, operands, has_multiple_ops) and returns: self