/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/iterator.pmc 32435 2008-11-06T20:41:25.337682Z coke  $

=head1 NAME

src/pmc/iterator.pmc - Iterator PMC

=head1 DESCRIPTION

These are the vtable functions for the Iterator base class. Iterators are
used in combination with other classes(mainly aggregates) to visit all entries
in that aggregate.

=head1 SYNOPSIS

=head2 default usage

    .local pmc iterator, array, entry
    iterator = new 'Iterator', array
  iter_loop:
    unless iterator, iter_end  # while (more values)
    entry = shift iterator     # get an entry
    ...
    goto iter_loop
  iter_end:

The C<new> can alteratively be written as:

  iterator = iter array

=head2 iterate from the end, for arrays

    .local pmc iterator, array, entry
    iterator = new 'Iterator', array
    iterator = .ITERATE_FROM_END
  iter_loop:
    unless iterator, iter_end  # while (more values)
    entry = pop iterator     # get an entry
    ...
    goto iter_loop
  iter_end:

=head2 iterate over a hash

    .local pmc iterator, hash, key, entry
    iterator = new 'Iterator', hash
  iter_loop:
    unless iterator, iter_end  # while (more values)
    key   = shift iterator     # get the key..
    entry = hash[key]
    ...
    goto iter_loop
  iter_end:

=head1 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"

pmclass Iterator no_ro {

    void class_init() {
        if (pass) {
            /* Make the shift_pmc vtable function available as
             * a method named "next" */
            register_nci_method(INTERP, enum_class_Iterator,
                    F2DPTR(Parrot_Iterator_shift_pmc), "next", "PJO");
        }
    }

/*

=item C<void init()>

Raises an exception. Use C<init_pmc()>.

=cut

*/

    VTABLE void init() {
        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                "Iterator init without aggregate");
    }

/*

=item C<void init_pmc(PMC *initializer)>

Initializes the iterator with an aggregate PMC.
Defaults iteration mode to iterate from start.

=cut

*/

    VTABLE void init_pmc(PMC *aggregate) {
        PMC_pmc_val(SELF)    = aggregate;
        PMC_struct_val(SELF) = NULL;
        PObj_custom_mark_SET(SELF);

        /* by default, iterate from start */
        SELF.set_integer_native(ITERATE_FROM_START);
    }

/*

=item C<void mark()>

Marks the current idx/key and the aggregate as live.

=cut

*/

    VTABLE void mark() {
        /* the KEY */
        if (PMC_struct_val(SELF))
             pobject_lives(INTERP, (PObj *)PMC_struct_val(SELF));

        /* the aggregate */
        if (PMC_pmc_val(SELF))
             pobject_lives(INTERP, (PObj *)PMC_pmc_val(SELF));
    }

/*

=item C<PMC *clone()>

Make a clone of the iterator.

=cut

*/

    VTABLE PMC *clone() {
        STRING     *name    = CONST_STRING(interp, "set_key");
        PMC * const key     = (PMC *)PMC_struct_val(SELF);
        PMC * const res     = pmc_new_init(INTERP, SELF->vtable->base_type,
                                           PMC_pmc_val(SELF));
        Parrot_PCCINVOKE(interp, res, name, "P->", VTABLE_clone(interp, key));
        return res;
    }

/*

=item C<INTVAL get_integer()>

Get number of remaining elements. Does not work for hashes yet.
TODO: keep track of current position and direction

=cut

*/

    VTABLE INTVAL get_integer() {
        return SELF.elements();
    }

/*

=item C<STRING *get_string()>

Returns a textual representation of the iterator.

=cut

*/

    VTABLE STRING *get_string() {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        return string_from_int(INTERP, key_integer(INTERP, key));
    }

