Return a unicode object with the Gremlin representation of this expression.

def to_gremlin(self):
        """Return a unicode object with the Gremlin representation of this expression."""
        self.validate()
        edge_direction, edge_name = self.fold_scope_location.get_first_folded_edge()
        validate_safe_string(edge_name)

        inverse_direction_table = {
            'out': 'in',
            'in': 'out',
        }
        inverse_direction = inverse_direction_table[edge_direction]

        base_location_name, _ = self.fold_scope_location.base_location.get_location_name()
        validate_safe_string(base_location_name)

        _, field_name = self.fold_scope_location.get_location_name()
        validate_safe_string(field_name)

        if not self.folded_ir_blocks:
            # There is no filtering nor type coercions applied to this @fold scope.
            #
            # This template generates code like:
            # (
            #     (m.base.in_Animal_ParentOf == null) ?
            #     [] : (
            #         m.base.in_Animal_ParentOf.collect{entry -> entry.outV.next().uuid}
            #     )
            # )
            template = (
                u'((m.{base_location_name}.{direction}_{edge_name} == null) ? [] : ('
                u'm.{base_location_name}.{direction}_{edge_name}.collect{{'
                u'entry -> entry.{inverse_direction}V.next().{field_name}{maybe_format}'
                u'}}'
                u'))'
            )
            filter_and_traverse_data = ''
        else:
            # There is filtering or type coercions in this @fold scope.
            #
            # This template generates code like:
            # (
            #     (m.base.in_Animal_ParentOf == null) ?
            #     [] : (
            #         m.base.in_Animal_ParentOf
            #          .collect{entry -> entry.outV.next()}
            #          .findAll{it.alias.contains($wanted)}
            #          .collect{it.uuid}
            #     )
            # )
            template = (
                u'((m.{base_location_name}.{direction}_{edge_name} == null) ? [] : ('
                u'm.{base_location_name}.{direction}_{edge_name}.collect{{'
                u'entry -> entry.{inverse_direction}V.next()'
                u'}}'
                u'.{filters_and_traverses}'
                u'.collect{{entry -> entry.{field_name}{maybe_format}}}'
                u'))'
            )
            filter_and_traverse_data = u'.'.join(block.to_gremlin()
                                                 for block in self.folded_ir_blocks)

        maybe_format = ''
        inner_type = strip_non_null_from_type(self.field_type.of_type)
        if GraphQLDate.is_same_type(inner_type):
            maybe_format = '.format("' + STANDARD_DATE_FORMAT + '")'
        elif GraphQLDateTime.is_same_type(inner_type):
            maybe_format = '.format("' + STANDARD_DATETIME_FORMAT + '")'

        template_data = {
            'base_location_name': base_location_name,
            'direction': edge_direction,
            'edge_name': edge_name,
            'field_name': field_name,
            'inverse_direction': inverse_direction,
            'maybe_format': maybe_format,
            'filters_and_traverses': filter_and_traverse_data,
        }
        return template.format(**template_data)