There are several ways to do this.
My inclination is to work from the right data structure. Something like this:
\nmy @actions = (\n "Here is a description of foo.",\n &foo,\n "Here is a description of bar.",\n &bar,\n);\n\nprint "This is a test program! Here is the plan:\\n\\n";\nmy $count = 0;\nfor my $action (@actions) {\n next if ref($action);\n $count++;\n print "Step $count:\\n$action\\n\\n";\n}\n\nprint "Let's go!";\n\n$count = 0;\nfor my $action (@actions) {\n if (ref($action)) {\n $action->(); # do it\n }\n else {\n $count++;\n print "\\n\\nStep $count:\\n$action";\n }\n}\n\nsub foo {\n # ...\n}\n\nsub bar {\n # ...\n}\n
This works and avoids doing any cleverly deep magic. Unfortunately you do have to structure your program just so for it to work - you can't easily retrofit this onto an existing script.
You can also generalize this. For instance you could store an array of anonymous hash references, which have a description and a function attached. And then you could dynamically put together a test plan, describe it, then do it.
But if you want to go with your original strategy, then you can use Filter::Simple. The idea being that you can just use source filtering to design a construct that will build a data structure up front, print it, then emit messages later on. That would be something like this:
\n#### Filter/ShowSteps.pm #######\n\npackage Filter::ShowSteps;\n\nuse Filter::Simple;\n@ISA = 'Filter::Simple';\n\nuse strict;\n\nmy @steps;\n\nFILTER {\n # Original code is passed in in $_.\n # Modified code is whatever ends up in $_.\n\n my $is_code = 1;\n my @code;\n for my $piece (split /\\n-----+\\n/, $_) {\n if ($is_code) {\n push @code, $piece;\n }\n else {\n push @steps, $piece;\n push @code, join "\\n",\n "Filter::ShowSteps::show(<<'END_OF_COMMENT');",\n $piece,\n "END_OF_COMMENT";\n }\n $is_code = !$is_code;\n }\n $_ = join "\\n", @code;\n};\n\nsub show {\n print "\\n", @_;\n}\n\nsub show_all {\n print "$_\\n" for @steps;\n}\n\n1;\n\n#### some program #######\n\n#! /usr/bin/perl -w\nuse strict;\n\nuse Filter::ShowSteps;\n\nprint "This is a test program! Here is the plan:\\n\\n";\n\nFilter::ShowSteps::show_all();\n\nprint "\\nNow here we go!\\n\\n";\n\n---------------------------\nThis is the first step.\n---------------------------\nprint "Do something.\\n";\n\n---------------------------\nThis is the second step.\n---------------------------\nprint "Do something else.\\n";\n
(I didn't mean to write your program for you, but I guess I did...)
Cheers,
Ben