File Coverage

blib/lib/Sub/Genius/Util.pm
Criterion Covered Total %
statement 12 107 11.2
branch 0 28 0.0
condition 0 25 0.0
subroutine 4 13 30.7
pod 3 5 60.0
total 19 178 10.6


line stmt bran cond sub pod time code
1             package Sub::Genius::Util;
2              
3 1     1   310293 use strict;
  1         3  
  1         45  
4 1     1   5 use warnings;
  1         2  
  1         84  
5              
6 1     1   8 use parent q{Sub::Genius};
  1         2  
  1         9  
7 1     1   1046 use Util::H2O::More qw/ddd/;
  1         15328  
  1         1989  
8              
9             # dispatch for invocation method
10             my $invocation = {
11             any => \&_as_any, # invoke plan with run_any
12             all => \&_as_all, # invoke plan with loop using `next` + run_once
13             once => \&_as_once, # invoke plan without dependency on Sub::Genius
14             };
15              
16             sub _as_once {
17 0     0     return qq{
18             ## initialize Sub::Genius (caching 'on' by default)
19             my \$sq = Sub::Genius->new(preplan => qq{\$preplan} );
20             \$sq->init_plan;
21             my \$final_scope = \$sq->run_once( scope => {}, ns => q{main}, verbose => 1);};
22             }
23              
24             sub _as_any {
25 0     0     return qq{
26             ## initialize Sub::Genius (caching 'on' by default)
27             my \$final_state = Sub::Genius->new(preplan => qq{\$preplan})->run_any( scope => {}, ns => q{main}, verbose => 1);};
28             }
29              
30             sub _as_all {
31 0     0     return qq/
32             ## initialize Sub::Genius (caching 'on' by default)
33             my \$sq = Sub::Genius->new(preplan => qq{\$preplan} );
34             \$sq->init_plan;
35             do {
36             my \$final_scope = \$sq->run_once( scope => {}, ns => q{main}, verbose => 1);
37             }
38             while (\$sq->next);
39             /
40             }
41              
42             sub export_as {
43 0     0 0   my ( $self, %opts ) = @_;
44              
45 0 0 0       die qq{'preplan' and 'prefile' are mutually exclusive\n} if ( $opts{preplan} and $opts{prefile} );
46              
47 0 0         if ( defined $opts{prefile} ) {
48 0           local $/ = undef;
49 0   0       open my $ph, q{<}, $opts{prefile} || die $!;
50 0           $opts{preplan} = <$ph>;
51 0           close $ph;
52             }
53              
54 0           my $sq = $self->new(%opts);
55 0           $sq->init_plan;
56 0           print $sq->dfa->as_graphviz; # this is a minimal DFA
57              
58 0           return;
59             }
60              
61             sub list {
62 0     0 0   my ( $self, %opts ) = @_;
63              
64 0 0 0       die qq{'preplan' and 'prefile' are mutually exclusive\n} if ( $opts{preplan} and $opts{prefile} );
65              
66 0 0         if ( defined $opts{prefile} ) {
67 0           local $/ = undef;
68 0   0       open my $ph, q{<}, $opts{prefile} || die $!;
69 0           $opts{preplan} = <$ph>;
70 0           close $ph;
71             }
72              
73 0           my $sq = $self->new(%opts);
74 0           $sq->init_plan;
75              
76 0           while (my $preplan = $sq->next) {
77 0           print qq{$preplan\n};
78             }
79              
80 0           return;
81             }
82              
83             sub subs2perl {
84 0     0 1   my ( $self, %opts ) = @_;
85              
86 0 0 0       die qq{'preplan' and 'prefile' are mutually exclusive\n} if ( $opts{preplan} and $opts{prefile} );
87              
88 0 0         if ( defined $opts{prefile} ) {
89 0           local $/ = undef;
90 0   0       open my $ph, q{<}, $opts{prefile} || die $!;
91 0           $opts{preplan} = <$ph>;
92 0           close $ph;
93             }
94              
95             # PRE is parsed, but not converted to validate it
96 0           my $sq = $self->new(%opts);
97              
98 0           my @subs = split /[^\w]/, $opts{preplan};
99 0           my @pre_tokens = ();
100 0           my @perlsubpod = ();
101              
102             # make sure subs are not repeated
103 0           my %uniq = ();
104 0           foreach my $sub ( @subs ) {
105 0           ++$uniq{$sub};
106             }
107 0 0         delete $uniq{q{}} if $uniq{q{}};
108              
109 0           @subs = ();
110             SUBS:
111 0           foreach my $sub ( keys %uniq ) {
112 0           push @subs, $sub;
113 0           push @pre_tokens, $sub;
114 0           push @perlsubpod, qq{ =item * C<$sub>\n};
115             }
116              
117 0           my $perlsub = $self->_dump_subs( \@subs );
118              
119 0           my $perlpre = $opts{preplan};
120 0           $perlpre =~ s/\n$//;
121 0           $perlpre =~ s/^/ /gm;
122 0           my $perlsubpod = join( qq{\n}, @perlsubpod );
123 0           my $invokemeth = $invocation->{ $opts{q{with-run}} }->();
124              
125 0           my $perl = qq{#!/usr/bin/env perl
126             use strict;
127             use warnings;
128             use feature 'state';
129              
130             use Sub::Genius ();
131              
132             my \$preplan = q{
133             $perlpre
134             };
135              
136             ## intialize hash ref as container for global memory
137             my \$GLOBAL = {};
138            
139             $invokemeth
140              
141             $perlsub
142             exit;
143             __END__
144              
145             =head1 NAME
146              
147             nameMe - something click bait worthy for CPAN
148              
149             =head1 SYNAPSIS
150              
151             ..pithy example of use
152              
153             =head1 DESCRIPTION
154              
155             ..extended wordings on what this thing does
156              
157             =head1 METHODS
158              
159             =over 4
160              
161             $perlsubpod
162             =back
163              
164             =head1 SEE ALSO
165              
166             L, L
167              
168             =head1 COPYRIGHT AND LICENSE
169              
170             Same terms as perl itself.
171              
172             =head1 AUTHOR
173              
174             Rosie Tay Robert Ertr\@example.tldE
175             };
176 0           $perl =~ s/^ //gm;
177 0           return $perl;
178             }
179              
180             sub _dump_subs {
181 0     0     my ( $self, $subs ) = @_;
182              
183 0           my $perl = q{
184             #
185             # S U B R O U T I N E S
186             #
187             };
188              
189             DUMPSUBS:
190 0           foreach my $sub (@$subs) {
191 0 0         if ($sub =~ m/::/g) {
192 0           warn qq{'$sub' appears to be a call to a fully qualified method from an external package. Skipping subroutine stub...\n};
193 0           next DUMPSUBS;
194             }
195 0           $perl .= qq/
196             #TODO - implement the logic!
197             sub $sub {
198             my \$scope = shift; # execution context passed by Sub::Genius::run_once
199             my \$private = {}; # private variable hash, reaped when execution is out of sub scope
200             state \$mystate = {}; # gives subroutine state from call to call
201            
202             #-- begin subroutine implementation here --#
203             print qq{Sub $sub: ELOH! Replace me, I am just placeholder!\\n};
204            
205             # return \$scope, which will be passed to next subroutine
206             return \$scope;
207             }
208             /;
209             }
210 0           return $perl;
211             }
212              
213             #
214             # ####
215             #
216              
217             sub plan2nodeps {
218 0     0 1   my ( $self, %opts ) = @_;
219              
220 0 0 0       die qq{'preplan' and 'prefile' are mutually exclusive\n} if ( $opts{preplan} and $opts{prefile} );
221              
222 0 0         if ( defined $opts{prefile} ) {
223 0           local $/ = undef;
224 0   0       open my $ph, q{<}, $opts{prefile} || die $!;
225 0           $opts{preplan} = <$ph>;
226 0           close $ph;
227             }
228            
229 0           my $sq = $self->new(%opts);
230              
231 0           my $preplan = $sq->original_preplan;
232 0           $preplan =~ s/^/# /gm;
233 0           $preplan =~ s/\n$//g;
234              
235 0           my $perl = qq{ #!/usr/bin/env perl
236             use strict;
237             use warnings;
238             use feature 'state';
239              
240             # Sub::Genius is not used, but this call list has been generated
241             # using Sub::Genius::Util::plan2nodeps,
242             #
243             ## intialize hash ref as container for global memory
244             # The following sequence of calls is consistent with the original preplan,
245             # my \$preplan = q{
246             $preplan
247             # };
248              
249             my \$GLOBAL = {};
250             my \$scope = { thing => 0, };
251             };
252              
253             # init (compiles to DFA)
254 0           $sq->init_plan;
255              
256             # gets serialized execution plan
257 0           my @subs = split / /, $sq->next;
258              
259             # generate shot callers, 50" blades on the empala's
260 0           foreach my $sub (@subs) {
261 0           $perl .= qq{\$scope = $sub(\$scope);\n};
262             }
263              
264             # get uniq list of subs for sub stub generation
265 0           my %uniq = map { $_ => 1 } @subs;
  0            
266              
267 0 0         delete $uniq{q{}} if $uniq{q{}};
268              
269 0           $perl .= $self->_dump_subs( [ keys %uniq ] );
270              
271 0           $perl =~ s/^ //gm;
272              
273 0           return $perl;
274             }
275              
276             sub precache {
277 0     0 1   my ( $self, %opts ) = @_;
278              
279 0 0 0       die qq{'preplan' and 'prefile' are mutually exclusive\n} if ( $opts{preplan} and $opts{prefile} );
280              
281             # clean %opts, otherwise Sub::Genius will disable caching
282             # the others are safely ignored
283 0 0         delete $opts{cachedir} if not defined $opts{cachedir};
284              
285 0 0         if ( defined $opts{prefile} ) {
286 0           local $/ = undef;
287 0   0       open my $ph, q{<}, $opts{prefile} || die $!;
288 0           $opts{preplan} = <$ph>;
289 0           close $ph;
290             }
291 0           my $sq = $self->new(%opts)->init_plan;
292 0           return $sq;
293             }
294              
295             1;
296              
297             =encoding UTF-8
298              
299             =head1 NAME
300              
301             Sub::Genius::Util - Utilities for generating and inspecting Perl code from Sub::Genius plans
302              
303             =head1 SYNOPSIS
304              
305             use Sub::Genius::Util;
306              
307             # Generate a standalone Perl script from a plan
308             print Sub::Genius::Util->plan2nodeps(
309             plan => q{ A & B & C }
310             );
311              
312             This module is primarily intended for use by tooling such as
313             L, but its methods may also be invoked directly when exploring,
314             debugging, or materializing Sub::Genius plans.
315              
316             =head1 DESCRIPTION
317              
318             C provides helper routines that operate I
319             L to make execution plans concrete and inspectable.
320              
321             Where C focuses on expressing and executing concurrency
322             semantics, this module focuses on:
323              
324             =over 4
325              
326             =item *
327             Generating Perl code from declarative plans
328              
329             =item *
330             Materializing execution order explicitly
331              
332             =item *
333             Bootstrapping scripts or modules from plans
334              
335             =item *
336             Eliminating runtime dependency on Sub::Genius when desired
337              
338             =back
339              
340             The utilities in this module are most commonly used during development,
341             experimentation, or build-time code generation, rather than in
342             long-running production systems.
343              
344             =head2 Generated Subroutine Shape
345              
346             When generating Perl code that corresponds to plan symbols, each
347             subroutine is emitted with a conventional structure compatible with
348             C:
349              
350             sub C {
351             my $scope = shift; # execution context
352             state $mystate = {}; # persistent state (coroutine-style)
353             my $myprivs = {}; # lexical scratch space
354              
355             # --- implementation goes here ---
356             print qq{Sub C: placeholder\n};
357              
358             return $scope;
359             }
360              
361             This reflects the core Sub::Genius execution model, where a mutable
362             C<$scope> hash reference is threaded through the execution plan.
363              
364             =head1 METHODS
365              
366             =head2 subs2perl
367              
368             Sub::Genius::Util->subs2perl(...);
369              
370             Generates Perl subroutine stubs corresponding to the symbols implied by
371             a plan.
372              
373             This method exists to support tooling that initializes scripts or
374             modules intended to be executed under Sub::Genius. The generated code
375             is a starting point and is expected to be edited by hand.
376              
377             =head2 plan2nodeps
378              
379             Sub::Genius::Util->plan2nodeps( plan => $pre );
380              
381             Given a PRE, generates a standalone Perl script that explicitly encodes
382             the execution order implied by the plan.
383              
384             The resulting script:
385              
386             =over 4
387              
388             =item *
389             Does not depend on L at runtime
390              
391             =item *
392             Contains explicit subroutine calls
393              
394             =item *
395             Passes a C<$scope> variable between calls
396              
397             =back
398              
399             Example:
400              
401             perl -MSub::Genius::Util \
402             -e 'print Sub::Genius::Util->plan2nodeps(
403             plan => q{A&B&C&D}
404             )' > my-script.pl
405              
406             This produces code equivalent to what
407             C would execute dynamically, but fully spelled
408             out:
409              
410             my $scope = {};
411             $scope = C($scope);
412             $scope = A($scope);
413             $scope = D($scope);
414             $scope = B($scope);
415             $scope = E($scope);
416              
417             The exact order depends on the chosen valid serialization.
418              
419             =head2 precache
420              
421             my $sg = Sub::Genius::Util->precache(%opts);
422              
423             Invokes Sub::Genius caching facilities and returns an initialized
424             C instance.
425              
426             This method centralizes cache-related setup and ensures that PREs are
427             compiled only once unless explicitly forced. It is primarily intended
428             for build-time or tooling workflows.
429              
430             =head1 DESIGN NOTES
431              
432             This module is intentionally narrow in scope.
433              
434             It does not attempt to abstract away the mechanics of Sub::Genius or hide
435             how execution plans are linearized. Instead, it aims to make those
436             mechanics explicit and inspectable.
437              
438             If you find yourself calling these utilities repeatedly at runtime, it
439             is worth reconsidering whether code generation is the appropriate tool
440             for that use case.
441              
442             =head1 SEE ALSO
443              
444             L,
445             L,
446             L
447              
448             =head1 COPYRIGHT AND LICENSE
449              
450             Same terms as Perl itself.
451              
452             =head1 AUTHOR
453              
454             OODLER 577 Eoodler@cpan.orgE