code: public static GroovyExpression copyWithNewLeafNode(AbstractFunctionExpression expr, GroovyExpression newLeaf) {
  
  
          AbstractFunctionExpression result = (AbstractFunctionExpression)expr.copy();
  
          //remove leading anonymous traversal expression, if there is one
          if(FACTORY.isLeafAnonymousTraversalExpression(expr)) {
              result = (AbstractFunctionExpression)newLeaf;
          } else {
              GroovyExpression newCaller = null;
              if (expr.getCaller() == null) {
                  newCaller = newLeaf;
              } else {
                  newCaller = copyWithNewLeafNode((AbstractFunctionExpression)result.getCaller(), newLeaf);
              }
              result.setCaller(newCaller);
          }
          return result;
      }
docs: Recursively copies and follows the caller hierarchy of the expression until we come
  to a function call with a null caller.  The caller of that expression is set
  to newLeaf.
  
  @param expr
  @param newLeaf
  @return the updated (/copied) expression