From: Bob Rogers <[EMAIL PROTECTED]>
   Date: Sat, 7 Oct 2006 18:52:29 -0400

   . . .

      Also, the "[oops; got 4 and X]" lines in the output seem to suggest
   that Parrot may be getting confused about parameters.  The "4" is really
   the length of one of the coro structures, which must be getting passed
   as the first arg.  Also, it fails with "too many args" if you don't
   accept at least two values from the coroutine_yield.  I suspect Parrot
   is reusing some old parameter information; I'll try to nail this down if
   I get a chance.

This turns out to have been a bug in my implementation; extensive
debugging shows that all parameter to all continuations are going to the
right places.  Shame on me for having suspected Parrot.  ;-}

   A corrected version is attached.

                                        -- Bob

.include "interpinfo.pasm"

### Coroutine implementation.

## Coroutine slots:
##
##  0.  Coroutine state:  1 is new or valid, 0 is dead.
##  1.  Initial sub.
##  2.  Continuation to which to return when yielding.
##  3.  Continuation from which to resume.

.sub coroutine_create
        .param pmc sub

        .local pmc coro, state
        coro = new .FixedPMCArray
        coro = 4
        state = new .Undef
        state = 1
        coro[0] = state
        coro[1] = sub
        .return (coro)
.end

## Invoke the coroutine.
.sub coroutine_resume
        .param pmc coro
        .param pmc args :slurpy

        ## Decide whether we're dead.
        .local pmc state
        state = coro[0]
        unless state goto dead

        ## Decide where to go.  If we've never been invoked before, we need to
        ## call the sub.
        .local pmc entry
        entry = coro[3]
        unless null entry goto doit
        entry = coro[1]

doit:
        ## Remember where to return when we yield.
        .local pmc cc
        cc = interpinfo .INTERPINFO_CURRENT_CONT
        coro[2] = cc

        ## Call the entry with our args.  Most of the time, it will yield (by
        ## calling our continuation for us) instead of returning directly.
        .local pmc result
        (result :slurpy) = entry(args :flat)
        ## If we returned normally, then the coroutine is dead.
        state = 0
        ## Note that the value of coro[2] will normally have been changed
        ## magically behind our backs by a subsequent yield/resume, so we can't
        ## just return directly.
        cc = coro[2]
        .return cc(result :flat)

dead:
        ## Complain about zombie creation.
        .local pmc error
        error = new .Exception
        error['_message'] = "Can't reanimate a dead coroutine.\n"
        throw error
.end

## Return values to the calling thread.
.sub coroutine_yield
        .param pmc coro
        .param pmc args :slurpy

        ## Remember where to go when we are resumed.
        .local pmc cc
        cc = interpinfo .INTERPINFO_CURRENT_CONT
        coro[3] = cc

        ## Return to the coro caller.
        cc = coro[2]
        .return cc(args :flat)
.end

### Recursive enumeration.

## build a complete N-ary tree of the specified depth, with the leaves being
## consecutive integer PMCs from start.
.sub make_nary_tree
        .param int start
        .param int end
        .param int node_width
        .param int depth

        .local pmc result
        if depth goto deeper
        result = new .Undef
        result = start
        inc start
        goto done
deeper:
        result = new .ResizablePMCArray
        dec depth
        .local int i
        i = 0
next:
        if i >= node_width goto done
        if start > end goto done
        ($P0, start) = make_nary_tree(start, end, node_width, depth)
        push result, $P0
        inc i
        goto next
done:
        .return (result, start)
.end

## non-coroutine traversal, for debugging.
.sub enumerate_tree
        .param pmc tree_node
        .param int depth :optional
        .param int depth_p :opt_flag

        if depth_p goto have_depth
        depth = 0
have_depth:
        inc depth

        $I0 = isa tree_node, 'ResizablePMCArray'
        if $I0 goto recur
        print "[leaf "
        print tree_node
        print "]\n"
done:
        .return ()
recur:
        .local int size, i
        i = 0
        size = tree_node
again:
        if i >= size goto done
        print "[recur: depth "
        print depth
        print ' elt '
        print i
        print "]\n"
        $P1 = tree_node[i]
        enumerate_tree($P1, depth)
        inc i
        goto again
.end

