File Coverage

blib/lib/PDL/PP/PDLCode.pm
Criterion Covered Total %
statement 256 560 45.7
branch 57 232 24.5
condition 6 27 22.2
subroutine 56 94 59.5
pod 0 1 0.0
total 375 914 41.0


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 PDL::PP::Code;
8 2     2   13 use Carp;
  2         4  
  2         131  
9             our @CARP_NOT;
10              
11 2     2   11 use strict;
  2         3  
  2         47  
12              
13             # check for bad value support
14             #
15 2     2   8 use PDL::Config;
  2         4  
  2         3755  
16             #use vars qw ( $bvalflag $usenan );
17             my $bvalflag = $PDL::Config{WITH_BADVAL} || 0;
18             my $usenan = $PDL::Config{BADVAL_USENAN} || 0;
19              
20 4     4   8 sub get_pdls {my($this) = @_; return ($this->{ParNames},$this->{ParObjs});}
  4         11  
21              
22             # we define the method separate_code() at the end of this
23             # file, so that it can call the constructors from the classes
24             # defined in this file. ugly...
25              
26             # Do the appropriate substitutions in the code.
27             sub new {
28 3     3   13 my($type,$code,$badcode,$parnames,$parobjs,$indobjs,$generictypes,
29             $extrageneric,$havethreading,$name,
30             $dont_add_thrloop, $nogeneric_loop, $backcode ) = @_;
31              
32 3 50       209 die "Error: missing name argument to PDL::PP::Code->new call!\n"
33             unless defined $name;
34              
35             # simple way of handling bad code check
36 3 50       139 $badcode = undef unless $bvalflag;
37 3         13 my $handlebad = defined($badcode);
38              
39             # last three arguments may not be supplied
40             # (in fact, the nogeneric_loop argument may never be supplied now?)
41             #
42             # "backcode" is a flag to the PDL::PP::Threadloop class indicating thre threadloop
43             # is for writeback code (typically used for writeback of data from child to parent PDL
44              
45 3 100       10 $dont_add_thrloop = 0 unless defined $dont_add_thrloop;
46 3 50       9 $nogeneric_loop = 0 unless defined $nogeneric_loop;
47              
48              
49             # C++ style comments
50             #
51             # This regexp isn't perfect because it doesn't cope with
52             # literal string constants.
53             #
54 3         10 $code =~ s,//.*?\n,,g;
55              
56 3 50       9 if ($::PP_VERBOSE) {
57 0         0 print "Processing code for $name\n";
58 0 0       0 print "DONT_ADD_THRLOOP!\n" if $dont_add_thrloop;
59             print "EXTRAGEN: {" .
60             join(" ",
61 0         0 map { "$_=>" . $$extrageneric{$_}} keys %$extrageneric)
  0         0  
62             . "}\n";
63 0         0 print "ParNAMES: ",(join ',',@$parnames),"\n";
64 0         0 print "GENTYPES: ", @$generictypes, "\n";
65 0         0 print "HandleBad: $handlebad\n";
66             }
67 3         28 my $this = bless {
68             IndObjs => $indobjs,
69             ParNames => $parnames,
70             ParObjs => $parobjs,
71             Gencurtype => [], # stack to hold GenType in generic loops
72             types => 0, # hack for PDL::PP::Types/GenericLoop
73             pars => {}, # hack for PDL::PP::NaNSupport/GenericLoop
74             Generictypes => $generictypes, # so that MacroAccess can check it
75             Name => $name,
76             }, $type;
77              
78 3         9 my $inccode = join '',map {$_->get_incregisters();} (sort values %{$this->{ParObjs}});
  3         17  
  3         25  
79              
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             #
84 3         19 my ( $threadloops, $coderef, $sizeprivs ) =
85             $this->separate_code( "{$inccode\n$code\n}" );
86              
87             # Now, if there is no explicit threadlooping in the code,
88             # enclose everything into it.
89 2 50 66     10 if(!$threadloops && !$dont_add_thrloop && $havethreading) {
      33        
90 0 0       0 print "Adding threadloop...\n" if $::PP_VERBOSE;
91 0         0 my $nc = $coderef;
92 0 0       0 if( !$backcode ){ # Normal readbackdata threadloop
93 0         0 $coderef = PDL::PP::ThreadLoop->new();
94             }
95             else{ # writebackcode threadloop
96 0         0 $coderef = PDL::PP::BackCodeThreadLoop->new();
97             }
98 0         0 push @{$coderef},$nc;
  0         0  
99             }
100              
101             # repeat for the bad code, then stick good and bad into
102             # a BadSwitch object which creates the necessary
103             # 'if (bad) { badcode } else { goodcode }' code
104             #
105             # NOTE: amalgamate sizeprivs from good and bad code
106             #
107 2 50       5 if ( $handlebad ) {
108 0 0       0 print "Processing 'bad' code...\n" if $::PP_VERBOSE;
109 0         0 my ( $bad_threadloops, $bad_coderef, $bad_sizeprivs ) =
110             $this->separate_code( "{$inccode\n$badcode\n}" );
111              
112 0 0 0     0 if(!$bad_threadloops && !$dont_add_thrloop && $havethreading) {
      0        
113 0 0       0 print "Adding 'bad' threadloop...\n" if $::PP_VERBOSE;
114 0         0 my $nc = $bad_coderef;
115 0 0       0 if( !$backcode ){ # Normal readbackdata threadloop
116 0         0 $bad_coderef = PDL::PP::ThreadLoop->new();
117             }
118             else{ # writebackcode threadloop
119 0         0 $bad_coderef = PDL::PP::BackCodeThreadLoop->new();
120             }
121 0         0 push @{$bad_coderef},$nc;
  0         0  
122             }
123              
124 0         0 my $good_coderef = $coderef;
125 0         0 $coderef = PDL::PP::BadSwitch->new( $good_coderef, $bad_coderef );
126              
127             # amalgamate sizeprivs from Code/BadCode segments
128             # (sizeprivs is a simple hash, with each element
129             # containing a string - see PDL::PP::Loop)
130 0         0 while ( my ( $bad_key, $bad_str ) = each %$bad_sizeprivs ) {
131 0         0 my $str = $$sizeprivs{$bad_key};
132 0 0       0 if ( defined $str ) {
133 0 0       0 die "ERROR: sizeprivs problem in PP/PDLCode.pm (BadVal stuff)\n"
134             unless $str eq $bad_str;
135             }
136 0         0 $$sizeprivs{$bad_key} = $bad_str; # copy over
137             }
138              
139             } # if: $handlebad
140              
141 2 50       6 print "SIZEPRIVSX: ",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
142              
143             # Enclose it all in a genericloop.
144 2 50       3 unless ($nogeneric_loop) {
145             # XXX Make genericloop understand denied pointers;...
146 2         5 my $nc = $coderef;
147             $coderef = PDL::PP::GenericLoop->new($generictypes,"",
148 2         5 [grep {!$extrageneric->{$_}} @$parnames],'$PRIV(__datatype)');
  2         13  
149 2         4 push @{$coderef},$nc;
  2         9  
150             }
151              
152             # Do we have extra generic loops?
153             # If we do, first reverse the hash:
154 2         3 my %glh;
155 2         6 for(keys %$extrageneric) {
156 0         0 push @{$glh{$extrageneric->{$_}}},$_;
  0         0  
157             }
158 2         4 my $no = 0;
159 2         3 for(keys %glh) {
160 0         0 my $nc = $coderef;
161             $coderef = PDL::PP::GenericLoop->new($generictypes,$no++,
162 0         0 $glh{$_},$_);
163 0         0 push @$coderef,$nc;
164             }
165              
166             # Then, in this form, put it together what we want the code to actually do.
167 2 50       6 print "SIZEPRIVS: ",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
168 2         14 $this->{Code} = "{".(join '',sort values %$sizeprivs).
169             $coderef->get_str($this,[])
170             ."}";
171 2         36 $this->{Code};
172              
173             } # new()
174              
175             # This sub determines the index name for this index.
176             # For example, a(x,y) and x0 becomes [x,x0]
177 3     3   7 sub make_loopind { my($this,$ind) = @_;
178 3         6 my $orig = $ind;
179 3         10 while(!$this->{IndObjs}{$ind}) {
180 0 0       0 if(!((chop $ind) =~ /[0-9]/)) {
181 0         0 confess("Index not found for $_ ($ind)!\n");
182             }
183             }
184 3         9 return [$ind,$orig];
185             }
186              
187              
188             #####################################################################
189             #
190             # Encapsulate the parsing code objects
191             #
192             # All objects have two methods:
193             # new - constructor
194             # get_str - get the string to be put into the xsub.
195              
196             ###########################
197             #
198             # Encapsulate a block
199              
200             package PDL::PP::Block;
201              
202 2     2   4 sub new { my($type) = @_; bless [],$type; }
  2         6  
