Author: jonathan Date: Thu Aug 14 02:51:37 2008 New Revision: 30221 Added: trunk/languages/perl6/t/pmc/perl6multisub-dispatch-arity.t (contents, props changed) Modified: trunk/languages/perl6/src/pmc/perl6multisub.pmc
Log: [rakudo] Get Perl 6 MultiSub implementation far along enough to do multi dispatch based on arity. Includes a couple of initial, passing tests. Modified: trunk/languages/perl6/src/pmc/perl6multisub.pmc ============================================================================== --- trunk/languages/perl6/src/pmc/perl6multisub.pmc (original) +++ trunk/languages/perl6/src/pmc/perl6multisub.pmc Thu Aug 14 02:51:37 2008 @@ -55,11 +55,28 @@ typedef struct candidate_info { PMC *sub; /* The sub that is the candidate. */ - INTVAL arity; /* The number of required arguments. */ + INTVAL min_arity; /* The number of required positonal arguments. */ + INTVAL max_arity; /* The number of required and optional positional arguments. */ PMC *type_cons; /* Any class or role type constraints. */ PMC *refinement_cons; /* Any refinement type constraints (C<subset>). */ } candidate_info; + +/* + +=item candidate_graph_node + +Represents the produced information about a candidate as well as the graph +edges originating from it. The edges array contains pointers to the edges +in the graph that we have arrows to. + +*/ +typedef struct candidate_graph_node { + candidate_info *info; + struct candidate_graph_node **edges; +} candidate_graph_node; + + /* =back @@ -192,7 +209,40 @@ */ static candidate_info** sort_candidiates(PARROT_INTERP, PMC *candidates) { - candidate_info** result = mem_allocate_n_zeroed_typed(2, candidate_info*); + INTVAL i; + + /* Allocate results array (just allocate it for worst case, which + * is no ties ever, so a null between all of them, and then space + * for the terminating null. */ + INTVAL num_candidates = VTABLE_elements(interp, candidates); + candidate_info** result = mem_allocate_n_zeroed_typed( + 2 * num_candidates + 1, candidate_info*); + + /* Create a node for each candidate in the graph. */ + candidate_graph_node** graph = mem_allocate_n_zeroed_typed( + num_candidates, candidate_graph_node*); + for (i = 0; i < num_candidates; i++) { + /* Get information about this candidate. */ + candidate_info *info = mem_allocate_zeroed_typed(candidate_info); + PMC *candidate = VTABLE_get_pmc_keyed_int(interp, candidates, i); + info->sub = candidate; + info->min_arity = VTABLE_get_integer(interp, + VTABLE_inspect_str(interp, candidate, CONST_STRING(interp, "pos_required"))); + info->max_arity = info->min_arity + VTABLE_get_integer(interp, + VTABLE_inspect_str(interp, candidate, CONST_STRING(interp, "pos_optional"))); +/* XXX handle slurpy */ + /* Add it to graph node. */ + graph[i] = mem_allocate_typed(candidate_graph_node); + graph[i]->info = info; + } + + /* XXX Here we do the topological sort. For now, just copy the values + * with the arity to the array, and nothing is narrower than anything + * else. */ + for (i = 0; i < num_candidates; i++) { + result[i] = graph[i]->info; + } + return result; } @@ -202,15 +252,62 @@ 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. +is false, 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; +static PMC* do_dispatch(PARROT_INTERP, candidate_info** candidates, PMC *args, int many, + int num_candidates, opcode_t *next) { + INTVAL num_args = VTABLE_elements(interp, args); + candidate_info **cur_candidate = candidates; + candidate_info **possibles = mem_allocate_n_typed(num_candidates, candidate_info*); + INTVAL possibles_count = 0; + + /* Iterate over the candidates and collect best ones; terminate + * when we see two nulls (may break out earlier). */ + while (*cur_candidate != NULL) { + /* Check if it's admissable by arity. */ + if (num_args < (*cur_candidate)->min_arity || num_args > (*cur_candidate)->max_arity) { + cur_candidate++; + continue; + } + + /* XXX Check if it's admissable by type. */ + + /* If we get here, it's an admissable candidate; add to list. */ + possibles[possibles_count] = *cur_candidate; + possibles_count++; + + /* Next candidate. */ + cur_candidate++; + if (*cur_candidate == NULL) { + /* If we're after just one candidate and we have found some, then + * we've hit the end of a tied group now, so stop looking if we are + * only after one. */ + if (!many) + break; + cur_candidate++; + } + } + + /* XXX Loooooads of other disambiguation logic comes here. */ + + if (!many) { + /* Need a unique candidate. */ + if (possibles_count == 1) { + return possibles[0]->sub; + } + else if (possibles_count == 0) { + Parrot_ex_throw_from_c_args(interp, next, 1, + "No applicable candidates found to dispatch to."); + } + else { + Parrot_ex_throw_from_c_args(interp, next, 1, + "Ambiguous dispatch."); + } + } } /* @@ -259,7 +356,7 @@ pmclass Perl6MultiSub extends MultiSub need_ext dynpmc group perl6_group { ATTR PMC *candidates; - ATTR struct candidate_info *candidates_sorted; + ATTR void *candidates_sorted; /* @@ -306,24 +403,25 @@ =cut */ - VTABLE opcode_t* invoke(void* next) { + VTABLE opcode_t *invoke(void *next) { PMC *found; /* Make sure that we have a candidate list built. */ candidate_info **candidates = NULL; + PMC *unsorted; GETATTR_Perl6MultiSub_candidates_sorted(interp, SELF, candidates); + GETATTR_Perl6MultiSub_candidates(interp, SELF, unsorted); 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); + /* Now do the dispatch on the args we are being invoked with; + * if it can't find anything, it will throw the required exception. */ + found = do_dispatch(interp, candidates, get_args(interp), 0, + VTABLE_elements(interp, unsorted), next); /* Invoke what was found. */ return VTABLE_invoke(interp, found, next); Added: trunk/languages/perl6/t/pmc/perl6multisub-dispatch-arity.t ============================================================================== --- (empty file) +++ trunk/languages/perl6/t/pmc/perl6multisub-dispatch-arity.t Thu Aug 14 02:51:37 2008 @@ -0,0 +1,73 @@ +#! ../../parrot +# Copyright (C) 2007-2008, The Perl Foundation. +# $Id$ + +=head1 NAME + +t/pmc/perl6multisub-dispatch-arity.t - Arity based dispatch tests + +=head1 SYNOPSIS + + % prove t/pmc/perl6multisub-dispatch-arity.t + +=head1 DESCRIPTION + +Tests for arity based dispatch using the Perl 6 MultiSub PMC. + +=cut + +.loadlib 'perl6_group' + +.sub main :main + .include 'include/test_more.pir' + load_bytecode "perl6.pbc" + + plan(4) + + 'simple'() +.end + + +.sub 'simple' + $P0 = new "Perl6MultiSub" + $P1 = find_global 'simple_1' + push $P0, $P1 + $P1 = find_global 'simple_2' + push $P0, $P1 + $P1 = find_global 'simple_3' + push $P0, $P1 + + $I0 = $P0() + is($I0, 0, 'simple call with 0 args') + $I0 = $P0(1) + is($I0, 1, 'simple call with 1 arg') + $I0 = $P0(1, 2, 3) + is($I0, 3, 'simple call with 3 args') + $I0 = 0 + push_eh fails + $P0(1, 2) + fails: + $I0 = 1 + ok: + is($I0, 1, 'call with no arity match fails') +.end +.sub 'simple_1' + .return (0) +.end +.sub 'simple_2' + .param int i + .return (1) +.end +.sub 'simple_3' + .param int i + .param int j + .param int k + .return (3) +.end + + +# Local Variables: +# mode: pir +# fill-column: 100 +# End: +# vim: expandtab shiftwidth=4 ft=pir: