Here's the Parrot::Coroutine patch; I will commit this if nobody
objects.

   It seems to me that this should be mentioned in docs/compiler_faq.pod
as an alternative way to do coroutines . . . I will take a look at this.

   In fact, it might be safer to deprecate Coroutine.pmc until it can be
redesigned.  It would be a shame if somebody looked at only that and
concluded that Parrot doesn't really support coroutines after all.

                                        -- Bob Rogers
                                           http://rgrjr.dyndns.org/

Diffs between last version checked in and current workfile(s):

Index: runtime/parrot/library/Parrot/Coroutine.pir
===================================================================
--- runtime/parrot/library/Parrot/Coroutine.pir (revision 0)
+++ runtime/parrot/library/Parrot/Coroutine.pir (revision 0)
@@ -0,0 +1,266 @@
+=head1 TITLE
+
+Parrot::Coroutine - A pure PIR implementation of coroutines
+
+=head1 VERSION
+
+ $Id:$
+
+=head1 SYNOPSIS
+
+       .sub __onload :load
+               load_bytecode 'Parrot/Coroutine.pir'
+       .end
+
+       ## Recursive coroutine to enumerate tree elements.  Each element that is
+       ## not a FixedPMCArray is yielded in turn.
+       .sub 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"
+               coro.'yield'(tree_node)
+               .return ()
+
+       recur:
+               ## Loop through array elements, recurring on each.
+               .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(coro, $P1, depth)
+               inc i
+               goto again
+       done:
+               .return ()
+       .end
+
+       .sub print_tree
+               .param pmc tree
+
+               .local int coro_class, idx
+               coro_class = find_type 'Parrot::Coroutine'
+               .local pmc coro
+               .const .Sub coro_sub = "enumerate_tree"
+               coro = new coro_class, coro_sub
+               ($P0 :optional, $I0 :opt_flag) = coro.'resume'(coro, tree)
+               idx = 0
+
+       loop:
+               unless $I0 goto done
+               print 'print_tree:  '
+               print idx
+               print ' => '
+               print $P0
+               print "\n"
+               ($P0 :optional, $I0 :opt_flag) = coro.'resume'()
+               goto loop
+       done:
+       .end
+
+=head1 DESCRIPTION
+
+This object class provides an implementation of coroutines that is written
+in pure PIR using continuations.
+
+=cut
+
+.const int slot_state       = 0 ## State:  1 is new/valid, 0 is dead.
+.const int slot_initial_sub = 1 ## Initial sub.
+.const int slot_yield_cont  = 2 ## Continuation to for yielding.
+.const int slot_resume_cont = 3 ## Continuation from which to resume.
+
+.sub __loadtime_create_class :load
+       find_type $I0, "Parrot::Coroutine"
+       if $I0 > 1 goto END
+       newclass $P0, "Parrot::Coroutine"
+       addattribute $P0, "state"
+       addattribute $P0, "initial_sub"
+       addattribute $P0, "yield_cont"
+       addattribute $P0, "resume_cont"
+END:
+       .return ()
+.end
+
+.namespace ["Parrot::Coroutine"]
+
+.include "interpinfo.pasm"
+
+=head2 METHODS
+
+=head3 B<__init(sub)>
+
+This method is normally called via the C<new> op:
+
+       .local int coro_class
+       coro_class = find_type 'Parrot::Coroutine'
+       .local pmc coro
+       .const .Sub coro_sub = "enumerate_tree"
+       coro = new coro_class, coro_sub
+
+Given a sub, it initializes a new C<Parrot::Coroutine> object.
+
+=cut
+
+.sub __init :method
+       .param pmc sub
+
+       ## [should complain if sub is not a sub or closure.  -- rgr, 8-Oct-06.]
+       .local pmc state
+       state = new .Undef
+       state = 1
+       setattribute self, slot_state, state
+       setattribute self, slot_initial_sub, sub
+.end
+
+## [it would be nice to include a pointer value.  -- rgr, 8-Oct-06.]
+.sub __get_string :method
+       $S0 = '<Parrot::Coroutine ?>'
+       .return ($S0)
+.end
+
+=head3 B<coro.resume(args...)>
+
+Invoke the coroutine.  The first time this is called on a new coroutine,
+the initial sub is invoked with the passed arguments.  The second and
+subsequent times, the args are delivered as the result of the previous
+C<yield> operation.
+
+If the coroutine subsequently yields, the values passed to the C<yield>
+method are returned as the values from C<resume>.
+
+If the coroutine returns normally (i.e. from the original sub), then those
+values are passed returned from the C<resume> method, and the coroutine is
+marked as dead, in which case it is an error to attempt to resume it again.
+
+=cut
+
+.sub resume :method
+       .param pmc args :slurpy
+
+       ## Decide whether we're dead.
+       .local pmc state
+       state = getattribute self, slot_state
+       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 = getattribute self, slot_resume_cont
+       unless null entry goto doit
+       entry = getattribute self, slot_initial_sub
+
+doit:
+       ## Remember where to return when we yield.
+       .local pmc cc
+       cc = interpinfo .INTERPINFO_CURRENT_CONT
+       setattribute self, slot_yield_cont, 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 the yield_cont slot will normally have been
+       ## changed magically behind our backs by a subsequent yield/resume, so
+       ## we can't just return directly.
+       cc = getattribute self, slot_yield_cont
+       .return cc(result :flat)
+
+dead:
+       ## Complain about attempted zombie creation.
+       .local pmc error
+       error = new .Exception
+       error['_message'] = "Can't reanimate a dead coroutine.\n"
+       throw error
+.end
+
+=head3 B<coro.yield(args...)>
+
+Within the coroutine, C<yield> returns arbitrary values back to the
+caller, making it look like the values came from the last C<resume> call.
+
+The next time the caller decides to resume the coroutine, the arguments
+passed to C<resume> are returned as the values from C<yield>.
+
+=cut
+
+## Return values to the calling thread.
+.sub yield :method
+       .param pmc args :slurpy
+
+       ## Remember where to go when we are resumed.
+       .local pmc cc
+       cc = interpinfo .INTERPINFO_CURRENT_CONT
+       setattribute self, slot_resume_cont, cc
+
+       ## Return to the coro caller.
+       cc = getattribute self, slot_yield_cont
+       .return cc(args :flat)
+.end
+
+=head1 BUGS
+
+=over 4
+
+=item 1.
+
+We should really keep more state details.  The only legal state
+transitions should be 'new' to 'resumed' to 'yielded' to 'resumed'
+to 'yielded' ..., except that one might at any time transition to
+'dead', which is (not surprisingly) the terminal state.
+
+=back
+
+Please report any others you find to C<E<lt>[EMAIL PROTECTED]<gt>>.
+
+=head1 SEE ALSO
+
+L<http://en.wikipedia.org/wiki/Coroutine> -- coroutines defined.
+
+C<t/library/coroutine.t> -- "same fringe" test case.
+
+C<src/pmc/coroutine.pmc> -- the C<pmclass> implementation.
+
+L<http://www.lua.org/manual/5.1/manual.html#2.11> -- definition of the
+coroutine API for the Lua programming language, upon which the
+C<Parrot::Coroutine> API is based.
+
+L<http://gd.tuwien.ac.at/languages/scheme/tutorial-dsitaram/t-y-scheme-Z-H-14.html>
+-- Scheme tutorial chapter that introduces call/cc and uses it to solve
+"same fringe" via coroutines.
+
+=head1 AUTHOR
+
+Bob Rogers C<E<lt>[EMAIL PROTECTED]<gt>>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006, The Perl Foundation.
+This program is free software. It is subject to the same
+license as The Parrot Interpreter.
+
+=for vim
+
+" vim: ts=8 expandtab
+
+=cut
Index: MANIFEST
===================================================================
--- MANIFEST    (revision 14921)
+++ MANIFEST    (working copy)
@@ -2128,6 +2128,7 @@
 runtime/parrot/library/HTTP/Daemon.pir                      [library]
 runtime/parrot/library/JSON.pir                             [library]
 runtime/parrot/library/MIME/Base64.pir                      [library]
