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         2  
  2         122  
9             our @CARP_NOT;
10              
11 2     2   12 use strict;
  2         3  
  2         47  
12              
13             # check for bad value support
14             #
15 2     2   9 use PDL::Config;
  2         5  
  2         3422  
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   5 sub get_pdls {my($this) = @_; return ($this->{ParNames},$this->{ParObjs});}
  4         10  
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       221 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       126 $badcode = undef unless $bvalflag;
37 3         11 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       7 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         25 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         8 my $inccode = join '',map {$_->get_incregisters();} (sort values %{$this->{ParObjs}});
  3         18  
  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         16 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     9 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       4 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       5 print "SIZEPRIVSX: ",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
142              
143             # Enclose it all in a genericloop.
144 2 50       4 unless ($nogeneric_loop) {
145             # XXX Make genericloop understand denied pointers;...
146 2         2 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         7  
150             }
151              
152             # Do we have extra generic loops?
153             # If we do, first reverse the hash:
154 2         3 my %glh;
155 2         5 for(keys %$extrageneric) {
156 0         0 push @{$glh{$extrageneric->{$_}}},$_;
  0         0  
157             }
158 2         4 my $no = 0;
159 2         4 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       4 print "SIZEPRIVS: ",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
168 2         13 $this->{Code} = "{".(join '',sort values %$sizeprivs).
169             $coderef->get_str($this,[])
170             ."}";
171 2         31 $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   6 sub make_loopind { my($this,$ind) = @_;
178 3         4 my $orig = $ind;
179 3         9 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         7 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         5  
203              
204 4     4   4 sub myoffs { return 0; }
205       4     sub myprelude {}
206 16     16   21 sub myitem {return "";}
207       4     sub mypostlude {}
208              
209             sub get_str {
210 10     10   16 my ($this,$parent,$context) = @_;
211 10         19 my $str = $this->myprelude($parent,$context);
212 10         32 $str .= $this->get_str_int($parent,$context);
213 10         23 $str .= $this->mypostlude($parent,$context);
214 10         66 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         10 my $str = "";
222 10         11 MYLOOP: while(1) {
223 22         40 my $it = $this->myitem($parent,$nth);
224 22 100 100     64 last MYLOOP if $nth and !$it;
225 12         25 $str .= $it;
226 28 100       73 $str .= (join '',map {ref $_ ? $_->get_str($parent,$context) : $_}
227 12         25 @{$this}[$this->myoffs()..$#{$this}]);
  12         18  
  12         19  
228 12         22 $nth++;
229             }
230 10         25 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   902 sub new { my($type,$args,$sizeprivs,$parent) = @_;
272 1         5 my $this = bless [$args],$type;
273 1         2 for(@{$this->[0]}) {
  1         7  
274 1 50       3 print "SIZP $sizeprivs, $_\n" if $::PP_VERBOSE;
275 1         3 my $i = $parent->make_loopind($_);
276 1         5 $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         2 return $this;
281             }
282              
283 2     2   2 sub myoffs { return 1; }
284 2     2   4 sub myprelude { my($this,$parent,$context) = @_;
285 2         3 my $text = ""; my $i;
  2         3  
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         8 $text .= "{PDL_COMMENT(\"Open $_\") register PDL_Indx $_;
290             for($_=0; $_<(__$i->[0]_size); $_++) {";
291 2         4 $i;
292 2         2 } @{$this->[0]};
  2         3  
293 2         5 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         7  
297 2         3 return join '',map {"}} PDL_COMMENT(\"Close $_\")"} @{$this->[0]};
  2         8  
  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         4  
  2         1674  
313             sub new {
314 2     2   4 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   5 sub myoffs {4}
320              
321             sub myprelude {
322 2     2   4 my($this,$parent,$context) = @_;
323 2         3 push @{$parent->{Gencurtype}},'PDL_undef'; # so that $GENERIC can get at it
  2         4  
324              
325             # horrible hack for PDL::PP::NaNSupport
326 2 50       5 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         7 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   9 my($this,$parent,$nth) = @_;
351             # print "GENERICITEM\n";
352 6         9 my $item = $this->[0]->[$nth];
353 6 100       12 if(!$item) {return "";}
  2         3  
354 4         7 $parent->{Gencurtype}->[-1] = $item->[1];
355              
356             # horrible hack for PDL::PP::NaNSupport
357 4 50       8 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       6 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         13 $parent->{ParObjs}{$_}->get_xsdatapdecl($item->[1]);
381 4         10 } (@{$this->[2]})));
  4         8  
382             }
383              
384             sub mypostlude {
385 2     2   4 my($this,$parent,$context) = @_;
386 2         2 pop @{$parent->{Gencurtype}}; # and clean up the Gentype stack
  2         4  
387              
388             # horrible hack for PDL::PP::NaNSupport
389 2 50       5 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   6 return PDL::PP::ComplexThreadLoop->new(@_);
404             }
405              
406             package PDL::PP::SimpleThreadLoop;
407 2     2   15 use Carp;
  2         3  
  2         616  
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   23 use Carp;
  2         3  
  2         972  
442             @PDL::PP::ComplexThreadLoop::ISA = "PDL::PP::Block";
443             our @CARP_NOT;
444              
445              
446             sub new {
447 1     1   10 my $type = shift;
448 1         5 bless [],$type;
449             }
450 2     2   3 sub myoffs { return 0; }
451             sub myprelude {
452 2     2   4 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         3 my $funcName = "readdata";
458 2 50       4 $funcName = "writebackdata" if( $backcode );
459              
460 2         4 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         8 ( map { "register PDL_Indx __tinc0_${_} = \$PRIV(__pdlthread).incs[${_}];"} 0..$#{$ord}),
  2         3  
472 2         5 ( map { "register PDL_Indx __tinc1_${_} = \$PRIV(__pdlthread).incs[__tnpdls+$_];"} 0.. $#{$ord}),
  2         4  
473 2         6 ( map { $ord->[$_] ."_datap += __offsp[$_];"} 0..$#{$ord} ),
  2         5  
474             'for( __tind2 = 0 ;
475             __tind2 < __tdims1 ;
476             __tind2++',
477 2         9 ( 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         6 ( map { "\t\t," . $ord->[$_] . "_datap += __tinc0_${_}"} 0..$#{$ord}),
  2         12  
  2         3  
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   4 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         5 ( map { $ord->[$_] . "_datap -= __tinc1_${_} * __tdims1 + __offsp[${_}];"} 0..$#{$ord} ),
  2         13  
  2         3  
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         266  
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   13 use Carp;
  2         3  
  2         124  
532 2     2   13 use PDL::Types ':All';
  2         4  
  2         792  
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   43 use Carp;
  2         6  
  2         1172  
559             our @CARP_NOT;
560              
561 2     2   6 sub new { my($type,$str,$parent) = @_;
562 2 50       8 $str =~ /^\$([a-zA-Z_]\w*)\s*\(([^)]*)\)/ or
563             confess ("Access wrong: '$str'\n");
564 2         6 my($pdl,$inds) = ($1,$2);
565 2 50       27 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   5 sub new { my($type,$pdl,$inds) = @_; bless [$pdl,$inds],$type; }
  2         9  
591 4     4   8 sub get_str {my($this) = @_;return "\$$this->[0]($this->[1])"}
  4         13  
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   16 use PDL::Types ':All'; # typefld et al.
  2         3  
  2         1198  
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             cdouble => 'PDL->bvals.CDouble', PDL_CDouble => 'PDL->bvals.CDouble',
635             );
636              
637             sub use_nan ($) {
638 0     0   0 my $type = shift;
639              
640 0         0 $type =~ s/^PDL_//;
641 0         0 $type = lc $type;
642             die "ERROR: Unknown type [$type] used in a 'Bad' macro."
643 0 0       0 unless exists $use_nan{$type};
644 0         0 return $use_nan{$type};
645             }
646              
647             sub convert ($$$$$) {
648 0     0   0 my ( $parent, $name, $lhs, $rhs, $opcode ) = @_;
649              
650 0         0 my $type = $parent->{Gencurtype}[-1];
651 0 0       0 die "ERROR: unable to find type info for $opcode access"
652             unless defined $type;
653              
654             # note: gentype may not be sensible because the
655             # actual piddle could have a 'fixed' type
656             die "ERROR: unable to find piddle $name in parent!"
657 0 0       0 unless exists $parent->{ParObjs}{$name};
658 0         0 my $pobj = $parent->{ParObjs}{$name};
659              
660             # based on code from from PdlParObj::ctype()
661             # - want to handle FlagTplus case
662             # - may not be correct
663             # - extended to include hack to GenericLoop
664             #
665 0 0 0     0 if ( exists $parent->{pars}{$name} ) {
    0          
666 0         0 $type = $parent->{pars}{$name};
667 0 0       0 print "#DBG: hacked <$name> to type <$type>\n" if $::PP_VERBOSE;
668             } elsif ( exists $pobj->{FlagTyped} and $pobj->{FlagTyped} ) {
669 0         0 $type = $pobj->{Type};
670              
671             # this should use Dev.pm - fortunately only worried about double/float here
672             # XXX - do I really know what I'm doing ?
673 0 0       0 if ( $pobj->{FlagTplus} ) {
674 0         0 my $gtype = $parent->{Gencurtype}[-1];
675 0 0       0 if ( $gtype eq "PDL_Double" ) {
    0          
676 0 0       0 $type = $gtype if $type ne "double";
677             } elsif ( $gtype eq "PDL_Float" ) {
678 0 0       0 $type = $gtype if $type !~ /^(float|double)$/; # note: ignore doubles
679             }
680             }
681             }
682              
683 0 0       0 if ( use_nan($type) ) {
684 0 0       0 if ( $opcode eq "SETBAD" ) {
685             # $rhs = "(0.0/0.0)";
686 0         0 $rhs = $set_nan{$type};
687             } else {
688 0         0 $rhs = "0";
689 0         0 $lhs = "finite($lhs)";
690             }
691             }
692              
693 0         0 return ( $lhs, $rhs );
694             }
695              
696             ###########################
697             #
698             # Encapsulate a check on whether a value is good or bad
699             # handles both checking (good/bad) and setting (bad)
700             #
701             # Integer types (BSUL) + floating point when no NaN (FD)
702             # $ISBAD($a(n)) -> $a(n) == a_badval
703             # $ISGOOD($a()) $a() != a_badval
704             # $SETBAD($a()) $a() = a_badval
705             #
706             # floating point with NaN
707             # $ISBAD($a(n)) -> finite($a(n)) == 0
708             # $ISGOOD($a()) finite($a()) != 0
709             # $SETBAD($a()) $a() = PDL->bvals.Float (or .Double)
710             #
711             # I've also got it so that the $ on the pdl name is not
712             # necessary - so $ISBAD(a(n)) is also accepted, so as to reduce the
713             # amount of line noise. This is actually done by the regexp
714             # in the separate_code() sub at the end of the file.
715             #
716             # note:
717             # we also expand out $a(n) etc as well here
718             #
719             # To do:
720             # need to allow use of F,D without NaN
721             #
722              
723             package PDL::PP::BadAccess;
724 2     2   16 use Carp;
  2         3  
  2         723  
725             our @CARP_NOT;
726              
727             sub new {
728 0     0   0 my ( $type, $opcode, $pdl_name, $inds, $parent ) = @_;
729              
730             # trying to avoid auto creation of hash elements
731 0         0 my $check = $parent->{ParObjs};
732             die "\nIt looks like you have tried a \$${opcode}() macro on an\n" .
733             " unknown piddle <$pdl_name($inds)>\n"
734 0 0 0     0 unless exists($check->{$pdl_name}) and defined($check->{$pdl_name});
735              
736 0         0 return bless [$opcode, $pdl_name, $inds], $type;
737             }
738              
739             our %ops = ( ISBAD => '==', ISGOOD => '!=', SETBAD => '=' );
740              
741             sub get_str {
742 0     0   0 my($this,$parent,$context) = @_;
743              
744 0         0 my $opcode = $this->[0];
745 0         0 my $name = $this->[1];
746 0         0 my $inds = $this->[2];
747              
748 0 0       0 print "PDL::PP::BadAccess sent [$opcode] [$name] [$inds]\n" if $::PP_VERBOSE;
749              
750 0         0 my $op = $ops{$opcode};
751 0 0       0 die "ERROR: unknown check <$opcode> sent to PDL::PP::BadAccess\n"
752             unless defined $op;
753              
754 0         0 my $obj = $parent->{ParObjs}{$name};
755 0 0       0 die "ERROR: something screwy in PDL::PP::BadAccess (PP/PDLCode.pm)\n"
756             unless defined( $obj );
757              
758 0         0 my $lhs = $obj->do_access($inds,$context);
759 0         0 my $rhs = "${name}_badval";
760              
761 0         0 ( $lhs, $rhs ) =
762             PDL::PP::NaNSupport::convert( $parent, $name, $lhs, $rhs, $opcode );
763              
764 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
765 0         0 return "$lhs $op $rhs";
766             }
767              
768              
769             ###########################
770             #
771             # Encapsulate a check on whether a value is good or bad
772             # handles both checking (good/bad) and setting (bad)
773             #
774             # Integer types (BSUL) + floating point when no NaN (FD)
775             # $ISBADVAR(foo,a) -> foo == a_badval
776             # $ISGOODVAR(foo,a) foo != a_badval
777             # $SETBADVAR(foo,a) foo = a_badval
778             #
779             # floating point with NaN
780             # $ISBADVAR(foo,a) -> finite(foo) == 0
781             # $ISGOODVAR(foo,a) finite(foo) != 0
782             # $SETBADVAR(foo,a) foo = PDL->bvals.Float (or .Double)
783             #
784              
785             package PDL::PP::BadVarAccess;
786 2     2   14 use Carp;
  2         18  
  2         753  
787             our @CARP_NOT;
788              
789             sub new {
790 0     0   0 my ( $type, $opcode, $var_name, $pdl_name, $parent ) = @_;
791              
792             # trying to avoid auto creation of hash elements
793 0         0 my $check = $parent->{ParObjs};
794             die "\nIt looks like you have tried a \$${opcode}() macro on an\n" .
795             " unknown piddle <$pdl_name>\n"
796 0 0 0     0 unless exists($check->{$pdl_name}) and defined($check->{$pdl_name});
797              
798 0         0 bless [$opcode, $var_name, $pdl_name], $type;
799             }
800              
801             our %ops = ( ISBAD => '==', ISGOOD => '!=', SETBAD => '=' );
802              
803             sub get_str {
804 0     0   0 my($this,$parent,$context) = @_;
805              
806 0         0 my $opcode = $this->[0];
807 0         0 my $var_name = $this->[1];
808 0         0 my $pdl_name = $this->[2];
809              
810 0 0       0 print "PDL::PP::BadVarAccess sent [$opcode] [$var_name] [$pdl_name]\n" if $::PP_VERBOSE;
811              
812 0         0 my $op = $ops{$opcode};
813 0 0       0 die "ERROR: unknown check <$opcode> sent to PDL::PP::BadVarAccess\n"
814             unless defined $op;
815              
816 0         0 my $obj = $parent->{ParObjs}{$pdl_name};
817 0 0       0 die "ERROR: something screwy in PDL::PP::BadVarAccess (PP/PDLCode.pm)\n"
818             unless defined( $obj );
819              
820 0         0 my $lhs = $var_name;
821 0         0 my $rhs = "${pdl_name}_badval";
822              
823 0         0 ( $lhs, $rhs ) =
824             PDL::PP::NaNSupport::convert( $parent, $pdl_name, $lhs, $rhs, $opcode );
825              
826 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
827 0         0 return "$lhs $op $rhs";
828             }
829              
830              
831             ###########################
832             #
833             # Encapsulate a check on whether a value is good or bad using PP
834             # handles both checking (good/bad) and setting (bad)
835              
836             # this is only an initial attempt - it will, almost certainly,
837             # need more work as more code is converted to handle bad values
838             #
839             # currently it can only handle cases like
840             # $PPISBAD(PARENT,[i]) -> PARENT_physdatap[i] == PARENT_badval
841             # etc
842             #
843             # if we use NaN's, then
844             # $PPISBAD(PARENT,[i]) -> finite(PARENT_physdatap[i]) == 0
845             # $PPISGOOD(PARENT,[i]) -> finite(PARENT_physdatap[i]) != 0
846             # $PPSETBAD(PARENT,[i]) -> PARENT_physdatap[i] = PDL->bvals.Float (or .Double)
847             #
848              
849             package PDL::PP::PPBadAccess;
850 2     2   15 use Carp;
  2         4  
  2         703  
851             our @CARP_NOT;
852              
853             sub new {
854 0     0   0 my ( $type, $opcode, $pdl_name, $inds, $parent ) = @_;
855              
856 0         0 $opcode =~ s/^PP//;
857 0         0 bless [$opcode, $pdl_name, $inds], $type;
858             }
859              
860             # PP is stripped in new()
861             our %ops = ( ISBAD => '==', ISGOOD => '!=', SETBAD => '=' );
862              
863             sub get_str {
864 0     0   0 my($this,$parent,$context) = @_;
865              
866 0         0 my $opcode = $this->[0];
867 0         0 my $name = $this->[1];
868 0         0 my $inds = $this->[2];
869              
870 0 0       0 print "PDL::PP::PPBadAccess sent [$opcode] [$name] [$inds]\n" if $::PP_VERBOSE;
871              
872 0         0 my $op = $ops{$opcode};
873 0 0       0 die "\nERROR: unknown check <$opcode> sent to PDL::PP::PPBadAccess\n"
874             unless defined $op;
875              
876 0         0 my $obj = $parent->{ParObjs}{$name};
877 0 0       0 die "\nERROR: ParObjs does not seem to exist for <$name> = problem in PDL::PP::PPBadAccess\n"
878             unless defined $obj;
879              
880 0         0 my $lhs = $obj->do_physpointeraccess() . "$inds";
881 0         0 my $rhs = "${name}_badval";
882              
883 0         0 ( $lhs, $rhs ) =
884             PDL::PP::NaNSupport::convert( $parent, $name, $lhs, $rhs, $opcode );
885              
886 0 0       0 print "DBG: [$lhs $op $rhs]\n" if $::PP_VERBOSE;
887 0         0 return "$lhs $op $rhs";
888             }
889              
890              
891             ###########################
892             #
893             # Encapsulate a check on whether the state flag of a piddle
894             # is set/change this state
895             #
896             # $PDLSTATEISBAD(a) -> ($PDL(a)->state & PDL_BADVAL) > 0
897             # $PDLSTATEISGOOD(a) -> ($PDL(a)->state & PDL_BADVAL) == 0
898             #
899             # $PDLSTATESETBAD(a) -> ($PDL(a)->state |= PDL_BADVAL)
900             # $PDLSTATESETGOOD(a) -> ($PDL(a)->state &= ~PDL_BADVAL)
901             #
902              
903             package PDL::PP::PDLStateBadAccess;
904 2     2   15 use Carp;
  2         4  
  2         757  
905             our @CARP_NOT;
906              
907             sub new {
908 0     0   0 my ( $type, $op, $val, $pdl_name, $parent ) = @_;
909              
910             # $op is one of: IS SET
911             # $val is one of: GOOD BAD
912              
913             # trying to avoid auto creation of hash elements
914 0         0 my $check = $parent->{ParObjs};
915             die "\nIt looks like you have tried a \$PDLSTATE${op}${val}() macro on an\n" .
916             " unknown piddle <$pdl_name>\n"
917 0 0 0     0 unless exists($check->{$pdl_name}) and defined($check->{$pdl_name});
918              
919 0         0 bless [$op, $val, $pdl_name], $type;
920             }
921              
922             our %ops = (
923             IS => { GOOD => '== 0', BAD => '> 0' },
924             SET => { GOOD => '&= ~', BAD => '|= ' },
925             );
926              
927             sub get_str {
928 0     0   0 my($this,$parent,$context) = @_;
929              
930 0         0 my $op = $this->[0];
931 0         0 my $val = $this->[1];
932 0         0 my $name = $this->[2];
933              
934 0 0       0 print "PDL::PP::PDLStateBadAccess sent [$op] [$val] [$name]\n" if $::PP_VERBOSE;
935              
936 0         0 my $opcode = $ops{$op}{$val};
937 0         0 my $type = $op . $val;
938 0 0       0 die "ERROR: unknown check <$type> sent to PDL::PP::PDLStateBadAccess\n"
939             unless defined $opcode;
940              
941 0         0 my $obj = $parent->{ParObjs}{$name};
942 0 0       0 die "\nERROR: ParObjs does not seem to exist for <$name> = problem in PDL::PP::PDLStateBadAccess\n"
943             unless defined $obj;
944              
945 0         0 my $state = $obj->do_pdlaccess() . "->state";
946              
947 0         0 my $str;
948 0 0       0 if ( $op eq 'IS' ) {
    0          
949 0         0 $str = "($state & PDL_BADVAL) $opcode";
950             } elsif ( $op eq 'SET' ) {
951 0         0 $str = "$state ${opcode}PDL_BADVAL";
952             }
953              
954 0 0       0 print "DBG: [$str]\n" if $::PP_VERBOSE;
955 0         0 return $str;
956             }
957              
958              
959             ###########################
960             #
961             # Encapsulate a Pointeraccess
962              
963             package PDL::PP::PointerAccess;
964 2     2   14 use Carp;
  2         5  
  2         356  
965             our @CARP_NOT;
966              
967 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
968              
969 0     0   0 sub get_str {my($this,$parent,$context) = @_;
970             croak ("can't access undefined pdl ".$this->[0])
971 0 0       0 unless defined($parent->{ParObjs}{$this->[0]});
972             # $parent->{ParObjs}{$this->[0]}->{FlagPaccess} = 1;
973 0         0 $parent->{ParObjs}{$this->[0]}->{FlagPhys} = 1;
974 0         0 $parent->{ParObjs}{$this->[0]}->do_pointeraccess();
975             }
976              
977              
978             ###########################
979             #
980             # Encapsulate a PhysPointeraccess
981              
982             package PDL::PP::PhysPointerAccess;
983 2     2   14 use Carp;
  2         19  
  2         297  
984             our @CARP_NOT;
985              
986 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
987              
988 0     0   0 sub get_str {my($this,$parent,$context) = @_;
989             $parent->{ParObjs}{$this->[0]}->do_physpointeraccess()
990 0 0       0 if defined($parent->{ParObjs}{$this->[0]});
991             }
992              
993             ###########################
994             #
995             # Encapsulate a PDLaccess
996              
997             package PDL::PP::PdlAccess;
998 2     2   14 use Carp;
  2         4  
  2         362  
999             our @CARP_NOT;
1000              
1001 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1002              
1003 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1004             croak ("can't access undefined pdl ".$this->[0])
1005 0 0       0 unless defined($parent->{ParObjs}{$this->[0]});
1006 0         0 $parent->{ParObjs}{$this->[0]}->do_pdlaccess();
1007             }
1008              
1009             ###########################
1010             #
1011             # Encapsulate a macroaccess
1012              
1013             package PDL::PP::MacroAccess;
1014 2     2   14 use Carp;
  2         3  
  2         114  
1015 2     2   12 use PDL::Types ':All';
  2         4  
  2         1388  
1016             my $types = join '',ppdefs;
1017             our @CARP_NOT;
1018              
1019 0     0   0 sub new { my($type,$pdl,$inds,$gentypes,$name) = @_;
1020 0 0       0 $pdl =~ /^\s*T([A-Z]+)\s*$/ or confess("Macroaccess wrong: $pdl\n");
1021 0         0 my @ilst = split '',$1;
1022 0         0 for my $gt (@$gentypes) {
1023             warn "$name has no Macro for generic type $gt (has $pdl)\n"
1024 0 0       0 unless grep {$gt eq $_} @ilst }
  0         0  
1025 0         0 for my $mtype (@ilst) {
1026             warn "Macro for unsupported generic type identifier $mtype".
1027             " (probably harmless)\n"
1028 0 0       0 unless grep {$mtype eq $_} @$gentypes;
  0         0  
1029             }
1030 0         0 return bless [$pdl,$inds,$name],
1031             $type; }
1032              
1033 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1034 0         0 my ($pdl,$inds,$name) = @{$this};
  0         0  
1035 0 0       0 $pdl =~ /^\s*T([A-Z]+)\s*$/
1036             or confess("Macroaccess wrong in $name (allowed types $types): was '$pdl'\n");
1037 0         0 my @lst = split ',',$inds;
1038 0         0 my @ilst = split '',$1;
1039 0 0       0 if($#lst != $#ilst) {confess("Macroaccess: different nos of args $pdl $inds\n");}
  0         0  
1040             croak "generic type access outside a generic loop in $name"
1041 0 0       0 unless defined $parent->{Gencurtype}->[-1];
1042 0         0 my $type = mapfld $parent->{Gencurtype}->[-1], 'ctype' => 'ppsym';
1043             # print "Type access: $type\n";
1044 0 0       0 croak "unknown Type in $name (generic type currently $parent->{Gencurtype}->[-1]"
1045             unless defined $type;
1046 0         0 for (0..$#lst) {
1047 0 0       0 return "$lst[$_]" if $ilst[$_] =~ /$type/;
1048             }
1049             }
1050              
1051              
1052             ###########################
1053             #
1054             # Encapsulate a SizeAccess
1055              
1056             package PDL::PP::SizeAccess;
1057 2     2   14 use Carp;
  2         4  
  2         425  
1058             our @CARP_NOT;
1059              
1060 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1061              
1062 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1063             croak "can't get SIZE of undefined dimension $this->[0]"
1064 0 0       0 unless defined($parent->{IndObjs}{$this->[0]});
1065 0         0 $parent->{IndObjs}{$this->[0]}->get_size();
1066             }
1067              
1068             ###########################
1069             #
1070             # Encapsulate a ReSizeAccess
1071              
1072             package PDL::PP::ReSizeAccess;
1073 2     2   14 use Carp;
  2         4  
  2         699  
1074             our @CARP_NOT;
1075              
1076 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1077              
1078 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1079 0 0       0 $this->[0] =~ /^([^,]+),([^,]+)$/ or
1080             croak "Can't interpret resize str $this->[0]";
1081             croak "can't RESIZE undefined dimension $1"
1082 0 0       0 unless defined($parent->{IndObjs}{$1});
1083              
1084 0         0 my $s = $parent->{IndObjs}{$1}->get_size();
1085              
1086             # XXX NOTE: All piddles must be output piddles, there must not be
1087             # a loop over this var (at all!) etc. Should check for these,
1088             # this is why not yet documented.
1089             # FURTHER NOTE: RESIZE DOESN'T COPY DATA PROPERLY!
1090              
1091 0         0 my($ord,$pdls) = $parent->get_pdls();
1092 0         0 my @p;
1093              
1094 0         0 for(@$ord) {
1095             push @p, $_
1096 0 0       0 if $pdls->{$_}->has_dim($1);
1097             }
1098 0         0 print "RESIZEACC: $1 $2, (",(join ',',@p),")\n";
1099 0         0 warn "RESIZE USED: DO YOU KNOW WHAT YOU ARE DOING???\n";
1100              
1101 0         0 return "$s = $2; ".(join '',map {$pdls->{$_}->do_resize($1,$2)} @p);
  0         0  
1102             }
1103              
1104              
1105             ###########################
1106             #
1107             # Encapsulate a GentypeAccess
1108              
1109             package PDL::PP::GentypeAccess;
1110 2     2   15 use Carp;
  2         5  
  2         416  
1111             our @CARP_NOT;
1112              
1113 0     0   0 sub new { my($type,$pdl,$inds) = @_; bless [$inds],$type; }
  0         0  
1114              
1115 0     0   0 sub get_str {my($this,$parent,$context) = @_;
1116             croak "generic type access outside a generic loop"
1117 0 0       0 unless defined $parent->{Gencurtype}->[-1];
1118 0         0 my $type = $parent->{Gencurtype}->[-1];
1119 0 0       0 if ($this->[0]) {
1120             croak "not a defined name"
1121 0 0       0 unless defined($parent->{ParObjs}{$this->[0]});
1122 0         0 $type = $parent->{ParObjs}{$this->[0]}->ctype($type);
1123             }
1124 0         0 return $type;
1125             }
1126              
1127             ########################
1128             #
1129             # Type coercion
1130             #
1131             # Now, if TYPES:F given and double arguments, will coerce.
1132              
1133             package PDL::PP::TypeConv;
1134              
1135             # make the typetable from info in PDL::Types
1136 2     2   15 use PDL::Types ':All';
  2         4  
  2         819  
1137             my @typetable = map {[$typehash{$_}->{ppsym},
1138             $typehash{$_}->{ctype},
1139             $typehash{$_}->{numval},
1140             ]} typesrtkeys;
1141              
1142 0     0   0 sub print_xscoerce { my($this) = @_;
1143 0         0 $this->printxs("\t__priv->datatype=PDL_B;\n");
1144             # First, go through all the types, selecting the most general.
1145 0         0 for(@{$this->{PdlOrder}}) {
  0         0  
1146 0         0 $this->printxs($this->{Pdls}{$_}->get_xsdatatypetest());
1147             }
1148             # See which types we are allowed to use.
1149 0         0 $this->printxs("\tif(0) {}\n");
1150 0         0 for(@{$this->get_generictypes()}) {
  0         0  
1151 0         0 $this->printxs("\telse if(__priv->datatype <= $_->[2]) __priv->datatype = $_->[2];\n");
1152             }
1153 0 0       0 $this->{Types} =~ /F/ and (
1154             $this->printxs("\telse if(__priv->datatype == PDL_D) {__priv->datatype = PDL_F; PDL_COMMENT(\"Cast double to float\")}\n"));
1155 0         0 $this->printxs(qq[\telse {croak("Too high type \%d given!\\n",__priv->datatype);}]);
1156             # Then, coerce everything to this type.
1157 0         0 for(@{$this->{PdlOrder}}) {
  0         0  
1158 0         0 $this->printxs($this->{Pdls}{$_}->get_xscoerce());
1159             }
1160             }
1161             # XXX Should use PDL::Core::Dev;
1162              
1163 2     2   15 no strict 'vars';
  2         5  
  2         2778  
1164              
1165             # STATIC!
1166 2     2 0 3 sub PDL::PP::get_generictyperecs { my($types) = @_;
1167 2         2 my $foo;
1168 2         4 return [map {$foo = $_;
  20         22  
1169 20 100       21 ( grep {/$foo->[0]/} (@$types) ) ?
  40         191  
1170             [mapfld($_->[0],'ppsym'=>'sym'),$_->[1],$_->[2],$_->[0]]
1171             : ()
1172             }
1173             @typetable];
1174             }
1175              
1176 0     0   0 sub xxx_get_generictypes { my($this) = @_;
1177             return [map {
1178 0 0       0 $this->{Types} =~ /$_->[0]/ ? [mapfld($_->[0],'ppsym'=>'sym'),$_->[1],$_->[2],$_->[0]] : ()
  0         0  
1179             }
1180             @typetable];
1181             }
1182              
1183              
1184             package PDL::PP::Code;
1185              
1186             # my ( $threadloops, $coderef, $sizeprivs ) = $this->separate_code( $code );
1187             #
1188             # umm, can't call classes defined later on in code ...
1189             # hence moved to end of file
1190             # (rather ugly...)
1191             #
1192             # XXX The above statement is almost certainly false. This module is parsed
1193             # before separate_code is ever called, so all of the class definitions
1194             # should exist. -- David Mertens, Dec 2 2011
1195             #
1196             # separates the code into an array of C fragments (strings),
1197             # variable references (strings starting with $) and
1198             # loops (array references, 1. item = variable.
1199             #
1200             sub separate_code {
1201             ## $DB::single=1;
1202 3     3   7 my ( $this, $code ) = @_;
1203              
1204             # First check for standard code errors:
1205 3         10 catch_code_errors($code);
1206              
1207 2         9 my $coderef = new PDL::PP::Block;
1208              
1209 2         3 my @stack = ($coderef);
1210 2         3 my $threadloops = 0;
1211 2         3 my $sizeprivs = {};
1212              
1213 2         3 local $_ = $code;
1214             ## print "Code to parse = [$_]\n" if $::PP_VERBOSE;
1215 2         5 while($_) {
1216             # Parse next statement
1217              
1218             # I'm not convinced that having the checks twice is a good thing,
1219             # since it makes it easy (for me at least) to forget to update one
1220             # of them
1221              
1222 8 50       139 s/^(.*?) # First, some noise is allowed. This may be bad.
1223             ( \$(ISBAD|ISGOOD|SETBAD)\s*\(\s*\$?[a-zA-Z_]\w*\s*\([^)]*\)\s*\) # $ISBAD($a(..)), ditto for ISGOOD and SETBAD
1224             |\$PP(ISBAD|ISGOOD|SETBAD)\s*\(\s*[a-zA-Z_]\w*\s*,\s*[^)]*\s*\) # $PPISBAD(CHILD,[1]) etc
1225             ### |\$STATE(IS|SET)(BAD|GOOD)\s*\(\s*[^)]*\s*\) # $STATEISBAD(a) etc
1226             |\$PDLSTATE(IS|SET)(BAD|GOOD)\s*\(\s*[^)]*\s*\) # $PDLSTATEISBAD(a) etc
1227             |\$[a-zA-Z_]\w*\s*\([^)]*\) # $a(...): access
1228             |\bloop\s*\([^)]+\)\s*%\{ # loop(..) %{
1229             |\btypes\s*\([^)]+\)\s*%\{ # types(..) %{
1230             |\bthreadloop\s*%\{ # threadloop %{
1231             |%} # %}
1232             |$)//xs
1233             or confess("Invalid program $_");
1234 8         17 my $control = $2;
1235             # Store the user code.
1236             # Some day we shall parse everything.
1237 8         9 push @{$stack[-1]},$1;
  8         24  
1238              
1239 8 50       15 if ( $control =~ /^\$STATE/ ) { print "\nDBG: - got [$control]\n\n"; }
  0         0  
1240              
1241             # Then, our control.
1242 8 100       14 if($control) {
1243 6 100       37 if($control =~ /^loop\s*\(([^)]+)\)\s*%\{/) {
    50          
    100          
    50          
    50          
    50          
    50          
    100          
    50          
1244 1         10 my $ob = new PDL::PP::Loop([split ',',$1],
1245             $sizeprivs,$this);
1246 1 50       3 print "SIZEPRIVSXX: $sizeprivs,",(join ',',%$sizeprivs),"\n" if $::PP_VERBOSE;
1247 1         2 push @{$stack[-1]},$ob;
  1         2  
1248 1         2 push @stack,$ob;
1249             } elsif($control =~ /^types\s*\(([^)]+)\)\s*%\{/) {
1250 0         0 my $ob = new PDL::PP::Types($1,$this);
1251 0         0 push @{$stack[-1]},$ob;
  0         0  
1252 0         0 push @stack,$ob;
1253             } elsif($control =~ /^threadloop\s*%\{/) {
1254 1         7 my $ob = new PDL::PP::ThreadLoop();
1255 1         2 push @{$stack[-1]},$ob;
  1         3  
1256 1         2 push @stack,$ob;
1257 1         3 $threadloops ++;
1258             } elsif($control =~ /^\$PP(ISBAD|ISGOOD|SETBAD)\s*\(\s*([a-zA-Z_]\w*)\s*,\s*([^)]*)\s*\)/) {
1259 0         0 push @{$stack[-1]},new PDL::PP::PPBadAccess($1,$2,$3,$this);
  0         0  
1260             } elsif($control =~ /^\$(ISBAD|ISGOOD|SETBAD)VAR\s*\(\s*([^)]*)\s*,\s*([^)]*)\s*\)/) {
1261 0         0 push @{$stack[-1]},new PDL::PP::BadVarAccess($1,$2,$3,$this);
  0         0  
1262             } elsif($control =~ /^\$(ISBAD|ISGOOD|SETBAD)\s*\(\s*\$?([a-zA-Z_]\w*)\s*\(([^)]*)\)\s*\)/) {
1263 0         0 push @{$stack[-1]},new PDL::PP::BadAccess($1,$2,$3,$this);
  0         0  
1264             # } elsif($control =~ /^\$STATE(IS|SET)(BAD|GOOD)\s*\(\s*([^)]*)\s*\)/) {
1265             # push @{$stack[-1]},new PDL::PP::StateBadAccess($1,$2,$3,$this);
1266             } elsif($control =~ /^\$PDLSTATE(IS|SET)(BAD|GOOD)\s*\(\s*([^)]*)\s*\)/) {
1267 0         0 push @{$stack[-1]},new PDL::PP::PDLStateBadAccess($1,$2,$3,$this);
  0         0  
1268             } elsif($control =~ /^\$[a-zA-Z_]\w*\s*\([^)]*\)/) {
1269 2         4 push @{$stack[-1]},new PDL::PP::Access($control,$this);
  2         8  
1270             } elsif($control =~ /^%}/) {
1271 2         5 pop @stack;
1272             } else {
1273 0         0 confess("Invalid control: $control\n");
1274             }
1275             } else {
1276 2 50       11 print("No \$2!\n") if $::PP_VERBOSE;
1277             }
1278             } # while: $_
1279              
1280 2         7 return ( $threadloops, $coderef, $sizeprivs );
1281              
1282             } # sub: separate_code()
1283              
1284             # This is essentially a collection of regexes that look for standard code
1285             # errors and croaks with an explanation if they are found.
1286             sub catch_code_errors {
1287 3     3   6 my $code_string = shift;
1288              
1289             # Look for constructs like
1290             # loop %{
1291             # which is invalid - you need to specify the dimension over which it
1292             # should loop
1293 3 100       32 report_error('Expected dimension name after "loop" and before "%{"', $1)
1294             if $code_string =~ /(.*\bloop\s*%\{)/s;
1295              
1296             }
1297              
1298             # Report an error as precisely as possible. If they have #line directives
1299             # in the code string, use that in the reporting; otherwise, use standard
1300             # Carp mechanisms
1301             my $line_re = qr/#\s*line\s+(\d+)\s+"([^"]*)"/;
1302             sub report_error {
1303 1     1   5 my ($message, $code) = @_;
1304              
1305             # Just croak if they didn't supply a #line directive:
1306 1 50       334 croak($message) if $code !~ $line_re;
1307              
1308             # Find the line at which the error occurred:
1309 0           my $line = 0;
1310 0           my $filename;
1311 0           LINE: foreach (split /\n/, $code) {
1312 0           $line++;
1313 0 0         if (/$line_re/) {
1314 0           $line = $1;
1315 0           $filename = $2;
1316             }
1317             }
1318              
1319 0           die "$message at $filename line $line\n";
1320             }
1321              
1322             # return true
1323             1;