File Coverage

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