Author: jonathan
Date: Wed Aug  6 10:39:32 2008
New Revision: 30064

Modified:
   trunk/languages/perl6/src/pmc/perl6multisub.pmc
   trunk/languages/perl6/t/pmc/perl6multisub-basic.t

Log:
[rakudo] Start to stub in some bits of the Perl6MultiSub PMC, plus add a few 
more sanity tests. Nothing to see or play with just yet, and what is here is 
very preliminary code and, in places, untested.

Modified: trunk/languages/perl6/src/pmc/perl6multisub.pmc
==============================================================================
--- trunk/languages/perl6/src/pmc/perl6multisub.pmc     (original)
+++ trunk/languages/perl6/src/pmc/perl6multisub.pmc     Wed Aug  6 10:39:32 2008
@@ -11,7 +11,243 @@
 Subclass of MultiSub that overrides invoke to implement the Perl 6 multiple
 dispatch algorithm, along with providing various other pieces.
 
-=head2 Methods
+Since we need to store some extra information, we cannot just actually be
+a ResizablePMCArray, but rather we need to have one.
+
+=head1 TODO
+
+This is a list of things that I need to deal with/come back and worry about
+later (it's not a complete todo list for finishing up the PMC itself, just
+of fixup tasks in what is already done).
+
+=over 4
+
+=item Use Perl 6 types when boxing native arguments in the arg list
+
+=item Fix pmc2c so we can have ATTR candidate_info **candidates_sorted. We
+will have to move them to their own .h file, but in pmc2c we need to be able
+to include that *before* this PMC's generated .h file (I couldn't work out
+how to do that) and also make it parse double indirections. Then we can toss
+any (candidate_info**) casts.
+
+=item Make sure we override everything that ResizablePMCArray and its parents
+would provide us with. Otherwise, we'll just get segfaults 'cus we don't store
+stuff the way it does.
+
+=back
+
+=head1 INTERNAL STRUCTURES
+
+We have some structures that we use to keep data around internally.
+
+=over 4
+
+=item candidate_info
+
+Represents a candidate. We extract various bits of information about it when
+we are building the sorted candidate list and store them in here for fast
+access during a dispatch.
+
+*/
+
+typedef struct candidate_info {
+    PMC *sub;               /* The sub that is the candidate. */
+    INTVAL arity;           /* The number of required arguments. */
+    PMC *type_cons;         /* Any class or role type constraints. */
+    PMC *refinement_cons;   /* Any refinement type constraints (C<subset>). */
+} candidate_info;
+
+/*
+
+=back
+
+=head1 FUNCTIONS
+
+These are worker functions used by the methods of the PMC, and not visible
+from the outside.
+
+=over 4
+
+=item C<static PMC* get_args()>
+
+Gets a list of the arguments that are being passed, taking them from the
+registers and the constants table and flattening any :flat arguments as
+required. Returns a ResizablePMCArray of them.
+
+=cut
+
+*/
+
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+static PMC*
+get_args(PARROT_INTERP)
+{
+    INTVAL sig_len, i;
+    PMC    *arg;
+    PMC    *sig;
+
+    /* Initialize results list. */
+    PMC    * const arg_list       = pmc_new(interp, 
enum_class_ResizablePMCArray);
+
+    /* Get constants table for current segment, so we can look up sig and any
+     * constant arguments. */
+    PackFile_Constant **constants = interp->code->const_table->constants;
+
+    /* Make sure we have a place to source the current arguments from. */
+    opcode_t   *args_op   = interp->current_args;
+    if (!args_op)
+        Parrot_ex_throw_from_c_args(interp, NULL, 1,
+            "No arguments found to dispatch on");
+    PARROT_ASSERT(*args_op == PARROT_OP_set_args_pc);
+
+    /* Get the (Parrot calling conventions) signature PMC. */
+    ++args_op;
+    sig = constants[*args_op]->u.key;
+    ASSERT_SIG_PMC(sig);
+    sig_len = SIG_ELEMS(sig);
+
+    /* If we have a zero-length signature, we're done. */
+    if (sig_len == 0)
+        return arg_list;
+
+    /* Otherwise, we have arguments. */
+    ++args_op;
+    for (i = 0; i < sig_len; ++i, ++args_op) {
+        INTVAL type = SIG_ITEM(sig, i);
+        const int idx = *args_op;
+
+        /* If we find a named argument, then we know there's no more positional
+         * arguments, since they come before named. And we don't dispatch on
+         * named arguments. */
+        if (type & PARROT_ARG_NAME)
+            break;
+
+        /* Put the argument in the list. For some arguments, we must box them 
into
+         * a PMC to be able to have them in the list. XXX Use Perl 6 box 
types. */
+        switch (type & (PARROT_ARG_TYPE_MASK | PARROT_ARG_FLATTEN)) {
+            case PARROT_ARG_INTVAL:
+                /* Integer constants always in register. */
+                arg = pmc_new(interp, enum_class_Integer);
+                VTABLE_set_integer_native(interp, arg, REG_INT(interp, idx));
+                VTABLE_push_pmc(interp, arg_list, arg);
+                break;
+            case PARROT_ARG_FLOATVAL:
+                /* May have value in an N register or constants table. */
+                arg = pmc_new(interp, enum_class_Float);
+                if ((type & PARROT_ARG_CONSTANT))
+                    VTABLE_set_number_native(interp, arg, 
constants[idx]->u.number);
+                else
+                    VTABLE_set_number_native(interp, arg, REG_NUM(interp, 
idx));
+                VTABLE_push_pmc(interp, arg_list, arg);
+                break;
+            case PARROT_ARG_STRING:
+                /* May have value in an S register or constnats table. */
+                arg = pmc_new(interp, enum_class_String);
+                if ((type & PARROT_ARG_CONSTANT))
+                    VTABLE_set_string_native(interp, arg, 
constants[idx]->u.string);
+                else
+                    VTABLE_set_string_native(interp, arg, REG_STR(interp, 
idx));
+                VTABLE_push_pmc(interp, arg_list, arg);
+                break;
+            case PARROT_ARG_PMC:
+                /* May have value in a P register or constants table. */
+                if ((type & PARROT_ARG_CONSTANT))
+                    arg = constants[idx]->u.key;
+                else
+                    arg = REG_PMC(interp, idx);
+                VTABLE_push_pmc(interp, arg_list, arg);
+                break;
+            case PARROT_ARG_FLATTEN | PARROT_ARG_PMC:  {
+                /* Expand flattening arguments; just loop over the array that
+                 * is being flattened and get all of the entries within it. */
+                int j, n;
+                const int idx = *args_op;
+                arg           = REG_PMC(interp, idx);
+                n             = VTABLE_elements(interp, arg);
+                for (j = 0; j < n; ++j)
+                    VTABLE_push_pmc(interp, arg_list,
+                        VTABLE_get_pmc_keyed_int(interp, arg, j));
+                break;
+            }
+            default:
+                Parrot_ex_throw_from_c_args(interp, NULL, 1,
+                        "Unknown signature type %d in 
Parrot_Perl6MultiSub_get_args", type);
+                break;
+        }
+    }
+
+    return arg_list;
+}
+
+/*
+
+=item C<static candidate_info** sort_candidiates(PMC *candidates)>
+
+Takes a ResizablePMCArray of the candidates, collects information about them
+and then does a topological sort of them.
+
+*/
+static candidate_info** sort_candidiates(PARROT_INTERP, PMC *candidates) {
+    candidate_info** result = mem_sys_allocate_zeroed(2 * sizeof 
(candidate_info*));
+    return result;
+}
+
+/*
+
+=item C<static PMC* do_dispatch(candidate_info** candidates, int many)>
+
+Runs the Perl 6 MMD algorithm. If many is set to a true value, returns a
+ResizablePMCArray of all possible candidates, which may be empty. If many
+is set to a false value, then returns either the one winning unambiguous
+candidate or throws an error saying that the dispatch failed if there were
+no candidates or that it was ambiguous if there were tied candidates.
+
+*/
+
+static PMC* do_dispatch(PARROT_INTERP, candidate_info** candidates, int many) {
+    Parrot_ex_throw_from_c_args(interp, NULL, 1, "Unimplemented.");
+    return PMCNULL;
+}
+
+/*
+
+=item C<static int assert_invokable(PARROT_INTERP, PMC *value)>
+
+Checks if a PMC is invokable; returns a true value if so and a false value if
+not.
+
+*/
+static int check_invokable(PARROT_INTERP, PMC *value) {
+    STRING * const _sub = CONST_STRING(interp, "Sub");
+    STRING * const _nci = CONST_STRING(interp, "NCI");
+    return VTABLE_isa(interp, value, _sub) || VTABLE_isa(interp, value, _nci);
+}
+
+/*
+
+=back
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item candidates
+
+Unsorted list of all candidates.
+
+=item candidates_sorted
+
+C array of canididate_info structures. It stores a sequence of candidates
+length one or greater that are tied, followed by a NULL, followed by the next
+bunch that are less narrow but tied and so forth. It is terminated by a double
+NULL.
+
+=back
+
+=head1 METHODS
+
+=over 4
 
 =cut
 
@@ -20,10 +256,127 @@
 #include "parrot/parrot.h"
 
 pmclass Perl6MultiSub extends MultiSub need_ext dynpmc group perl6_group {
+    ATTR PMC  *candidates;
+    ATTR void *candidates_sorted;
+
+/*
+
+=item VTABLE void init()
+
+Allocates the PMC's underlying storage.
+
+=cut
+
+*/
+    VTABLE void init() {
+        /* Allocate the underlying struct and make candidate list an empty
+         * ResizablePMCArray. */
+        PMC *candidates = pmc_new(interp, enum_class_ResizablePMCArray);
+        PMC_data(SELF) = mem_allocate_zeroed_typed(Parrot_Perl6MultiSub);
+        SETATTR_Perl6MultiSub_candidates(interp, SELF, candidates)
+
+        /* Need custom mark and destroy. */
+        PObj_custom_mark_SET(SELF);
+        PObj_active_destroy_SET(SELF);
+    }
+
+/*
+
+=item VTABLE void destroy()
+
+Frees the memory associated with this PMC's underlying storage.
+
+=cut
+
+*/
+    VTABLE void destroy() {
+        mem_sys_free(PMC_data(SELF));
+        PMC_data(SELF) = NULL;
+    }
+
+/*
+
+=item VTABLE opcode_t invoke()
+
+Does a dispatch to the best candidate with the current arguments, according to
+the Perl 6 MMD algorithm.
+
+=cut
+
+*/
+    VTABLE opcode_t* invoke(void* next) {
+        PMC *found;
+
+        /* Make sure that we have a candidate list built. */
+        candidate_info **candidates = NULL;
+        GETATTR_Perl6MultiSub_candidates_sorted(interp, SELF, candidates);
+        if (!candidates) {
+            PMC *unsorted;
+            GETATTR_Perl6MultiSub_candidates(interp, SELF, unsorted);
+            candidates = sort_candidiates(interp, unsorted);
+            SETATTR_Perl6MultiSub_candidates_sorted(interp, SELF, candidates);
+        }
+        if (!candidates)
+            Parrot_ex_throw_from_c_args(interp, next, 1, "Failed to build 
candidate list");
+
+        /* Now do the dispatch - if it can't find anything, it will throw the 
required
+         * exceptions. */
+        found = do_dispatch(interp, candidates, 0);
+
+        /* Invoke what was found. */
+        return VTABLE_invoke(interp, found, next);
+    }
+
+/*
+
+=item VTABLE void push_pmc(PMC *sub)
+
+Adds a new candidate to the candidate list.
+
+=cut
+
+*/
+    VTABLE void push_pmc(PMC *sub) {
+        PMC *candidates;
+
+        /* Make sure it's invokable. */
+        if (!check_invokable(interp, sub))
+            Parrot_ex_throw_from_c_args(interp, NULL, 
EXCEPTION_INVALID_OPERATION,
+                    "Cannot add non-Sub PMC to a MultiSub.");
+
+        /* Add it to the candidates list. */
+        GETATTR_Perl6MultiSub_candidates(interp, SELF, candidates);
+        VTABLE_push_pmc(interp, candidates, sub);
 
+        /* Invalidate the sorted list - we'll need to re-build it. */
+        SETATTR_Perl6MultiSub_candidates_sorted(interp, SELF, NULL);
+    }
+
+/*
+
+=item VTABLE INTVAL elements()
+
+Gets the number of candidate on the candidate list.
+
+=cut
+
+*/
+    VTABLE INTVAL elements() {
+        PMC *candidates;
+        GETATTR_Perl6MultiSub_candidates(interp, SELF, candidates);
+        return VTABLE_elements(interp, candidates);
+    }
 }
 
 /*
+
+=back
+
+=cut
+
+*/
+
+/*
  * Local variables:
  *   c-file-style: "parrot"
  * End:

Modified: trunk/languages/perl6/t/pmc/perl6multisub-basic.t
==============================================================================
--- trunk/languages/perl6/t/pmc/perl6multisub-basic.t   (original)
+++ trunk/languages/perl6/t/pmc/perl6multisub-basic.t   Wed Aug  6 10:39:32 2008
@@ -22,9 +22,10 @@
     .include 'include/test_more.pir'

     load_bytecode "perl6.pbc"

 

-    plan(1)

+    plan(4)

 

     'instantiate'()

+    'push_and_elements'()

 .end    

 

 

@@ -36,6 +37,37 @@
 .end

 

 

+.sub 'push_and_elements'

+    # Make sure we can push subs onto the multi-sub.

+    $P0 = new "Perl6MultiSub"

+    $P1 = find_name 'push_test1'

+    push $P0, $P1

+    $I0 = elements $P0

+    is($I0, 1, "added one sub")

+    $P1 = find_name 'push_test2'

+    push $P0, $P1

+    $I0 = elements $P0

+    is($I0, 2, "added two subs")

+

+    # Make sure pushing a non-invokable dies.

+    $P1 = new 'Integer'

+    $I0 = 0

+    push_eh fails_ok

+    push $P0, $P1

+    goto done

+  fails_ok:

+    $I0 = 1

+  done:

+    is($I0, 1, "cannot push a non-invokable")

+.end

+.sub push_test1

+    .return (1)

+.end

+.sub push_test2

+    .param pmc x

+    .return (2)

+.end

+

 # Local Variables:

 #   mode: pir

 #   fill-column: 100

Reply via email to