code: def roots(expr, types=(ops.PhysicalTable,)):
      
      stack = [
          arg.to_expr()
          for arg in reversed(expr.op().root_tables())
          if isinstance(arg, types)
      ]
  
      def extender(op):
          return reversed(
              list(
                  itertools.chain.from_iterable(
                      arg.op().root_tables()
                      for arg in op.flat_args()
                      if isinstance(arg, types)
                  )
              )
          )
  
      return _search_for_nodes(stack, extender, types)
docs: Yield every node of a particular type on which an expression depends.
  
      Parameters
      ----------
      expr : Expr
          The expression to analyze
      types : tuple(type), optional, default
              (:mod:`ibis.expr.operations.PhysicalTable`,)
          The node types to traverse
  
      Yields
      ------
      table : Expr
          Unique node types on which an expression depends
  
      Notes
      -----
      If your question is: "What nodes of type T does `expr` depend on?", then
      you've come to the right place. By default, we yield the physical tables
      that an expression depends on.