## Recursive coroutine to enumerate tree elements, each of which is yielded in
## turn.
.sub coro_enumerate_tree
        .param pmc coro
        .param pmc tree_node
        .param int depth :optional
        .param int depth_p :opt_flag

        if depth_p goto have_depth
        depth = 0
have_depth:
        inc depth

        $I0 = isa tree_node, 'FixedPMCArray'
        if $I0 goto recur
        ## print "[leaf "
        ## print tree_node
        ## print "]\n"
        ($P5 :optional, $I5 :opt_flag, $P6 :optional) = coroutine_yield(coro, 
tree_node)
        unless $I5 goto done
        ## [this is a bug; we shouldn't ever get *any* values from
        ## coroutine_resume.  -- rgr, 7-Oct-06.]
        print "[oops; got "
        print $P5
        unless $I6 goto no_p6
        print ' and '
        print $P6
no_p6:
        print "]\n"
        .return ()
recur:
        .local int size, i
        i = 0
        size = tree_node
again:
        if i >= size goto done
        ## print "[coro recur: depth "
        ## print depth
        ## print ' elt '
        ## print i
        ## print "]\n"
        $P1 = tree_node[i]
        coro_enumerate_tree(coro, $P1, depth)
        inc i
        goto again
done:
        .return ()
.end

## Solution to the "same fringe" problem that uses coroutines to enumerate each
## of two passed trees of numbers.  Prints 'equal' if the trees have the same
## fringe, else 'not equal.'
.sub same_fringe
        .param pmc tree1
        .param pmc tree2

        .local pmc coro1, coro2
        .const .Sub coro_sub = "coro_enumerate_tree"
        coro1 = coroutine_create(coro_sub)
        coro2 = coroutine_create(coro_sub)
        ($P0 :optional, $I0 :opt_flag) = coroutine_resume(coro1, coro1, tree1)
        ($P1 :optional, $I1 :opt_flag) = coroutine_resume(coro2, coro2, tree2)

loop:
        if $I0 goto got_first
        if $I1 goto not_equal
        goto equal
got_first:
        unless $I1 goto not_equal

        ## now have results from both.
        print "[got "
        print $P0
        print ' and '
        print $P1
        print "]\n"
        if $P0 != $P1 goto not_equal
        ## set up for the next iteration.
        ($P0 :optional, $I0 :opt_flag) = coroutine_resume(coro1)
        ($P1 :optional, $I1 :opt_flag) = coroutine_resume(coro2)
        goto loop
not_equal:
        print "not "
equal:
        print "equal.\n"
.end

## Main program to solve a "same fringe" test case.
.sub solve_same_fringe :main
        .local pmc binary, binary_4, ternary, ternary_2
        ## load_bytecode "Data/Dumper"
        binary = make_nary_tree(1, 8, 2, 3)
        ternary = make_nary_tree(1, 8, 3, 2)
        binary_4 = make_nary_tree(1, 16, 2, 4)
        ## now make a "damaged" one that we can decide that it is different.
        ternary_2 = make_nary_tree(1, 8, 3, 2)
        $P0 = ternary_2[1]
        $P0 = $P0[0]
        ternary_2[1] = $P0
        ## enumerate_tree(ternary)
        ## enumerate_tree(binary)

        same_fringe(binary, binary)
        same_fringe(binary, binary_4)
        same_fringe(binary, ternary)
        same_fringe(binary, ternary_2)
.end

## Main program to test tree creation and enumeration.
.sub enumerate_recursively
        ## make a data structure for traversal.
        .local pmc binary_3
        binary_3 = make_nary_tree(1, 8, 2, 3)
        ## enable this to show that make_nary_tree works.
        enumerate_tree(binary_3)

        ## list its contents via coroutine.
        .const .Sub coro_sub = "coro_enumerate_tree"
        .local pmc coro1
        coro1 = coroutine_create(coro_sub)
        ($P0 :optional, $I0 :opt_flag) = coroutine_resume(coro1, coro1, 
binary_3, 0)
loop:
        unless $I0 goto done
        if null $P0 goto oops
        print $P0
        print "\n"
        ($P0 :optional, $I0 :opt_flag) = coroutine_resume(coro1)
        goto loop
oops:
        ## we should never get here; the coroutine returns either no values, or
        ## one value that must be an integer PMC.
        print "oops.\n"
done:
.end

Reply via email to