/*

=item C<INTVAL get_integer_keyed(PMC *key)>

=cut

*/

    VTABLE INTVAL get_integer_keyed(PMC *key) {
        return VTABLE_get_integer_keyed(INTERP, PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL idx)>

Get integer value of current position plus idx.

=cut

*/

    VTABLE INTVAL get_integer_keyed_int(INTVAL idx) {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_integer_keyed_int(INTERP, agg, PMC_int_val(key) +idx);
    }

/*

=item C<FLOATVAL get_number_keyed(PMC *key)>

=cut

*/

    VTABLE FLOATVAL get_number_keyed(PMC *key) {
        return VTABLE_get_number_keyed(INTERP, PMC_pmc_val(SELF), key);
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL idx)>

Get number value of current position plus idx.

=cut

*/

    VTABLE FLOATVAL get_number_keyed_int(INTVAL idx) {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_number_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }



/*

=item C<STRING *get_string_keyed(PMC *key)>

=cut

*/

    VTABLE STRING *get_string_keyed(PMC *key) {
        return VTABLE_get_string_keyed(INTERP, PMC_pmc_val(SELF), key);
    }

/*

=item C<STRING *get_string_keyed_int(INTVAL idx)>

Get string value of current position plus idx.

=cut

*/

    VTABLE STRING *get_string_keyed_int(INTVAL idx) {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_string_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }

/*

=item C<INTVAL get_bool()>

Returns true if the idx/key is not C<-1>.

=cut

*/

    VTABLE INTVAL get_bool() {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        return key && PMC_int_val(key) != -1;
    }

/*

=item C<INTVAL elements()>

Returns the number of remaining elements in the aggregate. TODO.

=cut

*/

    VTABLE INTVAL elements() {
        return VTABLE_elements(INTERP, PMC_pmc_val(SELF));
    }

/*

=item C<PMC *get_pmc_keyed(PMC *key)>

Returns the element for C<*key>.

=cut

*/

    VTABLE PMC *get_pmc_keyed(PMC *key) {
        return VTABLE_get_pmc_keyed(INTERP, PMC_pmc_val(SELF), key);
    }

/*

=item C<PMC *get_pmc_keyed_int(INTVAL key)>

Returns the element for C<key>.

=cut

*/

    VTABLE PMC *get_pmc_keyed_int(INTVAL idx) {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_pmc_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }

/*

=item C<void set_integer_native(INTVAL value)>

Reset the Iterator. C<value> must be one of

 ITERATE_FROM_START        ... Iterate from start
 ITERATE_FROM_START_KEYS   ... OrderedHash by keys
 ITERATE_FROM_END          ... Arrays only

=cut

*/

    VTABLE void set_integer_native(INTVAL value) {
        PMC *key, *agg;
        if (value < ITERATE_FROM_START || value > ITERATE_FROM_END)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                    "Illegal set_integer on iterator");

        /* reset iterator on aggregate */
        agg = PMC_pmc_val(SELF);

        if (agg->vtable->base_type == enum_class_Slice) {
            /* it's an xrange serving as its own aggregate */
            PMC_struct_val(SELF) =
                VTABLE_nextkey_keyed(INTERP, agg, NULL, value);
        }
        else {
            if (!PMC_struct_val(SELF))
                key = key_new(INTERP);
            else
                key = (PMC *)PMC_struct_val(SELF);

            PMC_struct_val(SELF) =
                VTABLE_nextkey_keyed(INTERP, key, agg, value);
        }
    }

/*

=item C<INTVAL pop_integer()>

=cut

*/

    VTABLE INTVAL pop_integer() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        const INTVAL ret     = VTABLE_get_integer_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);

        return ret;
    }

/*

=item C<FLOATVAL pop_float()>

=cut

*/

    VTABLE FLOATVAL pop_float() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        const FLOATVAL ret   = VTABLE_get_number_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);

        return ret;
    }

/*

=item C<STRING *pop_string()>

=cut

*/

    VTABLE STRING *pop_string() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        STRING * const ret   = VTABLE_get_string_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);
        return ret;
    }

/*

=item C<PMC *pop_pmc()>

Returns the element for the current idx/key and sets the idx/key to
the previous one.

=cut

*/

    VTABLE PMC *pop_pmc() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        PMC * const ret      = VTABLE_get_pmc_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);

        return ret;
    }

