File Coverage

blib/lib/RPerl/CodeBlock/Subroutine.pm
Criterion Covered Total %
statement 36 36 100.0
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 48 48 100.0


line stmt bran cond sub pod time code
1             # [[[ HEADER ]]]
2             package RPerl::CodeBlock::Subroutine;
3 9     9   3835 use strict;
  9         35  
  9         220  
4 9     9   44 use warnings;
  9         18  
  9         184  
5 9     9   43 use RPerl::AfterSubclass;
  9         16  
  9         1269  
6             our $VERSION = 0.012_000;
7              
8             ## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls) # USER DEFAULT 1: allow numeric values & print operator
9             ## no critic qw(RequireInterpolationOfMetachars) # USER DEFAULT 2: allow single-quoted control characters & sigils
10             ## no critic qw(Capitalization ProhibitMultiplePackages ProhibitReusedNames) # SYSTEM DEFAULT 3: allow multiple & lower case package names
11             BEGIN {
12              
13             package # hide from PAUSE indexing
14             string_hashref::method;
15 9     9   61 use strict;
  9         22  
  9         187  
16 9     9   46 use warnings;
  9         24  
  9         335  
17 9     9   26 1;
18              
19             package # hide from PAUSE indexing
20             string::method;
21 9     9   52 use strict;
  9         19  
  9         185  
22 9     9   41 use warnings;
  9         21  
  9         255  
23 9         15 1;
24              
25             # DEV NOTE, CORRELATION #rp003: method types reside in Subroutine.pm, which is a sub-type of Subroutine.pm, causing the need to have this special BEGIN block
26             # to enable the *::method types, and Class.pm's INIT block symbol table entry generator needs us to switch back into the primary package so we have
27             # that happen in the following line, which furthermore triggers the need to avoid re-defining class accessor/mutator methods; how to fix?
28             package RPerl::CodeBlock::Subroutine;
29 9     9   52 use strict;
  9         19  
  9         173  
30 9     9   39 use warnings;
  9         20  
  9         163  
31 9         154 1;
32             }
33              
34             # [[[ OO INHERITANCE ]]]
35 9     9   45 use parent qw(RPerl::CodeBlock);
  9         19  
  9         56  
36 9     9   341 use RPerl::CodeBlock;
  9         19  
  9         13403  