+runtime/parrot/library/Parrot/Coroutine.pir                 [library]
 runtime/parrot/library/PGE/Dumper.pir                       [library]
 runtime/parrot/library/PGE/Glob.pir                         [library]
 runtime/parrot/library/PGE/Hs.pir                           [library]
@@ -2550,6 +2551,7 @@
 t/examples/subs.t                                           []
 t/harness                                                   []
 t/library/File_Spec.t                                       []
+t/library/coroutine.t                                       []
 t/library/data_escape.t                                     []
 t/library/dumper.t                                          []
 t/library/getopt_obj.t                                      []
Index: t/library/coroutine.t
===================================================================
--- t/library/coroutine.t       (revision 0)
+++ t/library/coroutine.t       (revision 0)
@@ -0,0 +1,219 @@
+#!./parrot 
+# Copyright (C) 2006, The Perl Foundation.
+# $Id:$
+
+=head1 NAME
+
+t/library/coroutine.t -- Test the Parrot::Coroutine class
+
+=head1 SYNOPSIS
+
+  ./parrot t/library/coroutine.t
+
+=head1 DESCRIPTION
+
+This script tests the C<Parrot::Coroutine> class using an implementation of the
+"same fringe" problem.
+
+=head1 SEE ALSO
+
+L<http://swiss.csail.mit.edu/classes/symbolic/spring06/psets/ps6/samefringe.scm>
+-- a collection of "same fringe" implementations in Scheme.
+
+=cut
+
+.const int N_TESTS = 6
+
+## Build an N-ary tree (where N is passed as node_width) of the specified 
depth,
+## with the leaves being consecutive integer PMCs from start but less than N.
+## The tree will be complete iff end-start+1 == node_width^depth.
+.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:
+       ## Loop through array elements, recurring on each.
+       .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 element that is not a
+## FixedPMCArray 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"
+       coro.'yield'(tree_node)
+       .return ()
+
+recur:
+       ## Loop through array elements, recurring on each.
+       .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.  Returns 1 if the trees have the same 
fringe,
+## else 0.
+.sub same_fringe
+       .param pmc tree1
+       .param pmc tree2
+
+       .local int coro_class
+       coro_class = find_type 'Parrot::Coroutine'
+       if coro_class goto found
+       printerr "Bug:  Can't find 'Parrot::Coroutine' class.\n"
+       die 5, 1
+found:
+       .local pmc coro1, coro2
+       .const .Sub coro_sub = "coro_enumerate_tree"
+       coro1 = new coro_class, coro_sub
+       coro2 = new coro_class, coro_sub
+       ($P0 :optional, $I0 :opt_flag) = coro1.'resume'(coro1, tree1)
+       ($P1 :optional, $I1 :opt_flag) = coro2.'resume'(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) = coro1.'resume'()
+       ($P1 :optional, $I1 :opt_flag) = coro2.'resume'()
+       goto loop
+not_equal:
+       .return (0)
+equal:
+       .return (1)
+.end
+
+.sub main :main
+       load_bytecode 'Test/Builder.pir'
+       .local pmc test    
+       test = new 'Test::Builder'
+       test.'plan'(N_TESTS)
+
+       push_eh cant_load
+       load_bytecode 'Parrot/Coroutine.pir'
+       clear_eh
+       test.'ok'(1, 'loaded bytecode')
+
+       ## grow some trees for traversal.
+       .local pmc binary, binary_4, ternary, ternary_2
+       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 will be different.
+       ternary_2 = make_nary_tree(1, 8, 3, 2)
+       $P0 = ternary_2[1]
+       $P0 = $P0[0]
+       ternary_2[1] = $P0
+       ## enumerate_tree(ternary_2)
+       test.'ok'(1, 'made test trees.')
+
+       $I0 = same_fringe(binary, binary)
+       test.'ok'($I0, 'binary [[[1,2],[3,4]],[[5,6],[7,8]]] vs. itself')
+       $I0 = same_fringe(binary, binary_4)
+       $I0 = 1 - $I0
+       test.'ok'($I0, 'binary 1..8 vs. binary 1..16')
+       $I0 = same_fringe(binary, ternary)
+       test.'ok'($I0, 'binary 1..8 vs. ternary [[1,2,3],[4,5,6],[7,8]]')
+       $I0 = same_fringe(binary, ternary_2)
+       $I0 = 1 - $I0
+       test.'ok'($I0, 'binary 1..8 vs. ternary [[1,2,3],4,[7,8]]')
+       test.'finish'()
+       end
+cant_load:
+       test.'ok'(0, 'Load failed')
+       test.'finish'()
+.end

Property changes on: t/library/coroutine.t
___________________________________________________________________
Name: svn:mime-type
   + text/plain


End of diffs.

Reply via email to