203              
204 4     4   5 sub myoffs { return 0; }
205       4     sub myprelude {}
206 16     16   25 sub myitem {return "";}
207       4     sub mypostlude {}
208              
209             sub get_str {
210 10     10   19 my ($this,$parent,$context) = @_;
211 10         22 my $str = $this->myprelude($parent,$context);
212 10         36 $str .= $this->get_str_int($parent,$context);
213 10         28 $str .= $this->mypostlude($parent,$context);
214 10         75 return $str;
215             }
216              
217             sub get_str_int {
218 10     10   15 my ( $this, $parent, $context ) = @_;
219              
220 10         12 my $nth=0;
221 10         16 my $str = "";
222 10         12 MYLOOP: while(1) {
223 22         47 my $it = $this->myitem($parent,$nth);
224 22 100 100     66 last MYLOOP if $nth and !$it;
225 12         27 $str .= $it;
226 28 100       86 $str .= (join '',map {ref $_ ? $_->get_str($parent,$context) : $_}
227 12         28 @{$this}[$this->myoffs()..$#{$this}]);
  12         23  
  12         18  
228 12         31 $nth++;
229             }
230 10         30 return $str;
231             } # get_str_int()
232              
233             ###########################
234             #
235             # Deal with bad code
236             # - ie create something like
237             # if ( badflag ) { badcode } else { goodcode }
238             #
239             package PDL::PP::BadSwitch;
240             @PDL::PP::BadSwitch::ISA = "PDL::PP::Block";
241              
242             sub new {
243 0     0   0 my($type,$good,$bad) = @_;
244 0         0 return bless [$good,$bad], $type;
245             }
246              
247             sub get_str {
248 0     0   0 my ($this,$parent,$context) = @_;
249              
250 0         0 my $good = $this->[0];
251 0         0 my $bad = $this->[1];
252              
253 0         0 my $str = PDL::PP::pp_line_numbers(__LINE__, "if ( \$PRIV(bvalflag) ) { PDL_COMMENT(\"** do 'bad' Code **\")\n");
254 0         0 $str .= "\n#define PDL_BAD_CODE\n";
255 0         0 $str .= $bad->get_str($parent,$context);
256 0         0 $str .= "\n#undef PDL_BAD_CODE\n";
257 0         0 $str .= "} else { PDL_COMMENT(\"** else do 'good' Code **\")\n";
258 0         0 $str .= $good->get_str($parent,$context);
259 0         0 $str .= "}\n";
260              
261 0         0 return $str;
262             }
263              
264             ###########################
265             #
266             # Encapsulate a loop
267              
268             package PDL::PP::Loop;
269             @PDL::PP::Loop::ISA = "PDL::PP::Block";
270              
271 1     1   952 sub new { my($type,$args,$sizeprivs,$parent) = @_;
272 1         7 my $this = bless [$args],$type;
273 1         2 for(@{$this->[0]}) {
  1         7  
274 1 50       4 print "SIZP $sizeprivs, $_\n" if $::PP_VERBOSE;
275 1         3 my $i = $parent->make_loopind($_);
276 1         7 $sizeprivs->{$i->[0]} =
277             "register PDL_Indx __$i->[0]_size = \$PRIV(__$i->[0]_size);\n";
278 1 50       4 print "SP :",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
279             }
280 1         3 return $this;
281             }
282              
283 2     2   3 sub myoffs { return 1; }
284 2     2   5 sub myprelude { my($this,$parent,$context) = @_;
285 2         3 my $text = ""; my $i;
  2         4  
286             push @$context, map {
287 2         4 $i = $parent->make_loopind($_);
288             # Used to be $PRIV(.._size) but now we have it in a register.
289 2         10 $text .= "{PDL_COMMENT(\"Open $_\") register PDL_Indx $_;
290             for($_=0; $_<(__$i->[0]_size); $_++) {";
291 2         5 $i;
292 2         3 } @{$this->[0]};
  2         4  
293 2         6 return PDL::PP::pp_line_numbers(__LINE__, $text);
294             }
295 2     2   5 sub mypostlude { my($this,$parent,$context) = @_;
296 2         2 splice @$context, - ($#{$this->[0]}+1);
  2         8  
297 2         5 return join '',map {"}} PDL_COMMENT(\"Close $_\")"} @{$this->[0]};
  2         10  
  2         3  
298             }
299              
300             ###########################
301             #
302             # Encapsulate a generic type loop
303             #
304             # we use the value of $parent->{types} [set by a PDL::PP::Types object]
305             # to determine whether to define/undefine the THISISxxx macros
306             # (makes the xs code easier to read)
307             #
308             package PDL::PP::GenericLoop;
309             @PDL::PP::GenericLoop::ISA = "PDL::PP::Block";
310              
311             # Types: BSULFD
312 2     2   17 use PDL::Types ':All';
  2         3  
  2         1862  
313             sub new {
314 2     2   5 my($type,$types,$name,$varnames,$whattype) = @_;
315 2         6 bless [(PDL::PP::get_generictyperecs($types)),$name,$varnames,
316             $whattype],$type;
317             }
318              
319 4     4   7 sub myoffs {4}
320              
321             sub myprelude {
322 2     2   5 my($this,$parent,$context) = @_;
323 2         3 push @{$parent->{Gencurtype}},'PDL_undef'; # so that $GENERIC can get at it
  2         5  
324              
325             # horrible hack for PDL::PP::NaNSupport
326 2 50       7 if ( $this->[1] ne "" ) {
327 0         0 my ( @test ) = keys %{$parent->{pars}};
  0         0  
328 0 0       0 die "ERROR: need to rethink NaNSupport in GenericLoop\n"
329             if $#test != -1;
330 0         0 $parent->{pars} = {};
331             }
332              
333 2         3 my $thisis_loop = '';
334 2 50       4 if ( $parent->{types} ) {
335             $thisis_loop = join '',
336             map {
337 0         0 "#undef THISIS$this->[1]_$_\n#define THISIS$this->[1]_$_(a)\n"
  0         0  
338             }
339             (ppdefs);
340             }
341              
342 2         8 return <
343             PDL_COMMENT("Start generic loop")
344             $thisis_loop
345             switch($this->[3]) { case -42: PDL_COMMENT("Warning eater") {(void)1;
346             WARNING_EATER
347             }
348              
349             sub myitem {
350 6     6   12 my($this,$parent,$nth) = @_;
351             # print "GENERICITEM\n";
352 6         10 my $item = $this->[0]->[$nth];
353 6 100       14 if(!$item) {return "";}
  2         5  
354 4         9 $parent->{Gencurtype}->[-1] = $item->[1];
355              
356             # horrible hack for PDL::PP::NaNSupport
357 4 50       9 if ( $this->[1] ne "" ) {
358 0         0 foreach my $parname ( @{$this->[2]} ) {
  0         0  
359 0         0 $parent->{pars}{$parname} = $item->[1];
360             }
361             }
362              
363 4         5 my $thisis_loop = '';
364 4 50       10 if ( $parent->{types} ) {
365             $thisis_loop = (
366             join '',
367             map {
368 0         0 "#undef THISIS$this->[1]_$_\n#define THISIS$this->[1]_$_(a)\n";
  0         0  
369             }
370             (ppdefs)
371             ) .
372             "#undef THISIS$this->[1]_$item->[3]\n" .
373             "#define THISIS$this->[1]_$item->[3](a) a\n";
374             }
375              
376             return PDL::PP::pp_line_numbers(__LINE__, "\t} break; case $item->[0]: {\n".
377             $thisis_loop .
378             (join '',map{
379             # print "DAPAT: '$_'\n";
380 4         18 $parent->{ParObjs}{$_}->get_xsdatapdecl($item->[1]);
381 4         42 } (@{$this->[2]})));
  4         10  
382             }
383              
384             sub mypostlude {
385 2     2   4 my($this,$parent,$context) = @_;
386 2         3 pop @{$parent->{Gencurtype}}; # and clean up the Gentype stack
  2         4  
387              
388             # horrible hack for PDL::PP::NaNSupport
389 2 50       7 if ( $this->[1] ne "" ) { $parent->{pars} = {}; }
  0         0  
390              
391 2         4 return "\tbreak;}
392             default:barf(\"PP INTERNAL ERROR! PLEASE MAKE A BUG REPORT\\n\");}\n";
393             }
394              
395              
396             ###########################
397             #
398             # Encapsulate a threadloop.
399             # There are several different
400              
401             package PDL::PP::ThreadLoop;
402             sub new {
403 1     1   16 return PDL::PP::ComplexThreadLoop->new(@_);
404             }
405              
406             package PDL::PP::SimpleThreadLoop;
407 2     2   16 use Carp;
  2         5  
  2         680  
408             @PDL::PP::SimpleThreadLoop::ISA = "PDL::PP::Block";
409             our @CARP_NOT;
410              
411 0     0   0 sub new { my($type) = @_; bless [],$type; }
  0         0  
412 0     0   0 sub myoffs { return 0; }
413 0     0   0 sub myprelude {my($this,$parent,$context) = @_;
414 0         0 my $no;
415 0         0 my ($ord,$pdls) = $parent->get_pdls();
416             PDL::PP::pp_line_numbers(__LINE__, ' PDL_COMMENT("THREADLOOPBEGIN")
417             if(PDL->startthreadloop(&($PRIV(__pdlthread)),$PRIV(vtable)->readdata,
418             __privtrans))) return;
419             do {
420 0         0 '.(join '',map {"${_}_datap += \$PRIV(__pdlthread).offs[".(0+$no++)."];\n"}
  0         0  
421             @$ord).'
422             ');
423             }
424              
425 0     0   0 sub mypostlude {my($this,$parent,$context) = @_;
426 0         0 my $no;
427 0         0 my ($ord,$pdls) = $parent->get_pdls();
428             ' PDL_COMMENT("THREADLOOPEND")
429 0         0 '.(join '',map {"${_}_datap -= \$PRIV(__pdlthread).offs[".(0+$no++)."];\n"}
  0         0  
430             @$ord).'
431             } while(PDL->iterthreadloop(&$PRIV(__pdlthread),0));
432             '
433             }
434              
435             ####
436             #
437             # This relies on PP.pm making sure that initthreadloop always sets
438             # up the two first dimensions even when they are not necessary.
439             #
440             package PDL::PP::ComplexThreadLoop;
441 2     2   24 use Carp;
  2         3  
  2         1012  
442             @PDL::PP::ComplexThreadLoop::ISA = "PDL::PP::Block";
443             our @CARP_NOT;
444              
445              
446             sub new {
447 1     1   5 my $type = shift;
448 1         3 bless [],$type;
449             }
450 2     2   4 sub myoffs { return 0; }
451             sub myprelude {
452 2     2   5 my($this,$parent,$context, $backcode) = @_;
453              
454             # Set appropriate function from the vtable to supply to threadthreadloop.
455             # Function name from the vtable is readdata for normal code
456             # function name for backcode is writebackdata
457 2         4 my $funcName = "readdata";
458 2 50       4 $funcName = "writebackdata" if( $backcode );
459              
460 2         6 my ($ord,$pdls) = $parent->get_pdls();
461              
462             PDL::PP::pp_line_numbers(__LINE__, join "\n ",
463             '',
464             'PDL_COMMENT("THREADLOOPBEGIN")',
465             'if ( PDL->startthreadloop(&($PRIV(__pdlthread)),$PRIV(vtable)->'.$funcName.', __tr) ) return;
466             do { register PDL_Indx __tind1=0,__tind2=0;
467             register PDL_Indx __tnpdls = $PRIV(__pdlthread).npdls;
468             register PDL_Indx __tdims1 = $PRIV(__pdlthread.dims[1]);
469             register PDL_Indx __tdims0 = $PRIV(__pdlthread.dims[0]);
470             register PDL_Indx *__offsp = PDL->get_threadoffsp(&$PRIV(__pdlthread));',
471 2         10 ( map { "register PDL_Indx __tinc0_${_} = \$PRIV(__pdlthread).incs[${_}];"} 0..$#{$ord}),
  2         4  
472 2         6 ( map { "register PDL_Indx __tinc1_${_} = \$PRIV(__pdlthread).incs[__tnpdls+$_];"} 0.. $#{$ord}),
  2         5  
473 2         8 ( map { $ord->[$_] ."_datap += __offsp[$_];"} 0..$#{$ord} ),
  2         5  
474             'for( __tind2 = 0 ;
475             __tind2 < __tdims1 ;
476             __tind2++',
477 2         8 ( map { "\t\t," . $ord->[$_] . "_datap += __tinc1_${_} - __tinc0_${_} * __tdims0"} 0..$#{$ord} ),
  2         3  
478             ')',
479             '{
480             for( __tind1 = 0 ;
481             __tind1 < __tdims0 ;
482             __tind1++',
483 2         7 ( map { "\t\t," . $ord->[$_] . "_datap += __tinc0_${_}"} 0..$#{$ord}),
  2         14  
  2         4  
484             ')',
485             '{ PDL_COMMENT("This is the tightest threadloop. Make sure inside is optimal.")'
486             );
487             }
488              
489             # Should possibly fold out thread.dims[0] and [1].
490 2     2   5 sub mypostlude {my($this,$parent,$context) = @_;
491              
492 2         5 my ($ord,$pdls) = $parent->get_pdls();
493             PDL::PP::pp_line_numbers(__LINE__, join "\n ",
494             '',
495             'PDL_COMMENT("THREADLOOPEND")',
496             '}',
497             '}',
498 2         6 ( map { $ord->[$_] . "_datap -= __tinc1_${_} * __tdims1 + __offsp[${_}];"} 0..$#{$ord} ),
  2         13  
  2         4  
499             '} while(PDL->iterthreadloop(&$PRIV(__pdlthread),2));'
500             )
501             }
502              
503             # Simple subclass of ComplexThreadLoop to implement writeback code
504             #
505             #
506             package PDL::PP::BackCodeThreadLoop;
507 2     2   15 use Carp;
  2         4  
  2         293  
508             @PDL::PP::BackCodeThreadLoop::ISA = "PDL::PP::ComplexThreadLoop";
509             our @CARP_NOT;
510              
511             sub myprelude {
512 0     0   0 my($this,$parent,$context, $backcode) = @_;
513              
514             # Set backcode flag if not defined. This will make the parent
515             # myprelude emit proper writeback code
516 0 0       0 $backcode = 1 unless defined($backcode);
517              
518 0         0 $this->SUPER::myprelude($parent, $context, $backcode);
519             }
520              
521              
522             ###########################
523             #
524             # Encapsulate a types() switch
525             #
526             # horrible hack:
527             # set $parent->{types} if we create this object so that
528             # PDL::PP::GenericLoop knows to define the THISIS ... macros
529             #
530             package PDL::PP::Types;
531 2     2   14 use Carp;
  2         5  
  2         143  
532 2     2   13 use PDL::Types ':All';
  2         4  
  2         885  
533             @PDL::PP::Types::ISA = "PDL::PP::Block";
534             our @CARP_NOT;
535              
536             sub new {
537 0     0   0 my($type,$ts,$parent) = @_;
538 0         0 my $types = join '', ppdefs; # BSUL....
539 0 0       0 $ts =~ /[$types]+/ or confess "Invalid type access with '$ts'!";
540 0         0 $parent->{types} = 1; # hack for PDL::PP::GenericLoop
541 0         0 bless [$ts],$type; }
542 0     0   0 sub myoffs { return 1; }
543             sub myprelude {
544 0     0   0 my($this,$parent,$context) = @_;
545 0         0 return "\n#if ". (join '||',map {"(THISIS_$_(1)+0)"} split '',$this->[0])."\n";
  0         0  
546             }
547              
548 0     0   0 sub mypostlude {my($this,$parent,$context) = @_;
549 0         0 "\n#endif\n"
550             }
551              
552              
553             ###########################
554             #
555             # Encapsulate an access
556              
557             package PDL::PP::Access;
558 2     2   16 use Carp;
  2         15  
  2         1265  
559             our @CARP_NOT;
560              
561 2     2   5 sub new { my($type,$str,$parent) = @_;
562 2 50       9 $str =~ /^\$([a-zA-Z_]\w*)\s*\(([^)]*)\)/ or
563             confess ("Access wrong: '$str'\n");
564 2         6 my($pdl,$inds) = ($1,$2);
565 2 50       18 if($pdl =~ /^T/) {new PDL::PP::MacroAccess($pdl,$inds,
    50          
    50          
    50          
    50          
    50          
    50          
    50          
566 0         0 $parent->{Generictypes},$parent->{Name});}
567 0         0 elsif($pdl =~ /^P$/) {new PDL::PP::PointerAccess($pdl,$inds);}
568 0         0 elsif($pdl =~ /^PP$/) {new PDL::PP::PhysPointerAccess($pdl,$inds);}
569 0         0 elsif($pdl =~ /^SIZE$/) {new PDL::PP::SizeAccess($pdl,$inds);}
570 0         0 elsif($pdl =~ /^RESIZE$/) {new PDL::PP::ReSizeAccess($pdl,$inds);}
571 0         0 elsif($pdl =~ /^GENERIC$/) {new PDL::PP::GentypeAccess($pdl,$inds);}
572 0         0 elsif($pdl =~ /^PDL$/) {new PDL::PP::PdlAccess($pdl,$inds);}
573 2         8 elsif(!defined $parent->{ParObjs}{$pdl}) {new PDL::PP::OtherAccess($pdl,$inds);}
574             else {
575 0         0 bless [$pdl,$inds],$type;
576             }
577             }
578              
579 0     0   0 sub get_str { my($this,$parent,$context) = @_;
580             # print "AC: $this->[0]\n";
581             $parent->{ParObjs}{$this->[0]}->do_access($this->[1],$context)
582 0 0       0 if defined($parent->{ParObjs}{$this->[0]});
583             }
584              
585             ###########################
586             #
587             # Just some other substituted thing.
588              
589             package PDL::PP::OtherAccess;
590 2     2   6 sub new { my($type,$pdl,$inds) = @_; bless [$pdl,$inds],$type; }
  2         9  
591 4     4   7 sub get_str {my($this) = @_;return "\$$this->[0]($this->[1])"}
  4         17  
592              
593              
594             ###########################
595             #
596             # used by BadAccess code to know when to use NaN support
597             # - the output depends on the value of the
598             # BADVAL_USENAN option in perldl.conf
599             # == 1 then we use NaN's
600             # 0 PDL.bvals.Float/Double
601             #
602             # note the *horrible hack* for piddles whose type have been
603             # specified using the FType option - see GenericLoop.
604             # There MUST be a better way than this...
605             #
606             package PDL::PP::NaNSupport;
607 2     2   23 use PDL::Types ':All'; # typefld et al.
  2         4  
  2         1316  
608              
609             # need to be lower-case because of FlagTyped stuff
610             #
611             # need to be able to handle signatures with fixed types
612             # which means parameters like 'int mask()',
613             # which means the hack to add 'int' to %use_nan
614             #
615             my %use_nan =
616             map {(typefld($_,'convertfunc') => typefld($_,'usenan')*$usenan)} typesrtkeys;
617             $use_nan{int} = 0;
618              
619             # original try
620             ##my %use_nan =
621             ## map {(typefld($_,'convertfunc') => typefld($_,'usenan')*$usenan)} typesrtkeys;
622              
623             # Was the following, before new Type "interface"
624             # ( byte => 0, short => 0, ushort => 0, long => 0,
625             # int => 0, longlong => 0, # necessary for fixed-type piddles (or something)
626             # float => $usenan,
627             # double => $usenan
628             # );
629              
630             my %set_nan =
631             (
632             float => 'PDL->bvals.Float', PDL_Float => 'PDL->bvals.Float',
633             double => 'PDL->bvals.Double', PDL_Double => 'PDL->bvals.Double',
634             cfloat => 'PDL->bvals.CFloat', PDL_CFloat => 'PDL->bvals.CFloat',
635             cdouble => 'PDL->bvals.CDouble', PDL_CDouble => 'PDL->bvals.CDouble',
636             );
637              
638             sub use_nan ($) {
639 0     0   0 my $type = shift;
640              
641 0         0 $type =~ s/^PDL_//;
642 0         0 $type = lc $type;
643             die "ERROR: Unknown type [$type] used in a 'Bad' macro."
644 0 0       0 unless exists $use_nan{$type};
645 0         0 return $use_nan{$type};
646             }
647              
648             sub convert ($$$$$) {
649 0     0   0 my ( $parent, $name, $lhs, $rhs, $opcode ) = @_;
650              
651 0         0 my $type = $parent->{Gencurtype}[-1];
652 0 0       0 die "ERROR: unable to find type info for $opcode access"
653             unless defined $type;
654              
655             # note: gentype may not be sensible because the
656             # actual piddle could have a 'fixed' type
657             die "ERROR: unable to find piddle $name in parent!"
658 0 0       0 unless exists $parent->{ParObjs}{$name};
659 0         0 my $pobj = $parent->{ParObjs}{$name};
660              
661             # based on code from from PdlParObj::ctype()
662             # - want to handle FlagTplus case
663             # - may not be correct
664             # - extended to include hack to GenericLoop
665             #
666 0 0 0     0 if ( exists $parent->{pars}{$name} ) {
    0          
667 0         0 $type = $parent->{pars}{$name};
668 0 0       0 print "#DBG: hacked <$name> to type <$type>\n" if $::PP_VERBOSE;
669             } elsif ( exists $pobj->{FlagTyped} and $pobj->{FlagTyped} ) {
670 0         0 $type = $pobj->{Type};
671              
672             # this should use Dev.pm - fortunately only worried about double/float here
673             # XXX - do I really know what I'm doing ?
674 0 0       0 if ( $pobj->{FlagTplus} ) {
675 0         0 my $gtype = $parent->{Gencurtype}[-1];
676 0 0       0 if ( $gtype eq "PDL_Double" ) {
    0          
677 0 0       0 $type = $gtype if $type ne "double";
678             } elsif ( $gtype eq "PDL_Float" ) {
679 0 0       0 $type = $gtype if $type !~ /^(float|double)$/; # note: ignore doubles
680             }
681             }
682             }
683              
684 0 0       0 if ( use_nan($type) ) {
685 0 0       0 if ( $opcode eq "SETBAD" ) {
686             # $rhs = "(0.0/0.0)";
687 0         0 $rhs = $set_nan{$type};
688             } else {
689 0         0 $rhs = "0";
690 0         0 $lhs = "finite($lhs)";
691             }
692             }
693              
694 0         0 return ( $lhs, $rhs );
695             }
696              
697             ###########################
698             #
699             # Encapsulate a check on whether a value is good or bad
700             # handles both checking (good/bad) and setting (bad)
701             #
702             # Integer types (BSUL) + floating point when no NaN (FD)
703             # $ISBAD($a(n)) -> $a(n) == a_badval
704             # $ISGOOD($a()) $a() != a_badval
705             # $SETBAD($a()) $a() = a_badval
706             #
707             # floating point with NaN
708             # $ISBAD($a(n)) -> finite($a(n)) == 0
709             # $ISGOOD($a()) finite($a()) != 0
710             # $SETBAD($a()) $a() = PDL->bvals.Float (or .Double)
711             #
712             # I've also got it so that the $ on the pdl name is not
713             # necessary - so $ISBAD(a(n)) is also accepted, so as to reduce the
714             # amount of line noise. This is actually done by the regexp
715             # in the separate_code() sub at the end of the file.
716             #
717             # note:
718             # we also expand out $a(n) etc as well here
719             #
720             # To do:
721             # need to allow use of F,D without NaN
722             #
723              
724             package PDL::PP::BadAccess;
725 2     2   24 use Carp;
  2         4  
  2         829  
726             our @CARP_NOT;
727              
728             sub new {
729 0     0   0 my ( $type, $opcode, $pdl_name, $inds, $parent ) = @_;
730              
731             # trying to avoid auto creation of hash elements
732 0         0 my $check = $parent->{ParObjs};
733             die "\nIt looks like you have tried a \$${opcode}() macro on an\n" .
734             " unknown piddle <$pdl_name($inds)>\n"
735 0 0 0     0 unless exists($check->{$pdl_name}) and defined($check->{$pdl_name});
736              
737 0         0 return bless [$opcode, $pdl_name, $inds], $type;
738             }
739              
740             our %ops = ( ISBAD => '==', ISGOOD => '!=', SETBAD => '=' );
741              
742             sub get_str {
743 0     0   0 my($this,$parent,$context) = @_;
744              
745 0         0 my $opcode = $this->[0];
746 0         0 my $name = $this->[1];
747 0         0 my $inds = $this->[2];
748              
749 0 0       0 print "PDL::PP::BadAccess sent [$opcode] [$name] [$inds]\n" if $::PP_VERBOSE;
750              
751 0         0 my $op = $ops{$opcode};
752 0 0       0 die "ERROR: unknown check <$opcode> sent to PDL::PP::BadAccess\n"
753             unless defined $op;
754              
755 0         0 my $obj = $parent->{ParObjs}{$name};
756 0 0       0 die "ERROR: something screwy in PDL::PP::BadAccess (PP/PDLCode.pm)\n"
757             unless defined( $obj );
758              
759 0         0 my $lhs = $obj->do_access($inds,$context);
760 0         0 my $rhs = "${name}_badval";
761              
762 0         0 ( $lhs, $rhs ) =
763             PDL::PP::NaNSupport::convert( $parent, $name, $lhs, $rhs, $opcode );
764              
765 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
766 0         0 return "$lhs $op $rhs";
767             }
768              
769              
770             ###########################
771             #
772             # Encapsulate a check on whether a value is good or bad
773             # handles both checking (good/bad) and setting (bad)
774             #
775             # Integer types (BSUL) + floating point when no NaN (FD)
776             # $ISBADVAR(foo,a) -> foo == a_badval
777             # $ISGOODVAR(foo,a) foo != a_badval
778             # $SETBADVAR(foo,a) foo = a_badval
779             #
780             # floating point with NaN
781             # $ISBADVAR(foo,a) -> finite(foo) == 0
782             # $ISGOODVAR(foo,a) finite(foo) != 0
783             # $SETBADVAR(foo,a) foo = PDL->bvals.Float (or .Double)
784             #
785              
786             package PDL::PP::BadVarAccess;
787 2     2   23 use Carp;
  2         3  
  2         883  
788             our @CARP_NOT;
789              
790             sub new {
791 0     0   0 my ( $type, $opcode, $var_name, $pdl_name, $parent ) = @_;
792              
793             # trying to avoid auto creation of hash elements
794 0         0 my $check = $parent->{ParObjs};
795             die "\nIt looks like you have tried a \$${opcode}() macro on an\n" .
796             " unknown piddle <$pdl_name>\n"
797 0 0 0     0 unless exists($check->{$pdl_name}) and defined($check->{$pdl_name});
798              
799 0         0 bless [$opcode, $var_name, $pdl_name], $type;
800             }
801              
802             our %ops = ( ISBAD => '==', ISGOOD => '!=', SETBAD => '=' );
803              
804             sub get_str {
805 0     0   0 my($this,$parent,$context) = @_;
806              
807 0         0 my $opcode = $this->[0];
808 0         0 my $var_name = $this->[1];
809 0         0 my $pdl_name = $this->[2];
810              
811 0 0       0 print "PDL::PP::BadVarAccess sent [$opcode] [$var_name] [$pdl_name]\n" if $::PP_VERBOSE;
812              
813 0         0 my $op = $ops{$opcode};
814 0 0       0 die "ERROR: unknown check <$opcode> sent to PDL::PP::BadVarAccess\n"
815             unless defined $op;
816              
817 0         0 my $obj = $parent->{ParObjs}{$pdl_name};
818 0 0       0 die "ERROR: something screwy in PDL::PP::BadVarAccess (PP/PDLCode.pm)\n"
819             unless defined( $obj );
820              
821 0         0 my $lhs = $var_name;
822 0         0 my $rhs = "${pdl_name}_badval";
823              
824 0         0 ( $lhs, $rhs ) =
825             PDL::PP::NaNSupport::convert( $parent, $pdl_name, $lhs, $rhs, $opcode );
826              
827 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
828 0         0 return "$lhs $op $rhs";
829             }
830              
831              
832             ###########################
833             #
834             # Encapsulate a check on whether a value is good or bad using PP
835             # handles both checking (good/bad) and setting (bad)
836              
837             # this is only an initial attempt - it will, almost certainly,
838             # need more work as more code is converted to handle bad values
839             #
840             # currently it can only handle cases like
841             # $PPISBAD(PARENT,[i]) -> PARENT_physdatap[i] == PARENT_badval
842             # etc
843             #
844             # if we use NaN's, then
845             # $PPISBAD(PARENT,[i]) -> finite(PARENT_physdatap[i]) == 0
846             # $PPISGOOD(PARENT,[i]) -> finite(PARENT_physdatap[i]) != 0
847             # $PPSETBAD(PARENT,[i]) -> PARENT_physdatap[i] = PDL->bvals.Float (or .Double)
848             #
849              
850             package PDL::PP::PPBadAccess;
851 2     2   16 use Carp;
  2         4  
  2         883  
852             our @CARP_NOT;
853              
854             sub new {
855 0     0   0 my ( $type, $opcode, $pdl_name, $inds, $parent ) = @_;
856              
857 0         0 $opcode =~ s/^PP//;
858 0         0 bless [$opcode, $pdl_name, $inds], $type;
859             }
860              
861             # PP is stripped in new()
862             our %ops = ( ISBAD => '==', ISGOOD => '!=', SETBAD => '=' );
863              
864             sub get_str {
865 0     0   0 my($this,$parent,$context) = @_;
866              
867 0         0 my $opcode = $this->[0];
868 0         0 my $name = $this->[1];
869 0         0 my $inds = $this->[2];
870              
871 0 0       0 print "PDL::PP::PPBadAccess sent [$opcode] [$name] [$inds]\n" if $::PP_VERBOSE;
872              
873 0         0 my $op = $ops{$opcode};
874 0 0       0 die "\nERROR: unknown check <$opcode> sent to PDL::PP::PPBadAccess\n"
875             unless defined $op;
876              
877 0         0 my $obj = $parent->{ParObjs}{$name};
878 0 0       0 die "\nERROR: ParObjs does not seem to exist for <$name> = problem in PDL::PP::PPBadAccess\n"
879             unless defined $obj;
880              
881 0         0 my $lhs = $obj->do_physpointeraccess() . "$inds";
882 0         0 my $rhs = "${name}_badval";
883              
884 0         0 ( $lhs, $rhs ) =
885             PDL::PP::NaNSupport::convert( $parent, $name, $lhs, $rhs, $opcode );
886              
887 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
888 0         0 return "$lhs $op $rhs";
889             }
890              
891              
892             ###########################
893             #
894             # Encapsulate a check on whether the state flag of a piddle
895             # is set/change this state
896             #
897             # $PDLSTATEISBAD(a) -> ($PDL(a)->state & PDL_BADVAL) > 0
898             # $PDLSTATEISGOOD(a) -> ($PDL(a)->state & PDL_BADVAL) == 0
899             #
900             # $PDLSTATESETBAD(a) -> ($PDL(a)->state |= PDL_BADVAL)
901             # $PDLSTATESETGOOD(a) -> ($PDL(a)->state &= ~PDL_BADVAL)
902             #
903              
904             package PDL::PP::PDLStateBadAccess;
905 2     2   17 use Carp;
  2         3  
  2         829  
906             our @CARP_NOT;
907              
908             sub new {
909 0     0   0 my ( $type, $op, $val, $pdl_name, $parent ) = @_;
910              
911             # $op is one of: IS SET
912             # $val is one of: GOOD BAD
913              
914             # trying to avoid auto creation of hash elements
915 0         0 my $check = $parent->{ParObjs};
916             die "\nIt looks like you have tried a \$PDLSTATE${op}${val}() macro on an\n" .
917             " unknown piddle <$pdl_name>\n"
918 0 0 0     0 unless exists($check->{$pdl_name}) and defined($check->{$pdl_name});
919              
920 0         0 bless [$op, $val, $pdl_name], $type;
921             }
922              
923             our %ops = (
924             IS => { GOOD => '== 0', BAD => '> 0' },
925             SET => { GOOD => '&= ~', BAD => '|= ' },
926             );
927              
928             sub get_str {
929 0     0   0 my($this,$parent,$context) = @_;
930              
931 0         0 my $op = $this->[0];
932 0         0 my $val = $this->[1];
933 0         0 my $name = $this->[2];
934              
935 0 0       0 print "PDL::PP::PDLStateBadAccess sent [$op] [$val] [$name]\n" if $::PP_VERBOSE;
936              
937 0         0 my $opcode = $ops{$op}{$val};
938 0         0 my $type = $op . $val;
939 0 0       0 die "ERROR: unknown check <$type> sent to PDL::PP::PDLStateBadAccess\n"
940             unless defined $opcode;
941              
942 0         0 my $obj = $parent->{ParObjs}{$name};
943 0 0       0 die "\nERROR: ParObjs does not seem to exist for <$name> = problem in PDL::PP::PDLStateBadAccess\n"
944             unless defined $obj;
945              
946 0         0 my $state = $obj->do_pdlaccess() . "->state";
947              
948 0         0 my $str;
949 0 0       0 if ( $op eq 'IS' ) {
    0          
950 0         0 $str = "($state & PDL_BADVAL) $opcode";
951             } elsif ( $op eq 'SET' ) {
952 0         0 $str = "$state ${opcode}PDL_BADVAL";
953             }
954              
955 0 0       0 print "DBG: [$str]\n" if $::PP_VERBOSE;
956 0         0 return $str;
957             }
958              
959              
960             ###########################
961             #
962             # Encapsulate a Pointeraccess
963              
964             package PDL::PP::PointerAccess;
965 2     2   15 use Carp;
  2         4  
  2         404  
966             our @CARP_NOT;
967              
968 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
969              
970 0     0   0 sub get_str {my($this,$parent,$context) = @_;
971             croak ("can't access undefined pdl ".$this->[0])
972 0 0       0 unless defined($parent->{ParObjs}{$this->[0]});
973             # $parent->{ParObjs}{$this->[0]}->{FlagPaccess} = 1;
974 0         0 $parent->{ParObjs}{$this->[0]}->{FlagPhys} = 1;
975 0         0 $parent->{ParObjs}{$this->[0]}->do_pointeraccess();
976             }
977              
978              
979             ###########################
980             #
981             # Encapsulate a PhysPointeraccess
982              
983             package PDL::PP::PhysPointerAccess;
984 2     2   28 use Carp;
  2         5  
  2         310  
985             our @CARP_NOT;
986              
987 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
988              
989 0     0   0 sub get_str {my($this,$parent,$context) = @_;
990             $parent->{ParObjs}{$this->[0]}->do_physpointeraccess()
991 0 0       0 if defined($parent->{ParObjs}{$this->[0]});
992             }
993              
994             ###########################
995             #
996             # Encapsulate a PDLaccess
997              
998             package PDL::PP::PdlAccess;
999 2     2   14 use Carp;
  2         4  
  2         356  
1000             our @CARP_NOT;
1001              
1002 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1003              
1004 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1005             croak ("can't access undefined pdl ".$this->[0])
1006 0 0       0 unless defined($parent->{ParObjs}{$this->[0]});
1007 0         0 $parent->{ParObjs}{$this->[0]}->do_pdlaccess();
1008             }
1009              
1010             ###########################
1011             #
1012             # Encapsulate a macroaccess
1013              
1014             package PDL::PP::MacroAccess;
1015 2     2   14 use Carp;
  2         4  
  2         123  
1016 2     2   13 use PDL::Types ':All';
  2         3  
  2         1470  
1017             my $types = join '',ppdefs;
1018             our @CARP_NOT;
1019              
1020 0     0   0 sub new { my($type,$pdl,$inds,$gentypes,$name) = @_;
1021 0 0       0 $pdl =~ /^\s*T([A-Z]+)\s*$/ or confess("Macroaccess wrong: $pdl\n");
1022 0         0 my @ilst = split '',$1;
1023 0         0 for my $gt (@$gentypes) {
1024             warn "$name has no Macro for generic type $gt (has $pdl)\n"
1025 0 0       0 unless grep {$gt eq $_} @ilst }
  0         0  
1026 0         0 for my $mtype (@ilst) {
1027             warn "Macro for unsupported generic type identifier $mtype".
1028             " (probably harmless)\n"
1029 0 0       0 unless grep {$mtype eq $_} @$gentypes;
  0         0  
1030             }
1031 0         0 return bless [$pdl,$inds,$name],
1032             $type; }
1033              
1034 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1035 0         0 my ($pdl,$inds,$name) = @{$this};
  0         0  
1036 0 0       0 $pdl =~ /^\s*T([A-Z]+)\s*$/
1037             or confess("Macroaccess wrong in $name (allowed types $types): was '$pdl'\n");
1038 0         0 my @lst = split ',',$inds;
1039 0         0 my @ilst = split '',$1;
1040 0 0       0 if($#lst != $#ilst) {confess("Macroaccess: different nos of args $pdl $inds\n");}
  0         0  
1041             croak "generic type access outside a generic loop in $name"
1042 0 0       0 unless defined $parent->{Gencurtype}->[-1];
1043 0         0 my $type = mapfld $parent->{Gencurtype}->[-1], 'ctype' => 'ppsym';
1044             # print "Type access: $type\n";
1045 0 0       0 croak "unknown Type in $name (generic type currently $parent->{Gencurtype}->[-1]"
1046             unless defined $type;
1047 0         0 for (0..$#lst) {
1048 0 0       0 return "$lst[$_]" if $ilst[$_] =~ /$type/;
1049             }
1050             }
1051              
1052              
1053             ###########################
1054             #
1055             # Encapsulate a SizeAccess
1056              
1057             package PDL::PP::SizeAccess;
1058 2     2   16 use Carp;
  2         3  
  2         431  
1059             our @CARP_NOT;
1060              
1061 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1062              
1063 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1064             croak "can't get SIZE of undefined dimension $this->[0]"
1065 0 0       0 unless defined($parent->{IndObjs}{$this->[0]});
1066 0         0 $parent->{IndObjs}{$this->[0]}->get_size();
1067             }
1068              
1069             ###########################
1070             #
1071             # Encapsulate a ReSizeAccess
1072              
1073             package PDL::PP::ReSizeAccess;
1074 2     2   15 use Carp;
  2         4  
  2         751  
1075             our @CARP_NOT;
1076              
1077 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1078              
1079 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1080 0 0       0 $this->[0] =~ /^([^,]+),([^,]+)$/ or
1081             croak "Can't interpret resize str $this->[0]";
1082             croak "can't RESIZE undefined dimension $1"
1083 0 0       0 unless defined($parent->{IndObjs}{$1});
1084              
1085 0         0 my $s = $parent->{IndObjs}{$1}->get_size();
1086              
1087             # XXX NOTE: All piddles must be output piddles, there must not be
1088             # a loop over this var (at all!) etc. Should check for these,
1089             # this is why not yet documented.
1090             # FURTHER NOTE: RESIZE DOESN'T COPY DATA PROPERLY!
1091              
1092 0         0 my($ord,$pdls) = $parent->get_pdls();
1093 0         0 my @p;
1094              
1095 0         0 for(@$ord) {
1096             push @p, $_
1097 0 0       0 if $pdls->{$_}->has_dim($1);
1098             }
1099 0         0 print "RESIZEACC: $1 $2, (",(join ',',@p),")\n";
1100 0         0 warn "RESIZE USED: DO YOU KNOW WHAT YOU ARE DOING???\n";
1101              
1102 0         0 return "$s = $2; ".(join '',map {$pdls->{$_}->do_resize($1,$2)} @p);
  0         0  
1103             }
1104              
1105              
1106             ###########################
1107             #
1108             # Encapsulate a GentypeAccess
1109              
1110             package PDL::PP::GentypeAccess;
1111 2     2   16 use Carp;
  2         5  
  2         457  
1112             our @CARP_NOT;
1113              
1114 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1115              
1116 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1117             croak "generic type access outside a generic loop"
1118 0 0       0 unless defined $parent->{Gencurtype}->[-1];
1119 0         0 my $type = $parent->{Gencurtype}->[-1];
1120 0 0       0 if ($this->[0]) {
1121             croak "not a defined name"
1122 0 0       0 unless defined($parent->{ParObjs}{$this->[0]});
1123 0         0 $type = $parent->{ParObjs}{$this->[0]}->ctype($type);
1124             }
1125 0         0 return $type;
1126             }
1127              
1128             ########################
1129             #
1130             # Type coercion
1131             #
1132             # Now, if TYPES:F given and double arguments, will coerce.
1133              
1134             package PDL::PP::TypeConv;
1135              
1136             # make the typetable from info in PDL::Types
1137 2     2   16 use PDL::Types ':All';
  2         3  
  2         897  
1138             my @typetable = map {[$typehash{$_}->{ppsym},
1139             $typehash{$_}->{ctype},
1140             $typehash{$_}->{numval},
1141             ]} typesrtkeys;
1142              
1143 0     0   0 sub print_xscoerce { my($this) = @_;
1144 0         0 $this->printxs("\t__priv->datatype=PDL_B;\n");
1145             # First, go through all the types, selecting the most general.
1146 0         0 for(@{$this->{PdlOrder}}) {
  0         0  
1147 0         0 $this->printxs($this->{Pdls}{$_}->get_xsdatatypetest());
1148             }
1149             # See which types we are allowed to use.
1150 0         0 $this->printxs("\tif(0) {}\n");
1151 0         0 for(@{$this->get_generictypes()}) {
  0         0  
1152 0         0 $this->printxs("\telse if(__priv->datatype <= $_->[2]) __priv->datatype = $_->[2];\n");
1153             }
1154 0 0       0 $this->{Types} =~ /F/ and (
1155             $this->printxs("\telse if(__priv->datatype == PDL_D) {__priv->datatype = PDL_F; PDL_COMMENT(\"Cast double to float\")}\n"));
1156 0         0 $this->printxs(qq[\telse {croak("Too high type \%d given!\\n",__priv->datatype);}]);
1157             # Then, coerce everything to this type.
1158 0         0 for(@{$this->{PdlOrder}}) {
  0         0  
1159 0         0 $this->printxs($this->{Pdls}{$_}->get_xscoerce());
1160             }
1161             }
1162             # XXX Should use PDL::Core::Dev;
1163              
1164 2     2   16 no strict 'vars';
  2         5  
  2         3108  
1165              
1166             # STATIC!
1167 2     2 0 4 sub PDL::PP::get_generictyperecs { my($types) = @_;
1168 2         3 my $foo;
1169 2         4 return [map {$foo = $_;
  20         27  
1170 20 100       29 ( grep {/$foo->[0]/} (@$types) ) ?
  40         219  
1171             [mapfld($_->[0],'ppsym'=>'sym'),$_->[1],$_->[2],$_->[0]]
1172             : ()
1173             }
1174             @typetable];
1175             }
1176              
1177 0     0   0 sub xxx_get_generictypes { my($this) = @_;
1178             return [map {
1179 0 0       0 $this->{Types} =~ /$_->[0]/ ? [mapfld($_->[0],'ppsym'=>'sym'),$_->[1],$_->[2],$_->[0]] : ()
  0         0  
1180             }
1181             @typetable];
1182             }
1183              
1184              
1185             package PDL::PP::Code;
1186              
1187             # my ( $threadloops, $coderef, $sizeprivs ) = $this->separate_code( $code );
1188             #
1189             # umm, can't call classes defined later on in code ...
1190             # hence moved to end of file
1191             # (rather ugly...)
1192             #
1193             # XXX The above statement is almost certainly false. This module is parsed
1194             # before separate_code is ever called, so all of the class definitions
1195             # should exist. -- David Mertens, Dec 2 2011
1196             #
1197             # separates the code into an array of C fragments (strings),
1198             # variable references (strings starting with $) and
1199             # loops (array references, 1. item = variable.
1200             #
1201             sub separate_code {
1202             ## $DB::single=1;
1203 3     3   10 my ( $this, $code ) = @_;
1204              
1205             # First check for standard code errors:
1206 3         10 catch_code_errors($code);
1207              
1208 2         8 my $coderef = new PDL::PP::Block;
1209              
1210 2         3 my @stack = ($coderef);
1211 2         4 my $threadloops = 0;
1212 2         3 my $sizeprivs = {};
1213              
1214 2         4 local $_ = $code;
1215             ## print "Code to parse = [$_]\n" if $::PP_VERBOSE;
1216 2         5 while($_) {
1217             # Parse next statement
1218              
1219             # I'm not convinced that having the checks twice is a good thing,
1220             # since it makes it easy (for me at least) to forget to update one
1221             # of them
1222              
1223 8 50       180 s/^(.*?) # First, some noise is allowed. This may be bad.
1224             ( \$(ISBAD|ISGOOD|SETBAD)\s*\(\s*\$?[a-zA-Z_]\w*\s*\([^)]*\)\s*\) # $ISBAD($a(..)), ditto for ISGOOD and SETBAD
1225             |\$PP(ISBAD|ISGOOD|SETBAD)\s*\(\s*[a-zA-Z_]\w*\s*,\s*[^)]*\s*\) # $PPISBAD(CHILD,[1]) etc
1226             ### |\$STATE(IS|SET)(BAD|GOOD)\s*\(\s*[^)]*\s*\) # $STATEISBAD(a) etc
1227             |\$PDLSTATE(IS|SET)(BAD|GOOD)\s*\(\s*[^)]*\s*\) # $PDLSTATEISBAD(a) etc
1228             |\$[a-zA-Z_]\w*\s*\([^)]*\) # $a(...): access
1229             |\bloop\s*\([^)]+\)\s*%\{ # loop(..) %{
1230             |\btypes\s*\([^)]+\)\s*%\{ # types(..) %{
1231             |\bthreadloop\s*%\{ # threadloop %{
1232             |%} # %}
1233             |$)//xs
1234             or confess("Invalid program $_");
1235 8         22 my $control = $2;
1236             # Store the user code.
1237             # Some day we shall parse everything.
1238 8         10 push @{$stack[-1]},$1;
  8         27  
1239              
1240 8 50       20 if ( $control =~ /^\$STATE/ ) { print "\nDBG: - got [$control]\n\n"; }
  0         0  
1241              
1242             # Then, our control.
1243 8 100       14 if($control) {
1244 6 100       43 if($control =~ /^loop\s*\(([^)]+)\)\s*%\{/) {
    50          
    100          
    50          
    50          
    50          
    50          
    100          
    50          
1245 1         18 my $ob = new PDL::PP::Loop([split ',',$1],
1246             $sizeprivs,$this);
1247 1 50       3 print "SIZEPRIVSXX: $sizeprivs,",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
1248 1         1 push @{$stack[-1]},$ob;
  1         3  
1249 1         3 push @stack,$ob;
1250             } elsif($control =~ /^types\s*\(([^)]+)\)\s*%\{/) {
1251 0         0 my $ob = new PDL::PP::Types($1,$this);
1252 0         0 push @{$stack[-1]},$ob;
  0         0  
1253 0         0 push @stack,$ob;
1254             } elsif($control =~ /^threadloop\s*%\{/) {
1255 1         8 my $ob = new PDL::PP::ThreadLoop();
1256 1         3 push @{$stack[-1]},$ob;
  1         3  
1257 1         2 push @stack,$ob;
1258 1         3 $threadloops ++;
1259             } elsif($control =~ /^\$PP(ISBAD|ISGOOD|SETBAD)\s*\(\s*([a-zA-Z_]\w*)\s*,\s*([^)]*)\s*\)/) {
1260 0         0 push @{$stack[-1]},new PDL::PP::PPBadAccess($1,$2,$3,$this);
  0         0  
1261             } elsif($control =~ /^\$(ISBAD|ISGOOD|SETBAD)VAR\s*\(\s*([^)]*)\s*,\s*([^)]*)\s*\)/) {
1262 0         0 push @{$stack[-1]},new PDL::PP::BadVarAccess($1,$2,$3,$this);
  0         0  
1263             } elsif($control =~ /^\$(ISBAD|ISGOOD|SETBAD)\s*\(\s*\$?([a-zA-Z_]\w*)\s*\(([^)]*)\)\s*\)/) {
1264 0         0 push @{$stack[-1]},new PDL::PP::BadAccess($1,$2,$3,$this);
  0         0  
1265             # } elsif($control =~ /^\$STATE(IS|SET)(BAD|GOOD)\s*\(\s*([^)]*)\s*\)/) {
1266             # push @{$stack[-1]},new PDL::PP::StateBadAccess($1,$2,$3,$this);
1267             } elsif($control =~ /^\$PDLSTATE(IS|SET)(BAD|GOOD)\s*\(\s*([^)]*)\s*\)/) {
1268 0         0 push @{$stack[-1]},new PDL::PP::PDLStateBadAccess($1,$2,$3,$this);
  0         0  
1269             } elsif($control =~ /^\$[a-zA-Z_]\w*\s*\([^)]*\)/) {
1270 2         5 push @{$stack[-1]},new PDL::PP::Access($control,$this);
  2         10  
1271             } elsif($control =~ /^%}/) {
1272 2         6 pop @stack;
1273             } else {
1274 0         0 confess("Invalid control: $control\n");
1275             }
1276             } else {
1277 2 50       13 print("No \$2!\n") if $::PP_VERBOSE;
1278             }
1279             } # while: $_
1280              
1281 2         7 return ( $threadloops, $coderef, $sizeprivs );
1282              
1283             } # sub: separate_code()
1284              
1285             # This is essentially a collection of regexes that look for standard code
1286             # errors and croaks with an explanation if they are found.
1287             sub catch_code_errors {
1288 3     3   7 my $code_string = shift;
1289              
1290             # Look for constructs like
1291             # loop %{
1292             # which is invalid - you need to specify the dimension over which it
1293             # should loop
1294 3 100       36 report_error('Expected dimension name after "loop" and before "%{"', $1)
1295             if $code_string =~ /(.*\bloop\s*%\{)/s;
1296              
1297             }
1298              
1299             # Report an error as precisely as possible. If they have #line directives
1300             # in the code string, use that in the reporting; otherwise, use standard
1301             # Carp mechanisms
1302             my $line_re = qr/#\s*line\s+(\d+)\s+"([^"]*)"/;
1303             sub report_error {
1304 1     1   5 my ($message, $code) = @_;
1305              
1306             # Just croak if they didn't supply a #line directive:
1307 1 50       306 croak($message) if $code !~ $line_re;
1308              
1309             # Find the line at which the error occurred:
1310 0           my $line = 0;
1311 0           my $filename;
1312 0           LINE: foreach (split /\n/, $code) {
1313 0           $line++;
1314 0 0         if (/$line_re/) {
1315 0           $line = $1;
1316 0           $filename = $2;
1317             }
1318             }
1319              
1320 0           die "$message at $filename line $line\n";
1321             }
1322              
1323             # return true
1324             1;