code: def main_execute(expr, params=None, scope=None, aggcontext=None, **kwargs):
      
      if scope is None:
          scope = {}
  
      if params is None:
          params = {}
  
      # TODO: make expresions hashable so that we can get rid of these .op()
      # calls everywhere
      params = {k.op() if hasattr(k, 'op') else k: v for k, v in params.items()}
  
      new_scope = toolz.merge(scope, params)
      return execute_with_scope(expr, new_scope, aggcontext=aggcontext, **kwargs)
docs: Execute an expression against data that are bound to it. If no data
      are bound, raise an Exception.
  
      Parameters
      ----------
      expr : ibis.expr.types.Expr
          The expression to execute
      params : Mapping[ibis.expr.types.Expr, object]
          The data that an unbound parameter in `expr` maps to
      scope : Mapping[ibis.expr.operations.Node, object]
          Additional scope, mapping ibis operations to data
      aggcontext : Optional[ibis.pandas.aggcontext.AggregationContext]
          An object indicating how to compute aggregations. For example,
          a rolling mean needs to be computed differently than the mean of a
          column.
      kwargs : Dict[str, object]
          Additional arguments that can potentially be used by individual node
          execution
  
      Returns
      -------
      result : Union[
          pandas.Series, pandas.DataFrame, ibis.pandas.core.simple_types
      ]
  
      Raises
      ------
      ValueError
          * If no data are bound to the input expression