37              
38             # [[[ OO PROPERTIES ]]]
39             our hashref $properties = {};
40              
41             # [[[ SUBROUTINES & OO METHODS ]]]
42              
43             our string_hashref::method $ast_to_rperl__generate = sub {
44             ( my object $self, my string_hashref $modes) = @ARG;
45             my string_hashref $rperl_source_group = { PMC => q{} };
46             my string_hashref $rperl_source_subgroup;
47              
48             # RPerl::diag( 'in Subroutine->ast_to_rperl__generate(), received $self = ' . "\n" . RPerl::Parser::rperl_ast__dump($self) . "\n" );
49              
50             # unwrap Subroutine_48 from SubroutineOrMethod_76, only if needed
51             if ((ref $self) eq 'SubroutineOrMethod_76') { $self = $self->{children}->[0]; }
52              
53             if ( ( ref $self ) ne 'Subroutine_48' ) {
54             die RPerl::Parser::rperl_rule__replace(
55             'ERROR ECOGEASRP00, CODE GENERATOR, ABSTRACT SYNTAX TO RPERL: Grammar rule ' . ( ref $self ) . ' found where Subroutine_48 expected, dying' )
56             . "\n";
57             }
58              
59             # RPerl::diag( 'in Subroutine->ast_to_rperl__generate(), have possibly-unwrapped $self = ' . "\n" . RPerl::Parser::rperl_ast__dump($self) . "\n" );
60              
61             my string $our = $self->{children}->[0];
62             my string $return_type = $self->{children}->[1];
63             my string $name = $self->{children}->[2];
64             my string $equal_sub = $self->{children}->[3];
65             my object $arguments_optional = $self->{children}->[4];
66             my object $operations_star = $self->{children}->[5];
67             my string $right_brace = $self->{children}->[6];
68             my string $semicolon = $self->{children}->[7];
69            
70             if ((substr $name, 1, 1) eq '_') {
71             die 'ERROR ECOGEASRP08, CODE GENERATOR, ABSTRACT SYNTAX TO RPERL: subroutine name ' . ($name)
72             . ' must not start with underscore, dying' . "\n";
73             }
74              
75             # CREATE SYMBOL TABLE ENTRY
76             $modes->{_symbol_table}->{_subroutine} = $name; # set current subroutine/method
77             $modes->{_symbol_table}->{$modes->{_symbol_table}->{_namespace}}->{_global}->{$name} = {isa => 'RPerl::CodeBlock::Subroutine', type => $return_type}; # create individual symtab entry
78              
79             # NEED UPGRADE, CORRELATION #rp035: allow multi-line subroutines & other code blocks, where they would be less than 160 chars if on a single line
80             # DEV NOTE: no newline appended in the next line, all newlines removed from subroutine body via regex replacement after foreach loop below,
81             # thus allowing for single-line subroutines as well as multi-line subroutines, at the control of Perl::Tidy
82             $rperl_source_group->{PMC} .= $our . q{ } . $return_type->{children}->[0] . q{ } . $name . q{ } . $equal_sub;
83              
84             if ( exists $arguments_optional->{children}->[0] ) {
85             $rperl_source_subgroup = $arguments_optional->{children}->[0]->ast_to_rperl__generate($modes);
86             RPerl::Generator::source_group_append( $rperl_source_group, $rperl_source_subgroup );
87             }
88              
89             foreach my object $operation ( @{ $operations_star->{children} } ) {
90             $rperl_source_subgroup = $operation->ast_to_rperl__generate($modes);
91             RPerl::Generator::source_group_append( $rperl_source_group, $rperl_source_subgroup );
92             }
93              
94             $rperl_source_group->{PMC} =~ s/\n/\ /gxms;
95              
96             $rperl_source_group->{PMC} .= $right_brace . $semicolon . "\n\n";
97             return $rperl_source_group;
98             };
99              
100             our string_hashref::method $ast_to_cpp__generate__CPPOPS_PERLTYPES = sub {
101             ( my object $self, my string_hashref $modes) = @ARG;
102             my string_hashref $cpp_source_group = { CPP => q{// <<< RP::CB::S __DUMMY_SOURCE_CODE CPPOPS_PERLTYPES >>>} . "\n" };
103              
104             #...
105             return $cpp_source_group;
106             };
107              
108             our string_hashref::method $ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES = sub {
109             ( my object $self, my string_hashref $modes) = @ARG;
110             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), received $modes->{_symbol_table} = ' . "\n" . Dumper($modes->{_symbol_table}) . "\n");
111            
112             my string_hashref $cpp_source_group = { H => q{} };
113              
114             # unwrap Subroutine_48 from SubroutineOrMethod_76, only if needed
115             if ((ref $self) eq 'SubroutineOrMethod_76') { $self = $self->{children}->[0]; }
116              
117             my string $return_type = $self->{children}->[1]->{children}->[0];
118             my string $name = $self->{children}->[2];
119             my object $arguments_optional = $self->{children}->[4];
120              
121             #RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), have $arguments_optional = ' . "\n" . RPerl::Parser::rperl_ast__dump($arguments_optional) . "\n" );
122             #RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), have $return_type = ' . "\n" . RPerl::Parser::rperl_ast__dump($return_type) . "\n" );
123              
124             substr $name, 0, 1, q{}; # remove leading $ sigil
125             my string_arrayref $arguments = [];
126              
127             if ((substr $name, 0, 1) eq '_') {
128             die 'ERROR ECOGEASCP08, CODE GENERATOR, ABSTRACT SYNTAX TO C++: subroutine name ' . ($name)
129             . ' must not start with underscore, dying' . "\n";
130             }
131              
132             # CREATE SYMBOL TABLE ENTRY
133             $modes->{_symbol_table}->{_subroutine} = $name; # set current subroutine/method
134             $modes->{_symbol_table}->{$modes->{_symbol_table}->{_namespace}}->{_global}->{$name} = {isa => 'RPerl::CodeBlock::Subroutine', type => $return_type}; # create individual symtab entry
135              
136             $return_type = RPerl::Generator::type_convert_perl_to_cpp($return_type, 1); # $pointerify_classes = 1
137             $modes->{_symbol_table}->{$modes->{_symbol_table}->{_namespace}}->{_global}->{$name}->{type_cpp} = $return_type; # add converted C++ type to symtab entry
138              
139             # DEV NOTE, CORRELATION #rp022: must prefix subroutine names with namespace-underscores to simulate Perl's behavior of not exporting subroutines by default
140             my string $namespace_underscores = q{};
141             if ((exists $modes->{_symbol_table}->{_namespace}) and (defined $modes->{_symbol_table}->{_namespace})) {
142             $namespace_underscores = $modes->{_symbol_table}->{_namespace};
143             $namespace_underscores =~ s/:/_/gxms;
144             }
145             $cpp_source_group->{H} .= $return_type . q{ } . $namespace_underscores . $name . '(';
146              
147             if ( exists $arguments_optional->{children}->[0] ) {
148             my object $cpp_source_subgroup = $arguments_optional->{children}->[0]->ast_to_cpp__generate__CPPOPS_CPPTYPES($modes);
149              
150             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), have $cpp_source_subgroup = ' . "\n" . RPerl::Parser::rperl_ast__dump($cpp_source_subgroup) . "\n" );
151             # DEV NOTE: don't use RPerl::Generator::source_group_append() due to cross-wiring H-from-CPP below
152             $cpp_source_group->{H} .= $cpp_source_subgroup->{CPP};
153             if ( ( exists $cpp_source_subgroup->{H_INCLUDES} ) and ( defined $cpp_source_subgroup->{H_INCLUDES} ) ) {
154             $cpp_source_group->{H_INCLUDES} .= $cpp_source_subgroup->{H_INCLUDES};
155             }
156             }
157              
158             $cpp_source_group->{H} .= ');';
159             return $cpp_source_group;
160             };
161              
162             our string_hashref::method $ast_to_cpp__generate__CPPOPS_CPPTYPES = sub {
163             ( my object $self, my string_hashref $modes) = @ARG;
164             my string_hashref $cpp_source_group = { CPP => q{} };
165              
166             # unwrap Subroutine_48 from SubroutineOrMethod_76, only if needed
167             if ((ref $self) eq 'SubroutineOrMethod_76') { $self = $self->{children}->[0]; }
168              
169             my string $return_type = $self->{children}->[1]->{children}->[0];
170             my string $name = $self->{children}->[2];
171             my object $arguments_optional = $self->{children}->[4];
172             my object $operations_star = $self->{children}->[5];
173              
174             substr $name, 0, 1, q{}; # remove leading $ sigil
175             my string_arrayref $arguments = [];
176             my object $cpp_source_subgroup;
177            
178             #RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), have $arguments_optional = ' . "\n" . RPerl::Parser::rperl_ast__dump($arguments_optional) . "\n" );
179             #RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), have $return_type = ' . "\n" . RPerl::Parser::rperl_ast__dump($return_type) . "\n" );
180              
181             $return_type = RPerl::Generator::type_convert_perl_to_cpp($return_type, 1); # $pointerify_classes = 1
182            
183             # DEV NOTE: must prefix subroutine names with namespace-underscores to simulate Perl's behavior of not exporting subroutines by default
184             my string $namespace_underscores = q{};
185             if ((exists $modes->{_symbol_table}->{_namespace}) and (defined $modes->{_symbol_table}->{_namespace})) {
186             $namespace_underscores = $modes->{_symbol_table}->{_namespace};
187             $namespace_underscores =~ s/:/_/gxms;
188             }
189             $cpp_source_group->{CPP} .= $return_type . q{ } . $namespace_underscores . $name . '(';
190              
191             if ( exists $arguments_optional->{children}->[0] ) {
192             $cpp_source_subgroup = $arguments_optional->{children}->[0]->ast_to_cpp__generate__CPPOPS_CPPTYPES($modes);
193              
194             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate__CPPOPS_CPPTYPES(), have $cpp_source_subgroup = ' . "\n" . RPerl::Parser::rperl_ast__dump($cpp_source_subgroup) . "\n" );
195             RPerl::Generator::source_group_append( $cpp_source_group, $cpp_source_subgroup );
196             }
197              
198             $cpp_source_group->{CPP} .= ') {' . "\n";
199             my string $CPP_saved = $cpp_source_group->{CPP};
200             $cpp_source_group->{CPP} = q{};
201              
202             foreach my object $operation ( @{ $operations_star->{children} } ) {
203              
204             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate_declaration__CPPOPS_CPPTYPES(), have $operation = ' . "\n" . RPerl::Parser::rperl_ast__dump($operation) . "\n" );
205             # disable *_CHECK() and *_CHECKTRACE() data checking routines in CPPOPS_CPPTYPES mode, this is instead handled in xs_unpack_*() called by typemap.rperl
206             if (( exists $operation->{children}->[0]->{children}->[0]->{children}->[0] )
207             and ( ( ( substr $operation->{children}->[0]->{children}->[0]->{children}->[0], -6, 6 ) eq '_CHECK' )
208             or ( ( substr $operation->{children}->[0]->{children}->[0]->{children}->[0], -11, 11 ) eq '_CHECKTRACE' ) )
209             )
210             {
211             next;
212             }
213             $cpp_source_subgroup = $operation->ast_to_cpp__generate__CPPOPS_CPPTYPES($modes);
214              
215             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate__CPPOPS_CPPTYPES(), have $cpp_source_subgroup = ' . "\n" . RPerl::Parser::rperl_ast__dump($cpp_source_subgroup) . "\n" );
216             RPerl::Generator::source_group_append( $cpp_source_group, $cpp_source_subgroup );
217             }
218              
219             # COMPILE-TIME OPTIMIZATION #02: declare all loop iterators at top of subroutine/method to avoid re-declarations in nested loops
220             if ((exists $modes->{_loop_iterators}) and (defined $modes->{_loop_iterators})) {
221             foreach my string $loop_iterator_symbol (sort keys %{$modes->{_loop_iterators}}) {
222             $CPP_saved .= $modes->{_loop_iterators}->{$loop_iterator_symbol} . q{ } . $loop_iterator_symbol . ';' . "\n";
223             }
224             delete $modes->{_loop_iterators};
225             }
226              
227             $CPP_saved .= $cpp_source_group->{CPP};
228             $cpp_source_group->{CPP} = $CPP_saved;
229              
230             $cpp_source_group->{CPP} .= '}';
231              
232             return $cpp_source_group;
233             };
234              
235             our string_hashref::method $ast_to_cpp__generate_shims__CPPOPS_CPPTYPES = sub {
236             ( my object $self, my string_hashref $modes) = @ARG;
237             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate_shims__CPPOPS_CPPTYPES(), received $modes->{_symbol_table} = ' . "\n" . Dumper($modes->{_symbol_table}) . "\n");
238            
239             my string_hashref $cpp_source_group = { CPP => q{} };
240             my object $cpp_source_subgroup = undef;
241              
242             # unwrap Subroutine_48 from SubroutineOrMethod_76, only if needed
243             if ((ref $self) eq 'SubroutineOrMethod_76') { $self = $self->{children}->[0]; }
244              
245             # my string $return_type = $self->{children}->[1]->{children}->[0]; # SHIM SUBS DEPRECATED IN FAVOR OF MACROS
246             my string $name = $self->{children}->[2];
247             my object $arguments_optional = $self->{children}->[4];
248              
249             #RPerl::diag( 'in Subroutine->ast_to_cpp__generate_shims__CPPOPS_CPPTYPES(), have $arguments_optional = ' . "\n" . RPerl::Parser::rperl_ast__dump($arguments_optional) . "\n" );
250             #RPerl::diag( 'in Subroutine->ast_to_cpp__generate_shims__CPPOPS_CPPTYPES(), have $return_type = ' . "\n" . RPerl::Parser::rperl_ast__dump($return_type) . "\n" );
251              
252             substr $name, 0, 1, q{}; # remove leading $ sigil
253            
254             if ((substr $name, 0, 1) eq '_') {
255             die 'ERROR ECOGEASCP08, CODE GENERATOR, ABSTRACT SYNTAX TO C++: subroutine name ' . ($name)
256             . ' must not start with underscore, dying' . "\n";
257             }
258              
259             # DEV NOTE, CORRELATION #rp022: must create shims to un-prefix subroutine names with namespace-underscores to un-simulate Perl's behavior of not exporting subroutines by default
260             my string $namespace_colons = q{};
261             my string $namespace_underscores = q{};
262             if ((exists $modes->{_symbol_table}->{_namespace}) and (defined $modes->{_symbol_table}->{_namespace})) {
263             $namespace_colons = $modes->{_symbol_table}->{_namespace};
264             $namespace_underscores = $namespace_colons;
265             $namespace_underscores =~ s/:/_/gxms;
266             }
267            
268             # DEV NOTE: only generate PMC output file in dynamic (default) subcompile mode
269             if ($modes->{subcompile} eq 'DYNAMIC') {
270             # hard-coded example, PMC subroutine
271             #undef &RPerl::Algorithm::Sort::Bubble::integer_bubblesort;
272             #*RPerl::Algorithm::Sort::Bubble::integer_bubblesort = sub { return main::RPerl__Algorithm__Sort__Bubble__integer_bubblesort(@ARG); };
273             $cpp_source_group->{PMC} = 'undef &' . $namespace_colons . $name . ';'. "\n";
274             $cpp_source_group->{PMC} .= '*' . $namespace_colons . $name . ' = sub { return main::' . $namespace_underscores . $name . '(@ARG); };';
275             }
276              
277             =DEPRECATED IN FAVOR OF MACROS
278             # hard-coded example, CPP subroutine
279             # void display_pi_digits(integer n) { return MathPerl__Geometry__PiDigits__display_pi_digits(n); }
280              
281             $cpp_source_group->{CPP} .= $return_type . q{ } . $name . '(';
282              
283             if ( exists $arguments_optional->{children}->[0] ) {
284             $cpp_source_subgroup = $arguments_optional->{children}->[0]->ast_to_cpp__generate__CPPOPS_CPPTYPES($modes);
285              
286             # RPerl::diag( 'in Subroutine->ast_to_cpp__generate_shims__CPPOPS_CPPTYPES(), have $cpp_source_subgroup = ' . "\n" . RPerl::Parser::rperl_ast__dump($cpp_source_subgroup) . "\n" );
287             $cpp_source_group->{CPP} .= $cpp_source_subgroup->{CPP};
288             }
289              
290             $cpp_source_group->{CPP} .= ') ' . ' { return ' . $namespace_underscores . $name . '(';
291              
292             # remove type declarations from arguments when passing from shim to real subroutine
293             if (defined $cpp_source_subgroup) {
294             # split on commas, split again on space, discard types, join names on space, join again on commas, append
295             my string_arrayref $split_arguments = [ split ', ', $cpp_source_subgroup->{CPP} ];
296             my string_arrayref $typeless_arguments = [];
297             foreach my string $argument (@{$split_arguments}) {
298             my string_arrayref $split_argument = [ split /[ ]/xms, $argument ];
299             push @{$typeless_arguments}, $split_argument->[1];
300             }
301             $cpp_source_group->{CPP} .= ( join ', ', @{$typeless_arguments} );
302             }
303              
304             $cpp_source_group->{CPP} .= '); }';
305             =cut
306              
307             # hard-coded example, CPP macro
308             # #define display_pi_digits(n) MathPerl__Geometry__PiDigits__display_pi_digits(n)
309             $cpp_source_group->{CPP} .= '#define ' . $name . '(';
310              
311             if ( exists $arguments_optional->{children}->[0] ) {
312             $cpp_source_subgroup = $arguments_optional->{children}->[0]->ast_to_cpp__generate__CPPOPS_CPPTYPES($modes);
313             }
314              
315             # remove type declarations from arguments
316             my string $typeless_arguments_joined = q{};
317             if (defined $cpp_source_subgroup) {
318             # split on commas, split again on space, discard types, join names on space, join again on commas, append
319             my string_arrayref $split_arguments = [ split ', ', $cpp_source_subgroup->{CPP} ];
320             my string_arrayref $typeless_arguments = [];
321             foreach my string $argument (@{$split_arguments}) {
322             my string_arrayref $split_argument = [ split /[ ]/xms, $argument ];
323             push @{$typeless_arguments}, $split_argument->[1];
324             }
325             $typeless_arguments_joined = ( join ', ', @{$typeless_arguments} );
326             }
327              
328             $cpp_source_group->{CPP} .= $typeless_arguments_joined . ') ' . $namespace_underscores . $name . '(' . $typeless_arguments_joined . ')';
329              
330             return $cpp_source_group;
331             };
332              
333             1; # end of class