/*

=item C<INTVAL shift_integer()>

Returns the element for the current idx/key and sets the idx/key to
the next one.

=cut

*/

    VTABLE INTVAL shift_integer() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        const INTVAL ret     = VTABLE_get_integer_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }

/*

=item C<opcode_t *invoke(void *next)>

Return the next element of the aggregate. The return type may depend on
the aggregate. If there are no more items in the aggregate, I1 .. I4 are
zero.

See F<docs/pdds/pdd03_calling_conventions.pod>.

=cut

*/
    VTABLE opcode_t *invoke(void *next) {
        PMC *agg;

        /* iterator constructor */
        if (REG_INT(interp, 3) == 1) {
            PMC *arg = REG_PMC(interp, 5);
            PMC *iter;

            if (PObj_is_object_TEST(arg)) {
                REG_PMC(interp, 5) = VTABLE_get_iter(INTERP, arg);
                return (opcode_t *)next;
            }

            REG_PMC(interp, 5) = iter = pmc_new_init(INTERP,
                    enum_class_Iterator, arg);
            VTABLE_set_integer_native(INTERP, iter, 0);
            return (opcode_t *)next;
        }
        /* TODO function + sentinel */
        else if (REG_INT(interp, 3) == 2)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                    "Iterator: invoke 2 args");

        REG_INT(interp, 1) =
            REG_INT(interp, 2) =
            REG_INT(interp, 3) =
            REG_INT(interp, 4) = 0;

        agg        = PMC_pmc_val(SELF);

        if (agg->vtable->base_type == enum_class_IntList) {
            const INTVAL ires  = SELF.shift_integer();
            REG_INT(interp, 1) = 1;
            REG_INT(interp, 5) = ires;
            return (opcode_t *)next;
        }
        else {
            PMC * const res    = SELF.shift_pmc();
            REG_INT(interp, 3) = 1;
            REG_PMC(interp, 5) = res;
            return (opcode_t *)next;
        }
    }

/*

=item C<FLOATVAL shift_float()>

=cut

*/

    VTABLE FLOATVAL shift_float() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        const FLOATVAL ret   = VTABLE_get_number_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }


/*

=item C<STRING *shift_string()>

=cut

*/

    VTABLE STRING *shift_string() {
        PMC * const key      = (PMC *)PMC_struct_val(SELF);
        PMC * const agg      = PMC_pmc_val(SELF);
        STRING * const ret   = VTABLE_get_string_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }

/*

=item C<PMC *shift_pmc()>

Returns the element for the current idx/key and sets the idx/key to
the next one.

=cut

*/

    VTABLE PMC *shift_pmc() {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        PMC        *ret;

        if (PMC_int_val(key) == -1)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        ret = VTABLE_get_pmc_keyed(INTERP, agg, key);

        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);

        return ret;
    }

/*

=item C<INTVAL exists_keyed(PMC *key)>

Returns whether an element for C<*key> exists in the aggregate.

=cut

*/

    VTABLE INTVAL exists_keyed(PMC *key) {
        return VTABLE_exists_keyed(INTERP, PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL exists_keyed_int(INTVAL idx)>

Returns whether an element for C<idx> exists in the aggregate.

=cut

*/

    VTABLE INTVAL exists_keyed_int(INTVAL idx) {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_exists_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }

/*

=item C<INTVAL defined()>

Returns whether the iterator contains an aggregate.

=cut

*/

    VTABLE INTVAL defined() {
        return (INTVAL)(PMC_pmc_val(SELF) != 0);
    }

/*

=item C<INTVAL defined_keyed(PMC *key)>

=cut

*/

    VTABLE INTVAL defined_keyed(PMC *key) {
        return VTABLE_defined_keyed(INTERP, PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL defined_keyed_int(INTVAL key)>

Returns the result of calling C<defined_keyed(key)> on the aggregate.

=cut

*/

    VTABLE INTVAL defined_keyed_int(INTVAL idx) {
        PMC * const key = (PMC *)PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_defined_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }

    VTABLE PMC *get_iter() {
        return SELF;
    }

    METHOD set_key(PMC *key) {
        PMC_struct_val(SELF) = key;
    }
}

/*

=back

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
