File Coverage

blib/lib/PDL/PP/PDLCode.pm
Criterion Covered Total %
statement 292 380 76.8
branch 59 154 38.3
condition 25 65 38.4
subroutine 62 75 82.6
pod n/a
total 438 674 64.9


line stmt bran cond sub pod time code
1             # This file provides a class that parses the Code -member
2             # of the PDL::PP code.
3             #
4             # This is what makes the nice loops go around etc.
5             #
6              
7             package # hide from PAUSE/MetaCPAN
8             PDL::PP::Code;
9              
10 3     3   21 use strict;
  3         8  
  3         121  
11 3     3   15 use warnings;
  3         38  
  3         208  
12 3     3   19 use Carp;
  3         6  
  3         20923  
13              
14 95     95   107 sub get_pdls { @{$_[0]}{qw(ParNames ParObjs)} }
  95         196  
15              
16             my @code_args_always = qw(BadFlag SignatureObj GenericTypes ExtraGenericSwitches HaveBroadcasting Name);
17             sub make_args {
18 9     9   19 my ($target) = @_;
19 9         151 ("${target}CodeParsed", ["${target}CodeUnparsed","Bad${target}CodeUnparsed?",@code_args_always]);
20             }
21              
22             # Do the appropriate substitutions in the code.
23             sub new {
24 10     10   48 my($class,$code,$badcode,
25             $handlebad, $sig,$generictypes,$extrageneric,$havebroadcasting,$name,
26             $dont_add_brcloop, $backcode, $nulldatacheck) = @_;
27 10         48 my $parnames = $sig->names_sorted;
28 10         48 $handlebad = !!$handlebad;
29              
30 10 50       36 confess "Error: missing name argument to PDL::PP::Code->new call!\n"
31             unless defined $name;
32             confess "Error: empty or undefined GenericTypes!\n"
33 10 50       11 unless @{$generictypes || []};
  10 50       52  
34              
35 10 50 0     25 $badcode //= $code if $handlebad;
36              
37             # last two arguments may not be supplied
38             #
39             # "backcode" is a flag to the PDL::PP::Broadcastloop class indicating the broadcastloop
40             # is for writeback code (typically used for writeback of data from child to parent PDL
41              
42 10   33     52 $dont_add_brcloop ||= !$havebroadcasting; # two have identical (though inverted) meaning so only track one
43              
44             # C++ style comments
45             #
46             # This regexp isn't perfect because it doesn't cope with
47             # literal string constants.
48             #
49 10         24 $code =~ s,//.*?\n,,g;
50              
51 10 50       32 if ($::PP_VERBOSE) {
52 0         0 print "Processing code for $name\n";
53 0 0       0 print "DONT_ADD_BRCLOOP!\n" if $dont_add_brcloop;
54 0         0 print "EXTRAGEN: {" .
55             join(" ",
56             map "$_=>$$extrageneric{$_}", sort keys %$extrageneric)
57             . "}\n";
58 0         0 print "ParNAMES: ",(join ',',@$parnames),"\n";
59 0         0 print "GENTYPES: ", @$generictypes, "\n";
60 0         0 print "HandleBad: $handlebad\n";
61             }
62 10         42 my $this = bless {
63             IndObjs => $sig->dims_obj,
64             ParNames => $parnames,
65             ParObjs => $sig->objs,
66             Sig => $sig,
67             Gencurtype => [], # stack to hold GenType in generic switches
68             ftypes_vars => {},
69             ftypes_type => undef,
70             Generictypes => $generictypes, # so that MacroAccess can check it
71             Name => $name,
72             NullDataCheck => $nulldatacheck,
73             BadFlag => $handlebad,
74             }, $class;
75              
76 10         29 my @codes = $code;
77 10 0 0     33 push @codes, $badcode if $handlebad && ($code ne $badcode || $badcode =~ /PDL_BAD_CODE|PDL_IF_BAD/);
      33        
78 10         21 my (@coderefs, @sizeprivs);
79 10         22 for my $c (@codes) {
80             # First, separate the code into an array of C fragments (strings),
81             # variable references (strings starting with $) and
82             # loops (array references, 1. item = variable.
83 10         48 my ( $broadcastloops, $coderef, $sizeprivs ) =
84             $this->separate_code( "{$c}" );
85             # Now, if there is no explicit broadcastlooping in the code,
86             # enclose everything into it.
87 9 50 66     50 if(!$broadcastloops && !$dont_add_brcloop) {
88 8 50       19 print "Adding broadcastloop...\n" if $::PP_VERBOSE;
89 8 50       64 $coderef = $coderef->enter(('PDL::PP::'.($backcode ? 'BackCode' : '').'BroadcastLoop')->new);
90             }
91             # Enclose it all in a generic switch.
92 9   50     53 my $if_gentype = ($code.($badcode//'')) =~ /PDL_IF_GENTYPE_/;
93             $coderef = $coderef->enter(PDL::PP::GenericSwitch->new($generictypes, undef,
94 9         44 [grep {!$extrageneric->{$_}} @$parnames],'$PRIV(__datatype)',$if_gentype));
  16         90  
95             # Do we have extra generic switches?
96             # If we do, first reverse the hash:
97 9         20 my %glh;
98 9         31 push @{$glh{$extrageneric->{$_}}},$_ for sort keys %$extrageneric;
  0         0  
99 9         19 my $no = 0;
100             $coderef = $coderef->enter(PDL::PP::GenericSwitch->new($generictypes,$no++,
101 9         26 $glh{$_},$_,$if_gentype)) for sort keys %glh;
102 9         17 push @coderefs, $coderef;
103 9         22 push @sizeprivs, $sizeprivs;
104             }
105 9 50       92 amalgamate_sizeprivs(@sizeprivs) if @sizeprivs > 1;
106 9         16 my $sizeprivs = $sizeprivs[0];
107 9         46 my $coderef = PDL::PP::BadSwitch->new( @coderefs );
108 9 50       24 print "SIZEPRIVSX: ",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
109              
110 9         34 my $pobjs = $sig->objs;
111             # Then, in this form, put it together what we want the code to actually do.
112 9 50       22 print "SIZEPRIVS: ",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
113             $this->{Code} = (join '',sort values %$sizeprivs).
114             ($dont_add_brcloop?'':join '', map "$_\n",
115             'if (!$PRIV(broadcast).incs) $CROAK("broadcast.incs NULL");',
116             '/* broadcastloop declarations */',
117             'int __brcloopval;',
118             'register PDL_Indx __tind0,__tind1; /* counters along dim */',
119             'register PDL_Indx __tnpdls = $PRIV(broadcast).npdls;',
120             '/* dims here are how many steps along those dims */',
121             (map "register PDL_Indx __tinc0_$parnames->[$_] = PDL_BRC_INC(\$PRIV(broadcast).incs,__tnpdls,$_,0);", 0..$#$parnames),
122             (map "register PDL_Indx __tinc1_$parnames->[$_] = PDL_BRC_INC(\$PRIV(broadcast).incs,__tnpdls,$_,1);", 0..$#$parnames),
123             eol_protect(
124             "#define ".$this->broadcastloop_macroname($backcode, 'START') . " " .
125             $this->broadcastloop_start($this->func_name($backcode))
126             ),
127             eol_protect(
128             "#define ".$this->broadcastloop_macroname($backcode, 'END') . " " .
129             $this->broadcastloop_end
130             ),
131 9 50       138 (grep $_, map $_->get_incregisters, @$pobjs{sort keys %$pobjs}),
132             ).
133             $this->params_declare.
134             $coderef->get_str($this,[])
135             ;
136 7         192 $this->{Code};
137             } # new
138              
139             # amalgamate sizeprivs from Code/BadCode segments
140             # (sizeprivs is a simple hash, with each element
141             # containing a string - see PDL::PP::Loop)
142             sub amalgamate_sizeprivs {
143 0     0   0 my ($sizeprivs, $bad_sizeprivs) = @_;
144 0         0 while ( my ( $bad_key, $bad_str ) = each %$bad_sizeprivs ) {
145 0         0 my $str = $$sizeprivs{$bad_key};
146 0 0 0     0 die "ERROR: sizeprivs problem in PP/PDLCode.pm (BadVal stuff)\n"
147             if defined $str and $str ne $bad_str;
148 0         0 $$sizeprivs{$bad_key} = $bad_str; # copy over
149             }
150             }
151              
152             sub eol_protect {
153 18     18   34 my ($text) = @_;
154 18         370 join " \\\n", grep /\S/, split /\n/, $text;
155             }
156              
157             sub params_declare {
158 9     9   20 my ($this) = @_;
159 9         23 my ($ord,$pdls) = $this->get_pdls;
160 9         56 my %istyped = map +($_=>1), grep $pdls->{$_}{FlagTypeOverride}, @$ord;
161             my @decls = map $_->get_xsdatapdecl($istyped{$_->name} ? "PDL_TYPE_PARAM_".$_->name : "PDL_TYPE_OP", $this->{NullDataCheck}, $istyped{$_->name} ? "PDL_PPSYM_PARAM_".$_->name : "PDL_PPSYM_OP", $this->{BadFlag}),
162 9 100       54 map $pdls->{$_}, @$ord;
    100          
163 9         48 my @param_names = ("PDL_TYPE_OP", "PDL_PPSYM_OP", map +("PDL_TYPE_PARAM_$_","PDL_PPSYM_PARAM_$_"), grep $istyped{$_}, @$ord);
164 9         40 <
165             #ifndef PDL_DECLARE_PARAMS_$this->{Name}_$this->{NullDataCheck}
166 9         52 #define PDL_DECLARE_PARAMS_$this->{Name}_$this->{NullDataCheck}(@{[join ',', @param_names]}) \\
167 9         91 @{[join " \\\n ", @decls]}
168             #endif
169             EOF
170             }
171              
172 161 50   161   450 sub func_name { $_[1] ? "writebackdata" : "readdata" }
173              
174             sub broadcastloop_macroname {
175 152     152   196 my ($this, $backcode, $which) = @_;
176 152         319 "PDL_BROADCASTLOOP_${which}_$this->{Name}_".$this->func_name($backcode);
177             }
178              
179             sub broadcastloop_start {
180 9     9   27 my ($this, $funcname) = @_;
181 9         34 my ($ord,$pdls) = $this->get_pdls;
182 9         20 <
183             PDL_BROADCASTLOOP_START(
184             $funcname,
185             \$PRIV(broadcast),
186             \$PRIV(vtable),
187 9         70 @{[ PDL::PP::indent 2, join "", map $pdls->{$ord->[$_]}->do_pointeraccess." += __offsp[$_];\n", 0..$#$ord ]} ,
188 9         49 (@{[ PDL::PP::indent 2, join "", map ",".$pdls->{$ord->[$_]}->do_pointeraccess." += __tinc1_$ord->[$_] - __tinc0_$ord->[$_] * __tdims0\n", 0..$#$ord ]} ),
189 9         23 (@{[ PDL::PP::indent 2, join "", map ",".$pdls->{$ord->[$_]}->do_pointeraccess." += __tinc0_$ord->[$_]\n", 0..$#{$ord} ]} )
  9         31  
190             )
191             EOF
192             }
193              
194             sub broadcastloop_end {
195 9     9   20 my ($this) = @_;
196 9         21 my ($ord,$pdls) = $this->get_pdls();
197 9         32 <
198             PDL_BROADCASTLOOP_END(
199             \$PRIV(broadcast),
200 9         44 @{[ PDL::PP::indent 2, join "", map $pdls->{$ord->[$_]}->do_pointeraccess." -= __tinc1_$ord->[$_] * __tdims1 + __offsp[$_];\n", 0..$#$ord ]}
201             )
202             EOF
203             }
204              
205 1     1   5 sub sig {$_[0]->{Sig}}
206              
207             # This sub determines the index name for this index.
208             # For example, a(x,y) and x0 becomes [x,x0]
209 3     3   7 sub make_loopind { my($this,$ind) = @_;
210 3         15 ($ind, my $cntrlval) = split /\s*=\s*/, $ind;
211 3         6 my $orig = $ind;
212 3         13 while(!$this->{IndObjs}{$ind}) {
213 0 0       0 if(!((chop $ind) =~ /[0-9]/)) {
214 0         0 confess("Index not found for $_ ($ind)!\n");
215             }
216             }
217 3   50     15 my ($initval, $endval, $inc) = split /\s*:\s*/, $cntrlval//'';
218 3         13 [$ind,$orig,$initval,$endval,$inc];
219             }
220              
221             my %access2class = (
222             GENERIC => 'PDL::PP::GentypeAccess',
223             PPSYM => 'PDL::PP::PpsymAccess',
224             );
225              
226             sub process {
227 9     9   26 my ($this, $code, $stack_ref, $broadcastloops_ref, $sizeprivs) = @_;
228 9         29 while($code) {
229             # Parse next statement
230 16 50       360 $code =~ s/^(.*?) # First, some noise is allowed. This may be bad.
231             ( \$(ISBAD|ISGOOD|SETBAD)\s*\(\s*\$?[a-zA-Z_]\w*\s*\([^)]*\)\s*\) # $ISBAD($a(..)), ditto for ISGOOD and SETBAD
232             |\$[a-zA-Z_]\w*\s*\([^)]*\) # $a(...): access
233             |\bloop\s*\([^)]+\)\s*%\{ # loop(..) %{
234             |\btypes\s*\([^)]+\)\s*%\{ # types(..) %{
235             |\b(?:thread|broadcast)loop\s*%\{ # broadcastloop %{
236             |%} # %}
237             |$)//xs
238             or confess("Invalid program $code");
239 16         47 my $control = $2;
240             # Store the user code.
241             # Some day we shall parse everything.
242 16         39 push @{$stack_ref->[-1]},$1;
  16         66  
243             # Then, our control.
244 16 50       38 if (!$control) { print("No \$2!\n") if $::PP_VERBOSE; next; }
  9 100       26  
  9         32  
245 7 100       61 if($control =~ /^loop\s*\(([^)]+)\)\s*%\{/) {
    50          
    100          
    100          
246 1         11 my $ob = PDL::PP::Loop->new([split ',',$1], $sizeprivs,$this);
247 1 50       3 print "SIZEPRIVSXX: $sizeprivs,",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
248 1         2 push @{$stack_ref->[-1]},$ob;
  1         3  
249 1         4 push @$stack_ref,$ob;
250             } elsif($control =~ /^types\s*\(([^)]+)\)\s*%\{/) {
251 0         0 my $ob = PDL::PP::Types->new($1,$this);
252 0         0 push @{$stack_ref->[-1]},$ob;
  0         0  
253 0         0 push @$stack_ref,$ob;
254             } elsif($control =~ /^(?:thread|broadcast)loop\s*%\{/) {
255 1         10 my $ob = PDL::PP::BroadcastLoop->new;
256 1         2 push @{$stack_ref->[-1]},$ob;
  1         3  
257 1         3 push @$stack_ref,$ob;
258 1         3 $$broadcastloops_ref++;
259             } elsif($control =~ /^%}/) {
260 2         6 pop @$stack_ref;
261             } else {
262 3         16 my ($rest, @add) = $this->expand($control.$code);
263 3         7 push @{$stack_ref->[-1]}, @add;
  3         7  
264 3         11 $code = $rest;
265             }
266             } # while: $code
267             }
268              
269             # my ( $broadcastloops, $coderef, $sizeprivs ) = $this->separate_code( $code );
270             #
271             # separates the code into an array of C fragments (strings),
272             # variable references (strings starting with $) and
273             # loops (array references, 1. item = variable.
274             #
275             sub separate_code {
276 10     10   26 my ( $this, $code ) = @_;
277             # First check for standard code errors:
278 10         48 $this->catch_code_errors($code);
279 9         60 my @stack = my $coderef = PDL::PP::Block->new;
280 9         22 my $broadcastloops = 0;
281 9         15 my $sizeprivs = {};
282 9         41 $this->process($code, \@stack, \$broadcastloops, $sizeprivs);
283 9         34 ( $broadcastloops, $coderef, $sizeprivs );
284             } # sub: separate_code()
285              
286             my $macro_pat = qr/\w+/;
287             sub expand {
288 3     3   9 my ($this, $text) = @_;
289 3         9 my (undef, $pdl, $inds, $rest) = PDL::PP::Rule::Substitute::macro_extract($text, $macro_pat);
290 3         8 my @add;
291 3 100       25 if($pdl =~ /^T/) {@add = PDL::PP::MacroAccess->new($pdl,$inds,
    50          
    50          
    50          
292 1         17 $this->{Generictypes},$this->{Name});}
293 0         0 elsif(my $c = $access2class{$pdl}) {@add = $c->new($pdl,$inds)}
294             elsif($pdl =~ /^(P|)(ISBAD|ISGOOD|SETBAD)(VAR|)$/) {
295 0         0 my ($opcode, $name) = ($2);
296 0   0     0 my $get = $1 || $3;
297 0 0       0 if (!$get) {
    0          
298 0         0 $inds =~ s/^\$?([a-zA-Z_]\w*)\s*//; # $ is optional
299 0         0 $name = $1;
300 0         0 $inds = substr $inds, 1, -1; # chop off brackets
301             } elsif ($get eq 'P') {
302 0         0 ($name, $inds) = PDL::PP::Rule::Substitute::split_cpp($inds);
303             } else {
304 0         0 ($inds, $name) = PDL::PP::Rule::Substitute::split_cpp($inds);
305             }
306 0         0 @add = PDL::PP::BadAccess->new($opcode,$get,$name,$inds,$this);
307             }
308 2         18 elsif($this->{ParObjs}{$pdl}) {@add = PDL::PP::Access->new($pdl,$inds)}
309             else {
310 0         0 confess "unknown construct $pdl($inds)";
311             }
312 3         12 ($rest, @add);
313             }
314              
315             # This is essentially a collection of regexes that look for standard code
316             # errors and croaks with an explanation if they are found.
317             sub catch_code_errors {
318 10     10   25 my ($this, $code_string) = @_;
319 10         75 my $prefix = "pp_def($this->{Name}): ";
320 10 100       135 report_error("${prefix}Expected dimension name after 'loop' and before '%{'", $1)
321             if $code_string =~ /(.*\bloop\s*%\{)/s;
322             }
323              
324             # Report an error as precisely as possible. If they have #line directives
325             # in the code string, use that in the reporting; otherwise, use standard
326             # Carp mechanisms
327             my $line_re = qr/(?:PDL_LINENO_START|#\s*line)\s+(\d+)\s+"([^"]*)"/;
328             sub report_error {
329 1     1   6 my ($message, $code) = @_;
330             # Just croak if they didn't supply a #line directive:
331 1 50       358 croak($message) if $code !~ $line_re;
332             # Find the line at which the error occurred:
333 0         0 my $line = 0;
334 0         0 my $filename;
335 0         0 LINE: foreach (split /\n/, $code) {
336 0         0 $line++;
337 0 0       0 if (/$line_re/) {
338 0         0 $line = $1;
339 0         0 $filename = $2;
340             }
341             }
342 0         0 die "$message at $filename line $line\n";
343             }
344              
345             #####################################################################
346             #
347             # Encapsulate the parsing code objects
348             #
349             # All objects have two methods:
350             # new - constructor
351             # get_str - get the string to be put into the xsub.
352              
353             package # hide from PAUSE/MetaCPAN
354             PDL::PP::Block;
355              
356 9     9   27 sub new { my($type) = @_; bless [],$type; }
  9         26  
357              
358 68     68   271 sub myoffs { 0 }
359 138     138   293 sub myextraindent { 0 }
360       68     sub myprelude {}
361       66     sub mypostlude {}
362              
363             sub get_str {
364 147     147   267 my ($this,$parent,$context) = @_;
365 147         309 my $str = $this->myprelude($parent,$context);
366 147   50     318 $str .= PDL::PP::indent 2, $this->get_str_int($parent,$context)//'';
367 141   100     255 $str .= $this->mypostlude($parent,$context)//'';
368 141         453 return $str;
369             }
370              
371             sub get_str_int {
372 147     147   198 my ( $this, $parent, $context ) = @_;
373 147         145 my $nth=0;
374 147         151 my $str = "";
375 147         152 MYLOOP: while(1) {
376 347   100     768 my $it = $this->can('myitemstart') && $this->myitemstart($parent,$nth);
377 347 100 100     701 last MYLOOP if $nth and !$it;
378 206   100     450 $str .= $it//'';
379 206         301 $str .= PDL::PP::indent $this->myextraindent, join '', $this->get_contained($parent,$context);
380 200 100 66     589 $str .= $it if $it = $this->can('myitemend') && $this->myitemend($parent,$nth);
381 200         210 $nth++;
382             }
383 141         364 return $str;
384             } # get_str_int()
385              
386             sub get_contained {
387 206     206   248 my ($this, $parent, $context) = @_;
388 206 100       294 map ref($_) ? $_->get_str($parent, $context) : $_,
389             @$this[$this->myoffs..$#$this];
390             }
391              
392             sub enter {
393 17     17   32 my ($this, $new) = @_;
394 17         63 push @$new, $this;
395 17         28 $new;
396             }
397              
398             ###########################
399             #
400             # Deal with bad code
401             # - ie create something like
402             # if ( badflag ) { badcode } else { goodcode }
403             #
404             package # hide from PAUSE/MetaCPAN
405             PDL::PP::BadSwitch;
406             our @ISA = "PDL::PP::Block";
407              
408             sub new {
409 9     9   22 my($type,$good,$bad) = @_;
410 9         21 return bless [$good,$bad], $type;
411             }
412              
413             sub get_str {
414 9     9   26 my ($this,$parent,$context) = @_;
415 9         25 my $good = $this->[0];
416 9         15 my $good_str = <
417             #define PDL_IF_BAD(t,f) f
418 9         59 @{[ $good->get_str($parent,$context)
419             ]}#undef PDL_IF_BAD
420             EOF
421 7 50       69 return $good_str if !defined(my $bad = $this->[1]);
422 0         0 my $str = <
423             if ( \$PRIV(bvalflag) ) { /* ** do 'bad' Code ** */
424             #define PDL_BAD_CODE
425             #define PDL_IF_BAD(t,f) t
426 0         0 @{[ PDL::PP::indent 2, $bad->get_str($parent,$context)
427             ]} #undef PDL_BAD_CODE
428             #undef PDL_IF_BAD
429             } else { /* ** else do 'good' Code ** */
430 0         0 @{[ PDL::PP::indent 2, $good_str
431             ]}}
432             EOF
433             }
434              
435             package # hide from PAUSE/MetaCPAN
436             PDL::PP::Loop;
437             our @ISA = "PDL::PP::Block";
438              
439 1     1   4 sub new { my($type,$args,$sizeprivs,$parent) = @_;
440 1         3 my $this = bless [$args],$type;
441 1         4 for (@$args) {
442 1 50       3 print "SIZP $sizeprivs, $_\n" if $::PP_VERBOSE;
443 1         5 my $i = $parent->make_loopind($_);
444 1         4 my $i_size = $parent->sig->dims_obj->ind_obj($i->[0])->get_size;
445 1         5 $sizeprivs->{$i->[0]} = "register PDL_Indx __$i->[0]_size = $i_size;\n";
446 1 50       6 print "SP :",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
447             }
448 1         3 return $this;
449             }
450              
451 2     2   24 sub myoffs { return 1; }
452 2     2   4 sub myprelude { my($this,$parent,$context) = @_;
453 2         15 my $text = "";
454             push @$context, map {
455 2         6 my $i = $parent->make_loopind($_);
456 2         7 my ($loopdim, $loopvar, $loopstart, $loopend, $loopinc) = @$i;
457 2         3 my $loopstopvar = "__${loopvar}_stop";
458 2   50     12 $loopinc ||= 1; my $cmp;
  2         3  
459 2 50       8 if ($loopinc =~ /^-/) {
460 0 0 0     0 $loopstart = !(defined $loopstart && length $loopstart) ? "(__${loopdim}_size-1)" :
    0          
461             $loopstart =~ /^-/ ? "PDLMIN((__${loopdim}_size$loopstart), (__${loopdim}_size-1))" :
462             "PDLMIN($loopstart, (__${loopdim}_size-1))";
463 0         0 $cmp = ">=";
464 0 0       0 $loopend = !$loopend ? 0 :
    0          
465             $loopend =~ /^-/ ? "PDLMAX((__${loopdim}_size$loopend),0)" :
466             "PDLMAX(($loopend),0)";
467             } else {
468             # count upwards
469 2 0       6 $loopstart = !$loopstart ? 0 :
    50          
470             $loopstart =~ /^-/ ? "PDLMAX((__${loopdim}_size$loopstart),0)" :
471             "PDLMAX(($loopstart),0)";
472 2         5 $cmp = "<";
473 2 0 33     8 $loopend = !(defined $loopend && length $loopend) ? "(__${loopdim}_size)" :
    50          
474             $loopend =~ /^-/ ? "(__${loopdim}_size$loopend)" :
475             "PDLMIN($loopend, (__${loopdim}_size))";
476             }
477 2         25 $text .= "{/* Open $_ */ PDL_EXPAND2(register PDL_Indx $loopvar=$loopstart, $loopstopvar=$loopend); for(; $loopvar$cmp$loopstopvar; $loopvar+=$loopinc) {";
478 2         8 $i;
479 2         4 } @{$this->[0]};
  2         6  
480 2         5 $text;
481             }
482 2     2   5 sub mypostlude { my($this,$parent,$context) = @_;
483 2         5 splice @$context, - ($#{$this->[0]}+1);
  2         8  
484 2         5 return join '', map "}} /* Close $_ */", @{$this->[0]};
  2         27  
485             }
486              
487             package # hide from PAUSE/MetaCPAN
488             PDL::PP::GenericSwitch;
489 3     3   38 use Carp;
  3         6  
  3         442  
490             our @ISA = "PDL::PP::Block";
491              
492             # make the typetable from info in PDL::Types
493 3     3   24 use PDL::Types ':All';
  3         5  
  3         5322  
494             my %type2canonical = map +($_->ppsym=>$_,$_->identifier=>$_), types();
495             my @typetable = map [$_->ppsym, $_], types();
496 9     9   20 sub get_generictyperecs { my($types) = @_;
497 9         76 my @bad = grep !$type2canonical{$_}, @$types;
498 9 50       24 confess "Invalid GenericType (@bad)!" if @bad;
499 9         16 my %wanted; @wanted{map $type2canonical{$_}->ppsym, @$types} = ();
  9         110  
500 9         204 [ map $_->[1], grep exists $wanted{$_->[0]}, @typetable ];
501             }
502              
503             # Types: BSULFD
504             sub new {
505 9     9   29 my ($type,$types,$name,$varnames,$whattype,$if_gentype) = @_;
506 9         14 my %vars; @vars{@$varnames} = ();
  9         27  
507 9         30 bless [get_generictyperecs($types), $name, \%vars, $whattype, $if_gentype], $type;
508             }
509              
510 68     68   249 sub myoffs {5}
511 68     68   132 sub myextraindent { 2 }
512              
513             sub myprelude {
514 9     9   18 my ($this,$parent,$context) = @_;
515 9         11 push @{$parent->{Gencurtype}}, undef; # so that $GENERIC can get at it
  9         25  
516             die "ERROR: need to rethink NaN support in GenericSwitch\n"
517 9 0 33     26 if defined $this->[1] and $parent->{ftypes_type};
518 9         27 qq[switch ($this->[3]) { /* Start generic switch */\n];
519             }
520              
521             my @GENTYPE_ATTRS = qw(integer real unsigned);
522             sub myitemstart {
523 75     75   104 my ($this,$parent,$nth) = @_;
524 75   100     152 my $item = $this->[0][$nth] || return "";
525 68         102 $parent->{Gencurtype}[-1] = $item;
526 68 50       110 @$parent{qw(ftypes_type ftypes_vars)} = ($item, $this->[2]) if defined $this->[1];
527 68         103 my ($ord,$pdls) = $parent->get_pdls;
528 68         210 my %istyped = map +($_=>1), grep $pdls->{$_}{FlagTypeOverride}, @$ord;
529             my @param_ctypes = ($item->ctype, $item->ppsym,
530             map +($pdls->{$_}->adjusted_type($item)->ctype,
531             $pdls->{$_}->adjusted_type($item)->ppsym),
532 68         118 grep $istyped{$_}, @$ord);
533 68         188 my $decls = keys %{$this->[2]} == @$ord
534 68         598 ? "PDL_DECLARE_PARAMS_$parent->{Name}_$parent->{NullDataCheck}(@{[join ',', @param_ctypes]})\n"
535             : join '', map $_->get_xsdatapdecl($_->adjusted_type($item)->ctype, $parent->{NullDataCheck}, $_->adjusted_type($item)->ppsym, $parent->{BadFlag}),
536 68 50       89 map $parent->{ParObjs}{$_}, sort keys %{$this->[2]};
  0         0  
537 68 0       151 my @gentype_decls = !$this->[4] ? () : map "#define PDL_IF_GENTYPE_".uc($_)."(t,f) ".
    50          
538             ($item->$_ ? 't' : 'f')."\n",
539             @GENTYPE_ATTRS;
540 68         75 "case @{[$item->sym]}: {\n" .
  68         128  
541             PDL::PP::indent 2, join '',
542             @gentype_decls,
543             $decls;
544             }
545              
546             sub myitemend {
547 66     66   91 my ($this,$parent,$nth) = @_;
548 66   50     150 my $item = $this->[0][$nth] || return "";
549 66 50       289 join '',
550             "\n",
551             (!$this->[4] ? () : map "#undef PDL_IF_GENTYPE_".uc($_)."\n", @GENTYPE_ATTRS),
552             "} break;\n";
553             }
554              
555             sub mypostlude {
556 7     7   18 my($this,$parent,$context) = @_;
557 7         24 pop @{$parent->{Gencurtype}}; # and clean up the Gentype stack
  7         21  
558 7 50       24 $parent->{ftypes_type} = undef if defined $this->[1];
559 7         12 my $supported = join '', map $_->ppsym, @{$this->[0]};
  7         26  
560 7         40 " default: return PDL->make_error(PDL_EUSERERROR, \"PP INTERNAL ERROR in $parent->{Name}: unhandled datatype(%d), only handles ($supported)! PLEASE MAKE A BUG REPORT\\n\", $this->[3]);\n}\n";
561             }
562              
563             ####
564             #
565             # This relies on PP.pm making sure that initbroadcaststruct always sets
566             # up the two first dimensions even when they are not necessary.
567             #
568             package # hide from PAUSE/MetaCPAN
569             PDL::PP::BroadcastLoop;
570 3     3   29 use Carp;
  3         6  
  3         810  
571             our @ISA = "PDL::PP::Block";
572              
573             sub new {
574 9     9   17 my $type = shift;
575 9         40 bless [],$type;
576             }
577 68     68   159 sub myoffs { return 0; }
578             sub myprelude {
579 68     68   100 my($this,$parent,$context,$backcode) = @_;
580 68         110 $parent->broadcastloop_macroname($backcode, 'START');
581             }
582              
583 66     66   95 sub mypostlude {my($this,$parent,$context,$backcode) = @_;
584 66         108 $parent->broadcastloop_macroname($backcode, 'END');
585             }
586              
587             # Simple subclass of BroadcastLoop to implement writeback code
588             #
589             #
590             package # hide from PAUSE/MetaCPAN
591             PDL::PP::BackCodeBroadcastLoop;
592 3     3   34 use Carp;
  3         7  
  3         737  
593             our @ISA = "PDL::PP::BroadcastLoop";
594              
595             sub myprelude {
596 0     0   0 my($this,$parent,$context,$backcode) = @_;
597             # Set backcode flag if not defined. This will make the parent
598             # myprelude emit proper writeback code
599 0   0     0 $this->SUPER::myprelude($parent, $context, $backcode // 1);
600             }
601              
602             sub mypostlude {
603 0     0   0 my($this,$parent,$context,$backcode) = @_;
604             # Set backcode flag if not defined. This will make the parent
605             # mypostlude emit proper writeback code
606 0   0     0 $this->SUPER::mypostlude($parent, $context, $backcode // 1);
607             }
608              
609             ###########################
610             #
611             # Encapsulate a types() switch
612             #
613             package # hide from PAUSE/MetaCPAN
614             PDL::PP::Types;
615 3     3   27 use Carp;
  3         7  
  3         276  
616 3     3   34 use PDL::Types ':All';
  3         6  
  3         2017  
617             our @ISA = "PDL::PP::Block";
618             my %types = map +($_=>1), ppdefs_all; # BSUL....
619              
620             sub new {
621 0     0   0 my($type,$ts,$parent) = @_;
622 0         0 my @bad = grep !$types{$_}, my @ts = split '', $ts;
623 0 0       0 confess "Invalid type access (@bad) in '$ts'!" if @bad;
624 0         0 bless [+{map +($_=>1), @ts}],$type; }
625 0     0   0 sub myoffs { return 1; }
626              
627             sub get_str {
628 0     0   0 my ($this,$parent,$context) = @_;
629             confess "types() outside a generic switch"
630 0 0       0 unless defined(my $type = $parent->{Gencurtype}[-1]);
631 0 0       0 return '' if !$this->[0]{$type->ppsym};
632 0         0 join '', $this->get_contained($parent,$context);
633             }
634              
635              
636             package # hide from PAUSE/MetaCPAN
637             PDL::PP::Access;
638 3     3   31 use Carp;
  3         7  
  3         870  
639              
640 2     2   7 sub new { my($type,$pdl,$inds) = @_;
641 2         8 bless [$pdl,$inds],$type;
642             }
643              
644 2     2   7 sub get_str { my($this,$parent,$context) = @_;
645             $parent->{ParObjs}{$this->[0]}->do_access($this->[1],$context)
646 2 50       28 if defined($parent->{ParObjs}{$this->[0]});
647             }
648              
649             ###########################
650             # Encapsulate a check on whether a value is good or bad
651             # handles both checking (good/bad) and setting (bad)
652             package # hide from PAUSE/MetaCPAN
653             PDL::PP::BadAccess;
654 3     3   99 use Carp;
  3         8  
  3         2761  
655              
656             sub new {
657 0     0   0 my ( $type, $opcode, $get, $name, $inds, $parent ) = @_;
658             die "\nIt looks like you have tried a $get \$${opcode}() macro on an" .
659             " unknown ndarray <$name($inds)>\n"
660 0 0       0 unless defined($parent->{ParObjs}{$name});
661 0         0 bless [$opcode, $get, $name, $inds], $type;
662             }
663              
664 0     0   0 sub _isbad { "PDL_ISBAD2($_[0],$_[1],$_[2],$_[3])" }
665             our %ops = (
666             ISBAD => \&_isbad,
667             ISGOOD => sub {'!'.&_isbad},
668             SETBAD => sub{join '=', @_[0,1]},
669             );
670             my %getters = (
671             '' => sub {my ($obj, $inds, $context)=@_; $obj->do_access($inds,$context)},
672             P => sub {my ($obj, $inds)=@_; $obj->do_pointeraccess.$inds},
673             VAR => sub {my ($obj, $inds)=@_; $inds},
674             );
675              
676             sub get_str {
677 0     0   0 my ($this,$parent,$context) = @_;
678 0         0 my ($opcode, $get, $name, $inds) = @$this;
679             confess "generic type access outside a generic switch in $name"
680 0 0       0 unless defined $parent->{Gencurtype}[-1];
681 0 0       0 print "PDL::PP::BadAccess sent [$opcode] [$name] [$inds]\n" if $::PP_VERBOSE;
682             die "ERROR: unknown check <$opcode> sent to PDL::PP::BadAccess\n"
683 0 0       0 unless defined( my $op = $ops{$opcode} );
684             die "ERROR: something screwy in PDL::PP::BadAccess (PP/PDLCode.pm)\n"
685 0 0       0 unless defined( my $obj = $parent->{ParObjs}{$name} );
686 0         0 my $lhs = $getters{$get}->($obj, $inds, $context);
687 0         0 my $rhs = "${name}_badval";
688 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
689             my $type = exists $parent->{ftypes_vars}{$name}
690             ? $parent->{ftypes_type}
691 0 0       0 : $obj->adjusted_type($parent->{Gencurtype}[-1]);
692 0         0 $op->($lhs, $rhs, $type->ppsym, $rhs."_isnan");
693             }
694              
695              
696             package # hide from PAUSE/MetaCPAN
697             PDL::PP::MacroAccess;
698 3     3   45 use Carp;
  3         8  
  3         219  
699 3     3   16 use PDL::Types ':All';
  3         5  
  3         2857  
700             my $types = join '',ppdefs_all;
701              
702             sub new {
703 1     1   5 my ($type, $pdl, $inds, $gentypes, $name) = @_;
704 1         13 my @normalised = map PDL::Type->new($_)->ppsym, @$gentypes;
705 1 50       11 $pdl =~ /^\s*T([A-Z]+)\s*$/
706             or confess("Macroaccess wrong in $name (allowed types $types): was '$pdl'\n");
707 1         8 my @ilst = split '', $1;
708 1         8 my @lst = PDL::PP::Rule::Substitute::split_cpp($inds);
709 1 50       5 confess "Macroaccess: different nos of args $pdl (@{[scalar @lst]}=@lst) vs (@{[scalar @ilst]}=@ilst)\n" if @lst != @ilst;
  0         0  
  0         0  
710 1         3 my %type2value; @type2value{@ilst} = @lst;
  1         8  
711             confess "$name has no Macro for generic type $_ (has $pdl)\n"
712 1         7 for grep !exists $type2value{$_}, @normalised;
713 1         3 my %gts; @gts{@normalised} = ();
  1         5  
714             warn "Macro for unsupported generic type identifier $_\n"
715 1         6 for grep !exists $gts{$_}, @ilst;
716 1         8 bless [\%type2value, $name], $type;
717             }
718              
719             sub get_str {
720 4     4   8 my ($this, $parent, $context) = @_;
721 4         5 my ($type2value, $name) = @{$this};
  4         48  
722             confess "generic type access outside a generic switch in $name"
723 4 50       16 unless defined $parent->{Gencurtype}[-1];
724 4         15 $type2value->{$parent->{Gencurtype}[-1]->ppsym};
725             }
726              
727             package # hide from PAUSE/MetaCPAN
728             PDL::PP::GentypeAccess;
729 3     3   26 use Carp;
  3         5  
  3         946  
730              
731 0     0     sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0            
732              
733 0     0     sub get_str {my($this,$parent,$context) = @_;
734             confess "generic type access outside a generic switch"
735 0 0         unless defined(my $type = $parent->{Gencurtype}[-1]);
736 0 0         return $type->ctype if !$this->[0];
737 0   0       my $pobj = $parent->{ParObjs}{$this->[0]} // confess "not a defined parname";
738 0           $pobj->adjusted_type($type)->ctype;
739             }
740              
741             package # hide from PAUSE/MetaCPAN
742             PDL::PP::PpsymAccess;
743 3     3   23 use Carp;
  3         49  
  3         1063  
744              
745 0     0     sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0            
746              
747 0     0     sub get_str {my($this,$parent,$context) = @_;
748             confess "generic type access outside a generic switch"
749 0 0         unless defined(my $type = $parent->{Gencurtype}[-1]);
750 0 0         return $type->ppsym if !$this->[0];
751 0   0       my $pobj = $parent->{ParObjs}{$this->[0]} // confess "not a defined parname";
752 0           $pobj->adjusted_type($type)->ppsym;
753             }
754              
755             1;