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